diff options
| -rw-r--r-- | lib/pleroma/web/controller_helper.ex | 6 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 3 | ||||
| -rw-r--r-- | lib/pleroma/web/oauth.ex | 14 | ||||
| -rw-r--r-- | lib/pleroma/web/oauth/oauth_controller.ex | 31 | ||||
| -rw-r--r-- | lib/pleroma/web/oauth/scopes.ex | 67 | 
5 files changed, 86 insertions, 35 deletions
diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index 181483664..55706eeb8 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -10,12 +10,6 @@ defmodule Pleroma.Web.ControllerHelper do    def truthy_param?(blank_value) when blank_value in [nil, ""], do: nil    def truthy_param?(value), do: value not in @falsy_param_values -  def oauth_scopes(params, default) do -    # Note: `scopes` is used by Mastodon — supporting it but sticking to -    # OAuth's standard `scope` wherever we control it -    Pleroma.Web.OAuth.parse_scopes(params["scope"] || params["scopes"], default) -  end -    def json_response(conn, status, json) do      conn      |> put_status(status) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 83ad90989..956736780 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -37,6 +37,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    alias Pleroma.Web.MediaProxy    alias Pleroma.Web.OAuth.App    alias Pleroma.Web.OAuth.Authorization +  alias Pleroma.Web.OAuth.Scopes    alias Pleroma.Web.OAuth.Token    alias Pleroma.Web.ControllerHelper @@ -50,7 +51,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    action_fallback(:errors)    def create_app(conn, params) do -    scopes = ControllerHelper.oauth_scopes(params, ["read"]) +    scopes = Scopes.fetch_scopes(params, ["read"])      app_attrs =        params diff --git a/lib/pleroma/web/oauth.ex b/lib/pleroma/web/oauth.ex index d2835a0ba..280cf28c0 100644 --- a/lib/pleroma/web/oauth.ex +++ b/lib/pleroma/web/oauth.ex @@ -3,18 +3,4 @@  # SPDX-License-Identifier: AGPL-3.0-only  defmodule Pleroma.Web.OAuth do -  def parse_scopes(scopes, _default) when is_list(scopes) do -    Enum.filter(scopes, &(&1 not in [nil, ""])) -  end - -  def parse_scopes(scopes, default) when is_binary(scopes) do -    scopes -    |> String.trim() -    |> String.split(~r/[\s,]+/) -    |> parse_scopes(default) -  end - -  def parse_scopes(_, default) do -    default -  end  end diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index e3c01217d..8ee0da667 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -15,8 +15,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do    alias Pleroma.Web.OAuth.Token    alias Pleroma.Web.OAuth.Token.Strategy.RefreshToken    alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken - -  import Pleroma.Web.ControllerHelper, only: [oauth_scopes: 2] +  alias Pleroma.Web.OAuth.Scopes    if Pleroma.Config.oauth_consumer_enabled?(), do: plug(Ueberauth) @@ -57,7 +56,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do    defp do_authorize(conn, params) do      app = Repo.get_by(App, client_id: params["client_id"])      available_scopes = (app && app.scopes) || [] -    scopes = oauth_scopes(params, nil) || available_scopes +    scopes = Scopes.fetch_scopes(params, available_scopes)      # Note: `params` might differ from `conn.params`; use `@params` not `@conn.params` in template      render(conn, Authenticator.auth_template(), %{ @@ -113,7 +112,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do    defp handle_create_authorization_error(           conn, -         {scopes_issue, _}, +         {:error, scopes_issue},           %{"authorization" => _} = params         )         when scopes_issue in [:unsupported_scopes, :missing_scopes] do @@ -184,9 +183,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do           %App{} = app <- get_app_from_request(conn, params),           {:auth_active, true} <- {:auth_active, User.auth_active?(user)},           {:user_active, true} <- {:user_active, !user.info.deactivated}, -         scopes <- oauth_scopes(params, app.scopes), -         [] <- scopes -- app.scopes, -         true <- Enum.any?(scopes), +         {:ok, scopes} <- validate_scopes(app, params),           {:ok, auth} <- Authorization.create_authorization(app, user, scopes),           {:ok, token} <- Token.exchange_token(app, auth) do        json(conn, response_token(user, token)) @@ -247,8 +244,9 @@ defmodule Pleroma.Web.OAuth.OAuthController do    @doc "Prepares OAuth request to provider for Ueberauth"    def prepare_request(conn, %{"provider" => provider, "authorization" => auth_attrs}) do      scope = -      oauth_scopes(auth_attrs, []) -      |> Enum.join(" ") +      auth_attrs +      |> Scopes.fetch_scopes([]) +      |> Scopes.to_string()      state =        auth_attrs @@ -326,7 +324,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do        client_id: auth_attrs["client_id"],        redirect_uri: auth_attrs["redirect_uri"],        state: auth_attrs["state"], -      scopes: oauth_scopes(auth_attrs, []), +      scopes: Scopes.fetch_scopes(auth_attrs, []),        nickname: auth_attrs["nickname"],        email: auth_attrs["email"]      }) @@ -401,10 +399,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do             {:get_user, (user && {:ok, user}) || Authenticator.get_user(conn)},           %App{} = app <- Repo.get_by(App, client_id: client_id),           true <- redirect_uri in String.split(app.redirect_uris), -         scopes <- oauth_scopes(auth_attrs, []), -         {:unsupported_scopes, []} <- {:unsupported_scopes, scopes -- app.scopes}, -         # Note: `scope` param is intentionally not optional in this context -         {:missing_scopes, false} <- {:missing_scopes, scopes == []}, +         {:ok, scopes} <- validate_scopes(app, auth_attrs),           {:auth_active, true} <- {:auth_active, User.auth_active?(user)} do        Authorization.create_authorization(app, user, scopes)      end @@ -458,4 +453,12 @@ defmodule Pleroma.Web.OAuth.OAuthController do      }      |> Map.merge(opts)    end + +  @spec validate_scopes(App.t(), map()) :: +          {:ok, list()} | {:error, :missing_scopes | :unsupported_scopes} +  defp validate_scopes(app, params) do +    params +    |> Scopes.fetch_scopes(app.scopes) +    |> Scopes.validates(app.scopes) +  end  end diff --git a/lib/pleroma/web/oauth/scopes.ex b/lib/pleroma/web/oauth/scopes.ex new file mode 100644 index 000000000..ad9dfb260 --- /dev/null +++ b/lib/pleroma/web/oauth/scopes.ex @@ -0,0 +1,67 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.OAuth.Scopes do +  @moduledoc """ +  Functions for dealing with scopes. +  """ + +  @doc """ +  Fetch scopes from requiest params. + +  Note: `scopes` is used by Mastodon — supporting it but sticking to +  OAuth's standard `scope` wherever we control it +  """ +  @spec fetch_scopes(map(), list()) :: list() +  def fetch_scopes(params, default) do +    parse_scopes(params["scope"] || params["scopes"], default) +  end + +  def parse_scopes(scopes, _default) when is_list(scopes) do +    Enum.filter(scopes, &(&1 not in [nil, ""])) +  end + +  def parse_scopes(scopes, default) when is_binary(scopes) do +    scopes +    |> to_list +    |> parse_scopes(default) +  end + +  def parse_scopes(_, default) do +    default +  end + +  @doc """ +  Convert scopes string to list +  """ +  @spec to_list(binary()) :: [binary()] +  def to_list(nil), do: [] + +  def to_list(str) do +    str +    |> String.trim() +    |> String.split(~r/[\s,]+/) +  end + +  @doc """ +  Convert scopes list to string +  """ +  @spec to_string(list()) :: binary() +  def to_string(scopes), do: Enum.join(scopes, " ") + +  @doc """ +  Validates scopes. +  """ +  @spec validates(list() | nil, list()) :: +          {:ok, list()} | {:error, :missing_scopes | :unsupported_scopes} +  def validates([], _app_scopes), do: {:error, :missing_scopes} +  def validates(nil, _app_scopes), do: {:error, :missing_scopes} + +  def validates(scopes, app_scopes) do +    case scopes -- app_scopes do +      [] -> {:ok, scopes} +      _ -> {:error, :unsupported_scopes} +    end +  end +end  | 
