    async def init_role_creation(self):
        """Load default and user-defined roles and scopes into the database"""
        if self.custom_scopes:
            self.log.info(f"Defining {len(self.custom_scopes)} custom scopes.")
            scopes.define_custom_scopes(self.custom_scopes)

        roles_to_load = self.load_roles[:]

        if self.authenticator.manage_roles and self.load_roles:
            offending_roles = []
            for role_spec in roles_to_load:
                user_role_assignments = role_spec.get('users', [])
                group_role_assignments = role_spec.get('groups', [])
                if user_role_assignments or group_role_assignments:
                    offending_roles.append(role_spec)
            if offending_roles:
                raise ValueError(
                    "When authenticator manages roles, `load_roles` can not"
                    " be used for assigning roles to users nor groups."
                    f" Offending roles: {offending_roles}"
                )

        if self.authenticator.manage_roles:
            managed_roles = await self.authenticator.load_managed_roles()
            for role in managed_roles:
                role['managed_by_auth'] = True
            roles_to_load.extend(managed_roles)

        self.log.debug('Loading roles into database')
        default_roles = roles.get_default_roles()
        config_role_names = [r['name'] for r in roles_to_load]

        default_roles_dict = {role["name"]: role for role in default_roles}
        init_roles = []
        roles_with_new_permissions = []
        for role_spec in roles_to_load:
            role_name = role_spec['name']
            self.log.debug("Loading role %s", role_name)
            if role_name in default_roles_dict:
                self.log.debug("Overriding default role %s", role_name)
                # merge custom role spec with default role spec when overriding
                # so the new role can be partially defined
                default_role_spec = default_roles_dict.pop(role_name)
                merged_role_spec = {}
                merged_role_spec.update(default_role_spec)
                merged_role_spec.update(role_spec)
                role_spec = merged_role_spec

            # Check for duplicates
            if config_role_names.count(role_name) > 1:
                raise ValueError(
                    f"Role {role_name} multiply defined. Please check the `load_roles` configuration"
                )
            init_roles.append(role_spec)
            # Check if some roles have obtained new permissions (to avoid 'scope creep')
            old_role = orm.Role.find(self.db, name=role_name)
            if old_role:
                if not set(role_spec.get('scopes', [])).issubset(old_role.scopes):
                    self.log.warning(f"Role {role_name} has obtained extra permissions")
                    roles_with_new_permissions.append(role_name)

        # make sure we load any default roles not overridden
        init_roles = list(default_roles_dict.values()) + init_roles

        if (
            self.db.query(orm.Role).first() is None
            and self.db.query(orm.User).first() is not None
        ):
            # apply rbac-upgrade default role assignment if there are users in the db,
            # but not any roles
            self._rbac_upgrade = True
        else:
            self._rbac_upgrade = False

        init_non_managed_role_names = [
            r['name'] for r in init_roles if not r.get('managed_by_auth', False)
        ]
        init_managed_role_names = [
            r['name'] for r in init_roles if r.get('managed_by_auth', False)
        ]

        # delete all roles that were defined by `load_roles()` config but are not longer there
        for role in self.db.query(orm.Role).filter(
            (orm.Role.managed_by_auth == False)
            & orm.Role.name.notin_(init_non_managed_role_names)
        ):
            self.log.warning(f"Deleting role {role.name}")
            self.db.delete(role)

        # delete all roles that were defined by authenticator but are not in `load_managed_roles()`
        if self.authenticator.reset_managed_roles_on_startup:
            deleted_stale_roles = (
                self.db.query(orm.Role)
                .filter(
                    (orm.Role.managed_by_auth == True)
                    & (orm.Role.name.notin_(init_managed_role_names))
                )
                .delete()
            )
            if deleted_stale_roles:
                self.log.info(
                    "Deleted %s stale roles previously added by an authenticator",
                    deleted_stale_roles,
                )

        for role in init_roles:
            roles.create_role(self.db, role, commit=False)

        self.db.commit()
Load default and user-defined roles and scopes into the database
jupyterhub/app.py