Introduce authorizations with Rajska

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2023-03-17 18:10:59 +01:00
parent b6875f6a4b
commit 8984bd7636
95 changed files with 4560 additions and 1505 deletions

View File

@@ -0,0 +1,86 @@
defmodule Mobilizon.GraphQL.Authorization do
@moduledoc """
Check authorizations
"""
use Rajska,
valid_roles: [:user, :moderator, :administrator],
super_role: :administrator,
default_rule: :default
alias Mobilizon.Applications.ApplicationToken
alias Mobilizon.GraphQL.Authorization.AppScope
alias Mobilizon.Users.User
import Mobilizon.Web.Gettext, only: [dgettext: 3]
@impl true
def has_user_access?(%User{}, _scope, _rule), do: true
def has_user_access?(%ApplicationToken{scope: scope} = _current_app_token, _struct, rule)
when rule != :forbid_app_access do
AppScope.has_app_access?(scope, rule)
end
def has_user_access?(_current_user, _scoped_struct, _rule), do: false
@impl true
def get_current_user(%{current_auth_app_token: app_token}), do: app_token
def get_current_user(%{current_user: current_user}), do: current_user
def get_current_user(_ctx), do: nil
@impl true
def role_authorized?(_user_role, :all), do: true
def role_authorized?(role, _allowed_role) when is_super_role(role), do: true
def role_authorized?(user_role, allowed_role) when is_atom(user_role) and is_atom(allowed_role),
do: user_role === allowed_role
def role_authorized?(user_role, allowed_roles)
when is_atom(user_role) and is_list(allowed_roles),
do: user_role in allowed_roles
@impl true
def get_user_role(%ApplicationToken{user: %{role: role}}), do: role
def get_user_role(%{role: role}), do: role
def get_user_role(nil), do: nil
@impl true
def unauthorized_message(resolution) do
case Map.get(resolution.context, :current_user) do
nil ->
"unauthenticated"
_ ->
"unauthorized"
end
end
@impl true
def unauthorized_query_scope_message(_resolution, object_type) do
dgettext("errors", "Not authorized to access this %{object_type}",
object_type: replace_underscore(object_type)
)
end
@impl true
def unauthorized_object_scope_message(_result_object, object) do
dgettext("errors", "Not authorized to access object %{object}", object: object.identifier)
end
@impl true
def unauthorized_object_message(_resolution, object) do
dgettext("errors", "Not authorized to access object %{object}", object: object.identifier)
end
@impl true
def unauthorized_field_message(_resolution, field),
do: dgettext("errors", "Not authorized to access field %{field}", field: field)
defp replace_underscore(string) when is_binary(string), do: String.replace(string, "_", " ")
defp replace_underscore(atom) when is_atom(atom) do
atom
|> Atom.to_string()
|> replace_underscore()
end
end

View File

@@ -0,0 +1,118 @@
defmodule Mobilizon.GraphQL.Authorization.AppScope do
@moduledoc """
Module referencing all scopes usable in the Mobilizon API
"""
require Logger
@global_scope %{
"write" => [
# Media
:"write:media:upload",
:"write:media:remove",
# Event permissions
:"write:event:create",
:"write:event:update",
:"write:event:delete",
# Comment permissions
:"write:comment:create",
:"write:comment:update",
:"write:comment:delete",
# Event participation permission
:"write:participation",
# User account permissions
:"write:user:settings",
:"write:user:setting:activity",
:"write:user:setting:push",
# Profile permissions
:"write:profile:create",
:"write:profile:update",
:"write:profile:delete",
:"write:profile:feed_token:create",
:"write:feed_token:delete",
# Membership permissions
:"write:group_membership",
# Group permissions
:"write:group:create",
:"write:group:update",
:"write:group:delete",
# Group discussions permissions
:"write:group:discussion:create",
:"write:group:discussion:update",
:"write:group:discussion:delete",
# Group resources permissions
:"write:group:resources:create",
:"write:group:resources:update",
:"write:group:resources:delete",
# Group members
:"write:group:members",
# Post permissions
:"write:group:post:create",
:"write:group:post:update",
:"write:group:post:delete"
],
"read" => [
:"read:event",
:"read:event:participants",
:"read:event:participants:export",
:"read:user:settings",
# Profile permissions
:"read:profile",
:"read:profile:organized_events",
:"read:profile:participations",
:"read:profile:memberships",
:"read:profile:follows",
# Group details permissions
:"read:group",
:"read:group:events",
:"read:group:discussions",
:"read:group:resources",
:"read:group:members",
:"read:group:followers",
:"read:group:todo_lists",
:"read:group:activities"
]
}
@spec get_scopes :: list(atom())
def get_scopes do
@global_scope
|> Map.values()
|> Enum.concat()
|> Enum.concat([:read, :write])
end
@spec scopes_valid?(String.t()) :: boolean()
def scopes_valid?(scopes) do
scopes
|> String.split(" ")
|> Enum.all?(&scope_valid?/1)
end
@spec scope_valid?(String.t() | atom()) :: boolean()
def scope_valid?(scope) when is_binary(scope) do
scope in Enum.map(get_scopes(), &to_string/1)
end
def scope_valid?(scope) when is_atom(scope) do
scope in get_scopes()
end
@spec has_app_access?(binary, atom | binary) :: boolean
def has_app_access?(scope, rule) do
Logger.debug("Has app token access? scope: #{inspect(scope)}, rule: #{inspect(rule)}")
scope = String.split(scope, " ")
scope_acceptable_for_rule?(scope, rule) or global_scopes_acceptable_for_rule?(scope, rule)
end
@spec scope_acceptable_for_rule?(list(String.t() | atom()), String.t() | atom()) :: boolean()
defp scope_acceptable_for_rule?(scope, rule) when is_list(scope) do
to_string(rule) in Enum.map(scope, &to_string/1)
end
defp global_scopes_acceptable_for_rule?(scope, rule),
do: Enum.any?(scope, &global_scope_acceptable_for_rule?(&1, rule))
defp global_scope_acceptable_for_rule?(global_scope, rule),
do: scope_acceptable_for_rule?(Map.get(@global_scope, global_scope, []), rule)
end

View File

