Merge branch 'feature/whitelist-registration' into 'master'

Add a config option to whitelist users registration

See merge request framasoft/mobilizon!358
This commit is contained in:
Thomas Citharel 2019-12-17 12:27:00 +01:00
commit bf0b1fdb27
19 changed files with 351 additions and 212 deletions

View File

@ -18,6 +18,7 @@ config :mobilizon, :instance,
version: "1.0.0-dev",
hostname: System.get_env("MOBILIZON_INSTANCE_HOST") || "localhost",
registrations_open: System.get_env("MOBILIZON_INSTANCE_REGISTRATIONS_OPEN") || false,
registration_email_whitelist: [],
demo: System.get_env("MOBILIZON_INSTANCE_DEMO_MODE") || false,
repository: Mix.Project.config()[:source_url],
allow_relay: true,

View File

@ -6,6 +6,7 @@ query {
name,
description,
registrationsOpen,
registrationsWhitelist,
demoMode,
countryCode,
location {

View File

@ -12,7 +12,6 @@
"Add to my calendar": "Zu meinem Kalender hinzufügen",
"Additional comments": "Zusätzliche Kommentare",
"Administration": "Administration",
"All data will be deleted every 48 hours, so please don't use this for anything real.": "Alle Daten werden alle 48 Stunden gelöscht, also benutze diesen Service bitte nur zu Testzwecken.",
"All the places have already been taken": "Alle Plätze sind besetzt|Ein Platz ist noch verfügbar|{places} Plätze sind noch verfügbar",
"Allow all comments": "Erlaube alle Kommentare",
"An error has occurred.": "Ein Fehler ist aufgetreten.",

View File

@ -7,17 +7,21 @@
"About this event": "About this event",
"About this instance": "About this instance",
"About": "About",
"Accepted": "Accepted",
"Add a note": "Add a note",
"Add an address": "Add an address",
"Add an instance": "Add an instance",
"Add some tags": "Add some tags",
"Add to my calendar": "Add to my calendar",
"Add": "Add",
"Additional comments": "Additional comments",
"Administration": "Administration",
"All data will be deleted every 48 hours, so please don't use this for anything real.": "All data will be deleted every 48 hours, so please don't use this for anything real.",
"All the places have already been taken": "All the places have been taken|One place is still available|{places} places are still available",
"Allow all comments": "Allow all comments",
"An error has occurred.": "An error has occurred.",
"Approve": "Approve",
"Are you sure you want to <b>delete</b> this comment? This action cannot be undone.": "Are you sure you want to <b>delete</b> this comment? This action cannot be undone.",
"Are you sure you want to <b>delete</b> this event? This action cannot be undone. You may want to engage the conversation with the event creator or edit its event instead.": "Are you sure you want to <b>delete</b> this event? This action cannot be undone. You may want to engage the conversation with the event creator or edit its event instead.",
"Are you sure you want to cancel the event creation? You'll lose all modifications.": "Are you sure you want to cancel the event creation? You'll lose all modifications.",
"Are you sure you want to cancel the event edition? You'll lose all modifications.": "Are you sure you want to cancel the event edition? You'll lose all modifications.",
"Are you sure you want to cancel your participation at event \"{title}\"?": "Are you sure you want to cancel your participation at event \"{title}\"?",
@ -40,6 +44,9 @@
"Click to select": "Click to select",
"Click to upload": "Click to upload",
"Close comments for all (except for admins)": "Close comments for all (except for admins)",
"Close": "Close",
"Closed": "Closed",
"Comment deleted": "Comment deleted",
"Comment from @{username} reported": "Comment from @{username} reported",
"Comments have been closed.": "Comments have been closed.",
"Comments": "Comments",
@ -60,18 +67,25 @@
"Create": "Create",
"Creator": "Creator",
"Current identity has been changed to {identityName} in order to manage this event.": "Current identity has been changed to {identityName} in order to manage this event.",
"Dashboard": "Dashboard",
"Date and time settings": "Date and time settings",
"Date parameters": "Date parameters",
"Date": "Date",
"Delete Comment": "Delete Comment",
"Delete Event": "Delete Event",
"Delete event": "Delete event",
"Delete this identity": "Delete this identity",
"Delete your identity": "Delete your identity",
"Delete {eventTitle}": "Delete {eventTitle}",
"Delete {preferredUsername}": "Delete {preferredUsername}",
"Delete": "Delete",
"Deleting comment": "Deleting comment",
"Deleting event": "Deleting event",
"Description": "Description",
"Didn't receive the instructions ?": "Didn't receive the instructions ?",
"Display name": "Display name",
"Display participation price": "Display participation price",
"Domain": "Domain",
"Draft": "Draft",
"Drafts": "Drafts",
"Edit": "Edit",
@ -96,12 +110,15 @@
"Event {eventTitle} reported": "Event {eventTitle} reported",
"Event": "Event",
"Events": "Events",
"Ex: test.mobilizon.org": "Ex: test.mobilizon.org",
"Exclude": "Exclude",
"Explore": "Explore",
"Featured events": "Featured events",
"Features": "Features",
"Find an address": "Find an address",
"Find an instance": "Find an instance",
"Followers": "Followers",
"Followings": "Followings",
"For instance: London, Taekwondo, Architecture…": "For instance: London, Taekwondo, Architecture…",
"Forgot your password ?": "Forgot your password ?",
"From a birthday party with friends and family to a march for climate change, right now, our gatherings are <b>trapped inside the tech giants platforms</b>. How can we organize, how can we click “Attend,” without <b>providing private data</b> to Facebook or <b>locking ourselves up</b> inside MeetUp?": "From a birthday party with friends and family to a march for climate change, right now, our gatherings are <b>trapped inside the tech giants platforms</b>. How can we organize, how can we click “Attend,” without <b>providing private data</b> to Facebook or <b>locking ourselves up</b> inside MeetUp?",
@ -130,6 +147,7 @@
"Impossible to login, your email or password seems incorrect.": "Impossible to login, your email or password seems incorrect.",
"In the meantime, please consider that the software is not (yet) finished. More information {onBlog}.": "In the meantime, please consider that the software is not (yet) finished. More information {onBlog}.",
"Installing Mobilizon will allow communities to free themselves from the services of tech giants by creating <b>their own event platform</b>.": "Installing Mobilizon will allow communities to free themselves from the services of tech giants by creating <b>their own event platform</b>.",
"Instances": "Instances",
"Join {instance}, a Mobilizon instance": "Join {instance}, a Mobilizon instance",
"Last published event": "Last published event",
"Last week": "Last week",
@ -147,6 +165,7 @@
"Login on Mobilizon!": "Login on Mobilizon!",
"Login": "Login",
"Manage participations": "Manage participations",
"Mark as resolved": "Mark as resolved",
"Members": "Members",
"Mobilizon is a free/libre software that will allow communities to create <b>their own spaces</b> to publish events in order to better emancipate themselves from tech giants.": "Mobilizon is a free/libre software that will allow communities to create <b>their own spaces</b> to publish events in order to better emancipate themselves from tech giants.",
"Mobilizon is under development, we will add new features to this site during regular updates, until the release of <b>version 1 of the software in the first half of 2020</b>.": "Mobilizon is under development, we will add new features to this site during regular updates, until the release of <b>version 1 of the software in the first half of 2020</b>.",
@ -156,16 +175,26 @@
"My events": "My events",
"My identities": "My identities",
"Name": "Name",
"New note": "New note",
"New password": "New password",
"No actors found": "No actors found",
"No address defined": "No address defined",
"No closed reports yet": "No closed reports yet",
"No comment": "No comment",
"No comments yet": "No comments yet",
"No end date": "No end date",
"No events found": "No events found",
"No group found": "No group found",
"No groups found": "No groups found",
"No instance follows your instance yet.": "No instance follows your instance yet.",
"No instance to approve|Approve instance|Approve {number} instances": "No instance to approve|Approve instance|Approve {number} instances",
"No instance to reject|Reject instance|Reject {number} instances": "No instance to reject|Reject instance|Reject {number} instances",
"No instance to remove|Remove instance|Remove {number} instances": "No instances to remove|Remove instance|Remove {number} instances",
"No open reports yet": "No open reports yet",
"No resolved reports yet": "No resolved reports yet",
"No results for \"{queryText}\"": "No results for \"{queryText}\"",
"No user account with this email was found. Maybe you made a typo?": "No user account with this email was found. Maybe you made a typo?",
"Notes": "Notes",
"Number of places": "Number of places",
"OK": "OK",
"Old password": "Old password",
@ -176,6 +205,7 @@
"One person is going": "No one is going | One person is going | {approved} persons are going",
"Only accessible through link and search (private)": "Only accessible through link and search (private)",
"Only alphanumeric characters and underscores are supported.": "Only alphanumeric characters and underscores are supported.",
"Open": "Open",
"Opened reports": "Opened reports",
"Organized by {name}": "Organized by {name}",
"Organized": "Organized",
@ -194,6 +224,7 @@
"Password reset": "Password reset",
"Password": "Password",
"Past events": "Passed events",
"Pending": "Pending",
"Pick an identity": "Pick an identity",
"Please check your spam folder if you didn't receive the email.": "Please check your spam folder if you didn't receive the email.",
"Please contact this instance's Mobilizon admin if you think this is a mistake.": "Please contact this instance's Mobilizon admin if you think this is a mistake.",
@ -219,15 +250,25 @@
"Register for an event by choosing one of your identities": "Register for an event by choosing one of your identities",
"Register": "Register",
"Registration is currently closed.": "Registration is currently closed.",
"Registrations are restricted by whitelisting.": "Registrations are restricted by whitelisting.",
"Reject": "Reject",
"Rejected participations": "Rejected participations",
"Rejected": "Rejected",
"Reopen": "Reopen",
"Reply": "Reply",
"Report this comment": "Report this comment",
"Report this event": "Report this event",
"Report": "Report",
"Reported by someone on {domain}": "Reported by someone on {domain}",
"Reported by {reporter}": "Reported by {reporter}",
"Reported by": "Reported by",
"Reported identity": "Reported identity",
"Reported": "Reported",
"Reports": "Reports",
"Requests": "Requests",
"Resend confirmation email": "Resend confirmation email",
"Reset my password": "Reset my password",
"Resolved": "Resolved",
"Save draft": "Save draft",
"Save": "Save",
"Search events, groups, etc.": "Search events, groups, etc.",
@ -273,13 +314,17 @@
"To confirm, type your event title \"{eventTitle}\"": "To confirm, type your event title \"{eventTitle}\"",
"To confirm, type your identity username \"{preferredUsername}\"": "To confirm, type your identity username \"{preferredUsername}\"",
"Transfer to {outsideDomain}": "Transfer to {outsideDomain}",
"Type": "Type",
"Unfortunately, this instance isn't opened to registrations": "Unfortunately, this instance isn't opened to registrations",
"Unfortunately, your participation request was rejected by the organizers.": "Unfortunately, your participation request was rejected by the organizers.",
"Unknown actor": "Unknown actor",
"Unknown error.": "Unknown error.",
"Unknown": "Unknown",
"Unsaved changes": "Unsaved changes",
"Upcoming": "Upcoming",
"Update event {name}": "Update event {name}",
"Update my event": "Update my event",
"Updated": "Updated",
"Username": "Username",
"Users": "Users",
"View a reply": "|View one reply|View {totalReplies} replies",
@ -307,6 +352,7 @@
"You can add tags by hitting the Enter key or by adding a comma": "You can add tags by hitting the Enter key or by adding a comma",
"You can try another search term or drag and drop the marker on the map": "You can try another search term or drag and drop the marker on the map",
"You can't remove your last identity.": "You can't remove your last identity.",
"You don't follow any instances yet.": "You don't follow any instances yet.",
"You have been disconnected": "You have been disconnected",
"You have cancelled your participation": "You have cancelled your participation",
"You have one event in {days} days.": "You have no events in {days} days | You have one event in {days} days. | You have {count} events in {days} days",
@ -317,12 +363,16 @@
"Your account has been validated": "Your account has been validated",
"Your account is being validated": "Your account is being validated",
"Your account is nearly ready, {username}": "Your account is nearly ready, {username}",
"Your email is not whitelisted, you can't register.": "Your email is not whitelisted, you can't register.",
"Your local administrator resumed its policy:": "Your local administrator resumed its policy:",
"Your participation has been confirmed": "Your participation has been confirmed",
"Your participation has been rejected": "Your participation has been rejected",
"Your participation has been requested": "Your participation has been requested",
"Your participation status has been changed": "Your participation status has been changed",
"[This comment has been deleted]": "[This comment has been deleted]",
"[deleted]": "[deleted]",
"a decentralised federation protocol": "a decentralised federation protocol",
"as {identity}": "as {identity}",
"e.g. 10 Rue Jangot": "e.g. 10 Rue Jangot",
"firstDayOfWeek": "0",
"iCal Feed": "iCal Feed",
@ -332,59 +382,10 @@
"resend confirmation email": "resend confirmation email",
"respect of the fundamental freedoms": "respect of the fundamental freedoms",
"with another identity…": "with another identity…",
"as {identity}": "as {identity}",
"{approved} / {total} seats": "{approved} / {total} seats",
"{count} participants": "No participants yet | One participant | {count} participants",
"{count} requests waiting": "{count} requests waiting",
"{license} guarantees {respect} of the people who will use it. Since {source}, anyone can audit it, which guarantees its transparency.": "{license} guarantees {respect} of the people who will use it. Since {source}, anyone can audit it, which guarantees its transparency.",
"© The Mobilizon Contributors {date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks": "© The Mobilizon Contributors {date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks",
"© The OpenStreetMap Contributors": "© The OpenStreetMap Contributors",
"Reply": "Reply",
"Accepted": "Accepted",
"Pending": "Pending",
"No instance to remove|Remove instance|Remove {number} instances": "No instances to remove|Remove instance|Remove {number} instances",
"Dashboard": "Dashboard",
"Reports": "Reports",
"Mark as resolved": "Mark as resolved",
"Reopen": "Reopen",
"Close": "Close",
"Reported identity": "Reported identity",
"Reported by": "Reported by",
"Reported": "Reported",
"Updated": "Updated",
"Open": "Open",
"Closed": "Closed",
"Resolved": "Resolved",
"Unknown": "Unknown",
"No comment": "No comment",
"Notes": "Notes",
"New note": "New note",
"Add a note": "Add a note",
"Deleting event": "Deleting event",
"Are you sure you want to <b>delete</b> this event? This action cannot be undone. You may want to engage the conversation with the event creator or edit its event instead.": "Are you sure you want to <b>delete</b> this event? This action cannot be undone. You may want to engage the conversation with the event creator or edit its event instead.",
"Delete Event": "Delete Event",
"Type": "Type",
"Domain": "Domain",
"Date": "Date",
"No instance to approve|Approve instance|Approve {number} instances": "No instance to approve|Approve instance|Approve {number} instances",
"No instance to reject|Reject instance|Reject {number} instances": "No instance to reject|Reject instance|Reject {number} instances",
"No instance follows your instance yet.": "No instance follows your instance yet.",
"Followers": "Followers",
"Add an instance": "Add an instance",
"Ex: test.mobilizon.org": "Ex: test.mobilizon.org",
"You don't follow any instances yet.": "You don't follow any instances yet.",
"Followings": "Followings",
"Instances": "Instances",
"Reported by {reporter}": "Reported by {reporter}",
"No open reports yet": "No open reports yet",
"No resolved reports yet": "No resolved reports yet",
"No closed reports yet": "No closed reports yet",
"Reported by someone on {domain}": "Reported by someone on {domain}",
"Your participation has been rejected": "Your participation has been rejected",
"Your participation status has been changed": "Your participation status has been changed",
"Unknown actor": "Unknown actor",
"Deleting comment": "Deleting comment",
"Are you sure you want to <b>delete</b> this comment? This action cannot be undone.": "Are you sure you want to <b>delete</b> this comment? This action cannot be undone.",
"Delete Comment": "Delete Comment",
"Comment deleted": "Comment deleted"
"© The OpenStreetMap Contributors": "© The OpenStreetMap Contributors"
}

View File

@ -16,7 +16,6 @@
"Add": "Ajouter",
"Additional comments": "Commentaires additionnels",
"Administration": "Administration",
"All data will be deleted every 48 hours, so please don't use this for anything real.": "Toutes les données seront effacées toutes les 48 heures, donc n'utilisez pas ce site à des fins autres que de démonstration.",
"All the places have already been taken": "Toutes les places ont été prises|Une place est encore disponible|{places} places sont encore disponibles",
"Allow all comments": "Autoriser tous les commentaires",
"An error has occurred.": "Une erreur est survenue.",
@ -253,6 +252,7 @@
"Register for an event by choosing one of your identities": "S'inscrire à un événement en choisissant une de vos identités",
"Register": "S'inscrire",
"Registration is currently closed.": "Les inscriptions sont actuellement fermées.",
"Registrations are restricted by whitelisting.": "Les inscriptions sont restreintes par liste blanche.",
"Reject": "Rejetter",
"Rejected participations": "Participations rejetées",
"Rejected": "Rejetés",
@ -365,6 +365,7 @@
"Your account has been validated": "Votre compte a été validé",
"Your account is being validated": "Votre compte est en cours de validation",
"Your account is nearly ready, {username}": "Votre compte est presque prêt, {username}",
"Your email is not whitelisted, you can't register.": "Votre email n'est pas sur la liste blanche, vous ne pouvez pas vous inscrire.",
"Your local administrator resumed its policy:": "Votre administrateur local a résumé sa politique ainsi :",
"Your participation has been confirmed": "Votre participation a été confirmée",
"Your participation has been rejected": "Votre participation a été rejettée",

View File

@ -12,7 +12,6 @@
"Add to my calendar": "Aan mijn kalender toevoegen",
"Additional comments": "Meer opmerkingen",
"Administration": "Administratie",
"All data will be deleted every 48 hours, so please don't use this for anything real.": "Alle gegevens worden iedere 48 uur verwijderd, dus gebruik dit enkel als test.",
"All the places have already been taken": "Ale plaatsen zijn bezet|Er is nog één plaats vrij|Er zijn nog {places} plaatsen vrij",
"Allow all comments": "Alle opmerkingen toestaan",
"An error has occurred.": "Er is een fout opgetreden.",

View File

@ -17,7 +17,6 @@
"Add": "Ajustar",
"Additional comments": "Comentari adicional",
"Administration": "Administracion",
"All data will be deleted every 48 hours, so please don't use this for anything real.": "Estant que totas las donadas son suprimidas cada 48 oras, utilizetz pas aquò per quicòm mai quuna demostracion.",
"All the places have already been taken": "Totas las plaças son presas|Una plaça es encara disponibla|{places} plaças son encara disponiblas",
"Allow all comments": "Autorizar totes los comentaris",
"An error has occurred.": "Una error ses producha.",

View File

@ -11,7 +11,6 @@
"Add to my calendar": "Dodaj do kalendarza",
"Additional comments": "Dodatkowe komentarze",
"Administration": "Administracja",
"All data will be deleted every 48 hours, so please don't use this for anything real.": "Wszystkie dane zostaną usunięte co 48 godzin, więc nie używaj tego do rzeczywistych działań.",
"Allow all comments": "Pozwól na wszystkie komentarze",
"An error has occurred.": "Wystąpił błąd.",
"Approve": "Zatwierdź",

View File

@ -12,7 +12,6 @@
"Add to my calendar": "Lägg till i min kalender",
"Additional comments": "Yttligare kommentarer",
"Administration": "Administration",
"All data will be deleted every 48 hours, so please don't use this for anything real.": "All data kommer raderas varje 48e timme, så använd inte detta för något riktigt.",
"All the places have already been taken": "Alla platser är bokade|Det finns en plats kvar|Det finns {places} kvar",
"Allow all comments": "Tillåt alla kommentarer",
"An error has occurred.": "Ett fel har uppstått.",

View File

@ -12,7 +12,7 @@ export const beforeRegisterGuard: NavigationGuard = async function (to, from, ne
const config: IConfig = data.config;
if (!config.registrationsOpen) {
if (!config.registrationsOpen && !config.registrationsWhitelist) {
return next({
name: ErrorRouteName.ERROR,
query: { code: ErrorCode.REGISTRATION_CLOSED },

View File

@ -3,6 +3,7 @@ export interface IConfig {
description: string;
registrationsOpen: boolean;
registrationsWhitelist: boolean;
demoMode: boolean;
countryCode: string;
location: {

View File

@ -48,6 +48,11 @@ export const errors: IError[] = [
value: i18n.t("The current identity doesn't have any permission on this event. You should probably change it.") as string,
suggestRefresh: false,
},
{
match: /Your email is not on the whitelist$/,
value: i18n.t("Your email is not whitelisted, you can't register.") as string,
suggestRefresh: false,
},
{
match: /Cannot remove the last identity of a user/,
value: i18n.t("You can't remove your last identity.") as string,

View File

@ -29,7 +29,6 @@
</p>
<ul>
<li>{{ $t('Enjoy discovering Mobilizon!') }}</li>
<li>{{ $t("All data will be deleted every 48 hours, so please don't use this for anything real.") }}</li>
</ul>
<!-- <p>-->
<!-- {{ $t('Please read the full rules') }}-->
@ -37,6 +36,7 @@
</div>
</div>
<div class="column">
<b-message type="is-warning" v-if="config.registrationsWhitelist">{{ $t('Registrations are restricted by whitelisting.') }}</b-message>
<form v-on:submit.prevent="submit()">
<b-field
:label="$t('Email')"
@ -105,6 +105,8 @@
import { CREATE_USER } from '@/graphql/user';
import { Component, Prop, Vue } from 'vue-property-decorator';
import { RouteName } from '@/router';
import { IConfig } from '@/types/config.model';
import { CONFIG } from '@/graphql/config';
@Component({
metaInfo() {
@ -115,6 +117,9 @@ import { RouteName } from '@/router';
titleTemplate: '%s | Mobilizon',
};
},
apollo: {
config: CONFIG,
},
})
export default class Register extends Vue {
@Prop({ type: String, required: false, default: '' }) email!: string;
@ -129,6 +134,7 @@ export default class Register extends Vue {
sendingValidation: boolean = false;
validationSent: boolean = false;
RouteName = RouteName;
config!: IConfig;
async submit() {
this.credentials.locale = this.$i18n.locale;

View File

@ -21,6 +21,12 @@ defmodule Mobilizon.Config do
@spec instance_registrations_open? :: boolean
def instance_registrations_open?, do: to_boolean(instance_config()[:registrations_open])
@spec instance_registrations_whitelist :: list(String.t())
def instance_registrations_whitelist, do: instance_config()[:registration_email_whitelist]
@spec instance_registrations_whitelist? :: boolean
def instance_registrations_whitelist?, do: length(instance_registrations_whitelist()) > 0
@spec instance_demo_mode? :: boolean
def instance_demo_mode?, do: to_boolean(instance_config()[:demo])

View File

@ -30,6 +30,7 @@ defmodule MobilizonWeb.Resolvers.Config do
%{
name: Config.instance_name(),
registrations_open: Config.instance_registrations_open?(),
registrations_whitelist: Config.instance_registrations_whitelist?(),
demo_mode: Config.instance_demo_mode?(),
description: Config.instance_description(),
location: location,

View File

@ -116,20 +116,46 @@ defmodule MobilizonWeb.Resolvers.User do
"""
@spec create_user(any(), map(), any()) :: tuple()
def create_user(_parent, args, _resolution) do
with {:registrations_open, true} <-
{:registrations_open, Config.instance_registrations_open?()},
with :registration_ok <- check_registration_config(args),
{:ok, %User{} = user} <- Users.register(args) do
Activation.send_confirmation_email(user, Map.get(args, :locale, "en"))
{:ok, user}
else
{:registrations_open, false} ->
:registration_closed ->
{:error, "Registrations are not enabled"}
:not_whitelisted ->
{:error, "Your email is not on the whitelist"}
error ->
error
end
end
@spec check_registration_config(map()) :: atom()
defp check_registration_config(%{email: email}) do
cond do
Config.instance_registrations_open?() ->
:registration_ok
Config.instance_registrations_whitelist?() ->
check_white_listed_email?(email)
true ->
:registration_closed
end
end
@spec check_white_listed_email?(String.t()) :: :registration_ok | :not_whitelisted
defp check_white_listed_email?(email) do
[_, domain] = String.split(email, "@", parts: 2, trim: true)
if domain in Config.instance_registrations_whitelist() or
email in Config.instance_registrations_whitelist(),
do: :registration_ok,
else: :not_whitelisted
end
@doc """
Validate an user, get its actor and a token
"""

View File

@ -13,6 +13,7 @@ defmodule MobilizonWeb.Schema.ConfigType do
field(:description, :string)
field(:registrations_open, :boolean)
field(:registrations_whitelist, :boolean)
field(:demo_mode, :boolean)
field(:country_code, :string)
field(:location, :lonlat)

View File

@ -1,5 +1,5 @@
# source: http://localhost:4000/api
# timestamp: Wed Dec 11 2019 15:24:29 GMT+0100 (heure normale dEurope centrale)
# timestamp: Tue Dec 17 2019 11:21:37 GMT+0100 (heure normale dEurope centrale)
schema {
query: RootQueryType
@ -256,6 +256,7 @@ type Config {
maps: Maps
name: String
registrationsOpen: Boolean
registrationsWhitelist: Boolean
}
type Dashboard {
@ -996,70 +997,6 @@ enum ReportStatus {
}
type RootMutationType {
"""Change default actor for user"""
changeDefaultActor(preferredUsername: String!): User
"""Create a new person for user"""
createPerson(
"""
The avatar for the profile, either as an object or directly the ID of an existing Picture
"""
avatar: PictureInput
"""
The banner for the profile, either as an object or directly the ID of an existing Picture
"""
banner: PictureInput
"""The displayed name for the new profile"""
name: String = ""
preferredUsername: String!
"""The summary for the new profile"""
summary: String = ""
): Person
"""Upload a picture"""
uploadPicture(actorId: ID!, alt: String, file: Upload!, name: String!): Picture
"""Delete an event"""
deleteEvent(actorId: ID!, eventId: ID!): DeletedObject
"""Create a note on a report"""
createReportNote(content: String, moderatorId: ID!, reportId: ID!): ReportNote
"""Accept a relay subscription"""
acceptRelay(address: String!): Follower
"""Delete a feed token"""
deleteFeedToken(token: String!): DeletedFeedToken
"""Validate an user after registration"""
validateUser(token: String!): Login
"""Resend registration confirmation token"""
resendConfirmationEmail(email: String!, locale: String): String
"""Update an identity"""
updatePerson(
"""
The avatar for the profile, either as an object or directly the ID of an existing Picture
"""
avatar: PictureInput
"""
The banner for the profile, either as an object or directly the ID of an existing Picture
"""
banner: PictureInput
id: ID!
"""The displayed name for this profile"""
name: String
"""The summary for this profile"""
summary: String
): Person
"""Create an event"""
createEvent(
beginsOn: DateTime!
@ -1087,47 +1024,24 @@ type RootMutationType {
visibility: EventVisibility = PUBLIC
): Event
"""Register a first profile on registration"""
registerPerson(
"""
The avatar for the profile, either as an object or directly the ID of an existing Picture
"""
avatar: PictureInput
"""Create a Feed Token"""
createFeedToken(actorId: ID): FeedToken
"""
The banner for the profile, either as an object or directly the ID of an existing Picture
"""
banner: PictureInput
"""Create a report"""
createReport(commentsIds: [ID] = [""], content: String, eventId: ID, forward: Boolean = false, reportedId: ID!, reporterId: ID!): Report
"""The email from the user previously created"""
email: String!
"""The displayed name for the new profile"""
name: String = ""
preferredUsername: String!
"""The summary for the new profile"""
summary: String = ""
): Person
"""Accept a participation"""
updateParticipation(id: ID!, moderatorActorId: ID!, role: ParticipantRoleEnum!): Participant
"""Delete a group"""
deleteGroup(actorId: ID!, groupId: ID!): DeletedObject
"""Change default actor for user"""
changeDefaultActor(preferredUsername: String!): User
deleteComment(actorId: ID!, commentId: ID!): Comment
"""Create an user"""
createUser(email: String!, locale: String, password: String!): User
"""Delete an event"""
deleteEvent(actorId: ID!, eventId: ID!): DeletedObject
"""Leave an event"""
leaveEvent(actorId: ID!, eventId: ID!): DeletedParticipant
"""Join an event"""
joinEvent(actorId: ID!, eventId: ID!): Participant
"""Refresh a token"""
refreshToken(refreshToken: String!): RefreshedToken
"""Join a group"""
joinGroup(actorId: ID!, groupId: ID!): Member
"""Reset user password"""
resetPassword(locale: String = "en", password: String!, token: String!): Login
"""Update an event"""
updateEvent(
@ -1156,42 +1070,75 @@ type RootMutationType {
visibility: EventVisibility = PUBLIC
): Event
"""Reset user password"""
resetPassword(locale: String = "en", password: String!, token: String!): Login
"""Change an user password"""
changePassword(newPassword: String!, oldPassword: String!): User
"""Create a report"""
createReport(commentsIds: [ID] = [""], content: String, eventId: ID, forward: Boolean = false, reportedId: ID!, reporterId: ID!): Report
"""Delete a feed token"""
deleteFeedToken(token: String!): DeletedFeedToken
"""Update a report"""
updateReportStatus(moderatorId: ID!, reportId: ID!, status: ReportStatus!): Report
deleteReportNote(moderatorId: ID!, noteId: ID!): DeletedObject
"""Register a first profile on registration"""
registerPerson(
"""
The avatar for the profile, either as an object or directly the ID of an existing Picture
"""
avatar: PictureInput
"""Delete a relay subscription"""
removeRelay(address: String!): Follower
"""
The banner for the profile, either as an object or directly the ID of an existing Picture
"""
banner: PictureInput
"""Create a comment"""
createComment(actorId: ID!, eventId: ID, inReplyToCommentId: ID, text: String!): Comment
"""The email from the user previously created"""
email: String!
"""The displayed name for the new profile"""
name: String = ""
preferredUsername: String!
"""The summary for the new profile"""
summary: String = ""
): Person
"""Delete an identity"""
deletePerson(id: ID!): Person
"""Reject a relay subscription"""
rejectRelay(address: String!): Follower
"""Leave an event"""
leaveEvent(actorId: ID!, eventId: ID!): DeletedParticipant
"""Accept a participation"""
updateParticipation(id: ID!, moderatorActorId: ID!, role: ParticipantRoleEnum!): Participant
"""Login an user"""
login(email: String!, password: String!): Login
"""Leave an event"""
leaveGroup(actorId: ID!, groupId: ID!): DeletedMember
"""Upload a picture"""
uploadPicture(actorId: ID!, alt: String, file: Upload!, name: String!): Picture
"""Change an user password"""
changePassword(newPassword: String!, oldPassword: String!): User
"""Create a new person for user"""
createPerson(
"""
The avatar for the profile, either as an object or directly the ID of an existing Picture
"""
avatar: PictureInput
"""Add a relay subscription"""
addRelay(address: String!): Follower
"""
The banner for the profile, either as an object or directly the ID of an existing Picture
"""
banner: PictureInput
"""Join an event"""
joinEvent(actorId: ID!, eventId: ID!): Participant
"""The displayed name for the new profile"""
name: String = ""
preferredUsername: String!
"""The summary for the new profile"""
summary: String = ""
): Person
"""Send a link through email to reset user password"""
sendResetPassword(email: String!, locale: String): String
"""Create a comment"""
createComment(actorId: ID!, eventId: ID, inReplyToCommentId: ID, text: String!): Comment
"""Create a group"""
createGroup(
@ -1218,11 +1165,65 @@ type RootMutationType {
summary: String = ""
): Group
"""Send a link through email to reset user password"""
sendResetPassword(email: String!, locale: String): String
"""Update a report"""
updateReportStatus(moderatorId: ID!, reportId: ID!, status: ReportStatus!): Report
"""Create a Feed Token"""
createFeedToken(actorId: ID): FeedToken
"""Create an user"""
createUser(email: String!, locale: String, password: String!): User
deleteReportNote(moderatorId: ID!, noteId: ID!): DeletedObject
"""Update an identity"""
updatePerson(
"""
The avatar for the profile, either as an object or directly the ID of an existing Picture
"""
avatar: PictureInput
"""
The banner for the profile, either as an object or directly the ID of an existing Picture
"""
banner: PictureInput
id: ID!
"""The displayed name for this profile"""
name: String
"""The summary for this profile"""
summary: String
): Person
"""Join a group"""
joinGroup(actorId: ID!, groupId: ID!): Member
"""Delete a group"""
deleteGroup(actorId: ID!, groupId: ID!): DeletedObject
"""Resend registration confirmation token"""
resendConfirmationEmail(email: String!, locale: String): String
"""Delete a relay subscription"""
removeRelay(address: String!): Follower
"""Refresh a token"""
refreshToken(refreshToken: String!): RefreshedToken
"""Add a relay subscription"""
addRelay(address: String!): Follower
"""Reject a relay subscription"""
rejectRelay(address: String!): Follower
"""Leave an event"""
leaveGroup(actorId: ID!, groupId: ID!): DeletedMember
"""Validate an user after registration"""
validateUser(token: String!): Login
"""Create a note on a report"""
createReportNote(content: String, moderatorId: ID!, reportId: ID!): ReportNote
"""Accept a relay subscription"""
acceptRelay(address: String!): Follower
}
"""

View File

@ -2,11 +2,10 @@ defmodule MobilizonWeb.Resolvers.UserResolverTest do
use MobilizonWeb.ConnCase
import Mobilizon.Factory
import Mock
use Bamboo.Test
alias Mobilizon.{Actors, Config, Users}
alias Mobilizon.{Actors, Users}
alias Mobilizon.Actors.Actor
alias Mobilizon.Service.Users.ResetPassword
alias Mobilizon.Users
@ -321,6 +320,123 @@ defmodule MobilizonWeb.Resolvers.UserResolverTest do
assert hd(json_response(res, 200)["errors"])["message"] == "This email is already used."
end
test "create_user/3 doesn't allow registration when registration is closed", %{conn: conn} do
Mobilizon.Config.put([:instance, :registrations_open], false)
Mobilizon.Config.put([:instance, :registration_email_whitelist], [])
mutation = """
mutation createUser($email: String!, $password: String!) {
createUser(
email: $email,
password: $password,
) {
id,
email
}
}
"""
res =
conn
|> AbsintheHelpers.graphql_query(
query: mutation,
variables: %{email: @user_creation.email, password: @user_creation.password}
)
assert hd(res["errors"])["message"] == "Registrations are not enabled"
Mobilizon.Config.put([:instance, :registrations_open], true)
end
test "create_user/3 doesn't allow registration when user email is not on the whitelist", %{
conn: conn
} do
Mobilizon.Config.put([:instance, :registrations_open], false)
Mobilizon.Config.put([:instance, :registration_email_whitelist], ["random.org"])
mutation = """
mutation createUser($email: String!, $password: String!) {
createUser(
email: $email,
password: $password,
) {
id,
email
}
}
"""
res =
conn
|> AbsintheHelpers.graphql_query(
query: mutation,
variables: %{email: @user_creation.email, password: @user_creation.password}
)
assert hd(res["errors"])["message"] == "Your email is not on the whitelist"
Mobilizon.Config.put([:instance, :registrations_open], true)
Mobilizon.Config.put([:instance, :registration_email_whitelist], [])
end
test "create_user/3 allows registration when user email domain is on the whitelist", %{
conn: conn
} do
Mobilizon.Config.put([:instance, :registrations_open], false)
Mobilizon.Config.put([:instance, :registration_email_whitelist], ["demo.tld"])
mutation = """
mutation createUser($email: String!, $password: String!) {
createUser(
email: $email,
password: $password,
) {
id,
email
}
}
"""
res =
conn
|> AbsintheHelpers.graphql_query(
query: mutation,
variables: %{email: @user_creation.email, password: @user_creation.password}
)
refute res["errors"]
assert res["data"]["createUser"]["email"] == @user_creation.email
Mobilizon.Config.put([:instance, :registrations_open], true)
Mobilizon.Config.put([:instance, :registration_email_whitelist], [])
end
test "create_user/3 allows registration when user email is on the whitelist", %{conn: conn} do
Mobilizon.Config.put([:instance, :registrations_open], false)
Mobilizon.Config.put([:instance, :registration_email_whitelist], [@user_creation.email])
mutation = """
mutation createUser($email: String!, $password: String!) {
createUser(
email: $email,
password: $password,
) {
id,
email
}
}
"""
res =
conn
|> AbsintheHelpers.graphql_query(
query: mutation,
variables: %{email: @user_creation.email, password: @user_creation.password}
)
refute res["errors"]
assert res["data"]["createUser"]["email"] == @user_creation.email
Mobilizon.Config.put([:instance, :registrations_open], true)
Mobilizon.Config.put([:instance, :registration_email_whitelist], [])
end
test "register_person/3 doesn't register a profile from an unknown email", context do
mutation = """
mutation {
@ -450,29 +566,6 @@ defmodule MobilizonWeb.Resolvers.UserResolverTest do
assert hd(json_response(res, 200)["errors"])["message"] ==
"Email doesn't fit required format"
end
test "test create_user/3 doesn't create a user when registration is disabled", context do
with_mock Config, instance_registrations_open?: fn -> false end do
mutation = """
mutation {
createUser(
email: "#{@user_creation.email}",
password: "#{@user_creation.password}",
) {
id,
email
}
}
"""
res =
context.conn
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] ==
"Registrations are not enabled"
end
end
end
describe "Resolver: Validate an user" do