aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAlexander Strizhakov <alex.strizhakov@gmail.com>2019-05-13 18:35:45 +0000
committerkaniini <nenolod@gmail.com>2019-05-13 18:35:45 +0000
commita2be420f940fb8f181feeb9b0fb9759d433dcae1 (patch)
tree523d1f1cfa399f4ee6d841ba3098ecd87d8e67e7 /lib
parent5a4d55cf910f85b07f111972647a8b4410b5eb6b (diff)
downloadpleroma-a2be420f940fb8f181feeb9b0fb9759d433dcae1.tar.gz
differences_in_mastoapi_responses.md: fullname & bio are optionnal
[ci skip]
Diffstat (limited to 'lib')
-rw-r--r--lib/mix/tasks/pleroma/user.ex2
-rw-r--r--lib/pleroma/plugs/oauth_plug.ex48
-rw-r--r--lib/pleroma/plugs/rate_limit_plug.ex36
-rw-r--r--lib/pleroma/user.ex11
-rw-r--r--lib/pleroma/user/info.ex30
-rw-r--r--lib/pleroma/web/admin_api/admin_api_controller.ex2
-rw-r--r--lib/pleroma/web/auth/pleroma_authenticator.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex57
-rw-r--r--lib/pleroma/web/oauth/app.ex1
-rw-r--r--lib/pleroma/web/oauth/authorization.ex39
-rw-r--r--lib/pleroma/web/oauth/oauth_controller.ex22
-rw-r--r--lib/pleroma/web/oauth/token.ex11
-rw-r--r--lib/pleroma/web/router.ex2
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api.ex29
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api_controller.ex2
15 files changed, 239 insertions, 55 deletions
diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex
index 6a83a8c0d..d130ff8c9 100644
--- a/lib/mix/tasks/pleroma/user.ex
+++ b/lib/mix/tasks/pleroma/user.ex
@@ -138,7 +138,7 @@ defmodule Mix.Tasks.Pleroma.User do
bio: bio
}
- changeset = User.register_changeset(%User{}, params, confirmed: true)
+ changeset = User.register_changeset(%User{}, params, need_confirmation: false)
{:ok, _user} = User.register(changeset)
Mix.shell().info("User #{nickname} created")
diff --git a/lib/pleroma/plugs/oauth_plug.ex b/lib/pleroma/plugs/oauth_plug.ex
index 9d43732eb..86bc4aa3a 100644
--- a/lib/pleroma/plugs/oauth_plug.ex
+++ b/lib/pleroma/plugs/oauth_plug.ex
@@ -8,6 +8,7 @@ defmodule Pleroma.Plugs.OAuthPlug do
alias Pleroma.Repo
alias Pleroma.User
+ alias Pleroma.Web.OAuth.App
alias Pleroma.Web.OAuth.Token
@realm_reg Regex.compile!("Bearer\:?\s+(.*)$", "i")
@@ -22,18 +23,39 @@ defmodule Pleroma.Plugs.OAuthPlug do
|> assign(:token, token_record)
|> assign(:user, user)
else
- _ -> conn
+ _ ->
+ # token found, but maybe only with app
+ with {:ok, app, token_record} <- fetch_app_and_token(access_token) do
+ conn
+ |> assign(:token, token_record)
+ |> assign(:app, app)
+ else
+ _ -> conn
+ end
end
end
def call(conn, _) do
- with {:ok, token_str} <- fetch_token_str(conn),
- {:ok, user, token_record} <- fetch_user_and_token(token_str) do
- conn
- |> assign(:token, token_record)
- |> assign(:user, user)
- else
- _ -> conn
+ case fetch_token_str(conn) do
+ {:ok, token} ->
+ with {:ok, user, token_record} <- fetch_user_and_token(token) do
+ conn
+ |> assign(:token, token_record)
+ |> assign(:user, user)
+ else
+ _ ->
+ # token found, but maybe only with app
+ with {:ok, app, token_record} <- fetch_app_and_token(token) do
+ conn
+ |> assign(:token, token_record)
+ |> assign(:app, app)
+ else
+ _ -> conn
+ end
+ end
+
+ _ ->
+ conn
end
end
@@ -54,6 +76,16 @@ defmodule Pleroma.Plugs.OAuthPlug do
end
end
+ @spec fetch_app_and_token(String.t()) :: {:ok, App.t(), Token.t()} | nil
+ defp fetch_app_and_token(token) do
+ query =
+ from(t in Token, where: t.token == ^token, join: app in assoc(t, :app), preload: [app: app])
+
+ with %Token{app: app} = token_record <- Repo.one(query) do
+ {:ok, app, token_record}
+ end
+ end
+
# Gets token from session by :oauth_token key
#
@spec fetch_token_from_session(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()}
diff --git a/lib/pleroma/plugs/rate_limit_plug.ex b/lib/pleroma/plugs/rate_limit_plug.ex
new file mode 100644
index 000000000..466f64a79
--- /dev/null
+++ b/lib/pleroma/plugs/rate_limit_plug.ex
@@ -0,0 +1,36 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Plugs.RateLimitPlug do
+ import Phoenix.Controller, only: [json: 2]
+ import Plug.Conn
+
+ def init(opts), do: opts
+
+ def call(conn, opts) do
+ enabled? = Pleroma.Config.get([:app_account_creation, :enabled])
+
+ case check_rate(conn, Map.put(opts, :enabled, enabled?)) do
+ {:ok, _count} -> conn
+ {:error, _count} -> render_error(conn)
+ %Plug.Conn{} = conn -> conn
+ end
+ end
+
+ defp check_rate(conn, %{enabled: true} = opts) do
+ max_requests = opts[:max_requests]
+ bucket_name = conn.remote_ip |> Tuple.to_list() |> Enum.join(".")
+
+ ExRated.check_rate(bucket_name, opts[:interval] * 1000, max_requests)
+ end
+
+ defp check_rate(conn, _), do: conn
+
+ defp render_error(conn) do
+ conn
+ |> put_status(:forbidden)
+ |> json(%{error: "Rate limit exceeded."})
+ |> halt()
+ end
+end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 427400aa1..474de9ba5 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -204,14 +204,15 @@ defmodule Pleroma.User do
end
def register_changeset(struct, params \\ %{}, opts \\ []) do
- confirmation_status =
- if opts[:confirmed] || !Pleroma.Config.get([:instance, :account_activation_required]) do
- :confirmed
+ need_confirmation? =
+ if is_nil(opts[:need_confirmation]) do
+ Pleroma.Config.get([:instance, :account_activation_required])
else
- :unconfirmed
+ opts[:need_confirmation]
end
- info_change = User.Info.confirmation_changeset(%User.Info{}, confirmation_status)
+ info_change =
+ User.Info.confirmation_changeset(%User.Info{}, need_confirmation: need_confirmation?)
changeset =
struct
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index 1b81619ce..5a50ee639 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -8,6 +8,8 @@ defmodule Pleroma.User.Info do
alias Pleroma.User.Info
+ @type t :: %__MODULE__{}
+
embedded_schema do
field(:banner, :map, default: %{})
field(:background, :map, default: %{})
@@ -210,21 +212,23 @@ defmodule Pleroma.User.Info do
])
end
- def confirmation_changeset(info, :confirmed) do
- confirmation_changeset(info, %{
- confirmation_pending: false,
- confirmation_token: nil
- })
- end
+ @spec confirmation_changeset(Info.t(), keyword()) :: Ecto.Changerset.t()
+ def confirmation_changeset(info, opts) do
+ need_confirmation? = Keyword.get(opts, :need_confirmation)
- def confirmation_changeset(info, :unconfirmed) do
- confirmation_changeset(info, %{
- confirmation_pending: true,
- confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64()
- })
- end
+ params =
+ if need_confirmation? do
+ %{
+ confirmation_pending: true,
+ confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64()
+ }
+ else
+ %{
+ confirmation_pending: false,
+ confirmation_token: nil
+ }
+ end
- def confirmation_changeset(info, params) do
cast(info, params, [:confirmation_pending, :confirmation_token])
end
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index b553d96a8..e00b33aba 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -59,7 +59,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
bio: "."
}
- changeset = User.register_changeset(%User{}, user_data, confirmed: true)
+ changeset = User.register_changeset(%User{}, user_data, need_confirmation: false)
{:ok, user} = User.register(changeset)
conn
diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex
index dd79cdcf7..c4a6fce08 100644
--- a/lib/pleroma/web/auth/pleroma_authenticator.ex
+++ b/lib/pleroma/web/auth/pleroma_authenticator.ex
@@ -74,7 +74,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
password_confirmation: random_password
},
external: true,
- confirmed: true
+ need_confirmation: false
)
|> Repo.insert(),
{:ok, _} <-
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index fd595031d..defd88a44 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -39,12 +39,22 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
alias Pleroma.Web.OAuth.Authorization
alias Pleroma.Web.OAuth.Scopes
alias Pleroma.Web.OAuth.Token
+ alias Pleroma.Web.TwitterAPI.TwitterAPI
alias Pleroma.Web.ControllerHelper
import Ecto.Query
require Logger
+ plug(
+ Pleroma.Plugs.RateLimitPlug,
+ %{
+ max_requests: Config.get([:app_account_creation, :max_requests]),
+ interval: Config.get([:app_account_creation, :interval])
+ }
+ when action in [:account_register]
+ )
+
@httpoison Application.get_env(:pleroma, :httpoison)
@local_mastodon_name "Mastodon-Local"
@@ -1693,6 +1703,53 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
+ def account_register(
+ %{assigns: %{app: app}} = conn,
+ %{"username" => nickname, "email" => _, "password" => _, "agreement" => true} = params
+ ) do
+ params =
+ params
+ |> Map.take([
+ "email",
+ "captcha_solution",
+ "captcha_token",
+ "captcha_answer_data",
+ "token",
+ "password"
+ ])
+ |> Map.put("nickname", nickname)
+ |> Map.put("fullname", params["fullname"] || nickname)
+ |> Map.put("bio", params["bio"] || "")
+ |> Map.put("confirm", params["password"])
+
+ with {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
+ {:ok, token} <- Token.create_token(app, user, %{scopes: app.scopes}) do
+ json(conn, %{
+ token_type: "Bearer",
+ access_token: token.token,
+ scope: app.scopes,
+ created_at: Token.Utils.format_created_at(token)
+ })
+ else
+ {:error, errors} ->
+ conn
+ |> put_status(400)
+ |> json(Jason.encode!(errors))
+ end
+ end
+
+ def account_register(%{assigns: %{app: _app}} = conn, _params) do
+ conn
+ |> put_status(400)
+ |> json(%{error: "Missing parameters"})
+ end
+
+ def account_register(conn, _) do
+ conn
+ |> put_status(403)
+ |> json(%{error: "Invalid credentials"})
+ end
+
def conversations(%{assigns: %{user: user}} = conn, params) do
participations = Participation.for_user_with_last_activity_id(user, params)
diff --git a/lib/pleroma/web/oauth/app.ex b/lib/pleroma/web/oauth/app.ex
index bccc2ac96..ddcdb1871 100644
--- a/lib/pleroma/web/oauth/app.ex
+++ b/lib/pleroma/web/oauth/app.ex
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.OAuth.App do
import Ecto.Changeset
@type t :: %__MODULE__{}
+
schema "apps" do
field(:client_name, :string)
field(:redirect_uris, :string)
diff --git a/lib/pleroma/web/oauth/authorization.ex b/lib/pleroma/web/oauth/authorization.ex
index ca3901cc4..b47688de1 100644
--- a/lib/pleroma/web/oauth/authorization.ex
+++ b/lib/pleroma/web/oauth/authorization.ex
@@ -14,6 +14,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
import Ecto.Query
@type t :: %__MODULE__{}
+
schema "oauth_authorizations" do
field(:token, :string)
field(:scopes, {:array, :string}, default: [])
@@ -25,28 +26,45 @@ defmodule Pleroma.Web.OAuth.Authorization do
timestamps()
end
+ @spec create_authorization(App.t(), User.t() | %{}, [String.t()] | nil) ::
+ {:ok, Authorization.t()} | {:error, Changeset.t()}
def create_authorization(%App{} = app, %User{} = user, scopes \\ nil) do
- scopes = scopes || app.scopes
- token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)
-
- authorization = %Authorization{
- token: token,
- used: false,
+ %{
+ scopes: scopes || app.scopes,
user_id: user.id,
- app_id: app.id,
- scopes: scopes,
- valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10)
+ app_id: app.id
}
+ |> create_changeset()
+ |> Repo.insert()
+ end
+
+ @spec create_changeset(map()) :: Changeset.t()
+ def create_changeset(attrs \\ %{}) do
+ %Authorization{}
+ |> cast(attrs, [:user_id, :app_id, :scopes, :valid_until])
+ |> validate_required([:app_id, :scopes])
+ |> add_token()
+ |> add_lifetime()
+ end
+
+ defp add_token(changeset) do
+ token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)
+ put_change(changeset, :token, token)
+ end
- Repo.insert(authorization)
+ defp add_lifetime(changeset) do
+ put_change(changeset, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10))
end
+ @spec use_changeset(Authtorizatiton.t(), map()) :: Changeset.t()
def use_changeset(%Authorization{} = auth, params) do
auth
|> cast(params, [:used])
|> validate_required([:used])
end
+ @spec use_token(Authorization.t()) ::
+ {:ok, Authorization.t()} | {:error, Changeset.t()} | {:error, String.t()}
def use_token(%Authorization{used: false, valid_until: valid_until} = auth) do
if NaiveDateTime.diff(NaiveDateTime.utc_now(), valid_until) < 0 do
Repo.update(use_changeset(auth, %{used: true}))
@@ -57,6 +75,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
def use_token(%Authorization{used: true}), do: {:error, "already used"}
+ @spec delete_user_authorizations(User.t()) :: {integer(), any()}
def delete_user_authorizations(%User{id: user_id}) do
from(
a in Pleroma.Web.OAuth.Authorization,
diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex
index 8ee0da667..862b8f8c9 100644
--- a/lib/pleroma/web/oauth/oauth_controller.ex
+++ b/lib/pleroma/web/oauth/oauth_controller.ex
@@ -218,6 +218,28 @@ defmodule Pleroma.Web.OAuth.OAuthController do
token_exchange(conn, params)
end
+ def token_exchange(conn, %{"grant_type" => "client_credentials"} = params) do
+ with %App{} = app <- get_app_from_request(conn, params),
+ {:ok, auth} <- Authorization.create_authorization(app, %User{}),
+ {:ok, token} <- Token.exchange_token(app, auth),
+ {:ok, inserted_at} <- DateTime.from_naive(token.inserted_at, "Etc/UTC") do
+ response = %{
+ token_type: "Bearer",
+ access_token: token.token,
+ refresh_token: token.refresh_token,
+ created_at: DateTime.to_unix(inserted_at),
+ expires_in: 60 * 10,
+ scope: Enum.join(token.scopes, " ")
+ }
+
+ json(conn, response)
+ else
+ _error ->
+ put_status(conn, 400)
+ |> json(%{error: "Invalid credentials"})
+ end
+ end
+
# Bad request
def token_exchange(conn, params), do: bad_request(conn, params)
diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/oauth/token.ex
index 4e5d1d118..ef047d565 100644
--- a/lib/pleroma/web/oauth/token.ex
+++ b/lib/pleroma/web/oauth/token.ex
@@ -45,12 +45,16 @@ defmodule Pleroma.Web.OAuth.Token do
|> Repo.find_resource()
end
+ @spec exchange_token(App.t(), Authorization.t()) ::
+ {:ok, Token.t()} | {:error, Changeset.t()}
def exchange_token(app, auth) do
with {:ok, auth} <- Authorization.use_token(auth),
true <- auth.app_id == app.id do
+ user = if auth.user_id, do: User.get_cached_by_id(auth.user_id), else: %User{}
+
create_token(
app,
- User.get_cached_by_id(auth.user_id),
+ user,
%{scopes: auth.scopes}
)
end
@@ -81,12 +85,13 @@ defmodule Pleroma.Web.OAuth.Token do
|> validate_required([:valid_until])
end
+ @spec create_token(App.t(), User.t(), map()) :: {:ok, Token} | {:error, Changeset.t()}
def create_token(%App{} = app, %User{} = user, attrs \\ %{}) do
%__MODULE__{user_id: user.id, app_id: app.id}
|> cast(%{scopes: attrs[:scopes] || app.scopes}, [:scopes])
- |> validate_required([:scopes, :user_id, :app_id])
+ |> validate_required([:scopes, :app_id])
|> put_valid_until(attrs)
- |> put_token
+ |> put_token()
|> put_refresh_token(attrs)
|> Repo.insert()
end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 8b84fbbad..51146d010 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -385,6 +385,8 @@ defmodule Pleroma.Web.Router do
scope "/api/v1", Pleroma.Web.MastodonAPI do
pipe_through(:api)
+ post("/accounts", MastodonAPIController, :account_register)
+
get("/instance", MastodonAPIController, :masto_instance)
get("/instance/peers", MastodonAPIController, :peers)
post("/apps", MastodonAPIController, :create_app)
diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex
index 3a7774647..1362ef57c 100644
--- a/lib/pleroma/web/twitter_api/twitter_api.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api.ex
@@ -128,7 +128,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end
end
- def register_user(params) do
+ def register_user(params, opts \\ []) do
token = params["token"]
params = %{
@@ -162,13 +162,22 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
# I have no idea how this error handling works
{:error, %{error: Jason.encode!(%{captcha: [error]})}}
else
- registrations_open = Pleroma.Config.get([:instance, :registrations_open])
- registration_process(registrations_open, params, token)
+ registration_process(
+ params,
+ %{
+ registrations_open: Pleroma.Config.get([:instance, :registrations_open]),
+ token: token
+ },
+ opts
+ )
end
end
- defp registration_process(registration_open, params, token)
- when registration_open == false or is_nil(registration_open) do
+ defp registration_process(params, %{registrations_open: true}, opts) do
+ create_user(params, opts)
+ end
+
+ defp registration_process(params, %{token: token}, opts) do
invite =
unless is_nil(token) do
Repo.get_by(UserInviteToken, %{token: token})
@@ -182,19 +191,15 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
invite when valid_invite? ->
UserInviteToken.update_usage!(invite)
- create_user(params)
+ create_user(params, opts)
_ ->
{:error, "Expired token"}
end
end
- defp registration_process(true, params, _token) do
- create_user(params)
- end
-
- defp create_user(params) do
- changeset = User.register_changeset(%User{}, params)
+ defp create_user(params, opts) do
+ changeset = User.register_changeset(%User{}, params, opts)
case User.register(changeset) do
{:ok, user} ->
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
index 21e6c555a..3c5a70be9 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -440,7 +440,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
true <- user.local,
true <- user.info.confirmation_pending,
true <- user.info.confirmation_token == token,
- info_change <- User.Info.confirmation_changeset(user.info, :confirmed),
+ info_change <- User.Info.confirmation_changeset(user.info, need_confirmation: false),
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_change),
{:ok, _} <- User.update_and_set_cache(changeset) do
conn