@@ -30,6 +30,17 @@ defmodule Mobilizon.GraphQL.Error do
handle(reason)
end
# It's unclear why returned errors are now binaries instead of atoms
# but we can still convert them back
def normalize(string) when is_binary(string) do
string
|> String.to_existing_atom()
|> handle()
rescue
ArgumentError ->
handle(string)
end
# Unhandled errors
def normalize(other) do
handle(other)
@@ -65,6 +76,9 @@ defmodule Mobilizon.GraphQL.Error do
end
defp handle(reason) when is_binary(reason) do
Logger.debug("Unknown error")
Logger.debug(reason)
%Error{
code: :unknown_error,
message: reason,
@@ -101,6 +115,11 @@ defmodule Mobilizon.GraphQL.Error do
defp metadata(:group_not_found), do: {404, dgettext("errors", "Group not found")}
defp metadata(:resource_not_found), do: {404, dgettext("errors", "Resource not found")}
defp metadata(:discussion_not_found), do: {404, dgettext("errors", "Discussion not found")}
defp metadata(:application_not_found), do: {404, dgettext("errors", "Application not found")}
defp metadata(:application_token_not_found),
do: {404, dgettext("errors", "Application token not found")}
defp metadata(:unknown), do: {500, dgettext("errors", "Something went wrong")}
defp metadata(code) do

View File

@@ -12,7 +12,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
require Logger
@doc """
Create an application
Authorize an application
"""
@spec authorize(any(), map(), Absinthe.Resolution.t()) :: {:ok, map()} | {:error, String.t()}
def authorize(
@@ -21,8 +21,16 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
%{context: %{current_user: %User{id: user_id}}}
) do
case Applications.autorize(client_id, redirect_uri, scope, user_id) do
{:ok, code} ->
{:ok, %{code: code, state: state}}
{:ok,
%ApplicationToken{
application: %Application{client_id: client_id},
scope: scope,
authorization_code: code
}} ->
{:ok, %{code: code, state: state, client_id: client_id, scope: scope}}
{:error, %Ecto.Changeset{} = err} ->
{:error, err}
{:error, :application_not_found} ->
{:error,
@@ -41,18 +49,18 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
end
def authorize(_parent, _args, _context) do
{:error, dgettext("errors", "You need to be logged-in to autorize applications")}
{:error, :unauthenticated}
end
@spec get_application(any(), map(), Absinthe.Resolution.t()) ::
{:ok, Application.t()} | {:error, :not_found | :unauthenticated}
{:ok, Application.t()} | {:error, :application_not_found | :unauthenticated}
def get_application(_parent, %{client_id: client_id}, %{context: %{current_user: %User{}}}) do
case ApplicationManager.get_application_by_client_id(client_id) do
%Application{} = application ->
{:ok, application}
nil ->
{:error, :not_found}
{:error, :application_not_found}
end
end
@@ -82,7 +90,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
end
_ ->
{:error, :not_found}
{:error, :application_token_not_found}
end
end
@@ -93,29 +101,53 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
def activate_device(_parent, %{user_code: user_code}, %{
context: %{current_user: %User{} = user}
}) do
with {:ok, %ApplicationDeviceActivation{} = app_device_activation} <-
Applications.activate_device(user_code, user) do
{:ok, app_device_activation |> Map.from_struct() |> Map.take([:application, :id, :scope])}
case Applications.activate_device(user_code, user) do
{:ok, %ApplicationDeviceActivation{} = app_device_activation} ->
{:ok, app_device_activation |> Map.from_struct() |> Map.take([:application, :id, :scope])}
{:error, :expired} ->
{:error, dgettext("errors", "The given user code has expired")}
{:error, :not_found} ->
{:error, dgettext("errors", "The given user code is invalid")}
end
end
def activate_device(_parent, _args, _resolution) do
{:error, :unauthenticated}
end
@spec authorize_device_application(any(), map(), Absinthe.Resolution.t()) ::
{:ok, map()} | {:error, String.t()}
def authorize_device_application(
_parent,
%{client_id: client_id, user_code: user_code},
%{context: %{current_user: %User{id: user_id}}}
%{context: %{current_user: %User{}}}
) do
case Applications.autorize_device_application(client_id, user_code, user_id) do
{:ok, %Application{} = app} ->
case Applications.autorize_device_application(client_id, user_code) do
{:ok, %ApplicationDeviceActivation{application: app}} ->
{:ok, app}
{:error, :application_not_found} ->
{:error, :not_confirmed} ->
{:error,
dgettext(
"errors",
"No application with this client_id was found"
"The device user code was not provided before approving the application"
)}
{:error, :not_found} ->
{:error,
dgettext(
"errors",
"The given user code is invalid"
)}
{:error, :expired} ->
{:error, dgettext("errors", "The given user code has expired")}
end
end
def authorize_device_application(_parent, _args, _resolution) do
{:error, :unauthenticated}
end
end

View File

@@ -44,7 +44,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Media do
def upload_media(
_parent,
%{file: %Plug.Upload{} = file} = args,
%{context: %{current_actor: %Actor{id: actor_id}}}
%{context: %{current_actor: %Actor{id: default_actor_id}}}
) do
with {:ok,
%{
@@ -62,7 +62,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Media do
{:ok, media = %Media{}} <-
Medias.create_media(%{
file: args,
actor_id: actor_id,
actor_id: Map.get(args, :actor_id, default_actor_id),
metadata: Map.take(uploaded, [:width, :height, :blurhash])
}) do
{:ok, transform_media(media)}

View File

@@ -20,8 +20,8 @@ defmodule Mobilizon.GraphQL.Schema do
alias Mobilizon.Actors.{Actor, Follower, Member}
alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.{Event, Participant}
alias Mobilizon.GraphQL.{Authorization, Schema}
alias Mobilizon.GraphQL.Middleware.{CurrentActorProvider, ErrorHandler, OperationNameLogger}
alias Mobilizon.GraphQL.Schema
alias Mobilizon.GraphQL.Schema.Custom
alias Mobilizon.Storage.Repo
@@ -57,11 +57,13 @@ defmodule Mobilizon.GraphQL.Schema do
@desc "A struct containing the id of the deleted object"
object :deleted_object do
meta(:authorize, :all)
field(:id, :id)
end
@desc "A JWT and the associated user ID"
object :login do
meta(:authorize, :all)
field(:access_token, non_null(:string), description: "A JWT Token for this session")
field(:refresh_token, non_null(:string),
@@ -75,6 +77,7 @@ defmodule Mobilizon.GraphQL.Schema do
Represents a notification for an user
"""
object :notification do
meta(:authorize, :user)
field(:id, :id, description: "The notification ID")
field(:user, :user, description: "The user to transmit the notification to")
field(:actor, :actor, description: "The notification target profile")
@@ -133,7 +136,9 @@ defmodule Mobilizon.GraphQL.Schema do
|> Dataloader.add_source(Resources, default_source)
|> Dataloader.add_source(Todos, default_source)
Map.put(ctx, :loader, loader)
ctx
|> Map.put(:loader, loader)
|> Map.put(:authorization, Authorization)
end
def plugins do
@@ -201,11 +206,19 @@ defmodule Mobilizon.GraphQL.Schema do
end
@spec middleware(list(module()), any(), map()) :: list(module())
def middleware(middleware, _field, %{identifier: type}) when type in [:query, :mutation] do
[CurrentActorProvider] ++ middleware ++ [ErrorHandler, OperationNameLogger]
def middleware(middleware, field, %{identifier: type}) when type in [:query, :mutation] do
[CurrentActorProvider | middleware]
|> Enum.map(&fix_middleware_format_for_rajska/1)
|> Rajska.add_query_authorization(field, Authorization)
|> Rajska.add_object_authorization()
|> List.insert_at(-1, ErrorHandler)
|> List.insert_at(-1, OperationNameLogger)
end
def middleware(middleware, _field, _object) do
middleware
def middleware(middleware, field, object) do
Rajska.add_field_authorization(middleware, field, object)
end
defp fix_middleware_format_for_rajska({mod, config}), do: {mod, config}
defp fix_middleware_format_for_rajska(mod), do: {mod, nil}
end

View File

@@ -25,11 +25,13 @@ defmodule Mobilizon.GraphQL.Schema.ActivityType do
end
object :activity_param_item do
meta(:authorize, :user)
field(:key, :string)
field(:value, :string)
end
interface :activity_object do
meta(:authorize, :user)
field(:id, :id)
resolve_type(fn
@@ -66,11 +68,13 @@ defmodule Mobilizon.GraphQL.Schema.ActivityType do
A paginated list of activity items
"""
object :paginated_activity_list do
meta(:authorize, :user)
field(:elements, list_of(:activity), description: "A list of activities")
field(:total, :integer, description: "The total number of elements in the list")
end
object :activity do
meta(:authorize, :user)
field(:id, :id, description: "The activity item ID")
field(:inserted_at, :datetime, description: "When was the activity inserted")
field(:priority, :integer)

View File

@@ -13,6 +13,7 @@ defmodule Mobilizon.GraphQL.Schema.ActorInterface do
@desc "An ActivityPub actor"
interface :actor do
meta(:authorize, :all)
field(:id, :id, description: "Internal ID for this actor")
field(:url, :string, description: "The ActivityPub actor's URL")
field(:type, :actor_type, description: "The type of Actor (Person, Group,…)")
@@ -65,18 +66,21 @@ defmodule Mobilizon.GraphQL.Schema.ActorInterface do
@desc "Suspend an actor"
field :suspend_profile, :deleted_object do
arg(:id, non_null(:id), description: "The remote profile ID to suspend")
middleware(Rajska.QueryAuthorization, permit: :moderator, scope: false)
resolve(&ActorResolver.suspend_profile/3)
end
@desc "Unsuspend an actor"
field :unsuspend_profile, :actor do
arg(:id, non_null(:id), description: "The remote profile ID to unsuspend")
middleware(Rajska.QueryAuthorization, permit: :moderator, scope: false)
resolve(&ActorResolver.unsuspend_profile/3)
end
@desc "Refresh a profile"
field :refresh_profile, :actor do
arg(:id, non_null(:id), description: "The remote profile ID to refresh")
middleware(Rajska.QueryAuthorization, permit: :moderator, scope: false)
resolve(&ActorResolver.refresh_profile/3)
end
end

View File

@@ -10,6 +10,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.ApplicationType do
Represents an application
"""
object :application do
meta(:authorize, :all)
interfaces([:actor])
field(:id, :id, description: "Internal ID for this application")

View File

@@ -9,6 +9,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.FollowerType do
Represents an actor's follower
"""
object :follower do
meta(:authorize, :user)
field(:id, :id, description: "The follow ID")
field(:target_actor, :actor, description: "What or who the profile follows")
field(:actor, :actor, description: "Which profile follows")
@@ -30,6 +31,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.FollowerType do
A paginated list of follower objects
"""
object :paginated_follower_list do
meta(:authorize, :user)
field(:elements, list_of(:follower), description: "A list of followers")
field(:total, :integer, description: "The total number of elements in the list")
end
@@ -43,6 +45,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.FollowerType do
description: "Whether the follower has been approved by the target actor or not"
)
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&Followers.update_follower/3)
end
end

View File

@@ -29,6 +29,9 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
Represents a group of actors
"""
object :group do
meta(:authorize, :all)
meta(:scope_field?, true)
interfaces([:actor, :interactable, :activity_object, :action_log_object, :group_search_result])
field(:id, :id, description: "Internal ID for this group")
@@ -77,7 +80,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
)
# This one should have a privacy setting
field :organized_events, :paginated_event_list do
field :organized_events, :paginated_event_list,
meta: [private: true, rule: :"read:group:events"] do
arg(:after_datetime, :datetime,
default_value: nil,
description: "Filter events that begin after this datetime"
@@ -94,7 +98,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
description("A list of the events this actor has organized")
end
field :discussions, :paginated_discussion_list do
field :discussions, :paginated_discussion_list,
meta: [private: true, rule: :"read:group:discussions"] do
arg(:page, :integer,
default_value: 1,
description: "The page in the paginated discussion list"
@@ -111,7 +116,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
description: "Whether the group is opened to all or has restricted access"
)
field :members, :paginated_member_list do
field :members, :paginated_member_list, meta: [private: true, rule: :"read:group:members"] do
arg(:name, :string, description: "A name to filter members by")
arg(:page, :integer, default_value: 1, description: "The page in the paginated member list")
arg(:limit, :integer, default_value: 10, description: "The limit of members per page")
@@ -120,7 +125,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
description("A paginated list of group members")
end
field :resources, :paginated_resource_list do
field :resources, :paginated_resource_list,
meta: [private: true, rule: :"read:group:resources"] do
arg(:page, :integer,
default_value: 1,
description: "The page in the paginated resource list"
@@ -138,7 +144,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
description("A paginated list of the posts this group has")
end
field :todo_lists, :paginated_todo_list_list do
field :todo_lists, :paginated_todo_list_list,
meta: [private: true, rule: :"read:group:todo_lists"] do
arg(:page, :integer,
default_value: 1,
description: "The page in the paginated todo-lists list"
@@ -149,7 +156,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
description("A paginated list of the todo lists this group has")
end
field :followers, :paginated_follower_list do
field :followers, :paginated_follower_list,
meta: [private: true, rule: :"read:group:followers"] do
arg(:page, :integer,
default_value: 1,
description: "The page in the paginated followers list"
@@ -166,7 +174,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
description("A paginated list of the followers this group has")
end
field :activity, :paginated_activity_list do
field :activity, :paginated_activity_list,
meta: [private: true, rule: :"read:group:activities"] do
arg(:page, :integer,
default_value: 1,
description: "The page in the paginated activity items list"
@@ -204,6 +213,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
A paginated list of groups
"""
object :paginated_group_list do
meta(:authorize, :all)
field(:elements, list_of(:group), description: "A list of groups")
field(:total, :integer, description: "The total number of groups in the list")
end
@@ -215,12 +225,6 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
value(:private, description: "Visible only to people with the link - or invited")
end
object :group_follow do
field(:group, :group, description: "The group followed")
field(:profile, :group, description: "The group followed")
field(:notify, :boolean, description: "Whether to notify profile from group activity")
end
object :group_queries do
@desc "Get all groups"
field :groups, :paginated_group_list do
@@ -236,12 +240,25 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
arg(:suspended, :boolean, default_value: false, description: "Filter by suspended status")
arg(:page, :integer, default_value: 1, description: "The page in the paginated group list")
arg(:limit, :integer, default_value: 10, description: "The limit of groups per page")
middleware(Rajska.QueryAuthorization,
permit: [:administrator, :moderator],
scope: Mobilizon.Actors.Actor,
args: %{}
)
resolve(&Group.list_groups/3)
end
@desc "Get a group by its ID"
field :get_group, :group do
arg(:id, non_null(:id), description: "The group ID")
middleware(Rajska.QueryAuthorization,
permit: [:administrator, :moderator],
scope: Mobilizon.Actors.Actor
)
resolve(&Group.get_group/3)
end
@@ -251,15 +268,9 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
description: "The group preferred_username, eventually containing their domain if remote"
)
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Group.find_group/3)
end
@desc "Get a group by its preferred username"
field :group_by_id, :group do
arg(:id, non_null(:id), description: "The group local ID")
resolve(&Group.find_group_by_id/3)
end
end
object :group_mutations do
@@ -291,7 +302,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
)
arg(:physical_address, :address_input, description: "The physical address for the group")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&Group.create_group/3)
end
@@ -323,14 +334,14 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
)
arg(:physical_address, :address_input, description: "The physical address for the group")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&Group.update_group/3)
end
@desc "Delete a group"
field :delete_group, :deleted_object do
arg(:group_id, non_null(:id), description: "The group ID")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&Group.delete_group/3)
end
@@ -343,6 +354,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
default_value: true
)
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&Group.follow_group/3)
end
@@ -355,13 +367,14 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
default_value: true
)
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&Group.update_group_follow/3)
end
@desc "Unfollow a group"
field :unfollow_group, :follower do
arg(:group_id, non_null(:id), description: "The group ID")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&Group.unfollow_group/3)
end
end

View File

@@ -10,6 +10,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
Represents a member of a group
"""
object :member do
meta(:authorize, :user)
interfaces([:activity_object])
field(:id, :id, description: "The member's ID")
field(:parent, :group, description: "Of which the profile is member")
@@ -37,6 +38,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
A paginated list of members
"""
object :paginated_member_list do
meta(:authorize, :user)
field(:elements, list_of(:member), description: "A list of members")
field(:total, :integer, description: "The total number of elements in the list")
end
@@ -46,6 +48,13 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
field :join_group, :member do
arg(:group_id, non_null(:id), description: "The group ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Member,
rule: :"write:group_membership",
args: %{parent_id: :group_id}
)
resolve(&Group.join_group/3)
end
@@ -53,9 +62,42 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
field :leave_group, :deleted_object do
arg(:group_id, non_null(:id), description: "The group ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Member,
rule: :"write:group_membership",
args: %{parent_id: :group_id}
)
resolve(&Group.leave_group/3)
end
@desc "Accept an invitation to a group"
field :accept_invitation, :member do
arg(:id, non_null(:id), description: "The member ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Member,
rule: :"write:group_membership"
)
resolve(&Member.accept_invitation/3)
end
@desc "Reject an invitation to a group"
field :reject_invitation, :member do
arg(:id, non_null(:id), description: "The member ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Member,
rule: :"write:group_membership"
)
resolve(&Member.reject_invitation/3)
end
@desc "Invite an actor to join the group"
field :invite_member, :member do
arg(:group_id, non_null(:id), description: "The group ID")
@@ -64,29 +106,29 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
description: "The targeted person's federated username"
)
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Member,
rule: :"write:group:members",
args: %{parent_id: :group_id}
)
resolve(&Member.invite_member/3)
end
@desc "Accept an invitation to a group"
field :accept_invitation, :member do
arg(:id, non_null(:id), description: "The member ID")
resolve(&Member.accept_invitation/3)
end
@desc "Reject an invitation to a group"
field :reject_invitation, :member do
arg(:id, non_null(:id), description: "The member ID")
resolve(&Member.reject_invitation/3)
end
@desc """
Approve a membership request
"""
field :approve_member, :member do
arg(:member_id, non_null(:id), description: "The member ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Member,
rule: :"write:group:members",
args: %{parent_id: :member_id}
)
resolve(&Member.approve_member/3)
end
@@ -96,6 +138,13 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
field :reject_member, :member do
arg(:member_id, non_null(:id), description: "The member ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Member,
rule: :"write:group:members",
args: %{parent_id: :member_id}
)
resolve(&Member.reject_member/3)
end
@@ -106,6 +155,13 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
arg(:member_id, non_null(:id), description: "The member ID")
arg(:role, non_null(:member_role_enum), description: "The new member role")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Member,
rule: :"write:group:members",
args: %{parent_id: :member_id}
)
resolve(&Member.update_member/3)
end
@@ -118,6 +174,13 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
description: "Whether the member should be excluded from the group"
)
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Member,
rule: :"write:group:members",
args: %{parent_id: :member_id}
)
resolve(&Member.remove_member/3)
end
end

View File

@@ -16,6 +16,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
Represents a person identity
"""
object :person do
meta(:authorize, :all)
meta(:scope_field?, true)
interfaces([:actor, :action_log_object])
field(:id, :id, description: "Internal ID for this person")
@@ -72,7 +74,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
# This one should have a privacy setting
field(:organized_events, :paginated_event_list,
description: "A list of the events this actor has organized"
description: "A list of the events this actor has organized",
meta: [private: true, rule: :"read:profile:organized_events"]
) do
arg(:page, :integer, default_value: 1, description: "The page in the paginated event list")
arg(:limit, :integer, default_value: 10, description: "The limit of events per page")
@@ -81,7 +84,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
@desc "The list of events this person goes to"
field(:participations, :paginated_participant_list,
description: "The list of events this person goes to"
description: "The list of events this person goes to",
meta: [private: true, rule: :"read:profile:participations"]
) do
arg(:event_id, :id, description: "Filter by event ID")
@@ -97,7 +101,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
@desc "The list of groups this person is member of"
field(:memberships, :paginated_member_list,
description: "The list of group this person is member of"
description: "The list of group this person is member of",
meta: [private: true, rule: :"read:profile:memberships"]
) do
arg(:group, :string, description: "Filter by group federated username")
arg(:group_id, :id, description: "Filter by group ID")
@@ -113,7 +118,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
@desc "The list of groups this person follows"
field(:follows, :paginated_follower_list,
description: "The list of groups this person follows"
description: "The list of groups this person follows",
meta: [private: true, rule: :"read:profile:follows"]
) do
arg(:group, :string, description: "Filter by group federated username")
@@ -131,6 +137,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
A paginated list of persons
"""
object :paginated_person_list do
meta(:authorize, :all)
field(:elements, list_of(:person), description: "A list of persons")
field(:total, :integer, description: "The total number of persons in the list")
end
@@ -138,23 +145,46 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
object :person_queries do
@desc "Get the current actor for the logged-in user"
field :logged_person, :person do
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Actor,
args: %{}
)
resolve(&Person.get_current_person/3)
end
@desc "Get a person by its (federated) username"
field :fetch_person, :person do
arg(:preferred_username, non_null(:string), description: "The person's federated username")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Actor,
args: %{preferred_username: :preferred_username}
)
resolve(&Person.fetch_person/3)
end
@desc "Get a person by its ID"
field :person, :person do
arg(:id, non_null(:id), description: "The person ID")
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Person.get_person/3)
end
@desc "Get the persons for an user"
field :identities, list_of(:person) do
deprecate("Use the loggedUser query instead")
middleware(Rajska.QueryAuthorization,
permit: [:user, :moderator, :administrator],
scope: Mobilizon.Actors.Actor,
args: %{},
rule: :user_self_identities
)
resolve(&Person.identities/3)
end
@@ -172,6 +202,13 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
arg(:suspended, :boolean, default_value: false, description: "Filter by suspended status")
arg(:page, :integer, default_value: 1, description: "The page in the paginated person list")
arg(:limit, :integer, default_value: 10, description: "The limit of persons per page")
middleware(Rajska.QueryAuthorization,
permit: [:administrator, :moderator],
scope: Mobilizon.Actors.Actor,
args: %{}
)
resolve(&Person.list_persons/3)
end
end
@@ -195,6 +232,13 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
"The banner for the profile, either as an object or directly the ID of an existing media"
)
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Actor,
args: %{},
rule: :"write:profile:create"
)
resolve(&Person.create_person/3)
end
@@ -216,6 +260,12 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
"The banner for the profile, either as an object or directly the ID of an existing media"
)
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Actor,
rule: :"write:profile:update"
)
resolve(&Person.update_person/3)
end
@@ -223,6 +273,12 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
field :delete_person, :person do
arg(:id, non_null(:id), description: "The person's ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Actor,
rule: :"write:profile:delete"
)
resolve(&Person.delete_person/3)
end
@@ -245,6 +301,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
"The banner for the profile, either as an object or directly the ID of an existing media"
)
middleware(Rajska.QueryAuthorization, permit: :all, scope: Mobilizon.Actors.Actor, args: %{})
resolve(&Person.register_person/3)
end
end
@@ -254,6 +312,12 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
field :event_person_participation_changed, :person do
arg(:person_id, non_null(:id), description: "The person's ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Actor,
args: %{id: :person_id}
)
config(fn args, _ ->
{:ok, topic: args.person_id}
end)
@@ -264,6 +328,12 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
arg(:person_id, non_null(:id), description: "The person's ID")
arg(:group, non_null(:string), description: "The group's federated username")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Actors.Actor,
args: %{id: :person_id}
)
config(fn args, _ ->
{:ok, topic: [args.group, args.person_id]}
end)

View File

@@ -10,6 +10,7 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
An address object
"""
object :address do
meta(:authorize, :all)
field(:geom, :point, description: "The geocoordinates for the point where this address is")
field(:street, :string, description: "The address's street name (with number)")
field(:locality, :string, description: "The address's locality")
@@ -29,6 +30,7 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
A phone address
"""
object :phone_address do
meta(:authorize, :all)
field(:phone, :string, description: "The phone number")
field(:info, :string, description: "Additional information about the phone number")
end
@@ -37,11 +39,13 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
An online address
"""
object :online_address do
meta(:authorize, :all)
field(:url, :string)
field(:info, :string)
end
object :picture_info_element do
meta(:authorize, :all)
field(:name, :string)
field(:url, :string)
end
@@ -50,6 +54,7 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
A picture associated with an address
"""
object :picture_info do
meta(:authorize, :all)
field(:url, :string)
field(:author, :picture_info_element)
field(:source, :picture_info_element)
@@ -100,7 +105,7 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
arg(:limit, :integer, default_value: 10, description: "The limit of search results per page")
arg(:type, :address_search_type, description: "Filter by type of results")
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Address.search/3)
end
@@ -115,6 +120,7 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
description: "The user's locale. Geocoding backends will make use of this value."
)
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Address.reverse_geocode/3)
end
end

View File

@@ -15,6 +15,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
@desc "An action log"
object :action_log do
meta(:authorize, :moderator)
field(:id, :id, description: "Internal ID for this comment")
field(:actor, :actor, description: "The actor that acted")
field(:object, :action_log_object, description: "The object that was acted upon")
@@ -26,6 +27,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
A paginated list of action logs
"""
object :paginated_action_log_list do
meta(:authorize, :moderator)
field(:elements, list_of(:action_log), description: "A list of action logs")
field(:total, :integer, description: "The total number of action logs in the list")
end
@@ -49,6 +51,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
@desc "The objects that can be in an action log"
interface :action_log_object do
meta(:authorize, [:moderator, :administrator])
field(:id, :id, description: "Internal ID for this object")
resolve_type(fn
@@ -82,6 +85,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
Language information
"""
object :language do
meta(:authorize, :administrator)
field(:code, :string, description: "The iso-639-3 language code")
field(:name, :string, description: "The language name")
end
@@ -90,6 +94,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
Dashboard information
"""
object :dashboard do
meta(:authorize, :administrator)
field(:last_public_event_published, :event, description: "Last public event published")
field(:last_group_created, :group, description: "Last public group created")
field(:number_of_users, :integer, description: "The number of local users")
@@ -109,6 +114,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
Admin settings
"""
object :admin_settings do
meta(:authorize, :administrator)
field(:instance_name, :string, description: "The instance's name")
field(:instance_description, :string, description: "The instance's description")
field(:instance_long_description, :string, description: "The instance's long description")
@@ -184,6 +190,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
An instance representation
"""
object :instance do
meta(:authorize, :administrator)
field(:domain, :id, description: "The domain name of the instance")
field(:follower_status, :instance_follow_status, description: "Do we follow this instance")
field(:followed_status, :instance_follow_status, description: "Does this instance follow us?")
@@ -226,6 +233,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
A paginated list of instances
"""
object :paginated_instance_list do
meta(:authorize, :administrator)
field(:elements, list_of(:instance), description: "A list of instances")
field(:total, :integer, description: "The total number of instances in the list")
end
@@ -235,6 +243,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
field :action_logs, type: :paginated_action_log_list do
arg(:page, :integer, default_value: 1)
arg(:limit, :integer, default_value: 10)
middleware(Rajska.QueryAuthorization, permit: :moderator, scope: false)
resolve(&Admin.list_action_logs/3)
end
@@ -247,6 +256,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
"The user's locale. The list of languages will be translated with this locale"
)
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Admin.get_list_of_languages/3)
end
@@ -254,6 +264,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
Get dashboard information
"""
field :dashboard, type: :dashboard do
middleware(Rajska.QueryAuthorization, permit: :administrator)
resolve(&Admin.get_dashboard/3)
end
@@ -261,6 +272,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
Get admin settings
"""
field :admin_settings, type: :admin_settings do
middleware(Rajska.QueryAuthorization, permit: :administrator)
resolve(&Admin.get_settings/3)
end
@@ -278,6 +290,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
description: "The limit of relay followers per page"
)
middleware(Rajska.QueryAuthorization, permit: :administrator)
resolve(&Admin.list_relay_followers/3)
end
@@ -301,6 +314,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
)
arg(:direction, :string, default_value: :desc, description: "The sorting direction")
middleware(Rajska.QueryAuthorization, permit: :administrator)
resolve(&Admin.list_relay_followings/3)
end
@@ -336,6 +350,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
)
arg(:direction, :string, default_value: :desc, description: "The sorting direction")
middleware(Rajska.QueryAuthorization, permit: :administrator)
resolve(&Admin.get_instances/3)
end
@@ -344,6 +359,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
"""
field :instance, :instance do
arg(:domain, non_null(:id), description: "The instance domain")
middleware(Rajska.QueryAuthorization, permit: :administrator)
resolve(&Admin.get_instance/3)
end
end
@@ -352,28 +368,28 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
@desc "Add an instance subscription"
field :add_instance, type: :instance do
arg(:domain, non_null(:string), description: "The instance domain to add")
middleware(Rajska.QueryAuthorization, permit: :administrator)
resolve(&Admin.create_instance/3)
end
@desc "Delete a relay subscription"
field :remove_relay, type: :follower do
arg(:address, non_null(:string), description: "The relay hostname to delete")
middleware(Rajska.QueryAuthorization, permit: :administrator)
resolve(&Admin.remove_relay/3)
end
@desc "Accept a relay subscription"
field :accept_relay, type: :follower do
arg(:address, non_null(:string), description: "The accepted relay hostname")
middleware(Rajska.QueryAuthorization, permit: :administrator)
resolve(&Admin.accept_subscription/3)
end
@desc "Reject a relay subscription"
field :reject_relay, type: :follower do
arg(:address, non_null(:string), description: "The rejected relay hostname")
middleware(Rajska.QueryAuthorization, permit: :administrator)
resolve(&Admin.reject_subscription/3)
end
@@ -402,7 +418,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
arg(:instance_rules, :string, description: "The instance's rules")
arg(:registrations_open, :boolean, description: "Whether the registrations are opened")
arg(:instance_languages, list_of(:string), description: "The instance's languages")
middleware(Rajska.QueryAuthorization, permit: :administrator)
resolve(&Admin.save_settings/3)
end
@@ -420,6 +436,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
description: "Whether or not to notify the user of the change"
)
middleware(Rajska.QueryAuthorization, permit: :administrator)
resolve(&Admin.update_user/3)
end
end

View File

@@ -7,15 +7,17 @@ defmodule Mobilizon.GraphQL.Schema.AuthApplicationType do
@desc "An application"
object :auth_application do
meta(:authorize, :user)
field(:id, :id)
field(:name, :string)
field(:client_id, :string)
field(:scopes, :string)
field(:scope, :string)
field(:website, :string)
end
@desc "An application"
object :auth_application_token do
meta(:authorize, :user)
field(:id, :id)
field(:inserted_at, :string)
field(:last_used_at, :string)
@@ -24,11 +26,15 @@ defmodule Mobilizon.GraphQL.Schema.AuthApplicationType do
@desc "The informations returned after authorization"
object :application_code_and_state do
meta(:authorize, :user)
field(:code, :string)
field(:state, :string)
field(:client_id, :string)
field(:scope, :string)
end
object :application_device_activation do
meta(:authorize, :user)
field(:id, :id)
field(:application, :auth_application)
field(:scope, :string)
@@ -38,6 +44,14 @@ defmodule Mobilizon.GraphQL.Schema.AuthApplicationType do
@desc "Get an application"
field :auth_application, :auth_application do
arg(:client_id, non_null(:string), description: "The application's client_id")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Applications.Application,
rule: :forbid_app_access,
args: %{client_id: :client_id}
)
resolve(&Application.get_application/3)
end
end
@@ -51,18 +65,33 @@ defmodule Mobilizon.GraphQL.Schema.AuthApplicationType do
description: "The URI to redirect to with the code and state"
)
arg(:scope, :string, description: "The scope for the authorization")
arg(:scope, non_null(:string), description: "The scope for the authorization")
arg(:state, :string,
description: "A state parameter to check that the request wasn't altered"
)
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Applications.Application,
rule: :forbid_app_access,
args: %{client_id: :client_id}
)
resolve(&Application.authorize/3)
end
@desc "Revoke an authorized application"
field :revoke_application_token, :deleted_object do
arg(:app_token_id, non_null(:string), description: "The application token's ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Applications.ApplicationToken,
rule: :forbid_app_access,
args: %{id: :app_token_id}
)
resolve(&Application.revoke_application_token/3)
end
@@ -72,13 +101,30 @@ defmodule Mobilizon.GraphQL.Schema.AuthApplicationType do
description: "The code provided by the application entered by the user"
)
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Applications.ApplicationDeviceActivation,
rule: :forbid_app_access,
args: %{id: :user_code}
)
resolve(&Application.activate_device/3)
end
@desc "Activate an user device"
field :authorize_device_application, :auth_application do
arg(:client_id, non_null(:string), description: "The application's client_id")
arg(:scope, :string, description: "The scope for the authorization")
arg(:user_code, non_null(:string),
description: "The code provided by the application entered by the user"
)
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Applications.ApplicationDeviceActivation,
rule: :forbid_app_access,
args: %{id: :client_id}
)
resolve(&Application.authorize_device_application/3)
end

View File

@@ -8,6 +8,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
@desc "A config object"
object :config do
meta(:authorize, :all)
# Instance name
field(:name, :string, description: "The instance's name")
field(:description, :string, description: "The instance's short description")
@@ -87,6 +88,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
The instance's terms configuration
"""
object :terms do
meta(:authorize, :all)
field(:url, :string, description: "The instance's terms URL.")
field(:type, :instance_terms_type, description: "The instance's terms type")
field(:body_html, :string, description: "The instance's terms body text")
@@ -96,6 +98,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
The instance's privacy policy configuration
"""
object :privacy do
meta(:authorize, :all)
field(:url, :string, description: "The instance's privacy policy URL")
field(:type, :instance_privacy_type, description: "The instance's privacy policy type")
field(:body_html, :string, description: "The instance's privacy policy body text")
@@ -105,6 +108,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Geographic coordinates
"""
object :lonlat do
meta(:authorize, :all)
field(:longitude, :float, description: "The coordinates longitude")
field(:latitude, :float, description: "The coordinates latitude")
# field(:accuracy_radius, :integer)
@@ -114,6 +118,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance geocoding configuration
"""
object :geocoding do
meta(:authorize, :all)
field(:autocomplete, :boolean,
description: "Whether autocomplete in address fields can be enabled"
)
@@ -125,6 +131,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance maps configuration
"""
object :maps do
meta(:authorize, :all)
field(:tiles, :tiles, description: "The instance's maps tiles configuration")
field(:routing, :routing, description: "The instance's maps routing configuration")
end
@@ -133,6 +140,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance map tiles configuration
"""
object :tiles do
meta(:authorize, :all)
field(:endpoint, :string, description: "The instance's tiles endpoint")
field(:attribution, :string, description: "The instance's tiles attribution text")
end
@@ -141,6 +149,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance map routing configuration
"""
object :routing do
meta(:authorize, :all)
field(:type, :routing_type, description: "The instance's routing type")
end
@@ -153,6 +162,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance anonymous configuration
"""
object :anonymous do
meta(:authorize, :all)
field(:participation, :anonymous_participation,
description: "The instance's anonymous participation settings"
)
@@ -172,6 +183,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance anonymous participation configuration
"""
object :anonymous_participation do
meta(:authorize, :all)
field(:allowed, :boolean, description: "Whether anonymous participations are allowed")
field(:validation, :anonymous_participation_validation,
@@ -183,6 +195,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance anonymous participation validation configuration
"""
object :anonymous_participation_validation do
meta(:authorize, :all)
field(:email, :anonymous_participation_validation_email,
description: "The policy to validate anonymous participations by email"
)
@@ -196,6 +210,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance anonymous participation with validation by email configuration
"""
object :anonymous_participation_validation_email do
meta(:authorize, :all)
field(:enabled, :boolean,
description: "Whether anonymous participation validation by email is enabled"
)
@@ -209,6 +225,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance anonymous participation with validation by captcha configuration
"""
object :anonymous_participation_validation_captcha do
meta(:authorize, :all)
field(:enabled, :boolean,
description: "Whether anonymous participation validation by captcha is enabled"
)
@@ -218,6 +236,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance anonymous event creation configuration
"""
object :anonymous_event_creation do
meta(:authorize, :all)
field(:allowed, :boolean, description: "Whether anonymous event creation is enabled")
field(:validation, :anonymous_event_creation_validation,
@@ -229,6 +248,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance anonymous event creation validation configuration
"""
object :anonymous_event_creation_validation do
meta(:authorize, :all)
field(:email, :anonymous_event_creation_validation_email,
description: "The policy to validate anonymous event creations by email"
)
@@ -242,6 +263,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance anonymous event creation email validation configuration
"""
object :anonymous_event_creation_validation_email do
meta(:authorize, :all)
field(:enabled, :boolean,
description: "Whether anonymous event creation with email validation is enabled"
)
@@ -255,6 +278,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance anonymous event creation captcha validation configuration
"""
object :anonymous_event_creation_validation_captcha do
meta(:authorize, :all)
field(:enabled, :boolean,
description: "Whether anonymous event creation with validation by captcha is enabled"
)
@@ -264,6 +289,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Instance anonymous reports
"""
object :anonymous_reports do
meta(:authorize, :all)
field(:allowed, :boolean, description: "Whether anonymous reports are allowed")
end
@@ -271,6 +297,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
A resource provider details
"""
object :resource_provider do
meta(:authorize, :all)
field(:type, :string, description: "The resource provider's type")
field(:endpoint, :string, description: "The resource provider's endpoint")
field(:software, :string, description: "The resource provider's software")
@@ -280,17 +307,22 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
The instance's features
"""
object :features do
meta(:authorize, :all)
field(:groups, :boolean, description: "Whether groups are activated on this instance")
field(:event_creation, :boolean,
description: "Whether event creation is allowed on this instance"
)
field(:antispam, :boolean, description: "Whether anti-spam is activated on this instance")
end
@desc """
The instance's restrictions
"""
object :restrictions do
meta(:authorize, :all)
field(:only_admin_can_create_groups, :boolean,
description: "Whether groups creation is allowed only for admin, not for all users"
)
@@ -304,6 +336,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
The instance's auth configuration
"""
object :auth do
meta(:authorize, :all)
field(:ldap, :boolean, description: "Whether or not LDAP auth is enabled")
field(:database_login, :boolean, description: "Whether or not database login is enabled")
field(:oauth_providers, list_of(:oauth_provider), description: "List of oauth providers")
@@ -313,6 +346,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
An oAuth Provider
"""
object :oauth_provider do
meta(:authorize, :all)
field(:id, :string, description: "The provider ID")
field(:label, :string, description: "The label for the auth provider")
end
@@ -321,21 +355,25 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
An upload limits configuration
"""
object :upload_limits do
meta(:authorize, :all)
field(:default, :integer, description: "The default limitation, in bytes")
field(:avatar, :integer, description: "The avatar limitation, in bytes")
field(:banner, :integer, description: "The banner limitation, in bytes")
end
object :instance_feeds do
meta(:authorize, :all)
field(:enabled, :boolean, description: "Whether the instance-wide feeds are enabled")
end
object :web_push do
meta(:authorize, :all)
field(:enabled, :boolean, description: "Whether the WebPush feature is enabled")
field(:public_key, :string, description: "The server's public WebPush VAPID key")
end
object :analytics do
meta(:authorize, :all)
field(:id, :string, description: "ID of the analytics service")
field(:enabled, :boolean, description: "Whether the service is activated or not")
@@ -352,16 +390,19 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
end
object :analytics_configuration do
meta(:authorize, :all)
field(:key, :string, description: "The key for the analytics configuration element")
field(:value, :string, description: "The value for the analytics configuration element")
field(:type, :analytics_configuration_type, description: "The analytics configuration type")
end
object :search_settings do
meta(:authorize, :all)
field(:global, :global_search_settings, description: "The instance's global search settings")
end
object :global_search_settings do
meta(:authorize, :all)
field(:is_enabled, :boolean, description: "Whether global search is enabled")
field(:is_default, :boolean, description: "Whether global search is the default")
end
@@ -370,6 +411,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Export formats configuration
"""
object :export_formats do
meta(:authorize, :all)
field(:event_participants, list_of(:string),
description: "The list of formats the event participants can be exported to"
)
@@ -379,6 +422,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
Event categories list configuration
"""
object :event_category_option do
meta(:authorize, :all)
field(:id, :string, description: "The ID of the event category")
field(:label, :string, description: "The translated name of the event category")
end
@@ -386,6 +430,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
object :config_queries do
@desc "Get the instance config"
field :config, :config do
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Config.get_config/3)
end
end

View File

@@ -11,6 +11,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
@desc "A comment"
object :comment do
meta(:authorize, :all)
interfaces([:action_log_object, :activity_object])
field(:id, :id, description: "Internal ID for this comment")
field(:uuid, :uuid, description: "An UUID for this comment")
@@ -73,6 +74,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
@desc "A paginated list of comments"
object :paginated_comment_list do
meta(:authorize, :all)
field(:elements, list_of(:comment), description: "A list of comments")
field(:total, :integer, description: "The total number of comments in the list")
end
@@ -81,6 +83,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
@desc "Get replies for thread"
field :thread, type: list_of(:comment) do
arg(:id, non_null(:id), description: "The comment ID")
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Comment.get_thread/3)
end
end
@@ -95,6 +98,13 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
arg(:is_announcement, :boolean, description: "Should this comment be announced to everyone?")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Discussions.Comment,
rule: :"write:comment:create",
args: %{event_id: :event_id}
)
resolve(&Comment.create_comment/3)
end
@@ -106,6 +116,13 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
arg(:is_announcement, :boolean, description: "Should this comment be announced to everyone?")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Discussions.Comment,
rule: :"write:comment:update",
args: %{id: :comment_id}
)
resolve(&Comment.update_comment/3)
end
@@ -113,6 +130,13 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
field :delete_comment, type: :comment do
arg(:comment_id, non_null(:id), description: "The comment ID")
middleware(Rajska.QueryAuthorization,
permit: [:user, :moderator],
scope: Mobilizon.Discussions.Comment,
rule: :"write:comment:delete",
args: %{id: :comment_id}
)
resolve(&Comment.delete_comment/3)
end
end

