Main body of work to handle the new approach of per app builders support.
This commit is contained in:
parent
af5d024d89
commit
39746e0bf0
|
@ -14,8 +14,6 @@ export enum ViewName {
|
||||||
USER_BY_APP = "by_app",
|
USER_BY_APP = "by_app",
|
||||||
USER_BY_EMAIL = "by_email2",
|
USER_BY_EMAIL = "by_email2",
|
||||||
BY_API_KEY = "by_api_key",
|
BY_API_KEY = "by_api_key",
|
||||||
/** @deprecated - could be deleted */
|
|
||||||
USER_BY_BUILDERS = "by_builders",
|
|
||||||
LINK = "by_link",
|
LINK = "by_link",
|
||||||
ROUTING = "screen_routes",
|
ROUTING = "screen_routes",
|
||||||
AUTOMATION_LOGS = "automation_logs",
|
AUTOMATION_LOGS = "automation_logs",
|
||||||
|
|
|
@ -105,16 +105,6 @@ export const createApiKeyView = async () => {
|
||||||
await createView(db, viewJs, ViewName.BY_API_KEY)
|
await createView(db, viewJs, ViewName.BY_API_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createUserBuildersView = async () => {
|
|
||||||
const db = getGlobalDB()
|
|
||||||
const viewJs = `function(doc) {
|
|
||||||
if (doc.builder && doc.builder.global === true) {
|
|
||||||
emit(doc._id, doc._id)
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
await createView(db, viewJs, ViewName.USER_BY_BUILDERS)
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface QueryViewOptions {
|
export interface QueryViewOptions {
|
||||||
arrayResponse?: boolean
|
arrayResponse?: boolean
|
||||||
}
|
}
|
||||||
|
@ -223,7 +213,6 @@ export const queryPlatformView = async <T>(
|
||||||
const CreateFuncByName: any = {
|
const CreateFuncByName: any = {
|
||||||
[ViewName.USER_BY_EMAIL]: createNewUserEmailView,
|
[ViewName.USER_BY_EMAIL]: createNewUserEmailView,
|
||||||
[ViewName.BY_API_KEY]: createApiKeyView,
|
[ViewName.BY_API_KEY]: createApiKeyView,
|
||||||
[ViewName.USER_BY_BUILDERS]: createUserBuildersView,
|
|
||||||
[ViewName.USER_BY_APP]: createUserAppView,
|
[ViewName.USER_BY_APP]: createUserAppView,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { processors } from "./processors"
|
||||||
import { newid } from "../utils"
|
import { newid } from "../utils"
|
||||||
import * as installation from "../installation"
|
import * as installation from "../installation"
|
||||||
import * as configs from "../configs"
|
import * as configs from "../configs"
|
||||||
|
import * as users from "../users"
|
||||||
import { withCache, TTL, CacheKey } from "../cache/generic"
|
import { withCache, TTL, CacheKey } from "../cache/generic"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,8 +165,8 @@ const identifyUser = async (
|
||||||
const id = user._id as string
|
const id = user._id as string
|
||||||
const tenantId = await getEventTenantId(user.tenantId)
|
const tenantId = await getEventTenantId(user.tenantId)
|
||||||
const type = IdentityType.USER
|
const type = IdentityType.USER
|
||||||
let builder = user.builder?.global || false
|
let builder = users.hasBuilderPermissions(user)
|
||||||
let admin = user.admin?.global || false
|
let admin = users.hasAdminPermissions(user)
|
||||||
let providerType
|
let providerType
|
||||||
if (isSSOUser(user)) {
|
if (isSSOUser(user)) {
|
||||||
providerType = user.providerType
|
providerType = user.providerType
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { BBContext } from "@budibase/types"
|
import { UserCtx } from "@budibase/types"
|
||||||
|
import { isBuilder } from "../users"
|
||||||
|
import { getAppId } from "../context"
|
||||||
|
|
||||||
export default async (ctx: BBContext, next: any) => {
|
export default async (ctx: UserCtx, next: any) => {
|
||||||
if (
|
const appId = getAppId()
|
||||||
!ctx.internal &&
|
if (!ctx.internal && !isBuilder(ctx.user, appId)) {
|
||||||
(!ctx.user || !ctx.user.builder || !ctx.user.builder.global)
|
|
||||||
) {
|
|
||||||
ctx.throw(403, "Builder user only endpoint.")
|
ctx.throw(403, "Builder user only endpoint.")
|
||||||
}
|
}
|
||||||
return next()
|
return next()
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { BBContext } from "@budibase/types"
|
import { UserCtx } from "@budibase/types"
|
||||||
|
import { isBuilder, isAdmin } from "../users"
|
||||||
|
import { getAppId } from "../context"
|
||||||
|
|
||||||
export default async (ctx: BBContext, next: any) => {
|
export default async (ctx: UserCtx, next: any) => {
|
||||||
if (
|
const appId = getAppId()
|
||||||
!ctx.internal &&
|
if (!ctx.internal && !isBuilder(ctx.user, appId) && !isAdmin(ctx.user)) {
|
||||||
(!ctx.user || !ctx.user.builder || !ctx.user.builder.global) &&
|
ctx.throw(403, "Admin/Builder user only endpoint.")
|
||||||
(!ctx.user || !ctx.user.admin || !ctx.user.admin.global)
|
|
||||||
) {
|
|
||||||
ctx.throw(403, "Builder user only endpoint.")
|
|
||||||
}
|
}
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,12 @@ import {
|
||||||
UNICODE_MAX,
|
UNICODE_MAX,
|
||||||
ViewName,
|
ViewName,
|
||||||
} from "./db"
|
} from "./db"
|
||||||
import { BulkDocsResponse, SearchUsersRequest, User } from "@budibase/types"
|
import {
|
||||||
|
BulkDocsResponse,
|
||||||
|
SearchUsersRequest,
|
||||||
|
User,
|
||||||
|
ContextUser,
|
||||||
|
} from "@budibase/types"
|
||||||
import { getGlobalDB } from "./context"
|
import { getGlobalDB } from "./context"
|
||||||
import * as context from "./context"
|
import * as context from "./context"
|
||||||
|
|
||||||
|
@ -178,7 +183,7 @@ export const getGlobalUserByAppPage = (appId: string, user: User) => {
|
||||||
* Performs a starts with search on the global email view.
|
* Performs a starts with search on the global email view.
|
||||||
*/
|
*/
|
||||||
export const searchGlobalUsersByEmail = async (
|
export const searchGlobalUsersByEmail = async (
|
||||||
email: string,
|
email: string | unknown,
|
||||||
opts: any,
|
opts: any,
|
||||||
getOpts?: GetOpts
|
getOpts?: GetOpts
|
||||||
) => {
|
) => {
|
||||||
|
@ -248,3 +253,56 @@ export async function getUserCount() {
|
||||||
})
|
})
|
||||||
return response.total_rows
|
return response.total_rows
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checks if a user is specifically a builder, given an app ID
|
||||||
|
export function isBuilder(user: User | ContextUser, appId?: string) {
|
||||||
|
if (user.builder?.global) {
|
||||||
|
return true
|
||||||
|
} else if (appId && user.builder?.apps?.includes(appId)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// alias for hasAdminPermission, currently do the same thing
|
||||||
|
// in future whether someone has admin permissions and whether they are
|
||||||
|
// an admin for a specific resource could be separated
|
||||||
|
export function isAdmin(user: User | ContextUser) {
|
||||||
|
return hasAdminPermissions(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if a user is capable of building any app
|
||||||
|
export function hasBuilderPermissions(user?: User | ContextUser) {
|
||||||
|
if (!user) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return user.builder?.global || user.builder?.apps?.length !== 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if a user is capable of being an admin
|
||||||
|
export function hasAdminPermissions(user?: User | ContextUser) {
|
||||||
|
if (!user) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return user.admin?.global
|
||||||
|
}
|
||||||
|
|
||||||
|
// used to remove the builder/admin permissions, for processing the
|
||||||
|
// user as an app user (they may have some specific role/group
|
||||||
|
export function removePortalUserPermissions(user: User | ContextUser) {
|
||||||
|
delete user.admin
|
||||||
|
delete user.builder
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cleanseUserObject(user: User | ContextUser, base?: User) {
|
||||||
|
delete user.admin
|
||||||
|
delete user.builder
|
||||||
|
delete user.roles
|
||||||
|
if (base) {
|
||||||
|
user.admin = base.admin
|
||||||
|
user.builder = base.builder
|
||||||
|
user.roles = base.roles
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import {
|
||||||
objectStore,
|
objectStore,
|
||||||
roles,
|
roles,
|
||||||
tenancy,
|
tenancy,
|
||||||
|
users,
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
import { USERS_TABLE_SCHEMA } from "../../constants"
|
import { USERS_TABLE_SCHEMA } from "../../constants"
|
||||||
import {
|
import {
|
||||||
|
@ -222,6 +223,7 @@ export async function fetchAppDefinition(ctx: UserCtx) {
|
||||||
|
|
||||||
export async function fetchAppPackage(ctx: UserCtx) {
|
export async function fetchAppPackage(ctx: UserCtx) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
|
const appId = context.getAppId()
|
||||||
let application = await db.get<any>(DocumentType.APP_METADATA)
|
let application = await db.get<any>(DocumentType.APP_METADATA)
|
||||||
const layouts = await getLayouts()
|
const layouts = await getLayouts()
|
||||||
let screens = await getScreens()
|
let screens = await getScreens()
|
||||||
|
@ -233,7 +235,7 @@ export async function fetchAppPackage(ctx: UserCtx) {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Only filter screens if the user is not a builder
|
// Only filter screens if the user is not a builder
|
||||||
if (!(ctx.user.builder && ctx.user.builder.global)) {
|
if (!users.isBuilder(ctx.user, appId)) {
|
||||||
const userRoleId = getUserRoleId(ctx)
|
const userRoleId = getUserRoleId(ctx)
|
||||||
const accessController = new roles.AccessController()
|
const accessController = new roles.AccessController()
|
||||||
screens = await accessController.checkScreensAccess(screens, userRoleId)
|
screens = await accessController.checkScreensAccess(screens, userRoleId)
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
import { roles, permissions, auth, context } from "@budibase/backend-core"
|
import {
|
||||||
|
roles,
|
||||||
|
permissions,
|
||||||
|
auth,
|
||||||
|
context,
|
||||||
|
users,
|
||||||
|
} from "@budibase/backend-core"
|
||||||
import { Role } from "@budibase/types"
|
import { Role } from "@budibase/types"
|
||||||
import builderMiddleware from "./builder"
|
import builderMiddleware from "./builder"
|
||||||
import { isWebhookEndpoint } from "./utils"
|
import { isWebhookEndpoint } from "./utils"
|
||||||
|
@ -21,8 +27,9 @@ const checkAuthorized = async (
|
||||||
permType: any,
|
permType: any,
|
||||||
permLevel: any
|
permLevel: any
|
||||||
) => {
|
) => {
|
||||||
|
const appId = context.getAppId()
|
||||||
// check if this is a builder api and the user is not a builder
|
// check if this is a builder api and the user is not a builder
|
||||||
const isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
|
const isBuilder = users.isBuilder(ctx.user, appId)
|
||||||
const isBuilderApi = permType === permissions.PermissionType.BUILDER
|
const isBuilderApi = permType === permissions.PermissionType.BUILDER
|
||||||
if (isBuilderApi && !isBuilder) {
|
if (isBuilderApi && !isBuilder) {
|
||||||
return ctx.throw(403, "Not Authorized")
|
return ctx.throw(403, "Not Authorized")
|
||||||
|
|
|
@ -4,12 +4,14 @@ import {
|
||||||
roles,
|
roles,
|
||||||
tenancy,
|
tenancy,
|
||||||
context,
|
context,
|
||||||
|
users,
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
import { generateUserMetadataID, isDevAppID } from "../db/utils"
|
import { generateUserMetadataID, isDevAppID } from "../db/utils"
|
||||||
import { getCachedSelf } from "../utilities/global"
|
import { getCachedSelf } from "../utilities/global"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import { isWebhookEndpoint } from "./utils"
|
import { isWebhookEndpoint } from "./utils"
|
||||||
import { UserCtx } from "@budibase/types"
|
import { UserCtx, ContextUser } from "@budibase/types"
|
||||||
|
import { removePortalUserPermissions } from "@budibase/backend-core/src/users"
|
||||||
|
|
||||||
export default async (ctx: UserCtx, next: any) => {
|
export default async (ctx: UserCtx, next: any) => {
|
||||||
// try to get the appID from the request
|
// try to get the appID from the request
|
||||||
|
@ -42,8 +44,7 @@ export default async (ctx: UserCtx, next: any) => {
|
||||||
roleId = globalUser.roleId || roleId
|
roleId = globalUser.roleId || roleId
|
||||||
|
|
||||||
// Allow builders to specify their role via a header
|
// Allow builders to specify their role via a header
|
||||||
const isBuilder =
|
const isBuilder = users.isBuilder(globalUser, appId)
|
||||||
globalUser && globalUser.builder && globalUser.builder.global
|
|
||||||
const isDevApp = appId && isDevAppID(appId)
|
const isDevApp = appId && isDevAppID(appId)
|
||||||
const roleHeader =
|
const roleHeader =
|
||||||
ctx.request &&
|
ctx.request &&
|
||||||
|
@ -56,8 +57,7 @@ export default async (ctx: UserCtx, next: any) => {
|
||||||
roleId = roleHeader
|
roleId = roleHeader
|
||||||
|
|
||||||
// Delete admin and builder flags so that the specified role is honoured
|
// Delete admin and builder flags so that the specified role is honoured
|
||||||
delete ctx.user.builder
|
ctx.user = users.removePortalUserPermissions(ctx.user) as ContextUser
|
||||||
delete ctx.user.admin
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Swallow error and do nothing
|
// Swallow error and do nothing
|
||||||
|
@ -81,9 +81,7 @@ export default async (ctx: UserCtx, next: any) => {
|
||||||
!tenancy.isUserInAppTenant(requestAppId, ctx.user)
|
!tenancy.isUserInAppTenant(requestAppId, ctx.user)
|
||||||
) {
|
) {
|
||||||
// don't error, simply remove the users rights (they are a public user)
|
// don't error, simply remove the users rights (they are a public user)
|
||||||
delete ctx.user.builder
|
ctx.user = users.cleanseUserObject(ctx.user) as ContextUser
|
||||||
delete ctx.user.admin
|
|
||||||
delete ctx.user.roles
|
|
||||||
ctx.isAuthenticated = false
|
ctx.isAuthenticated = false
|
||||||
roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
||||||
skipCookie = true
|
skipCookie = true
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import { events, db as dbUtils } from "@budibase/backend-core"
|
import {
|
||||||
|
events,
|
||||||
|
db as dbUtils,
|
||||||
|
users as usersCore,
|
||||||
|
} from "@budibase/backend-core"
|
||||||
import { User, CloudAccount } from "@budibase/types"
|
import { User, CloudAccount } from "@budibase/types"
|
||||||
import { DEFAULT_TIMESTAMP } from ".."
|
import { DEFAULT_TIMESTAMP } from ".."
|
||||||
|
|
||||||
|
@ -30,11 +34,11 @@ export const backfill = async (
|
||||||
await events.identification.identifyUser(user, account, timestamp)
|
await events.identification.identifyUser(user, account, timestamp)
|
||||||
await events.user.created(user, timestamp)
|
await events.user.created(user, timestamp)
|
||||||
|
|
||||||
if (user.admin?.global) {
|
if (usersCore.hasAdminPermissions(user)) {
|
||||||
await events.user.permissionAdminAssigned(user, timestamp)
|
await events.user.permissionAdminAssigned(user, timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.builder?.global) {
|
if (usersCore.hasBuilderPermissions(user)) {
|
||||||
await events.user.permissionBuilderAssigned(user, timestamp)
|
await events.user.permissionBuilderAssigned(user, timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
cache,
|
cache,
|
||||||
tenancy,
|
tenancy,
|
||||||
context,
|
context,
|
||||||
|
users,
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import { groups } from "@budibase/pro"
|
import { groups } from "@budibase/pro"
|
||||||
|
@ -22,8 +23,7 @@ export function updateAppRole(
|
||||||
}
|
}
|
||||||
// if in an multi-tenancy environment make sure roles are never updated
|
// if in an multi-tenancy environment make sure roles are never updated
|
||||||
if (env.MULTI_TENANCY && appId && !tenancy.isUserInAppTenant(appId, user)) {
|
if (env.MULTI_TENANCY && appId && !tenancy.isUserInAppTenant(appId, user)) {
|
||||||
delete user.builder
|
user = users.removePortalUserPermissions(user)
|
||||||
delete user.admin
|
|
||||||
user.roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
user.roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ export function updateAppRole(
|
||||||
user.roleId = user.roles[dbCore.getProdAppID(appId)]
|
user.roleId = user.roles[dbCore.getProdAppID(appId)]
|
||||||
}
|
}
|
||||||
// if a role wasn't found then either set as admin (builder) or public (everyone else)
|
// if a role wasn't found then either set as admin (builder) or public (everyone else)
|
||||||
if (!user.roleId && user.builder && user.builder.global) {
|
if (!user.roleId && users.isBuilder(user, appId)) {
|
||||||
user.roleId = roles.BUILTIN_ROLE_IDS.ADMIN
|
user.roleId = roles.BUILTIN_ROLE_IDS.ADMIN
|
||||||
} else if (!user.roleId && !user?.userGroups?.length) {
|
} else if (!user.roleId && !user?.userGroups?.length) {
|
||||||
user.roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
user.roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
||||||
|
|
|
@ -47,7 +47,6 @@ export interface User extends Document {
|
||||||
}
|
}
|
||||||
admin?: {
|
admin?: {
|
||||||
global: boolean
|
global: boolean
|
||||||
apps?: string[]
|
|
||||||
}
|
}
|
||||||
password?: string
|
password?: string
|
||||||
status?: UserStatus
|
status?: UserStatus
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
import env from "../../environment"
|
import env from "../../environment"
|
||||||
import { events, accounts, tenancy } from "@budibase/backend-core"
|
import { events, accounts, tenancy, users } from "@budibase/backend-core"
|
||||||
import { User, UserRoles, CloudAccount } from "@budibase/types"
|
import { User, UserRoles, CloudAccount } from "@budibase/types"
|
||||||
|
|
||||||
|
const hasBuilderPerm = users.hasBuilderPermissions
|
||||||
|
const hasAdminPerm = users.hasAdminPermissions
|
||||||
|
|
||||||
export const handleDeleteEvents = async (user: any) => {
|
export const handleDeleteEvents = async (user: any) => {
|
||||||
await events.user.deleted(user)
|
await events.user.deleted(user)
|
||||||
|
|
||||||
if (isBuilder(user)) {
|
if (hasBuilderPerm(user)) {
|
||||||
await events.user.permissionBuilderRemoved(user)
|
await events.user.permissionBuilderRemoved(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAdmin(user)) {
|
if (hasAdminPerm(user)) {
|
||||||
await events.user.permissionAdminRemoved(user)
|
await events.user.permissionAdminRemoved(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,23 +106,20 @@ export const handleSaveEvents = async (
|
||||||
await handleAppRoleEvents(user, existingUser)
|
await handleAppRoleEvents(user, existingUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isBuilder = (user: any) => user.builder && user.builder.global
|
|
||||||
const isAdmin = (user: any) => user.admin && user.admin.global
|
|
||||||
|
|
||||||
export const isAddingBuilder = (user: any, existingUser: any) => {
|
export const isAddingBuilder = (user: any, existingUser: any) => {
|
||||||
return isAddingPermission(user, existingUser, isBuilder)
|
return isAddingPermission(user, existingUser, hasBuilderPerm)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isRemovingBuilder = (user: any, existingUser: any) => {
|
export const isRemovingBuilder = (user: any, existingUser: any) => {
|
||||||
return isRemovingPermission(user, existingUser, isBuilder)
|
return isRemovingPermission(user, existingUser, hasBuilderPerm)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isAddingAdmin = (user: any, existingUser: any) => {
|
const isAddingAdmin = (user: any, existingUser: any) => {
|
||||||
return isAddingPermission(user, existingUser, isAdmin)
|
return isAddingPermission(user, existingUser, hasAdminPerm)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isRemovingAdmin = (user: any, existingUser: any) => {
|
const isRemovingAdmin = (user: any, existingUser: any) => {
|
||||||
return isRemovingPermission(user, existingUser, isAdmin)
|
return isRemovingPermission(user, existingUser, hasAdminPerm)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isOnboardingComplete = (user: any, existingUser: any) => {
|
const isOnboardingComplete = (user: any, existingUser: any) => {
|
||||||
|
|
|
@ -252,9 +252,7 @@ export const save = async (
|
||||||
let builtUser = await buildUser(user, opts, tenantId, dbUser)
|
let builtUser = await buildUser(user, opts, tenantId, dbUser)
|
||||||
// don't allow a user to update its own roles/perms
|
// don't allow a user to update its own roles/perms
|
||||||
if (opts.currentUserId && opts.currentUserId === dbUser?._id) {
|
if (opts.currentUserId && opts.currentUserId === dbUser?._id) {
|
||||||
builtUser.builder = dbUser.builder
|
builtUser = usersCore.cleanseUserObject(builtUser, dbUser) as User
|
||||||
builtUser.admin = dbUser.admin
|
|
||||||
builtUser.roles = dbUser.roles
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dbUser && roles?.length) {
|
if (!dbUser && roles?.length) {
|
||||||
|
|
Loading…
Reference in New Issue