diff options
| author | Roger Braun <roger@rogerbraun.net> | 2017-09-06 19:06:25 +0200 | 
|---|---|---|
| committer | Roger Braun <roger@rogerbraun.net> | 2017-09-06 19:06:25 +0200 | 
| commit | 2a298d70f9938d1b6d5af04d8b8863fdd3299f46 (patch) | |
| tree | 7029989860d19246a0840a7991db46ad5b3207df | |
| parent | 4e785df984bed0e2ffc3f5a773a961ed3efd4760 (diff) | |
| download | pleroma-2a298d70f9938d1b6d5af04d8b8863fdd3299f46.tar.gz pleroma-2a298d70f9938d1b6d5af04d8b8863fdd3299f46.zip | |
Add very basic oauth and mastodon api support.
| -rw-r--r-- | lib/pleroma/app.ex | 29 | ||||
| -rw-r--r-- | lib/pleroma/plugs/oauth_plug.ex | 22 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_api.ex | 0 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 32 | ||||
| -rw-r--r-- | lib/pleroma/web/oauth/authorization.ex | 30 | ||||
| -rw-r--r-- | lib/pleroma/web/oauth/oauth_controller.ex | 44 | ||||
| -rw-r--r-- | lib/pleroma/web/oauth/oauth_view.ex | 4 | ||||
| -rw-r--r-- | lib/pleroma/web/oauth/token.ex | 31 | ||||
| -rw-r--r-- | lib/pleroma/web/router.ex | 18 | ||||
| -rw-r--r-- | lib/pleroma/web/templates/layout/app.html.eex | 11 | ||||
| -rw-r--r-- | lib/pleroma/web/templates/o_auth/o_auth/results.html.eex | 2 | ||||
| -rw-r--r-- | lib/pleroma/web/templates/o_auth/o_auth/show.html.eex | 14 | ||||
| -rw-r--r-- | lib/pleroma/web/views/layout_view.ex | 3 | ||||
| -rw-r--r-- | priv/repo/migrations/20170906120646_add_mastodon_apps.exs | 16 | ||||
| -rw-r--r-- | priv/repo/migrations/20170906143140_create_o_auth_authorizations.exs | 15 | ||||
| -rw-r--r-- | priv/repo/migrations/20170906152508_create_o_auth_token.exs | 15 | 
16 files changed, 286 insertions, 0 deletions
| diff --git a/lib/pleroma/app.ex b/lib/pleroma/app.ex new file mode 100644 index 000000000..d467595ea --- /dev/null +++ b/lib/pleroma/app.ex @@ -0,0 +1,29 @@ +defmodule Pleroma.App do +  use Ecto.Schema +  import Ecto.{Changeset} + +  schema "apps" do +    field :client_name, :string +    field :redirect_uris, :string +    field :scopes, :string +    field :website, :string +    field :client_id, :string +    field :client_secret, :string + +    timestamps() +  end + +  def register_changeset(struct, params \\ %{}) do +    changeset = struct +    |> cast(params, [:client_name, :redirect_uris, :scopes, :website]) +    |> validate_required([:client_name, :redirect_uris, :scopes]) + +    if changeset.valid? do +      changeset +      |> put_change(:client_id, :crypto.strong_rand_bytes(32) |> Base.url_encode64) +      |> put_change(:client_secret, :crypto.strong_rand_bytes(32) |> Base.url_encode64) +    else +      changeset +    end +  end +end diff --git a/lib/pleroma/plugs/oauth_plug.ex b/lib/pleroma/plugs/oauth_plug.ex new file mode 100644 index 000000000..fc2a907a2 --- /dev/null +++ b/lib/pleroma/plugs/oauth_plug.ex @@ -0,0 +1,22 @@ +defmodule Pleroma.Plugs.OAuthPlug do +  import Plug.Conn +  alias Pleroma.User +  alias Pleroma.Repo +  alias Pleroma.Web.OAuth.Token + +  def init(options) do +    options +  end + +  def call(%{assigns: %{user: %User{}}} = conn, _), do: conn +  def call(conn, opts) do +    with ["Bearer " <> header] <- get_req_header(conn, "authorization"), +         %Token{user_id: user_id} <- Repo.get_by(Token, token: header), +         %User{} = user <- Repo.get(User, user_id) do +      conn +      |> assign(:user, user) +    else +      _ -> conn +    end +  end +end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex new file mode 100644 index 000000000..89e37d6ab --- /dev/null +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -0,0 +1,32 @@ +defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do +  use Pleroma.Web, :controller +  alias Pleroma.{Repo, App} + +  def create_app(conn, params) do +    with cs <- App.register_changeset(%App{}, params) |> IO.inspect, +         {:ok, app} <- Repo.insert(cs) |> IO.inspect do +      res = %{ +        id: app.id, +        client_id: app.client_id, +        client_secret: app.client_secret +      } + +      json(conn, res) +    end +  end + +  def verify_credentials(%{assigns: %{user: user}} = conn, params) do +    account = %{ +      id: user.id, +      username: user.nickname, +      acct: user.nickname, +      display_name: user.name, +      locked: false, +      created_at: user.inserted_at, +      note: user.bio, +      url: "" +    } + +    json(conn, account) +  end +end diff --git a/lib/pleroma/web/oauth/authorization.ex b/lib/pleroma/web/oauth/authorization.ex new file mode 100644 index 000000000..9423c9632 --- /dev/null +++ b/lib/pleroma/web/oauth/authorization.ex @@ -0,0 +1,30 @@ +defmodule Pleroma.Web.OAuth.Authorization do +  use Ecto.Schema + +  alias Pleroma.{App, User, Repo} +  alias Pleroma.Web.OAuth.Authorization + +  schema "oauth_authorizations" do +    field :token, :string +    field :valid_until, :naive_datetime +    field :used, :boolean, default: false +    belongs_to :user, Pleroma.User +    belongs_to :app, Pleroma.App + +    timestamps() +  end + +  def create_authorization(%App{} = app, %User{} = user) do +    token = :crypto.strong_rand_bytes(32) |> Base.url_encode64 + +    authorization = %Authorization{ +      token: token, +      used: false, +      user_id: user.id, +      app_id: app.id, +      valid_until: NaiveDateTime.add(NaiveDateTime.utc_now, 60 * 10) +    } + +    Repo.insert(authorization) +  end +end diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex new file mode 100644 index 000000000..f0e091ac2 --- /dev/null +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -0,0 +1,44 @@ +defmodule Pleroma.Web.OAuth.OAuthController do +  use Pleroma.Web, :controller + +  alias Pleroma.Web.OAuth.{Authorization, Token} +  alias Pleroma.{Repo, User, App} +  alias Comeonin.Pbkdf2 + +  def authorize(conn, params) do +    render conn, "show.html", %{ +      response_type: params["response_type"], +      client_id: params["client_id"], +      scope: params["scope"], +      redirect_uri: params["redirect_uri"] +    } +  end + +  def create_authorization(conn, %{"authorization" => %{"name" => name, "password" => password, "client_id" => client_id}} = params) do +    with %User{} = user <- User.get_cached_by_nickname(name), +         true <- Pbkdf2.checkpw(password, user.password_hash), +         %App{} = app <- Pleroma.Repo.get_by(Pleroma.App, client_id: client_id), +         {:ok, auth} <- Authorization.create_authorization(app, user) do +      render conn, "results.html", %{ +        auth: auth +      } +    end +  end + +  # TODO CRITICAL +  # - Check validity of auth token +  def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do +    with %App{} = app <- Repo.get_by(App, client_id: params["client_id"], client_secret: params["client_secret"]), +         %Authorization{} = auth <- Repo.get_by(Authorization, token: params["code"], app_id: app.id), +         {:ok, token} <- Token.create_token(app, Repo.get(User, auth.user_id)) do +      response = %{ +        token_type: "Bearer", +        access_token: token.token, +        refresh_token: token.refresh_token, +        expires_in: 60 * 10, +        scope: "read write follow" +      } +      json(conn, response) +    end +  end +end diff --git a/lib/pleroma/web/oauth/oauth_view.ex b/lib/pleroma/web/oauth/oauth_view.ex new file mode 100644 index 000000000..b3923fcf5 --- /dev/null +++ b/lib/pleroma/web/oauth/oauth_view.ex @@ -0,0 +1,4 @@ +defmodule Pleroma.Web.OAuth.OAuthView do +  use Pleroma.Web, :view +  import Phoenix.HTML.Form +end diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/oauth/token.ex new file mode 100644 index 000000000..49e72428c --- /dev/null +++ b/lib/pleroma/web/oauth/token.ex @@ -0,0 +1,31 @@ +defmodule Pleroma.Web.OAuth.Token do +  use Ecto.Schema + +  alias Pleroma.{App, User, Repo} +  alias Pleroma.Web.OAuth.Token + +  schema "oauth_tokens" do +    field :token, :string +    field :refresh_token, :string +    field :valid_until, :naive_datetime +    belongs_to :user, Pleroma.User +    belongs_to :app, Pleroma.App + +    timestamps() +  end + +  def create_token(%App{} = app, %User{} = user) do +    token = :crypto.strong_rand_bytes(32) |> Base.url_encode64 +    refresh_token = :crypto.strong_rand_bytes(32) |> Base.url_encode64 + +    token = %Token{ +      token: token, +      refresh_token: refresh_token, +      user_id: user.id, +      app_id: app.id, +      valid_until: NaiveDateTime.add(NaiveDateTime.utc_now, 60 * 10) +    } + +    Repo.insert(token) +  end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index c20ec3e80..6081016d6 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -16,6 +16,7 @@ defmodule Pleroma.Web.Router do    pipeline :authenticated_api do      plug :accepts, ["json"]      plug :fetch_session +    plug Pleroma.Plugs.OAuthPlug      plug Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1}    end @@ -31,10 +32,27 @@ defmodule Pleroma.Web.Router do      plug :accepts, ["json"]    end +  pipeline :oauth do +    plug :accepts, ["html", "json"] +  end + +  scope "/oauth", Pleroma.Web.OAuth do +    get "/authorize", OAuthController, :authorize +    post "/authorize", OAuthController, :create_authorization +    post "/token", OAuthController, :token_exchange +  end +    scope "/api/v1", Pleroma.Web do      pipe_through :masto_config      # TODO: Move this      get "/instance", TwitterAPI.UtilController, :masto_instance +    post "/apps", MastodonAPI.MastodonAPIController, :create_app +  end + +  scope "/api/v1", Pleroma.Web.MastodonAPI do +    pipe_through :authenticated_api + +    get "/accounts/verify_credentials", MastodonAPIController, :verify_credentials    end    scope "/api", Pleroma.Web do diff --git a/lib/pleroma/web/templates/layout/app.html.eex b/lib/pleroma/web/templates/layout/app.html.eex new file mode 100644 index 000000000..6cc3b7ac5 --- /dev/null +++ b/lib/pleroma/web/templates/layout/app.html.eex @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +  <head> +    <meta charset=utf-8 /> +    <title>Pleroma</title> +  </head> +  <body> +    <h1>Welcome to Pleroma</h1> +    <%= render @view_module, @view_template, assigns %> +  </body> +</html> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/results.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/results.html.eex new file mode 100644 index 000000000..8443d906b --- /dev/null +++ b/lib/pleroma/web/templates/o_auth/o_auth/results.html.eex @@ -0,0 +1,2 @@ +<h1>Successfully authorized</h1> +<h2>Token code is <%= @auth.token %></h2> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex new file mode 100644 index 000000000..ce295ed05 --- /dev/null +++ b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex @@ -0,0 +1,14 @@ +<h2>OAuth Authorization</h2> +<%= form_for @conn, o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %> +<%= label f, :name, "Name" %> +<%= text_input f, :name %> +<br> +<%= label f, :password, "Password" %> +<%= password_input f, :password %> +<br> +<%= hidden_input f, :client_id, value: @client_id %> +<%= hidden_input f, :response_type, value: @response_type %> +<%= hidden_input f, :redirect_uri, value: @redirect_uri %> +<%= hidden_input f, :scope, value: @scope %> +<%= submit "Authorize" %> +<% end %> diff --git a/lib/pleroma/web/views/layout_view.ex b/lib/pleroma/web/views/layout_view.ex new file mode 100644 index 000000000..d4d4c3bd3 --- /dev/null +++ b/lib/pleroma/web/views/layout_view.ex @@ -0,0 +1,3 @@ +defmodule Pleroma.Web.LayoutView do +  use Pleroma.Web, :view +end diff --git a/priv/repo/migrations/20170906120646_add_mastodon_apps.exs b/priv/repo/migrations/20170906120646_add_mastodon_apps.exs new file mode 100644 index 000000000..d3dd317dd --- /dev/null +++ b/priv/repo/migrations/20170906120646_add_mastodon_apps.exs @@ -0,0 +1,16 @@ +defmodule Pleroma.Repo.Migrations.AddMastodonApps do +  use Ecto.Migration + +  def change do +    create table(:apps) do +      add :client_name, :string +      add :redirect_uris, :string +      add :scopes, :string +      add :website, :string +      add :client_id, :string +      add :client_secret, :string + +      timestamps() +    end +  end +end diff --git a/priv/repo/migrations/20170906143140_create_o_auth_authorizations.exs b/priv/repo/migrations/20170906143140_create_o_auth_authorizations.exs new file mode 100644 index 000000000..b4332870e --- /dev/null +++ b/priv/repo/migrations/20170906143140_create_o_auth_authorizations.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.CreateOAuthAuthorizations do +  use Ecto.Migration + +  def change do +    create table(:oauth_authorizations) do +      add :app_id, references(:apps) +      add :user_id, references(:users) +      add :token, :string +      add :valid_until, :naive_datetime +      add :used, :boolean, default: false + +      timestamps() +    end +  end +end diff --git a/priv/repo/migrations/20170906152508_create_o_auth_token.exs b/priv/repo/migrations/20170906152508_create_o_auth_token.exs new file mode 100644 index 000000000..7f8550f33 --- /dev/null +++ b/priv/repo/migrations/20170906152508_create_o_auth_token.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.CreateOAuthToken do +  use Ecto.Migration + +  def change do +    create table(:oauth_tokens) do +      add :app_id, references(:apps) +      add :user_id, references(:users) +      add :token, :string +      add :refresh_token, :string +      add :valid_until, :naive_datetime + +      timestamps() +    end +  end +end | 