View File

@@ -11,6 +11,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
@desc "A discussion"
object :discussion do
meta(:authorize, :user)
interfaces([:activity_object])
field(:id, :id, description: "Internal ID for this discussion")
field(:title, :string, description: "The title for this discussion")
@@ -36,6 +37,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
@desc "A paginated list of discussions"
object :paginated_discussion_list do
meta(:authorize, :user)
field(:elements, list_of(:discussion), description: "A list of discussion")
field(:total, :integer, description: "The total number of discussions in the list")
end
@@ -45,6 +47,13 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
field :discussion, type: :discussion do
arg(:id, :id, description: "The discussion's ID")
arg(:slug, :string, description: "The discussion's slug")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Discussions.Discussion,
rule: :"read:group:discussions"
)
resolve(&Discussion.get_discussion/3)
end
end
@@ -56,6 +65,13 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
arg(:text, non_null(:string), description: "The discussion's first comment body")
arg(:actor_id, non_null(:id), description: "The discussion's group ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Discussions.Discussion,
rule: :"write:group:discussion:create",
args: %{actor_id: :actor_id}
)
resolve(&Discussion.create_discussion/3)
end
@@ -63,6 +79,14 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
field :reply_to_discussion, type: :discussion do
arg(:discussion_id, non_null(:id), description: "The discussion's ID")
arg(:text, non_null(:string), description: "The discussion's reply body")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Discussions.Discussion,
rule: :"write:group:discussion:update",
args: %{id: :discussion_id}
)
resolve(&Discussion.reply_to_discussion/3)
end
@@ -70,6 +94,14 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
field :update_discussion, type: :discussion do
arg(:title, non_null(:string), description: "The updated discussion's title")
arg(:discussion_id, non_null(:id), description: "The discussion's ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Discussions.Discussion,
rule: :"write:group:discussion:update",
args: %{id: :discussion_id}
)
resolve(&Discussion.update_discussion/3)
end
@@ -77,6 +109,13 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
field :delete_discussion, type: :discussion do
arg(:discussion_id, non_null(:id), description: "The discussion's ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Discussions.Discussion,
rule: :"write:group:discussion:delete",
args: %{id: :discussion_id}
)
resolve(&Discussion.delete_discussion/3)
end
end

