Adds POST /api/global/users/sso endpoint
This commit is contained in:
parent
d947d91721
commit
b77106480e
|
@ -20,7 +20,7 @@ export async function lookupTenantId(userId: string) {
|
||||||
return user.tenantId
|
return user.tenantId
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getUserDoc(emailOrId: string): Promise<PlatformUser> {
|
export async function getUserDoc(emailOrId: string): Promise<PlatformUser> {
|
||||||
const db = getPlatformDB()
|
const db = getPlatformDB()
|
||||||
return db.get(emailOrId)
|
return db.get(emailOrId)
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,17 @@ async function addUserDoc(emailOrId: string, newDocFn: () => PlatformUser) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function addSsoUser(
|
||||||
|
ssoId: string,
|
||||||
|
email: string,
|
||||||
|
userId: string,
|
||||||
|
tenantId: string
|
||||||
|
) {
|
||||||
|
return addUserDoc(ssoId, () =>
|
||||||
|
newUserSsoIdDoc(ssoId, email, userId, tenantId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export async function addUser(
|
export async function addUser(
|
||||||
tenantId: string,
|
tenantId: string,
|
||||||
userId: string,
|
userId: string,
|
||||||
|
@ -91,9 +102,7 @@ export async function addUser(
|
||||||
]
|
]
|
||||||
|
|
||||||
if (ssoId) {
|
if (ssoId) {
|
||||||
promises.push(
|
promises.push(addSsoUser(ssoId, email, userId, tenantId))
|
||||||
addUserDoc(ssoId, () => newUserSsoIdDoc(ssoId, email, userId, tenantId))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(promises)
|
await Promise.all(promises)
|
||||||
|
|
|
@ -86,6 +86,7 @@ export function ssoUser(
|
||||||
oauth2: opts.details?.oauth2,
|
oauth2: opts.details?.oauth2,
|
||||||
provider: opts.details?.provider!,
|
provider: opts.details?.provider!,
|
||||||
providerType: opts.details?.providerType!,
|
providerType: opts.details?.providerType!,
|
||||||
|
ssoId: opts.details?.userId || uuid(),
|
||||||
thirdPartyProfile: {
|
thirdPartyProfile: {
|
||||||
email: base.email,
|
email: base.email,
|
||||||
picture: base.pictureUrl,
|
picture: base.pictureUrl,
|
||||||
|
|
|
@ -68,6 +68,11 @@ export interface CreateAdminUserRequest {
|
||||||
ssoId?: string
|
ssoId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AddSSoUserRequest {
|
||||||
|
ssoId: string
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface CreateAdminUserResponse {
|
export interface CreateAdminUserResponse {
|
||||||
_id: string
|
_id: string
|
||||||
_rev: string
|
_rev: string
|
||||||
|
|
|
@ -3,6 +3,7 @@ import env from "../../../environment"
|
||||||
import {
|
import {
|
||||||
AcceptUserInviteRequest,
|
AcceptUserInviteRequest,
|
||||||
AcceptUserInviteResponse,
|
AcceptUserInviteResponse,
|
||||||
|
AddSSoUserRequest,
|
||||||
BulkUserRequest,
|
BulkUserRequest,
|
||||||
BulkUserResponse,
|
BulkUserResponse,
|
||||||
CloudAccount,
|
CloudAccount,
|
||||||
|
@ -15,6 +16,7 @@ import {
|
||||||
LockName,
|
LockName,
|
||||||
LockType,
|
LockType,
|
||||||
MigrationType,
|
MigrationType,
|
||||||
|
PlatformUserByEmail,
|
||||||
SaveUserResponse,
|
SaveUserResponse,
|
||||||
SearchUsersRequest,
|
SearchUsersRequest,
|
||||||
User,
|
User,
|
||||||
|
@ -53,6 +55,23 @@ export const save = async (ctx: UserCtx<User, SaveUserResponse>) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const addSsoSupport = async (ctx: Ctx<AddSSoUserRequest>) => {
|
||||||
|
const { email, ssoId } = ctx.request.body
|
||||||
|
try {
|
||||||
|
// Status is changed to 404 from getUserDoc if user is not found
|
||||||
|
let userByEmail = (await platform.users.getUserDoc(email)) as PlatformUserByEmail
|
||||||
|
await platform.users.addSsoUser(
|
||||||
|
ssoId,
|
||||||
|
email,
|
||||||
|
userByEmail.userId,
|
||||||
|
userByEmail.tenantId
|
||||||
|
)
|
||||||
|
ctx.status = 200
|
||||||
|
} catch (err: any) {
|
||||||
|
ctx.throw(err.status || 400, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const bulkDelete = async (userIds: string[], currentUserId: string) => {
|
const bulkDelete = async (userIds: string[], currentUserId: string) => {
|
||||||
if (userIds?.indexOf(currentUserId) !== -1) {
|
if (userIds?.indexOf(currentUserId) !== -1) {
|
||||||
throw new Error("Unable to delete self.")
|
throw new Error("Unable to delete self.")
|
||||||
|
|
|
@ -41,6 +41,10 @@ const PUBLIC_ENDPOINTS = [
|
||||||
route: "/api/global/users/init",
|
route: "/api/global/users/init",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
route: "/api/global/users/sso",
|
||||||
|
method: "POST",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
route: "/api/global/users/invite/accept",
|
route: "/api/global/users/invite/accept",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -81,6 +85,11 @@ const NO_TENANCY_ENDPOINTS = [
|
||||||
route: "/api/global/users/init",
|
route: "/api/global/users/init",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
},
|
},
|
||||||
|
// tenant is retrieved from the user found by the requested email
|
||||||
|
{
|
||||||
|
route: "/api/global/users/sso",
|
||||||
|
method: "POST",
|
||||||
|
},
|
||||||
// deprecated single tenant sso callback
|
// deprecated single tenant sso callback
|
||||||
{
|
{
|
||||||
route: "/api/admin/auth/google/callback",
|
route: "/api/admin/auth/google/callback",
|
||||||
|
|
|
@ -520,10 +520,50 @@ describe("/api/global/users", () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createPasswordUser() {
|
||||||
|
return config.doInTenant(() => {
|
||||||
|
const user = structures.users.user()
|
||||||
|
return userSdk.db.save(user)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
it("should be able to update an sso user that has no password", async () => {
|
it("should be able to update an sso user that has no password", async () => {
|
||||||
const user = await createSSOUser()
|
const user = await createSSOUser()
|
||||||
await config.api.users.saveUser(user)
|
await config.api.users.saveUser(user)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("sso support couldn't be used by admin. It is cloud restricted and needs internal key", async () => {
|
||||||
|
const user = await config.createUser()
|
||||||
|
const ssoId = "fake-ssoId"
|
||||||
|
await config.api.users
|
||||||
|
.addSsoSupportDefaultAuth(ssoId, user.email)
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(403)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("if user email doesn't exist, SSO support couldn't be added. Not found error returned", async () => {
|
||||||
|
const ssoId = "fake-ssoId"
|
||||||
|
const email = "fake-email@budibase.com"
|
||||||
|
await config.api.users
|
||||||
|
.addSsoSupportInternalAPIAuth(ssoId, email)
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(404)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("if user email exist, SSO support is added", async () => {
|
||||||
|
const user = await createPasswordUser()
|
||||||
|
const ssoId = "fakessoId"
|
||||||
|
await config.api.users
|
||||||
|
.addSsoSupportInternalAPIAuth(ssoId, user.email)
|
||||||
|
.expect(200)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("if user ssoId is already assigned, no change will be applied", async () => {
|
||||||
|
const user = await createSSOUser()
|
||||||
|
await config.api.users
|
||||||
|
.addSsoSupportInternalAPIAuth(user.ssoId, user.email)
|
||||||
|
.expect(200)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,12 @@ router
|
||||||
users.buildUserSaveValidation(),
|
users.buildUserSaveValidation(),
|
||||||
controller.save
|
controller.save
|
||||||
)
|
)
|
||||||
|
.post(
|
||||||
|
"/api/global/users/sso",
|
||||||
|
cloudRestricted,
|
||||||
|
users.buildAddSsoSupport(),
|
||||||
|
controller.addSsoSupport
|
||||||
|
)
|
||||||
.post(
|
.post(
|
||||||
"/api/global/users/bulk",
|
"/api/global/users/bulk",
|
||||||
auth.adminOnly,
|
auth.adminOnly,
|
||||||
|
|
|
@ -41,6 +41,15 @@ export const buildUserSaveValidation = () => {
|
||||||
return auth.joiValidator.body(Joi.object(schema).required().unknown(true))
|
return auth.joiValidator.body(Joi.object(schema).required().unknown(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const buildAddSsoSupport = () => {
|
||||||
|
return auth.joiValidator.body(
|
||||||
|
Joi.object({
|
||||||
|
ssoId: Joi.string().required(),
|
||||||
|
email: Joi.string().required(),
|
||||||
|
}).required()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const buildUserBulkUserValidation = (isSelf = false) => {
|
export const buildUserBulkUserValidation = (isSelf = false) => {
|
||||||
if (!isSelf) {
|
if (!isSelf) {
|
||||||
schema = {
|
schema = {
|
||||||
|
|
|
@ -127,6 +127,20 @@ export class UserAPI extends TestAPI {
|
||||||
.expect(status ? status : 200)
|
.expect(status ? status : 200)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addSsoSupportInternalAPIAuth = (ssoId: string, email: string) => {
|
||||||
|
return this.request
|
||||||
|
.post(`/api/global/users/sso`)
|
||||||
|
.send({ ssoId, email })
|
||||||
|
.set(this.config.internalAPIHeaders())
|
||||||
|
}
|
||||||
|
|
||||||
|
addSsoSupportDefaultAuth = (ssoId: string, email: string) => {
|
||||||
|
return this.request
|
||||||
|
.post(`/api/global/users/sso`)
|
||||||
|
.send({ ssoId, email })
|
||||||
|
.set(this.config.defaultHeaders())
|
||||||
|
}
|
||||||
|
|
||||||
deleteUser = (userId: string, status?: number) => {
|
deleteUser = (userId: string, status?: number) => {
|
||||||
return this.request
|
return this.request
|
||||||
.delete(`/api/global/users/${userId}`)
|
.delete(`/api/global/users/${userId}`)
|
||||||
|
|
Loading…
Reference in New Issue