App roles

Here you will read about global app roles and team roles

In our app, you may find two types of roles:

  • Global App Role - This role is related to the UserEntity and is visible in any team. Roles are connected to users with a @ManyToMany relation. As you understand, you can add multiple roles to the UserRoleEntity and assign as many as you want to a user. Currently, there are only three roles: "User", "Admin" (customer support) and "Super Admin."

  • Team Role - This role is related to the TeamMembershipEntity and is visible only when you are in a team. A team member can have only one team role, so the role field in the TeamMembershipEntity is an enum that consists of "Viewer," "Manager," and "Owner."

You can be an Admin globally but have different roles within teams.

Team role descriptions

Action
Owner
Manager
Viewer

Assign "Manager" role

Demote "Manager" to "Viewer"

Delete, edit, or restore team members

❌ (only "Viewer")

Invite members to the team

✅ (all roles)

✅ ("Viewer" or "Manager")

Manage the team's subscription

Full access to the Businesses page

✅ (CRUD + comments)

🔒 (read-only + comments)

🔒 (read-only)

Full access to the Leads page

✅ (CRUD + comments)

✅ (CRUD + comments)

🔒 (read-only)

Full access to the Settings page

Access to the Dashboard page

Delete or transfer ownership of the team

Ability to leave the team

Owner The primary role in the team, assigned to the person who creates the team. Each team can have only one Owner. The Owner has the following permissions:

  • Grant the "Manager" role to other members or demote them to "Viewer."

  • Add, edit, delete, or restore team members who were archived when the subscription ended.

  • Full CRUD access on the Businesses page with the ability to leave comments.

  • Full CRUD access on the Leads page with the ability to leave comments.

  • Full CRUD access on the Settings page (lead statuses).

  • Ability to delete the team or leave it while transferring ownership.

  • Manage the team's subscription.

  • Access to the Dashboard page.

Manager The secondary role in the team, which can be assigned by another Manager or the team Owner. A team can have multiple Managers. Managers have the following permissions:

  • Grant the "Manager" role to "Viewers."

  • Delete and edit members with the "Viewer" role.

  • Invite members to the team with "Viewer" or "Manager" roles.

  • Read-only access on the Businesses page with the ability to leave comments.

  • Full CRUD access on the Leads page with the ability to leave comments.

  • Ability to leave the team.

  • Access to the Dashboard page.

Viewer The third role in the team, set by default when a Manager or Owner invites a new member. A team can have multiple Viewers. Viewers have the following permissions:

  • Read-only access on the Team page.

  • Read-only access on the Businesses page.

  • Read-only access on the Leads page.

  • Access to the Dashboard page.

  • Ability to leave the team.

To edit a membership role, we use the update() function in team-membership.service.ts. This function updates the entire TeamMembershipEntity, including the role. In the code, we use a simple if statement to update the role.

const updateData: any = {};

if (membership.role) {
    updateData.role = membership.role;
}

App roles description

Action
Super Admin
Admin
User

Access to Users List page

✅ (full access: delete/edit/restore)

🔒 (read-only, manage memberships)

Manage user roles and memberships

✅ (all except Owner memberships)

✅ (manage memberships only)

Access to Teams List page

Impersonation

✅ (except Super Admins)

✅ (default users only)

Global search by users, businesses, and leads

✅ (only realted to team businesses/leads)

Delete other users' comments

✅ (via API)

Manage subscription tiers

Access to Demo page

General app functionality (teams, leads, etc.)

✅ (based on team role)

Super Admin

The main role in the app is the Super Admin. By default, it is created with the initial seed, and after that, the only way to grant other users this role is manually through the database. Ideally, there should be only one Super Admin. The Super Admin has access to all pages in the app but can also be a viewer on a team, without permission to edit certain businesses or leads. Additionally, the Super Admin has some extra permissions:

  • Access to Users List page

  • Ability to delete, archive, restore, edit user roles and manage user memberships (archive/restore them but super admin can not archive owners memberships)

  • Access to Teams List page

  • Impersonation (including other admins, but not super admins)

  • Global search by all users, businesses, leads

  • Delete other users comments (through API)

  • Manage subscription tiers

  • Access to Demo page and working with it