View File

@@ -17,6 +17,8 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
@desc "An event"
object :event do
meta(:authorize, :all)
meta(:scope_field?, true)
interfaces([:action_log_object, :interactable, :activity_object, :event_search_result])
field(:id, :id, description: "Internal ID for this event")
field(:uuid, :uuid, description: "The Event UUID")
@@ -61,10 +63,9 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
description: "The event's organizer (as a person)"
)
field(:tags, list_of(:tag),
resolve: &Tag.list_tags_for_event/3,
description: "The event's tags"
)
field(:tags, list_of(:tag), description: "The event's tags") do
resolve(&Tag.list_tags_for_event/3)
end
field(:category, :event_category, description: "The event's category")
@@ -75,7 +76,10 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
resolve: &Event.stats_participants/3
)
field(:participants, :paginated_participant_list, description: "The event's participants") do
field(:participants, :paginated_participant_list,
description: "The event's participants",
meta: [private: true, rule: :"read:event:participants"]
) do
arg(:page, :integer,
default_value: 1,
description: "The page in the paginated participants list"
@@ -134,12 +138,14 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
@desc "A paginated list of events"
object :paginated_event_list do
meta(:authorize, :all)
field(:elements, list_of(:event), description: "A list of events")
field(:total, :integer, description: "The total number of events in the list")
end
@desc "Participation statistics"
object :participant_stats do
meta(:authorize, :all)
field(:going, :integer, description: "The number of approved participants")
field(:not_approved, :integer, description: "The number of not approved participants")
field(:not_confirmed, :integer, description: "The number of not confirmed participants")
@@ -158,6 +164,7 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
An event offer
"""
object :event_offer do
meta(:authorize, :all)
field(:price, :float, description: "The price amount for this offer")
field(:price_currency, :string, description: "The currency for this price offer")
field(:url, :string, description: "The URL to access to this offer")
@@ -167,6 +174,7 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
An event participation condition
"""
object :event_participation_condition do
meta(:authorize, :all)
field(:title, :string, description: "The title for this condition")
field(:content, :string, description: "The content for this condition")
field(:url, :string, description: "The URL to access this condition")
@@ -201,6 +209,8 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
Event options
"""
object :event_options do
meta(:authorize, :all)
field(:maximum_attendee_capacity, :integer,
description: "The maximum attendee capacity for this event"
)
@@ -307,6 +317,7 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
end
object :event_metadata do
meta(:authorize, :all)
field(:key, :string, description: "The key for the metadata")
field(:title, :string, description: "The title for the metadata")
field(:value, :string, description: "The value for the metadata")
@@ -350,12 +361,15 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
description: "Direction for the sort"
)
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Event.list_events/3)
end
@desc "Get an event by uuid"
field :event, :event do
arg(:uuid, non_null(:uuid), description: "The event's UUID")
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Event.find_event/3)
end
end
@@ -416,6 +430,13 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
arg(:contacts, list_of(:contact), default_value: [], description: "The events contacts")
arg(:language, :string, description: "The event language", default_value: "und")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Events.Event,
rule: :"write:event:create",
args: %{organizer_actor_id: :organizer_actor_id}
)
resolve(&Event.create_event/3)
end
@@ -460,6 +481,13 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
arg(:contacts, list_of(:contact), default_value: [], description: "The events contacts")
arg(:language, :string, description: "The event language", default_value: "und")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Events.Event,
args: %{id: :event_id},
rule: :"write:event:update"
)
resolve(&Event.update_event/3)
end
@@ -467,6 +495,13 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
field :delete_event, :deleted_object do
arg(:event_id, non_null(:id), description: "The event ID to delete")
middleware(Rajska.QueryAuthorization,
permit: [:user, :moderator, :administrator],
scope: Mobilizon.Events.Event,
rule: :"write:event:delete",
args: %{id: :event_id}
)
resolve(&Event.delete_event/3)
end
end

View File

@@ -17,6 +17,8 @@ defmodule Mobilizon.GraphQL.Schema.Events.FeedTokenType do
or an Atom feed for just a profile.
"""
object :feed_token do
meta(:authorize, :user)
field(
:actor,
:actor,
@@ -36,6 +38,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.FeedTokenType do
@desc "Represents a deleted feed_token"
object :deleted_feed_token do
meta(:authorize, :user)
field(:user, :deleted_object, description: "The user that owned the deleted feed token")
field(:actor, :deleted_object, description: "The actor that owned the deleted feed token")
end
@@ -45,6 +48,13 @@ defmodule Mobilizon.GraphQL.Schema.Events.FeedTokenType do
field :create_feed_token, :feed_token do
arg(:actor_id, :id, description: "The actor ID for the feed token")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Events.FeedToken,
rule: :"write:profile:feed_token:create",
args: %{}
)
resolve(&FeedToken.create_feed_token/3)
end
@@ -52,6 +62,13 @@ defmodule Mobilizon.GraphQL.Schema.Events.FeedTokenType do
field :delete_feed_token, :deleted_feed_token do
arg(:token, non_null(:string), description: "The token to delete")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Events.FeedToken,
rule: :"write:feed_token:delete",
args: %{token: :token}
)
resolve(&FeedToken.delete_feed_token/3)
end
end

