Skip to content

Modules

Adrian Rocke edited this page Mar 1, 2025 · 2 revisions

Access Module

Scope

  • Authorizing user access to query data and perform actions

Exports

  • A Policy defines what roles are needed to perform an action.
  • For all policies, user claims are loaded to check system roles.
  • If a language code is provided to the policy, language claims are also loaded to check language roles
import Policy from '@/modules/access/public/Policy'

// Requires either system admin or language admin role.
const policy = new Policy({
  systemRoles: [Policy.SystemRole.Admin],
  languageRoles: [Policy.LanguageRole.Admin]
})

// Loads user and language claims from the database to determine access
const isAuthorized = await policy.authorize({
  actorId: 'user-id',
  languageCode: 'spa'
})

Model

erDiagram
  ActorClaims {
    string id
    SystemRole[] systemRoles
  }

  LanguageClaims {
    string code
    LanguageRole[] roles
  }

  Policy {
    SystemRole[] systemRoles
    LanguageRole[] languageRoles
  }
Loading

Coupling concerns

  • Reads from user and language module data to determine whether to authorize a user. This could be separated so it has it's own model that it derives from these modules through events.

User Module

Scope

  • User identity
  • Authentication
  • Invitations

Use Cases

  • AcceptInvite - Takes a token and the users basic profile to complete their account setup
  • ChangeUserRoles - Changes a user's system roles
  • DisableUser - Disables the user so they can no longer log in or accept their invite
  • InviteUser - Sends an email to a new user to invite the create an account
  • LogIn - Verifies a user's password to log them into the system
  • ProcessEmailRejection - Processes a bounce or a complaint of an email
  • ResetPassword - Completes the reset password process with a new password
  • StartPasswordReset - Sends an email to start the password reset
  • UpdateProfile - Updates the users profile information, starts the email verification process if changed
  • VerifyEmail - Uses a token to verify ownership over an email address waiting to be changed.

Domain Model

erDiagram
  User{
    string id
    string name
    UserEmail email
    EmailVerifcation emailVerification
    UserStatus status
    Password password
    PasswordReset[] passwordResets
    Invitation[] invitation
  }
  User ||--o{ UserStatus : has
  User }o--o{ SystemRole : has

  UserEmail {
    string address
    EmailStatus status
  }
  User ||--|| UserEmail : has
  UserEmail ||--o{ EmailStatus : has

  EmailVerification {
    string email
    string token
    Date expiresAt
  }
  EmailVerification ||--o| User : has

  Password {
    string hash
  }
  User |o--|| Password : has

  PasswordReset {
    string token
    Date expiresAt
  }
  PasswordReset ||--o{ User : has

  Session {
    string id
    string userId
    Date expiresAt
  }
  Session ||..o{ User : references

  Invitation {
    string id
    string userId
    string token
    Date expiresAt
  }
  Invitation ||--o{ User : has
Loading

Database Model

erDiagram
  reset_password_token{
    ~uuid~ user_id
    ~text~ token
    ~bigint~ expires
  }

  session{
    ~text~ id
    ~uuid~ user_id
    ~timestamp(3) without time zone~ expires_at
  }

  users{
    ~uuid~ id
    ~text~ name
    ~email_status~ email_status
    ~text~ email
    ~text~ hashed_password
    ~user_status~ status
  }

  user_email_verification{
    ~uuid~ user_id
    ~text~ email
    ~text~ token
    ~bigint~ expires
  }

  user_invitation{
    ~uuid~ user_id
    ~text~ token
    ~bigint~ expires
  }

  user_system_role{
    ~uuid~ user_id
    ~system_role~ role
  }

  session }|..|| users : session_user_id_fkey
  user_email_verification }|..|| users : user_email_verification_user_id_fkey
  user_invitation }|..|| users : user_invitation_user_id_fkey
  user_system_role }|..|| users : user_system_role_user_id_fkey
  reset_password_token }|..|| users : reset_password_token_user_id_fkey
Loading

Coupling concerns

  • Disabling a user has side effects in other modules. Those should be decoupled through an event system, so that the users module is not responsible for executing those side effects.
  • Currently has responsibility for handling email complaints and bounces. The block list from those events should be handled by a notifications module, while email verification (for ownership purposes) should still belong to the users module since it involves changing the user's profile.
  • Currently has responsibility for maintaining a user's system roles. That could be transferred to the access control domain. At present those domains are going to be coupled since one writes the data and the other reads it.

Language Module

Scope

  • Language definition and configuration
  • Language membership

Use Cases

  • ChangeLanguageMemberRoles - Changes a user's roles on the language - viewer, translator, admin
  • CreateLanguage - Create a new language
  • InviteLanguageMember - Invite a user to the language, and to the platform as well if necessary
  • RemoveLanguageMember - Remove a user from a language
  • RemoveUserFromLanguages - Remove a user from all languages - used when a user is disabled
  • UpdateLanguageSettings - Update a language's configuration - name, font, translations, etc

Model

erDiagram
  Language{
    string id
    string name
    string code
    string font
    TextDirection textDirection
    string[] translationIds
  }

  LanguageMember{
    string languageId
    string userId
    LanguageRole[] roles
  }
  LanguageMember ||..o{ Language : has

Loading

Database Model

erDiagram
  language{
    ~uuid~ id
    ~text~ code
    ~text~ name
    ~text~ font
    ~text[]~ translation_ids
    ~text_direction~ text_direction
  }

  language_import_job{
    ~uuid~ language_id
    ~timestamp(3) without time zone~ start_date
    ~timestamp(3) without time zone~ end_date
    ~boolean~ succeeded
    ~uuid~ user_id
  }

  language_member_role{
    ~uuid~ user_id
    ~uuid~ language_id
    ~language_role~ role
  }

  language_member_role }|..|| users : language_member_role_user_id_fkey
  language_member_role }|..|| language : language_member_role_language_id_fkey
  language_import_job }|..|| language : language_import_job_language_id_fkey
  language_import_job }|..|| users : language_import_job_user_id_fkey
Loading

Coupling Concerns

  • Provides a use case for the users module to execute when a user is disabled. This should really be replaced by an event handler so as to not couple the two modules.
  • Showing a list of members in the language requires access to a users name and email which is managed by the users module. This is loaded via a database query for now.

Clone this wiki locally