Admin

The second main role in the app is the Admin (primarily used for customer support). There can be multiple Admins in the app, and only the Super Admin can grant this role. The Admin has access to almost all pages but with limited functionality. The Admin role does not provide any special privileges within a team, as team functionality is based solely on team roles. Additionally, the Admin has some extra permissions:

  • Read only access to Users list (but can manage user memberships)

  • Access to Teams List page

  • Impersonation (including only default users)

  • Global search by all users, businesses, leads

User

This is an autogenerated role assigned upon registration. The role does not provide any special privileges; it simply allows you to use basic app functionality, such as creating teams and managing businesses, leads, and team members, based on your team role.

To update a user's app roles, we use the updateUserRoles() function in users-admin/users-admin.service.ts. The function accepts a user ID and new role IDs that should be granted to the user. The function checks if a user with the provided ID exists and if roles with the provided IDs exist. After that, it deletes any roles that were removed from the user and adds any newly granted roles. Full code:

async updateUserRoles(userId: number, roleIds: number[]): Promise<any> {
        const foundRoles = await this.roleRepository.find({
            where: { id: In([...roleIds]) },
        });
        if (!foundRoles) {
            throw new Error('Role not found');
        }

        const user = await this.userRepository.findOne({
            where: { id: userId },
            relations: ['userRoles'],
        });
        if (!user) {
            throw new Error('User not found');
        }

        const roleIdsForAdding = roleIds.filter(
            roleId => !user.userRoles.map(userRole => userRole.roleId).includes(roleId)
        );

        const roleIdsForRemoving = user.userRoles
            .map(userRole => userRole.roleId)
            .filter(roleId => !roleIds.includes(roleId));

        for (const roleId of roleIdsForRemoving) {
            const userRoles = new UserRoleEntity();
            userRoles.userId = user.id;
            userRoles.roleId = roleId;
            await this.userRoleRepository.delete(userRoles);
        }

        for (const roleId of roleIdsForAdding) {
            const userRoles = new UserRoleEntity();
            userRoles.userId = user.id;
            userRoles.roleId = roleId;
            await this.userRoleRepository.save(userRoles);
        }
    }

Security

For security checks on both the backend and frontend, we use guards. On the backend, CASL-based guards verify if a user with a specific role has permission to perform an action. On the frontend, we have a custom guard that checks the user’s role from the context and grants access to the selected page or redirects them to the Dashboard page.

Backend role guards:

  • admin.guard.ts (SuperAdminGuard) - checks if user is Admin or Super Admin

  • super-admin.guard.ts (AdminGuard) - checks if user is Super Admin

  • member.guard.ts (AbilitiesGuard) - checks if user role in current team has permission to perform action

All membership role permissions are set in api/src/team-memberships/team-membership-permissions.ts and are synchronized with AbilitiesGuard. For app role permissions, we create a file in the module directory, for example, api/src/users-admin/users-admin.permissions.ts.

Frontend role guards:

  • SuperAdminGuard

  • TeamRoleGuard

  • AdminGuard

All guards can be found in ui/src/components/guards. Example of usage frontend guard:

<TeamRoleGuard role={teamRole.MANAGER}>
    <Settings />
</TeamRoleGuard>
<SuperAdminGuard>
    <Orders />
</SuperAdminGuard>

Code example of SuperAdminGuard:

export const SuperAdminGuard = ({ children }) => {
    const { currentUser } = useAuth();
    const hasPermission = checkForSuperAdmin(currentUser);
    return hasPermission ? children : <Navigate to={'/dashboard'} />;
};

Last updated