View File

@@ -12,6 +12,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
@desc "Represents a participant to an event"
object :participant do
meta(:authorize, :all)
field(:id, :id, description: "The participation ID")
field(
@@ -41,6 +42,8 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
Metadata about a participant
"""
object :participant_metadata do
meta(:authorize, :all)
field(:cancellation_token, :string,
description: "The eventual token to leave an event when user is anonymous"
)
@@ -53,6 +56,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
A paginated list of participants
"""
object :paginated_participant_list do
meta(:authorize, :user)
field(:elements, list_of(:participant), description: "A list of participants")
field(:total, :integer, description: "The total number of participants in the list")
end
@@ -78,6 +82,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
@desc "Represents a deleted participant"
object :deleted_participant do
meta(:authorize, :all)
field(:id, :id, description: "The participant ID")
field(:event, :deleted_object, description: "The participant's event")
field(:actor, :deleted_object, description: "The participant's actor")
@@ -92,7 +97,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
arg(:message, :string, description: "The anonymous participant's message")
arg(:locale, :string, description: "The anonymous participant's locale")
arg(:timezone, :string, description: "The anonymous participant's timezone")
middleware(Rajska.QueryAuthorization, permit: :all, rule: :"write:participation")
resolve(&Participant.actor_join_event/3)
end
@@ -101,7 +106,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
arg(:event_id, non_null(:id), description: "The event ID the participant left")
arg(:actor_id, non_null(:id), description: "The actor ID for the participant")
arg(:token, :string, description: "The anonymous participant participation token")
middleware(Rajska.QueryAuthorization, permit: :all, rule: :"write:participation")
resolve(&Participant.actor_leave_event/3)
end
@@ -110,12 +115,19 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
arg(:id, non_null(:id), description: "The participant ID")
arg(:role, non_null(:participant_role_enum), description: "The participant new role")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Events.Participant,
rule: :"write:participation"
)
resolve(&Participant.update_participation/3)
end
@desc "Confirm a participation"
field :confirm_participation, :participant do
arg(:confirmation_token, non_null(:string), description: "The participation token")
middleware(Rajska.QueryAuthorization, permit: :all, rule: :"write:participation")
resolve(&Participant.confirm_participation_from_token/3)
end
@@ -131,6 +143,14 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
)
arg(:format, :export_format_enum, description: "The format in which to return the file")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Events.Event,
rule: :"read:event:participants:export",
args: %{id: :event_id}
)
resolve(&Participant.export_event_participants/3)
end
end

View File

@@ -6,12 +6,14 @@ defmodule Mobilizon.GraphQL.Schema.FollowedGroupActivityType do
@desc "A paginated list of follow group events"
object :paginated_followed_group_events do
meta(:authorize, :user)
field(:elements, list_of(:followed_group_event), description: "A list of follow group events")
field(:total, :integer, description: "The total number of follow group events in the list")
end
@desc "A follow group event"
object :followed_group_event do
meta(:authorize, :user)
field(:user, :user)
field(:profile, :person)
field(:group, :group)

View File

@@ -8,6 +8,7 @@ defmodule Mobilizon.GraphQL.Schema.MediaType do
@desc "A media"
object :media do
meta(:authorize, :all)
field(:id, :id, description: "The media's ID")
field(:alt, :string, description: "The media's alternative text")
field(:name, :string, description: "The media's name")
@@ -21,6 +22,7 @@ defmodule Mobilizon.GraphQL.Schema.MediaType do
A paginated list of medias
"""
object :paginated_media_list do
meta(:authorize, :all)
field(:elements, list_of(:media), description: "The list of medias")
field(:total, :integer, description: "The total number of medias in the list")
end
@@ -29,6 +31,7 @@ defmodule Mobilizon.GraphQL.Schema.MediaType do
Some metadata associated with a media
"""
object :media_metadata do
meta(:authorize, :all)
field(:width, :integer, description: "The media width (if a picture)")
field(:height, :integer, description: "The media width (if a height)")
field(:blurhash, :string, description: "The media blurhash (if a picture")
@@ -54,6 +57,7 @@ defmodule Mobilizon.GraphQL.Schema.MediaType do
@desc "Get a media"
field :media, :media do
arg(:id, non_null(:id), description: "The media ID")
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Media.media/3)
end
end
@@ -64,6 +68,15 @@ defmodule Mobilizon.GraphQL.Schema.MediaType do
arg(:name, non_null(:string), description: "The media's name")
arg(:alt, :string, description: "The media's alternative text")
arg(:file, non_null(:upload), description: "The media file")
arg(:actor_id, :id, description: "The actor that uploads the media")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Medias.Media,
rule: :"write:media:upload",
args: %{}
)
resolve(&Media.upload_media/3)
end
@@ -72,6 +85,13 @@ defmodule Mobilizon.GraphQL.Schema.MediaType do
"""
field :remove_media, :deleted_object do
arg(:id, non_null(:id), description: "The media's ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Medias.Media,
rule: :"write:media:remove"
)
resolve(&Media.remove_media/3)
end
end

View File

@@ -7,6 +7,7 @@ defmodule Mobilizon.GraphQL.Schema.PostType do
@desc "A post"
object :post do
meta(:authorize, :all)
interfaces([:activity_object])
field(:id, :id, description: "The post's ID")
field(:title, :string, description: "The post's title")
@@ -22,21 +23,20 @@ defmodule Mobilizon.GraphQL.Schema.PostType do
field(:updated_at, :datetime, description: "The post's last update date")
field(:language, :string, description: "The post language")
field(:tags, list_of(:tag),
resolve: &Tag.list_tags_for_post/3,
description: "The post's tags"
)
field(:tags, list_of(:tag), description: "The post's tags") do
resolve(&Tag.list_tags_for_post/3)
end
field(:picture, :media,
description: "The posts's media",
resolve: &Media.media/3
)
field(:picture, :media, description: "The posts's media") do
resolve(&Media.media/3)
end
end
@desc """
A paginated list of posts
"""
object :paginated_post_list do
meta(:authorize, :all)
field(:elements, list_of(:post), description: "A list of posts")
field(:total, :integer, description: "The total number of posts in the list")
end
@@ -56,6 +56,7 @@ defmodule Mobilizon.GraphQL.Schema.PostType do
@desc "Get a post"
field :post, :post do
arg(:slug, non_null(:string), description: "The post's slug")
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Post.get_post/3)
end
end
@@ -84,6 +85,13 @@ defmodule Mobilizon.GraphQL.Schema.PostType do
"The banner for the post, either as an object or directly the ID of an existing media"
)
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Posts.Post,
rule: :"write:group:post:create",
args: %{}
)
resolve(&Post.create_post/3)
end
@@ -108,12 +116,25 @@ defmodule Mobilizon.GraphQL.Schema.PostType do
"The banner for the post, either as an object or directly the ID of an existing media"
)
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Posts.Post,
rule: :"write:group:post:update"
)
resolve(&Post.update_post/3)
end
@desc "Delete a post"
field :delete_post, :deleted_object do
arg(:id, non_null(:id), description: "The post's ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Posts.Post,
rule: :"write:group:post:delete"
)
resolve(&Post.delete_post/3)
end
end

View File

@@ -11,11 +11,12 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
@desc "A report object"
object :report do
meta(:authorize, :all)
interfaces([:action_log_object])
field(:id, :id, description: "The internal ID of the report")
field(:content, :string, description: "The comment the reporter added about this report")
field(:status, :report_status, description: "Whether the report is still active")
field(:uri, :string, description: "The URI of the report")
field(:uri, :string, description: "The URI of the report", meta: [private: true])
field(:reported, :actor, description: "The actor that is being reported")
field(:reporter, :actor, description: "The actor that created the report")
field(:event, :event, description: "The event that is being reported")
@@ -23,6 +24,7 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
field(:notes, list_of(:report_note),
description: "The notes made on the event",
meta: [private: true],
resolve: dataloader(Reports)
)
@@ -31,12 +33,14 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
end
object :paginated_report_list do
meta(:authorize, :moderator)
field(:elements, list_of(:report), description: "A list of reports")
field(:total, :integer, description: "The total number of reports in the list")
end
@desc "A report note object"
object :report_note do
meta(:authorize, :moderator)
interfaces([:action_log_object])
field(:id, :id, description: "The internal ID of the report note")
field(:content, :string, description: "The content of the note")
@@ -73,12 +77,20 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
arg(:limit, :integer, default_value: 10, description: "The limit of reports per page")
arg(:status, :report_status, default_value: :open, description: "Filter reports by status")
arg(:domain, :string, default_value: nil, description: "Filter reports by domain name")
middleware(Rajska.QueryAuthorization,
permit: :moderator,
scope: Mobilizon.Reports.Report,
args: %{}
)
resolve(&Report.list_reports/3)
end
@desc "Get a report by id"
field :report, :report do
arg(:id, non_null(:id), description: "The report ID")
middleware(Rajska.QueryAuthorization, permit: :moderator, scope: Mobilizon.Reports.Report)
resolve(&Report.get_report/3)
end
end
@@ -101,6 +113,8 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
"Whether to forward the report to the original instance if the content is remote"
)
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Report.create_report/3)
end
@@ -113,6 +127,12 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
description: "The feedback to send to the anti-spam system"
)
middleware(Rajska.QueryAuthorization,
permit: :moderator,
scope: Mobilizon.Reports.Report,
args: %{id: :report_id}
)
resolve(&Report.update_report/3)
end
@@ -120,12 +140,26 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
field :create_report_note, type: :report_note do
arg(:content, :string, description: "The note's content")
arg(:report_id, non_null(:id), description: "The report's ID")
middleware(Rajska.QueryAuthorization,
permit: :moderator,
scope: Mobilizon.Reports.Report,
args: %{id: :report_id}
)
resolve(&Report.create_report_note/3)
end
@desc "Delete a note on a report"
field :delete_report_note, type: :deleted_object do
arg(:note_id, non_null(:id), description: "The note's ID")
middleware(Rajska.QueryAuthorization,
permit: :moderator,
scope: Mobilizon.Reports.Note,
args: %{id: :note_id}
)
resolve(&Report.delete_report_note/3)
end
end

View File

@@ -9,6 +9,7 @@ defmodule Mobilizon.GraphQL.Schema.ResourceType do
@desc "A resource"
object :resource do
meta(:authorize, :user)
interfaces([:activity_object])
field(:id, :id, description: "The resource's ID")
field(:title, :string, description: "The resource's title")
@@ -44,6 +45,7 @@ defmodule Mobilizon.GraphQL.Schema.ResourceType do
A paginated list of resources
"""
object :paginated_resource_list do
meta(:authorize, :user)
field(:elements, list_of(:resource), description: "A list of resources")
field(:total, :integer, description: "The total number of resources in the list")
end
@@ -52,6 +54,7 @@ defmodule Mobilizon.GraphQL.Schema.ResourceType do
The metadata associated to the resource
"""
object :resource_metadata do
meta(:authorize, :user)
field(:type, :string, description: "The type of the resource")
field(:title, :string, description: "The resource's metadata title")
field(:description, :string, description: "The resource's metadata description")
@@ -84,6 +87,13 @@ defmodule Mobilizon.GraphQL.Schema.ResourceType do
description: "The federated username for the group resource"
)
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Resources.Resource,
rule: :"read:group:resources",
args: %{path: :path}
)
resolve(&Resource.get_resource/3)
end
end
@@ -101,6 +111,13 @@ defmodule Mobilizon.GraphQL.Schema.ResourceType do
arg(:resource_url, :string, description: "This resource's own original URL")
arg(:type, :string, default_value: "link", description: "The type for this resource")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Resources.Resource,
rule: :"write:group:resources:create",
args: %{actor_id: :actor_id}
)
resolve(&Resource.create_resource/3)
end
@@ -112,18 +129,39 @@ defmodule Mobilizon.GraphQL.Schema.ResourceType do
arg(:parent_id, :id, description: "The new resource parent ID (if the resource is moved)")
arg(:resource_url, :string, description: "The new resource URL")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Resources.Resource,
rule: :"write:group:resources:update"
)
resolve(&Resource.update_resource/3)
end
@desc "Delete a resource"
field :delete_resource, :deleted_object do
arg(:id, non_null(:id), description: "The resource ID")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Resources.Resource,
rule: :"write:group:resources:delete"
)
resolve(&Resource.delete_resource/3)
end
@desc "Get a preview for a resource link"
field :preview_resource_link, :resource_metadata do
arg(:resource_url, non_null(:string), description: "The link to crawl to get of preview of")
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: Mobilizon.Resources.Resource,
rule: :"read:group:resources",
args: %{}
)
resolve(&Resource.preview_resource_link/3)
end
end

View File

@@ -10,6 +10,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
alias Mobilizon.Service.GlobalSearch.{EventResult, GroupResult}
interface :event_search_result do
meta(:authorize, :all)
field(:id, :id, description: "Internal ID for this event")
field(:uuid, :uuid, description: "The Event UUID")
field(:url, :string, description: "The ActivityPub Event URL")
@@ -43,6 +44,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
@desc "Search event result"
object :event_result do
meta(:authorize, :all)
interfaces([:event_search_result])
field(:id, :id, description: "Internal ID for this event")
field(:uuid, :uuid, description: "The Event UUID")
@@ -65,6 +67,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
end
interface :group_search_result do
meta(:authorize, :all)
field(:id, :id, description: "Internal ID for this group")
field(:url, :string, description: "The ActivityPub actor's URL")
field(:type, :actor_type, description: "The type of Actor (Person, Group,…)")
@@ -92,6 +95,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
@desc "Search group result"
object :group_result do
meta(:authorize, :all)
interfaces([:group_search_result])
field(:id, :id, description: "Internal ID for this group")
field(:url, :string, description: "The ActivityPub actor's URL")
@@ -109,18 +113,21 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
@desc "Search persons result"
object :persons do
meta(:authorize, [:administrator, :moderator])
field(:total, non_null(:integer), description: "Total elements")
field(:elements, non_null(list_of(:person)), description: "Person elements")
end
@desc "Search groups result"
object :groups do
meta(:authorize, :all)
field(:total, non_null(:integer), description: "Total elements")
field(:elements, non_null(list_of(:group_search_result)), description: "Group elements")
end
@desc "Search events result"
object :events do
meta(:authorize, :all)
field(:total, non_null(:integer), description: "Total elements")
field(:elements, non_null(list_of(:event_search_result)), description: "Event elements")
end
@@ -179,7 +186,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
arg(:term, :string, default_value: "", description: "Search term")
arg(:page, :integer, default_value: 1, description: "Result page")
arg(:limit, :integer, default_value: 10, description: "Results limit per page")
middleware(Rajska.QueryAuthorization, permit: [:administrator, :moderator], scope: false)
resolve(&Search.search_persons/3)
end
@@ -225,6 +232,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
description: "How to sort search results"
)
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Search.search_groups/3)
end
@@ -275,13 +283,14 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
description: "How to sort search results"
)
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Search.search_events/3)
end
@desc "Interact with an URI"
field :interact, :interactable do
arg(:uri, non_null(:string), description: "The URI for to interact with")
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Search.interact/3)
end
end

View File

@@ -8,6 +8,7 @@ defmodule Mobilizon.GraphQL.Schema.StatisticsType do
@desc "A statistics object"
object :statistics do
meta(:authorize, :all)
# Instance name
field(:number_of_users, :integer, description: "The number of local users")
field(:number_of_events, :integer, description: "The total number of events")
@@ -27,6 +28,7 @@ defmodule Mobilizon.GraphQL.Schema.StatisticsType do
end
object :category_statistics do
meta(:authorize, :all)
field(:key, :string, description: "The key for the category")
field(:number, :integer, description: "The number of events for the given category")
end
@@ -34,11 +36,13 @@ defmodule Mobilizon.GraphQL.Schema.StatisticsType do
object :statistics_queries do
@desc "Get the instance statistics"
field :statistics, :statistics do
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Statistics.get_statistics/3)
end
@desc "Get the instance's category statistics"
field :category_statistics, list_of(:category_statistics) do
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&Statistics.get_category_statistics/3)
end
end

View File

@@ -8,6 +8,7 @@ defmodule Mobilizon.GraphQL.Schema.TagType do
@desc "A tag"
object :tag do
meta(:authorize, :all)
field(:id, :id, description: "The tag's ID")
field(:slug, :string, description: "The tags's slug")
field(:title, :string, description: "The tag's title")
@@ -26,6 +27,7 @@ defmodule Mobilizon.GraphQL.Schema.TagType do
arg(:filter, :string, description: "The filter to apply to the search")
arg(:page, :integer, default_value: 1, description: "The page in the paginated tags list")
arg(:limit, :integer, default_value: 10, description: "The limit of tags per page")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&Tag.list_tags/3)
end
end

View File

@@ -9,6 +9,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoType do
@desc "A todo"
object :todo do
meta(:authorize, :user)
field(:id, :id, description: "The todo's ID")
field(:title, :string, description: "The todo's title")
field(:status, :boolean, description: "The todo's status")
@@ -30,6 +31,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoType do
A paginated list of todos
"""
object :paginated_todo_list do
meta(:authorize, :user)
field(:elements, list_of(:todo), description: "A list of todos")
field(:total, :integer, description: "The total number of todos in the list")
end
@@ -38,6 +40,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoType do
@desc "Get a todo"
field :todo, :todo do
arg(:id, non_null(:id), description: "The todo ID")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&TodoResolver.get_todo/3)
end
end
@@ -50,6 +53,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoType do
arg(:status, :boolean, description: "The todo status")
arg(:due_date, :datetime, description: "The todo due date")
arg(:assigned_to_id, :id, description: "The actor this todo is assigned to")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&TodoResolver.create_todo/3)
end
@@ -62,7 +66,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoType do
arg(:status, :boolean, description: "The new todo status")
arg(:due_date, :datetime, description: "The new todo due date")
arg(:assigned_to_id, :id, description: "The new id of the actor this todo is assigned to")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&TodoResolver.update_todo/3)
end

