1
0
Fork 0
mirror of synced 2024-05-17 10:53:15 +12:00
budibase/packages/worker/src/api/controllers/global/auth.ts

277 lines
8.3 KiB
TypeScript
Raw Normal View History

const core = require("@budibase/backend-core")
const { getScopedConfig } = require("@budibase/backend-core/db")
2022-01-13 01:54:25 +13:00
const { google } = require("@budibase/backend-core/middleware")
const { oidc } = require("@budibase/backend-core/middleware")
const { Configs, EmailTemplatePurpose } = require("../../../constants")
const { sendEmail, isEmailConfigured } = require("../../../utilities/email")
2022-04-08 12:28:22 +12:00
const { setCookie, getCookie, clearCookie, hash, platformLogout } = core.utils
const { Cookies, Headers } = core.constants
const { passport } = core.auth
const { checkResetPasswordCode } = require("../../../utilities/redis")
const {
getGlobalDB,
getTenantId,
isMultiTenant,
} = require("@budibase/backend-core/tenancy")
const env = require("../../../environment")
2022-05-24 20:54:36 +12:00
import { events, users as usersCore, context } from "@budibase/backend-core"
2022-04-08 12:28:22 +12:00
import { users } from "../../../sdk"
2022-05-24 20:54:36 +12:00
import { User } from "@budibase/types"
const ssoCallbackUrl = async (config: any, type: any) => {
// incase there is a callback URL from before
if (config && config.callbackURL) {
return config.callbackURL
}
const db = getGlobalDB()
const publicConfig = await getScopedConfig(db, {
type: Configs.SETTINGS,
})
let callbackUrl = `/api/global/auth`
if (isMultiTenant()) {
callbackUrl += `/${getTenantId()}`
}
callbackUrl += `/${type}/callback`
return `${publicConfig.platformUrl}${callbackUrl}`
}
export const googleCallbackUrl = async (config: any) => {
return ssoCallbackUrl(config, "google")
}
export const oidcCallbackUrl = async (config: any) => {
return ssoCallbackUrl(config, "oidc")
}
2021-07-23 02:26:14 +12:00
async function authInternal(ctx: any, user: any, err = null, info = null) {
if (err) {
console.error("Authentication error", err)
2021-07-09 00:12:25 +12:00
return ctx.throw(403, info ? info : "Unauthorized")
}
2021-04-07 22:33:16 +12:00
if (!user) {
2021-07-09 00:12:25 +12:00
return ctx.throw(403, info ? info : "Unauthorized")
}
2021-04-12 21:47:48 +12:00
// set a cookie for browser access
2021-12-04 01:39:20 +13:00
setCookie(ctx, user.token, Cookies.Auth, { sign: false })
// set the token in a header as well for APIs
ctx.set(Headers.TOKEN, user.token)
// get rid of any app cookies on login
// have to check test because this breaks cypress
if (!env.isTest()) {
clearCookie(ctx, Cookies.CurrentApp)
}
}
export const authenticate = async (ctx: any, next: any) => {
return passport.authenticate(
"local",
async (err: any, user: User, info: any) => {
await authInternal(ctx, user, err, info)
await context.identity.doInUserContext(user, async () => {
2022-05-24 20:54:36 +12:00
await events.auth.login("local")
})
ctx.status = 200
}
)(ctx, next)
}
2021-04-11 22:35:55 +12:00
export const setInitInfo = (ctx: any) => {
2021-11-05 02:03:18 +13:00
const initInfo = ctx.request.body
setCookie(ctx, initInfo, Cookies.Init)
ctx.status = 200
}
export const getInitInfo = (ctx: any) => {
try {
ctx.body = getCookie(ctx, Cookies.Init) || {}
} catch (err) {
clearCookie(ctx, Cookies.Init)
ctx.body = {}
}
2021-11-05 02:03:18 +13:00
}
/**
* Reset the user password, used as part of a forgotten password flow.
*/
export const reset = async (ctx: any) => {
const { email } = ctx.request.body
const configured = await isEmailConfigured()
if (!configured) {
2021-05-06 02:19:44 +12:00
ctx.throw(
400,
"Please contact your platform administrator, SMTP is not configured."
)
}
try {
2022-05-24 20:54:36 +12:00
const user = (await usersCore.getGlobalUserByEmail(email)) as User
// only if user exists, don't error though if they don't
if (user) {
await sendEmail(email, EmailTemplatePurpose.PASSWORD_RECOVERY, {
user,
subject: "{{ company }} platform password reset",
})
2022-05-24 09:14:44 +12:00
await events.user.passwordResetRequested(user)
}
} catch (err) {
console.log(err)
// don't throw any kind of error to the user, this might give away something
}
ctx.body = {
message: "Please check your email for a reset link.",
}
}
/**
* Perform the user password update if the provided reset code is valid.
*/
export const resetUpdate = async (ctx: any) => {
const { resetCode, password } = ctx.request.body
2021-05-06 02:17:15 +12:00
try {
2021-09-18 00:41:22 +12:00
const { userId } = await checkResetPasswordCode(resetCode)
const db = getGlobalDB()
2021-05-06 02:17:15 +12:00
const user = await db.get(userId)
user.password = await hash(password)
await db.put(user)
ctx.body = {
message: "password reset successfully.",
}
2022-04-12 23:34:36 +12:00
// remove password from the user before sending events
delete user.password
2022-05-24 09:14:44 +12:00
await events.user.passwordReset(user)
2021-05-06 02:17:15 +12:00
} catch (err) {
2022-06-09 23:33:10 +12:00
console.error(err)
2021-05-06 02:17:15 +12:00
ctx.throw(400, "Cannot reset password.")
}
}
export const logout = async (ctx: any) => {
if (ctx.user && ctx.user._id) {
await platformLogout({ ctx, userId: ctx.user._id })
}
ctx.body = { message: "User logged out." }
2021-04-14 00:56:28 +12:00
}
export const datasourcePreAuth = async (ctx: any, next: any) => {
2022-01-18 03:52:10 +13:00
const provider = ctx.params.provider
const middleware = require(`@budibase/backend-core/middleware`)
const handler = middleware.datasource[provider]
2022-01-06 21:08:54 +13:00
setCookie(
ctx,
{
2022-01-18 03:52:10 +13:00
provider,
2022-01-06 21:08:54 +13:00
appId: ctx.query.appId,
datasourceId: ctx.query.datasourceId,
},
2022-01-18 03:52:10 +13:00
Cookies.DatasourceAuth
2022-01-06 21:08:54 +13:00
)
2022-01-18 03:52:10 +13:00
return handler.preAuth(passport, ctx, next)
2022-01-06 21:08:54 +13:00
}
export const datasourceAuth = async (ctx: any, next: any) => {
2022-01-18 03:52:10 +13:00
const authStateCookie = getCookie(ctx, Cookies.DatasourceAuth)
const provider = authStateCookie.provider
const middleware = require(`@budibase/backend-core/middleware`)
const handler = middleware.datasource[provider]
return handler.postAuth(passport, ctx, next)
2022-01-06 21:08:54 +13:00
}
2021-04-22 22:45:22 +12:00
/**
* The initial call that google authentication makes to take you to the google login screen.
* On a successful login, you will be redirected to the googleAuth callback route.
*/
export const googlePreAuth = async (ctx: any, next: any) => {
const db = getGlobalDB()
const config = await core.db.getScopedConfig(db, {
2021-04-22 22:45:22 +12:00
type: Configs.GOOGLE,
workspace: ctx.query.workspace,
2021-04-22 22:45:22 +12:00
})
let callbackUrl = await exports.googleCallbackUrl(config)
const strategy = await google.strategyFactory(config, callbackUrl, users.save)
2021-04-22 08:08:04 +12:00
return passport.authenticate(strategy, {
scope: ["profile", "email"],
})(ctx, next)
}
2021-04-22 05:40:32 +12:00
export const googleAuth = async (ctx: any, next: any) => {
const db = getGlobalDB()
2021-04-23 00:46:54 +12:00
const config = await core.db.getScopedConfig(db, {
2021-04-22 22:48:37 +12:00
type: Configs.GOOGLE,
workspace: ctx.query.workspace,
2021-04-22 22:48:37 +12:00
})
const callbackUrl = await exports.googleCallbackUrl(config)
const strategy = await google.strategyFactory(config, callbackUrl, users.save)
2021-04-22 08:08:04 +12:00
2021-04-21 23:12:22 +12:00
return passport.authenticate(
2021-04-22 08:08:04 +12:00
strategy,
{ successRedirect: "/", failureRedirect: "/error" },
async (err: any, user: User, info: any) => {
await authInternal(ctx, user, err, info)
await context.identity.doInUserContext(user, async () => {
await events.auth.login("google-internal")
2022-05-24 20:54:36 +12:00
})
2021-04-21 23:12:22 +12:00
ctx.redirect("/")
}
)(ctx, next)
2021-04-11 22:35:55 +12:00
}
2021-06-28 02:46:04 +12:00
2022-06-24 01:29:19 +12:00
export const oidcStrategyFactory = async (ctx: any, configId: any) => {
const db = getGlobalDB()
const config = await core.db.getScopedConfig(db, {
type: Configs.OIDC,
group: ctx.query.group,
})
const chosenConfig = config.configs.filter((c: any) => c.uuid === configId)[0]
let callbackUrl = await exports.oidcCallbackUrl(chosenConfig)
2022-06-24 01:29:19 +12:00
//Remote Config
const enrichedConfig = await oidc.fetchOIDCStrategyConfig(
chosenConfig,
callbackUrl
)
return oidc.strategyFactory(enrichedConfig, users.save)
}
2021-06-28 02:46:04 +12:00
/**
* The initial call that OIDC authentication makes to take you to the configured OIDC login screen.
* On a successful login, you will be redirected to the oidcAuth callback route.
*/
export const oidcPreAuth = async (ctx: any, next: any) => {
const { configId } = ctx.params
const strategy = await oidcStrategyFactory(ctx, configId)
setCookie(ctx, configId, Cookies.OIDC_CONFIG)
2021-06-28 02:46:04 +12:00
return passport.authenticate(strategy, {
// required 'openid' scope is added by oidc strategy factory
2022-06-24 01:29:19 +12:00
scope: ["profile", "email", "offline_access"], //auth0 offline_access scope required for the refresh token behaviour.
2021-06-28 02:46:04 +12:00
})(ctx, next)
}
export const oidcAuth = async (ctx: any, next: any) => {
const configId = getCookie(ctx, Cookies.OIDC_CONFIG)
const strategy = await oidcStrategyFactory(ctx, configId)
2021-06-28 02:46:04 +12:00
return passport.authenticate(
strategy,
{ successRedirect: "/", failureRedirect: "/error" },
async (err: any, user: any, info: any) => {
await authInternal(ctx, user, err, info)
await context.identity.doInUserContext(user, async () => {
2022-05-24 20:54:36 +12:00
await events.auth.login("oidc")
})
2021-04-21 23:12:22 +12:00
ctx.redirect("/")
}
)(ctx, next)
2021-04-11 22:35:55 +12:00
}