View File

@@ -9,6 +9,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoListType do
@desc "A todo list"
object :todo_list do
meta(:authorize, :user)
field(:id, :id, description: "The todo list's ID")
field(:title, :string, description: "The todo list's title")
@@ -37,6 +38,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoListType do
A paginated list of todo-lists
"""
object :paginated_todo_list_list do
meta(:authorize, :user)
field(:elements, list_of(:todo_list), description: "A list of todo lists")
field(:total, :integer, description: "The total number of todo lists in the list")
end
@@ -45,6 +47,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoListType do
@desc "Get a todo list"
field :todo_list, :todo_list do
arg(:id, non_null(:id), description: "The todo-list ID")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&Todos.get_todo_list/3)
end
end
@@ -54,6 +57,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoListType do
field :create_todo_list, :todo_list do
arg(:title, non_null(:string), description: "The todo list title")
arg(:group_id, non_null(:id), description: "The group ID")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&Todos.create_todo_list/3)
end
end

View File

@@ -15,6 +15,8 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
@desc "A local user of Mobilizon"
object :user do
meta(:authorize, :all)
meta(:scope_field?, true)
interfaces([:action_log_object])
field(:id, :id, description: "The user's ID")
field(:email, non_null(:string), description: "The user's email")
@@ -63,7 +65,8 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
field(:disabled, :boolean, description: "Whether the user is disabled")
field(:participations, :paginated_participant_list,
description: "The list of participations this user has"
description: "The list of participations this user has",
meta: [private: true]
) do
arg(:after_datetime, :datetime, description: "Filter participations by event start datetime")
@@ -83,7 +86,8 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
end
field(:memberships, :paginated_member_list,
description: "The list of memberships for this user"
description: "The list of memberships for this user",
meta: [private: true]
) do
arg(:name, :string, description: "A name to filter members by")
@@ -97,7 +101,8 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
end
field(:drafts, :paginated_event_list,
description: "The list of draft events this user has created"
description: "The list of draft events this user has created",
meta: [private: true]
) do
arg(:page, :integer,
default_value: 1,
@@ -109,7 +114,8 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
end
field(:followed_group_events, :paginated_followed_group_events,
description: "The suggested events from the groups this user follows"
description: "The suggested events from the groups this user follows",
meta: [private: true]
) do
arg(:page, :integer,
default_value: 1,
@@ -128,7 +134,10 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
resolve(&User.user_followed_group_events/3)
end
field(:settings, :user_settings, description: "The list of settings for this user") do
field(:settings, :user_settings,
description: "The list of settings for this user",
meta: [private: true]
) do
resolve(&User.user_settings/3)
end
@@ -142,7 +151,10 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
description: "The IP adress the user's currently signed-in with"
)
field(:media, :paginated_media_list, description: "The user's media objects") do
field(:media, :paginated_media_list,
description: "The user's media objects",
meta: [private: true]
) do
arg(:page, :integer,
default_value: 1,
description: "The page in the paginated user media list"
@@ -158,14 +170,18 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
)
field(:activity_settings, list_of(:activity_setting),
resolve: &ActivitySettings.user_activity_settings/3,
description: "The user's activity settings"
)
description: "The user's activity settings",
meta: [private: true]
) do
resolve(&ActivitySettings.user_activity_settings/3)
end
field(:auth_authorized_applications, list_of(:auth_application_token),
resolve: &Application.get_user_applications/3,
description: "The user's authorized authentication apps"
)
description: "The user's authorized authentication apps",
meta: [private: true, rule: :forbid_app_access]
) do
resolve(&Application.get_user_applications/3)
end
end
@desc "The list of roles an user can have"
@@ -177,12 +193,14 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
@desc "Token"
object :refreshed_token do
meta(:authorize, :all)
field(:access_token, non_null(:string), description: "Generated access token")
field(:refresh_token, non_null(:string), description: "Generated refreshed token")
end
@desc "Users list"
object :users do
meta(:authorize, [:administrator, :moderator])
field(:total, non_null(:integer), description: "Total elements")
field(:elements, non_null(list_of(:user)), description: "User elements")
end
@@ -196,6 +214,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
A set of user settings
"""
object :user_settings do
meta(:authorize, :user)
field(:timezone, :string, description: "The timezone for this user")
field(:notification_on_day, :boolean,
@@ -254,6 +273,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
end
object :location do
meta(:authorize, :user)
field(:range, :integer, description: "The range in kilometers the user wants to see events")
field(:geohash, :string, description: "A geohash representing the user's preferred location")
@@ -276,11 +296,13 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
@desc "Get an user"
field :user, :user do
arg(:id, non_null(:id))
middleware(Rajska.QueryAuthorization, permit: [:administrator, :moderator], scope: false)
resolve(&User.find_user/3)
end
@desc "Get the current user"
field :logged_user, :user do
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&User.get_current_user/3)
end
@@ -297,7 +319,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
arg(:sort, :sortable_user_field, default_value: :id, description: "Sort column")
arg(:direction, :sort_direction, default_value: :desc, description: "Sort direction")
middleware(Rajska.QueryAuthorization, permit: [:administrator, :moderator], scope: false)
resolve(&User.list_users/3)
end
end
@@ -308,7 +330,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
arg(:email, non_null(:string), description: "The new user's email")
arg(:password, non_null(:string), description: "The new user's password")
arg(:locale, :string, description: "The new user's locale")
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&User.create_user/3)
end
@@ -318,6 +340,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
description: "The token that will be used to validate the user"
)
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&User.validate_user/3)
end
@@ -325,6 +348,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
field :resend_confirmation_email, type: :string do
arg(:email, non_null(:string), description: "The email used to register")
arg(:locale, :string, description: "The user's locale")
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&User.resend_confirmation_email/3)
end
@@ -332,6 +356,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
field :send_reset_password, type: :string do
arg(:email, non_null(:string), description: "The user's email")
arg(:locale, :string, description: "The user's locale")
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&User.send_reset_password/3)
end
@@ -343,6 +368,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
arg(:password, non_null(:string), description: "The new password")
arg(:locale, :string, default_value: "en", description: "The user's locale")
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&User.reset_password/3)
end
@@ -350,24 +376,28 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
field :login, type: :login do
arg(:email, non_null(:string), description: "The user's email")
arg(:password, non_null(:string), description: "The user's password")
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&User.login_user/3)
end
@desc "Refresh a token"
field :refresh_token, type: :refreshed_token do
arg(:refresh_token, non_null(:string), description: "A refresh token")
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&User.refresh_token/3)
end
@desc "Logout an user, deleting a refresh token"
field :logout, :string do
arg(:refresh_token, non_null(:string))
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&User.logout/3)
end
@desc "Change default actor for user"
field :change_default_actor, :user do
arg(:preferred_username, non_null(:string), description: "The actor preferred_username")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&User.change_default_actor/3)
end
@@ -375,6 +405,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
field :change_password, :user do
arg(:old_password, non_null(:string), description: "The user's current password")
arg(:new_password, non_null(:string), description: "The user's new password")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&User.change_password/3)
end
@@ -382,6 +413,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
field :change_email, :user do
arg(:email, non_null(:string), description: "The user's new email")
arg(:password, non_null(:string), description: "The user's current password")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&User.change_email/3)
end
@@ -391,6 +423,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
description: "The token that will be used to validate the email change"
)
middleware(Rajska.QueryAuthorization, permit: :all)
resolve(&User.validate_email/3)
end
@@ -398,6 +431,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
field :delete_account, :deleted_object do
arg(:password, :string, description: "The user's password")
arg(:user_id, :id, description: "The user's ID")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&User.delete_account/3)
end
@@ -435,12 +469,14 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
description: "A geohash of the user's preferred location, where they want to see events"
)
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&User.set_user_setting/3)
end
@desc "Update the user's locale"
field :update_locale, :user do
arg(:locale, :string, description: "The user's new locale")
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
resolve(&User.update_locale/3)
end
end

View File

@@ -6,6 +6,7 @@ defmodule Mobilizon.GraphQL.Schema.Users.ActivitySetting do
alias Mobilizon.GraphQL.Resolvers.Users.ActivitySettings
object :activity_setting do
meta(:authorize, :user)
field(:key, :string)
field(:method, :string)
field(:enabled, :boolean)
@@ -17,6 +18,13 @@ defmodule Mobilizon.GraphQL.Schema.Users.ActivitySetting do
arg(:key, non_null(:string))
arg(:method, non_null(:string))
arg(:enabled, non_null(:boolean))
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: false,
rule: :"write:user:setting:activity"
)
resolve(&ActivitySettings.upsert_user_activity_setting/3)
end
end

View File

@@ -26,11 +26,25 @@ defmodule Mobilizon.GraphQL.Schema.Users.PushSubscription do
arg(:endpoint, non_null(:string))
arg(:auth, non_null(:string))
arg(:p256dh, non_null(:string))
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: false,
rule: :"write:user:setting:push"
)
resolve(&PushSubscription.register_push_subscription/3)
end
field :unregister_push, :string do
arg(:endpoint, non_null(:string))
middleware(Rajska.QueryAuthorization,
permit: :user,
scope: false,
rule: :"write:user:setting:push"
)
resolve(&PushSubscription.unregister_push_subscription/3)
end
end

View File

@@ -12,6 +12,7 @@ defmodule Mobilizon.Applications do
defenum(ApplicationDeviceActivationStatus, [
"success",
"pending",
"confirmed",
"incorrect_device_code",
"access_denied"
])
@@ -144,12 +145,15 @@ defmodule Mobilizon.Applications do
"""
def list_application_tokens do
Repo.all(ApplicationToken)
ApplicationToken
|> Repo.all()
|> Repo.preload(:application)
end
@doc """
Returns the list of application tokens for a given user_id
"""
@spec list_application_tokens_for_user_id(number() | String.t()) :: list(ApplicationToken.t())
def list_application_tokens_for_user_id(user_id) do
ApplicationToken
|> where(user_id: ^user_id)
@@ -172,7 +176,11 @@ defmodule Mobilizon.Applications do
** (Ecto.NoResultsError)
"""
def get_application_token!(id), do: Repo.get!(ApplicationToken, id)
def get_application_token!(id) do
ApplicationToken
|> Repo.get!(id)
|> Repo.preload([:application])
end
@doc """
Gets a single application_token.
@@ -211,6 +219,13 @@ defmodule Mobilizon.Applications do
%ApplicationToken{}
|> ApplicationToken.changeset(attrs)
|> Repo.insert(on_conflict: :replace_all, conflict_target: [:user_id, :application_id])
|> case do
{:ok, application_token} ->
{:ok, Repo.preload(application_token, :application)}
error ->
error
end
end
@doc """
@@ -303,10 +318,14 @@ defmodule Mobilizon.Applications do
def get_application_device_activation(id), do: Repo.get(ApplicationDeviceActivation, id)
def get_application_device_activation_by_user_code(user_code),
do: Repo.get_by(ApplicationDeviceActivation, user_code: user_code)
def get_application_device_activation_by_user_code(user_code) do
ApplicationDeviceActivation
|> where([ada], ada.user_code == ^user_code)
|> preload(:application)
|> Repo.one()
end
def get_application_device_activation(client_id, device_code) do
def get_application_device_activation_by_device_code(client_id, device_code) do
ApplicationDeviceActivation
|> join(:left, [ada], a in assoc(ada, :application))
|> where([_, a], a.client_id == ^client_id)

View File

@@ -6,16 +6,16 @@ defmodule Mobilizon.Applications.Application do
use Ecto.Schema
import Ecto.Changeset
@required_attrs [:name, :client_id, :client_secret, :redirect_uris]
@optional_attrs [:scopes, :website, :owner_type, :owner_id]
@required_attrs [:name, :client_id, :client_secret, :redirect_uris, :scope]
@optional_attrs [:website, :owner_type, :owner_id]
@attrs @required_attrs ++ @optional_attrs
schema "applications" do
field(:name, :string)
field(:client_id, :string)
field(:client_secret, :string)
field(:redirect_uris, :string)
field(:scopes, :string)
field(:redirect_uris, {:array, :string})
field(:scope, :string)
field(:website, :string)
field(:owner_type, :string)
field(:owner_id, :integer)

View File

@@ -19,7 +19,7 @@ defmodule Mobilizon.Applications.ApplicationDeviceActivation do
timestamps()
end
@required_attrs [:user_code, :device_code, :expires_in, :application_id]
@required_attrs [:user_code, :device_code, :expires_in, :application_id, :scope]
@optional_attrs [:status, :user_id]
@attrs @required_attrs ++ @optional_attrs

View File

@@ -11,7 +11,7 @@ defmodule Mobilizon.Applications.ApplicationToken do
belongs_to(:user, User)
belongs_to(:application, Application)
field(:authorization_code, :string)
field(:status, ApplicationTokenStatus)
field(:status, ApplicationTokenStatus, default: :pending)
field(:scope, :string)
timestamps()

View File

@@ -4,9 +4,12 @@ defmodule Mobilizon.Service.Auth.Applications do
"""
alias Mobilizon.Applications
alias Mobilizon.Applications.{Application, ApplicationDeviceActivation, ApplicationToken}
alias Mobilizon.GraphQL.Authorization.AppScope
alias Mobilizon.Service.Auth.Authenticator
alias Mobilizon.Users.User
alias Mobilizon.Web.Auth.Guardian
alias Mobilizon.Web.Router.Helpers, as: Routes
require Logger
@app_access_tokens_ttl {8, :hour}
@app_refresh_tokens_ttl {26, :week}
@@ -20,37 +23,43 @@ defmodule Mobilizon.Service.Auth.Applications do
required(:token_type) => String.t()
}
def create(name, redirect_uris, scopes, website) do
client_id = :crypto.strong_rand_bytes(42) |> Base.encode64() |> binary_part(0, 42)
client_secret = :crypto.strong_rand_bytes(42) |> Base.encode64() |> binary_part(0, 42)
@spec create(String.t(), list(String.t()), String.t(), String.t() | nil) ::
{:ok, Application.t()} | {:error, Ecto.Changeset.t()} | {:error, :invalid_scope}
def create(name, redirect_uris, scope, website \\ nil) do
if AppScope.scopes_valid?(scope) do
client_id = :crypto.strong_rand_bytes(42) |> Base.encode64() |> binary_part(0, 42)
client_secret = :crypto.strong_rand_bytes(42) |> Base.encode64() |> binary_part(0, 42)
Applications.create_application(%{
name: name,
redirect_uris: redirect_uris,
scopes: scopes,
website: website,
client_id: client_id,
client_secret: client_secret
})
Applications.create_application(%{
name: name,
redirect_uris: redirect_uris,
scope: scope,
website: website,
client_id: client_id,
client_secret: client_secret
})
else
{:error, :invalid_scope}
end
end
@spec autorize(String.t(), String.t(), String.t(), integer()) ::
{:ok, String.t()}
{:ok, ApplicationToken.t()}
| {:error, :application_not_found}
| {:error, :redirect_uri_not_in_allowed}
def autorize(client_id, redirect_uri, _scope, user_id) do
| {:error, Ecto.Changeset.t()}
def autorize(client_id, redirect_uri, scope, user_id) do
with %Application{redirect_uris: redirect_uris, id: app_id} <-
Applications.get_application_by_client_id(client_id),
{:redirect_uri, true} <-
{:redirect_uri, redirect_uri in String.split(redirect_uris, "\n")},
code <- :crypto.strong_rand_bytes(16) |> Base.encode64() |> binary_part(0, 16),
{:ok, %ApplicationToken{}} <-
Applications.create_application_token(%{
user_id: user_id,
application_id: app_id,
authorization_code: code
}) do
{:ok, code}
{:redirect_uri, redirect_uri in redirect_uris},
code <- :crypto.strong_rand_bytes(16) |> Base.encode64() |> binary_part(0, 16) do
Applications.create_application_token(%{
user_id: user_id,
application_id: app_id,
authorization_code: code,
scope: scope
})
else
nil ->
{:error, :application_not_found}
@@ -60,35 +69,62 @@ defmodule Mobilizon.Service.Auth.Applications do
end
end
@spec autorize_device_application(String.t(), String.t()) ::
{:ok, ApplicationDeviceActivation.t()}
| {:error, Ecto.Changeset.t()}
| {:error, :expired}
| {:error, :access_denied}
| {:error, :not_found}
def autorize_device_application(client_id, user_code) do
case Applications.get_application_device_activation(client_id, user_code) do
%ApplicationDeviceActivation{status: :confirmed} = app_device_activation ->
Applications.update_application_device_activation(app_device_activation, %{
status: :success
})
Logger.debug(
"Authorizing device application client_id: #{client_id}, user_code: #{user_code}"
)
case Applications.get_application_device_activation_by_user_code(user_code) do
%ApplicationDeviceActivation{
status: :confirmed,
application: %Application{client_id: ^client_id}
} = app_device_activation ->
if device_activation_expired?(app_device_activation) do
{:error, :expired}
else
Applications.update_application_device_activation(app_device_activation, %{
status: :success
})
end
# The device activation is confirmed, but does not match the given app client_id, so we say it's not found
%ApplicationDeviceActivation{status: :confirmed} ->
{:error, :not_found}
%ApplicationDeviceActivation{} ->
{:error, :not_confirmed}
nil ->
{:error, :not_found}
end
end
@spec generate_access_token(String.t(), String.t(), String.t(), String.t()) ::
@spec generate_access_token(String.t(), String.t(), String.t(), String.t(), String.t()) ::
{:ok, access_token_details()}
| {:error,
:application_not_found
| :redirect_uri_not_in_allowed
| :provided_code_does_not_match
| :invalid_client_secret
| :app_token_not_found
| :invalid_or_expired
| any()}
def generate_access_token(client_id, client_secret, code, redirect_uri) do
def generate_access_token(client_id, client_secret, code, redirect_uri, scope) do
with {:application,
%Application{
id: application_id,
client_secret: app_client_secret,
scopes: scopes,
redirect_uris: redirect_uris
}} <-
{:application, Applications.get_application_by_client_id(client_id)},
# TODO: check that app token scope still are acceptable
{:redirect_uri, true} <-
{:redirect_uri, redirect_uri in String.split(redirect_uris, "\n")},
{:redirect_uri, redirect_uri in redirect_uris},
{:app_token, %ApplicationToken{} = app_token} <-
{:app_token, Applications.get_application_token_by_authorization_code(code)},
{:ok, %ApplicationToken{application_id: application_id_from_token} = app_token} <-
@@ -105,7 +141,7 @@ defmodule Mobilizon.Service.Auth.Applications do
expires_in: ttl_to_seconds(@app_access_tokens_ttl),
refresh_token: refresh_token,
refresh_token_expires_in: ttl_to_seconds(@app_refresh_tokens_ttl),
scope: scopes,
scope: scope,
token_type: "bearer"
}}
else
@@ -122,15 +158,20 @@ defmodule Mobilizon.Service.Auth.Applications do
{:error, :redirect_uri_not_in_allowed}
{:app_token, _} ->
{:error, :app_token_not_found}
{:error, :invalid_or_expired}
{:error, err} ->
{:error, err}
end
end
def generate_access_token(client_id, device_code) do
case Applications.get_application_device_activation(client_id, device_code) do
def generate_access_token_for_device_flow(client_id, device_code) do
Logger.debug("Generating access token for application device with",
client_id: client_id,
device_code: device_code
)
case Applications.get_application_device_activation_by_device_code(client_id, device_code) do
%ApplicationDeviceActivation{status: :success, scope: scope, user_id: user_id} =
app_device_activation ->
if device_activation_expired?(app_device_activation) do
@@ -164,13 +205,17 @@ defmodule Mobilizon.Service.Auth.Applications do
end
%ApplicationDeviceActivation{status: :incorrect_device_code} ->
Logger.error("Incorrect device code set")
{:error, :incorrect_device_code}
%ApplicationDeviceActivation{status: :access_denied} ->
{:error, :access_denied}
nil ->
Logger.error("nil returned")
{:error, :incorrect_device_code}
err ->
require Logger
Logger.error(inspect(err))
{:error, :incorrect_device_code}
end
@@ -191,35 +236,42 @@ defmodule Mobilizon.Service.Auth.Applications do
@spec register_device_code(String.t(), String.t() | nil) ::
{:ok, ApplicationDeviceActivation.t()}
| {:error, :application_not_found}
| {:error, :scope_not_included}
| {:error, Ecto.Changeset.t()}
def register_device_code(client_id, scope) do
%Application{} = application = Applications.get_application_by_client_id(client_id)
device_code = string_of_length(40)
user_code = string_of_length(8)
verification_uri = Routes.page_url(Mobilizon.Web.Endpoint, :auth_device)
expires_in = @expires_in
interval = @interval
case Applications.create_application_device_activation(%{
device_code: device_code,
user_code: user_code,
expires_in: expires_in,
application_id: application.id,
scope: scope
}) do
{:ok, %ApplicationDeviceActivation{} = application_device_activation} ->
{:ok,
application_device_activation
|> Map.from_struct()
|> Map.take([:device_code, :user_code, :expires_in])
|> Map.update!(:user_code, &user_code_displayed/1)
|> Map.merge(%{
interval: interval,
verification_uri: verification_uri
})}
with {:app, %Application{scope: app_scope} = application} <-
{:app, Applications.get_application_by_client_id(client_id)},
{device_code, user_code, verification_uri} <-
{string_of_length(40), string_of_length(8),
Routes.page_url(Mobilizon.Web.Endpoint, :auth_device)},
{:scope_included, true} <- {:scope_included, request_scope_valid?(app_scope, scope)},
{:ok, %ApplicationDeviceActivation{} = application_device_activation} <-
Applications.create_application_device_activation(%{
device_code: device_code,
user_code: user_code,
expires_in: @expires_in,
application_id: application.id,
scope: scope
}) do
{:ok,
application_device_activation
|> Map.from_struct()
|> Map.take([:device_code, :user_code, :expires_in])
|> Map.update!(:user_code, &user_code_displayed/1)
|> Map.merge(%{
interval: @interval,
verification_uri: verification_uri
})}
else
{:error, %Ecto.Changeset{} = err} ->
{:error, err}
{:app, nil} ->
{:error, :application_not_found}
{:scope_included, false} ->
{:error, :scope_not_included}
end
end
@@ -245,6 +297,45 @@ defmodule Mobilizon.Service.Auth.Applications do
end
end
@spec refresh_tokens(String.t(), String.t(), String.t()) ::
{:ok, access_token_details()}
| {:error, :invalid_client_credentials}
| {:error, :invalid_refresh_token}
| {:error, any()}
def refresh_tokens(refresh_token, user_client_id, user_client_secret) do
with {:resource_from_token,
{:ok,
%ApplicationToken{
application: %Application{client_id: app_client_id, client_secret: app_client_secret},
scope: scope
} = app_token,
_claims}} <- {:resource_from_token, Guardian.resource_from_token(refresh_token)},
{:valid_client_credentials, true} <-
{:valid_client_credentials,
app_client_id == user_client_id and app_client_secret == user_client_secret},
{:ok, _old, {exchanged_token, _claims}} <-
Guardian.exchange(refresh_token, "refresh", "access", ttl: @app_access_tokens_ttl),
{:ok, new_refresh_token} <-
Authenticator.generate_refresh_token(app_token, @app_refresh_tokens_ttl),
{:ok, _claims} <- Guardian.revoke(refresh_token) do
{:ok,
%{
access_token: exchanged_token,
expires_in: ttl_to_seconds(@app_access_tokens_ttl),
refresh_token: new_refresh_token,
refresh_token_expires_in: ttl_to_seconds(@app_refresh_tokens_ttl),
scope: scope,
token_type: "bearer"
}}
else
{:valid_client_credentials, false} ->
{:error, :invalid_client_credentials}
{:resource_from_token, _} ->
{:error, :invalid_refresh_token}
end
end
defp user_code_displayed(user_code) do
String.slice(user_code, 0..3) <> "-" <> String.slice(user_code, 4..7)
end
@@ -265,6 +356,12 @@ defmodule Mobilizon.Service.Auth.Applications do
expires_in: expires_in
}) do
NaiveDateTime.compare(NaiveDateTime.add(inserted_at, expires_in), NaiveDateTime.utc_now()) ==
:gt
:lt
end
defp request_scope_valid?(app_scope, request_scope) do
app_scopes = app_scope |> String.split(" ") |> MapSet.new()
request_scopes = request_scope |> String.split(" ") |> MapSet.new()
MapSet.subset?(request_scopes, app_scopes)
end
end

View File

@@ -38,7 +38,7 @@ defmodule Mobilizon.Web.Auth.Context do
|> set_app_token_context(context, app_token)
|> set_user_context(user)
nil ->
_ ->
{conn, context}
end

View File

@@ -3,11 +3,12 @@ defmodule Mobilizon.Web.Auth.ErrorHandler do
In case we have an auth error
"""
import Plug.Conn
require Logger
# sobelow_skip ["XSS.SendResp"]
@spec auth_error(Plug.Conn.t(), any(), any()) :: Plug.Conn.t()
def auth_error(conn, {type, _reason}, _opts) do
body = Jason.encode!(%{message: to_string(type)})
def auth_error(conn, {type, reason}, _opts) do
body = Jason.encode!(%{message: to_string(type), details: inspect(reason)})
send_resp(conn, 401, body)
end
end

View File

@@ -43,7 +43,9 @@ defmodule Mobilizon.Web.Auth.Guardian do
{:error, :invalid_id}
end
rescue
Ecto.NoResultsError -> {:error, :no_result}
e in Ecto.NoResultsError ->
Logger.warn("Received token claim for non existing user: #{inspect(e)}")
{:error, :no_result}
end
end
@@ -62,7 +64,9 @@ defmodule Mobilizon.Web.Auth.Guardian do
{:error, :invalid_id}
end
rescue
Ecto.NoResultsError -> {:error, :no_result}
e in Ecto.NoResultsError ->
Logger.info("Received token claim for non existing app token: #{inspect(e.message)}")
{:error, :no_result}
end
end
@@ -79,6 +83,8 @@ defmodule Mobilizon.Web.Auth.Guardian do
@spec on_verify(any(), any(), any()) :: {:ok, map()} | {:error, :token_not_found}
def on_verify(claims, token, _options) do
Logger.debug("[Guardian] Called on_verify")
with {:ok, _} <- Guardian.DB.on_verify(claims, token) do
{:ok, claims}
end
@@ -86,6 +92,8 @@ defmodule Mobilizon.Web.Auth.Guardian do
@spec on_revoke(any(), any(), any()) :: {:ok, map()} | {:error, :could_not_revoke_token}
def on_revoke(claims, token, _options) do
Logger.debug("[Guardian] Called on_revoke")
with {:ok, _} <- Guardian.DB.on_revoke(claims, token) do
{:ok, claims}
end
@@ -94,6 +102,8 @@ defmodule Mobilizon.Web.Auth.Guardian do
@spec on_refresh({any(), any()}, {any(), any()}, any()) ::
{:ok, {String.t(), map()}, {String.t(), map()}} | {:error, any()}
def on_refresh({old_token, old_claims}, {new_token, new_claims}, _options) do
Logger.debug("[Guardian] Called on_refresh")
with {:ok, _, _} <- Guardian.DB.on_refresh({old_token, old_claims}, {new_token, new_claims}) do
{:ok, {old_token, old_claims}, {new_token, new_claims}}
end
@@ -101,7 +111,10 @@ defmodule Mobilizon.Web.Auth.Guardian do
@spec on_exchange(any(), any(), any()) ::
{:ok, {String.t(), map()}, {String.t(), map()}} | {:error, any()}
def on_exchange(old_stuff, new_stuff, options), do: on_refresh(old_stuff, new_stuff, options)
def on_exchange(old_stuff, new_stuff, options) do
Logger.debug("[Guardian] Called on_exchange")
on_refresh(old_stuff, new_stuff, options)
end
# def build_claims(claims, _resource, opts) do
# claims = claims

View File

@@ -1,7 +1,7 @@
defmodule Mobilizon.Web.ApplicationController do
use Mobilizon.Web, :controller
alias Mobilizon.Applications.{Application, ApplicationDeviceActivation}
alias Mobilizon.Applications.Application
alias Mobilizon.Service.Auth.Applications
plug(:put_layout, false)
import Mobilizon.Web.Gettext, only: [dgettext: 2]
@@ -11,11 +11,14 @@ defmodule Mobilizon.Web.ApplicationController do
Create an application
"""
@spec create_application(Plug.Conn.t(), map()) :: Plug.Conn.t()
def create_application(conn, %{"name" => name, "redirect_uris" => redirect_uris} = args) do
def create_application(
conn,
%{"name" => name, "redirect_uris" => redirect_uris, "scope" => scope} = args
) do
case Applications.create(
name,
redirect_uris,
Map.get(args, "scopes"),
String.split(redirect_uris, "\n"),
scope,
Map.get(args, "website")
) do
{:ok, %Application{} = app} ->
@@ -24,7 +27,19 @@ defmodule Mobilizon.Web.ApplicationController do
Map.take(app, [:name, :website, :redirect_uris, :client_id, :client_secret, :scope])
)
{:error, _error} ->
{:error, :invalid_scope} ->
send_resp(
conn,
400,
dgettext(
"errors",
"The scope parameter is not a space separated list of valid scopes"
)
)
{:error, error} ->
Logger.error(inspect(error))
send_resp(
conn,
500,
@@ -42,7 +57,7 @@ defmodule Mobilizon.Web.ApplicationController do
400,
dgettext(
"errors",
"Both name and redirect_uri parameters are required to create an application"
"All of name, scope and redirect_uri parameters are required to create an application"
)
)
end
@@ -60,14 +75,15 @@ defmodule Mobilizon.Web.ApplicationController do
client_id = conn.query_params["client_id"]
redirect_uri = conn.query_params["redirect_uri"]
state = conn.query_params["state"]
scope = conn.query_params["scope"]
if is_binary(client_id) and is_binary(redirect_uri) and is_binary(state) do
if is_binary(client_id) and is_binary(redirect_uri) and is_binary(state) and is_binary(scope) do
redirect(conn,
to:
Routes.page_path(conn, :authorize,
client_id: client_id,
redirect_uri: redirect_uri,
scope: conn.query_params["scope"],
scope: scope,
state: state
)
)
@@ -77,14 +93,14 @@ defmodule Mobilizon.Web.ApplicationController do
400,
dgettext(
"errors",
"You need to specify client_id, redirect_uri and state to autorize an application"
"You need to specify client_id, redirect_uri, scope and state to autorize an application"
)
)
end
end
def device_code(conn, %{"client_id" => client_id} = args) do
case Applications.register_device_code(client_id, Map.get(args, "scope")) do
def device_code(conn, %{"client_id" => client_id, "scope" => scope}) do
case Applications.register_device_code(client_id, scope) do
{:ok, res} when is_map(res) ->
case get_format(conn) do
"json" ->
@@ -94,6 +110,12 @@ defmodule Mobilizon.Web.ApplicationController do
send_resp(conn, 200, URI.encode_query(res))
end
{:error, :scope_not_included} ->
send_resp(conn, 400, "The given scope is not in the list of the app declared scopes")
{:error, :application_not_found} ->
send_resp(conn, 400, "No application with this client_id was found")
{:error, %Ecto.Changeset{} = err} ->
Logger.error(inspect(err))
send_resp(conn, 500, "Unable to produce device code")
@@ -101,7 +123,11 @@ defmodule Mobilizon.Web.ApplicationController do
end
def device_code(conn, _args) do
send_resp(conn, 400, "You need to send to send at least client_id to obtain a device code")
send_resp(
conn,
400,
"You need to pass both client_id and scope as parameters to obtain a device code"
)
end
@spec generate_access_token(Plug.Conn.t(), map()) :: Plug.Conn.t()
@@ -109,59 +135,106 @@ defmodule Mobilizon.Web.ApplicationController do
"client_id" => client_id,
"client_secret" => client_secret,
"code" => code,
"redirect_uri" => redirect_uri
"redirect_uri" => redirect_uri,
"scope" => scope,
"grant_type" => "authorization_code"
}) do
case Applications.generate_access_token(client_id, client_secret, code, redirect_uri) do
case do_generate_access_token(client_id, client_secret, code, redirect_uri, scope) do
{:ok, token} ->
redirect(conn, external: generate_redirect_with_query_params(redirect_uri, token))
json(conn, token)
{:error, :application_not_found} ->
send_resp(conn, 400, dgettext("errors", "No application was found with this client_id"))
{:error, :redirect_uri_not_in_allowed} ->
send_resp(conn, 400, dgettext("errors", "This redirect URI is not allowed"))
{:error, :invalid_or_expired} ->
send_resp(conn, 400, dgettext("errors", "The provided code is invalid or expired"))
{:error, :invalid_client_id} ->
send_resp(
conn,
400,
dgettext("errors", "The provided client_id does not match the provided code")
)
{:error, :invalid_client_secret} ->
send_resp(conn, 400, dgettext("errors", "The provided client_secret is invalid"))
{:error, :user_not_found} ->
send_resp(conn, 400, dgettext("errors", "The user for this code was not found"))
{:error, msg} ->
Logger.debug(msg)
json(conn, %{error: true, details: msg})
end
end
def generate_access_token(conn, %{
"client_id" => client_id,
"device_code" => device_code,
"grant_type" => "urn:ietf:params:oauth:grant-type:device_code",
"_format" => "json"
}) do
json(conn, Applications.generate_access_token(client_id, device_code))
end
def generate_access_token(conn, %{
"client_id" => client_id,
"device_code" => device_code,
"grant_type" => "urn:ietf:params:oauth:grant-type:device_code"
}) do
case Applications.generate_access_token_for_device_flow(client_id, device_code) do
{:ok, res} ->
case get_format(conn) do
"json" ->
json(conn, res)
_ ->
send_resp(
conn,
200,
URI.encode_query(res)
)
end
{:error, :incorrect_device_code} ->
send_resp(conn, 400, "The client_id provided or the device_code associated is invalid")
{:error, :access_denied} ->
send_resp(conn, 401, "The user rejected the requested authorization")
{:error, :expired} ->
send_resp(conn, 400, "The given device_code has expired")
end
end
def generate_access_token(conn, %{
"refresh_token" => refresh_token,
"grant_type" => "refresh_token",
"client_id" => client_id,
"client_secret" => client_secret
}) do
case Applications.refresh_tokens(refresh_token, client_id, client_secret) do
{:ok, res} ->
json(conn, res)
{:error, :invalid_client_credentials} ->
send_resp(conn, 400, "Invalid client credentials provided")
{:error, :invalid_refresh_token} ->
send_resp(conn, 400, "Invalid refresh token provided")
{:error, err} when is_atom(err) ->
send_resp(conn, 500, to_string(err))
end
end
def generate_access_token(conn, _args) do
send_resp(
conn,
200,
URI.encode_query(Applications.generate_access_token(client_id, device_code))
400,
"Incorrect parameters sent. You need to provide at least the grant_type and client_id parameters, depending on the grant type being used."
)
end
@spec generate_redirect_with_query_params(String.t(), map()) :: String.t()
defp generate_redirect_with_query_params(redirect_uri, query_params) do
redirect_uri |> URI.parse() |> URI.merge("?" <> URI.encode_query(query_params)) |> to_string()
@spec do_generate_access_token(String.t(), String.t(), String.t(), String.t(), String.t()) ::
{:ok, Applications.access_token_details()} | {:error, String.t()}
defp do_generate_access_token(client_id, client_secret, code, redirect_uri, scope) do
case Applications.generate_access_token(
client_id,
client_secret,
code,
redirect_uri,
scope
) do
{:ok, token} ->
{:ok, token}
{:error, :application_not_found} ->
{:error, dgettext("errors", "No application was found with this client_id")}
{:error, :redirect_uri_not_in_allowed} ->
{:error, dgettext("errors", "This redirect URI is not allowed")}
{:error, :invalid_or_expired} ->
{:error, dgettext("errors", "The provided code is invalid or expired")}
{:error, :provided_code_does_not_match} ->
{:error, dgettext("errors", "The provided client_id does not match the provided code")}
{:error, :invalid_client_secret} ->
{:error, dgettext("errors", "The provided client_secret is invalid")}
end
end
end

View File

@@ -208,7 +208,6 @@ defmodule Mobilizon.Web.Router do
post("/apps", ApplicationController, :create_application)
get("/oauth/authorize", ApplicationController, :authorize)
post("/oauth/token", ApplicationController, :generate_access_token)
get("/oauth/autorize_approve", PageController, :authorize)
get("/login/device", PageController, :auth_device)
end
@@ -217,10 +216,11 @@ defmodule Mobilizon.Web.Router do
plug(:accepts, ["html", "json"])
end
scope "/login", Mobilizon.Web do
scope "/", Mobilizon.Web do
pipe_through(:login)
post("/device/code", ApplicationController, :device_code)
post("/login/device/code", ApplicationController, :device_code)
post("/oauth/token", ApplicationController, :generate_access_token)
end
scope "/proxy/", Mobilizon.Web do