mirror of
https://github.com/sasjs/server.git
synced 2026-01-10 07:50:05 +00:00
feat: implemented LDAP authentication
This commit is contained in:
185
api/src/controllers/authConfig.ts
Normal file
185
api/src/controllers/authConfig.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import express from 'express'
|
||||
import { Security, Route, Tags, Get, Post, Example } from 'tsoa'
|
||||
|
||||
import { LDAPClient, LDAPUser, LDAPGroup, AuthProviderType } from '../utils'
|
||||
import { randomBytes } from 'crypto'
|
||||
import User from '../model/User'
|
||||
import Group from '../model/Group'
|
||||
import Permission from '../model/Permission'
|
||||
|
||||
@Security('bearerAuth')
|
||||
@Route('SASjsApi/authConfig')
|
||||
@Tags('Auth_Config')
|
||||
export class AuthConfigController {
|
||||
/**
|
||||
* @summary Gives the detail of Auth Mechanism.
|
||||
*
|
||||
*/
|
||||
@Example({
|
||||
ldap: {
|
||||
LDAP_URL: 'ldaps://my.ldap.server:636',
|
||||
LDAP_BIND_DN: 'cn=admin,ou=system,dc=cloudron',
|
||||
LDAP_BIND_PASSWORD: 'secret',
|
||||
LDAP_USERS_BASE_DN: 'ou=users,dc=cloudron',
|
||||
LDAP_GROUPS_BASE_DN: 'ou=groups,dc=cloudron'
|
||||
}
|
||||
})
|
||||
@Get('/')
|
||||
public getDetail() {
|
||||
return getAuthConfigDetail()
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Synchronizes LDAP users and groups with internal DB and returns the count of imported users and groups.
|
||||
*
|
||||
*/
|
||||
@Example({
|
||||
users: 5,
|
||||
groups: 3
|
||||
})
|
||||
@Post('/synchronizeWithLDAP')
|
||||
public async synchronizeWithLDAP() {
|
||||
return synchronizeWithLDAP()
|
||||
}
|
||||
}
|
||||
|
||||
const synchronizeWithLDAP = async () => {
|
||||
process.logger.info('Syncing LDAP with internal DB')
|
||||
|
||||
const permissions = await Permission.get({})
|
||||
await Permission.deleteMany()
|
||||
await User.deleteMany({ authProvider: AuthProviderType.LDAP })
|
||||
await Group.deleteMany({ authProvider: AuthProviderType.LDAP })
|
||||
|
||||
const ldapClient = await LDAPClient.init()
|
||||
|
||||
process.logger.info('fetching LDAP users')
|
||||
const users = await ldapClient.getAllLDAPUsers()
|
||||
|
||||
process.logger.info('inserting LDAP users to DB')
|
||||
|
||||
const existingUsers: string[] = []
|
||||
const importedUsers: LDAPUser[] = []
|
||||
|
||||
for (const user of users) {
|
||||
const usernameExists = await User.findOne({ username: user.username })
|
||||
if (usernameExists) {
|
||||
existingUsers.push(user.username)
|
||||
continue
|
||||
}
|
||||
|
||||
const hashPassword = User.hashPassword(randomBytes(64).toString('hex'))
|
||||
|
||||
await User.create({
|
||||
displayName: user.displayName,
|
||||
username: user.username,
|
||||
password: hashPassword,
|
||||
authProvider: AuthProviderType.LDAP
|
||||
})
|
||||
|
||||
importedUsers.push(user)
|
||||
}
|
||||
|
||||
if (existingUsers.length > 0) {
|
||||
process.logger.info(
|
||||
'Failed to insert following users as they already exist in DB:'
|
||||
)
|
||||
existingUsers.forEach((user) => process.logger.log(`* ${user}`))
|
||||
}
|
||||
|
||||
process.logger.info('fetching LDAP groups')
|
||||
const groups = await ldapClient.getAllLDAPGroups()
|
||||
|
||||
process.logger.info('inserting LDAP groups to DB')
|
||||
|
||||
const existingGroups: string[] = []
|
||||
const importedGroups: LDAPGroup[] = []
|
||||
|
||||
for (const group of groups) {
|
||||
const groupExists = await Group.findOne({ name: group.name })
|
||||
if (groupExists) {
|
||||
existingGroups.push(group.name)
|
||||
continue
|
||||
}
|
||||
|
||||
await Group.create({
|
||||
name: group.name,
|
||||
authProvider: AuthProviderType.LDAP
|
||||
})
|
||||
|
||||
importedGroups.push(group)
|
||||
}
|
||||
|
||||
if (existingGroups.length > 0) {
|
||||
process.logger.info(
|
||||
'Failed to insert following groups as they already exist in DB:'
|
||||
)
|
||||
existingGroups.forEach((group) => process.logger.log(`* ${group}`))
|
||||
}
|
||||
|
||||
process.logger.info('associating users and groups')
|
||||
|
||||
for (const group of importedGroups) {
|
||||
const dbGroup = await Group.findOne({ name: group.name })
|
||||
if (dbGroup) {
|
||||
for (const member of group.members) {
|
||||
const user = importedUsers.find((user) => user.uid === member)
|
||||
if (user) {
|
||||
const dbUser = await User.findOne({ username: user.username })
|
||||
if (dbUser) await dbGroup.addUser(dbUser)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process.logger.info('setting permissions')
|
||||
|
||||
for (const permission of permissions) {
|
||||
const newPermission = new Permission({
|
||||
path: permission.path,
|
||||
type: permission.type,
|
||||
setting: permission.setting
|
||||
})
|
||||
|
||||
if (permission.user) {
|
||||
const dbUser = await User.findOne({ username: permission.user.username })
|
||||
if (dbUser) newPermission.user = dbUser._id
|
||||
} else if (permission.group) {
|
||||
const dbGroup = await Group.findOne({ name: permission.group.name })
|
||||
if (dbGroup) newPermission.group = dbGroup._id
|
||||
}
|
||||
await newPermission.save()
|
||||
}
|
||||
|
||||
process.logger.info('LDAP synchronization completed!')
|
||||
|
||||
return {
|
||||
userCount: importedUsers.length,
|
||||
groupCount: importedGroups.length
|
||||
}
|
||||
}
|
||||
|
||||
const getAuthConfigDetail = () => {
|
||||
const { AUTH_PROVIDERS } = process.env
|
||||
|
||||
const returnObj: any = {}
|
||||
|
||||
if (AUTH_PROVIDERS === AuthProviderType.LDAP) {
|
||||
const {
|
||||
LDAP_URL,
|
||||
LDAP_BIND_DN,
|
||||
LDAP_BIND_PASSWORD,
|
||||
LDAP_USERS_BASE_DN,
|
||||
LDAP_GROUPS_BASE_DN
|
||||
} = process.env
|
||||
|
||||
returnObj.ldap = {
|
||||
LDAP_URL: LDAP_URL ?? '',
|
||||
LDAP_BIND_DN: LDAP_BIND_DN ?? '',
|
||||
LDAP_BIND_PASSWORD: LDAP_BIND_PASSWORD ?? '',
|
||||
LDAP_USERS_BASE_DN: LDAP_USERS_BASE_DN ?? '',
|
||||
LDAP_GROUPS_BASE_DN: LDAP_GROUPS_BASE_DN ?? ''
|
||||
}
|
||||
}
|
||||
return returnObj
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './auth'
|
||||
export * from './authConfig'
|
||||
export * from './client'
|
||||
export * from './code'
|
||||
export * from './drive'
|
||||
|
||||
@@ -5,7 +5,12 @@ import { readFile } from '@sasjs/utils'
|
||||
|
||||
import User from '../model/User'
|
||||
import Client from '../model/Client'
|
||||
import { getWebBuildFolder, generateAuthCode } from '../utils'
|
||||
import {
|
||||
getWebBuildFolder,
|
||||
generateAuthCode,
|
||||
AuthProviderType,
|
||||
LDAPClient
|
||||
} from '../utils'
|
||||
import { InfoJWT } from '../types'
|
||||
import { AuthController } from './auth'
|
||||
|
||||
@@ -80,8 +85,16 @@ const login = async (
|
||||
const user = await User.findOne({ username })
|
||||
if (!user) throw new Error('Username is not found.')
|
||||
|
||||
const validPass = user.comparePassword(password)
|
||||
if (!validPass) throw new Error('Invalid password.')
|
||||
if (
|
||||
process.env.AUTH_MECHANISM === AuthProviderType.LDAP &&
|
||||
user.authProvider === AuthProviderType.LDAP
|
||||
) {
|
||||
const ldapClient = await LDAPClient.init()
|
||||
await ldapClient.verifyUser(username, password)
|
||||
} else {
|
||||
const validPass = user.comparePassword(password)
|
||||
if (!validPass) throw new Error('Invalid password.')
|
||||
}
|
||||
|
||||
req.session.loggedIn = true
|
||||
req.session.user = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { RequestHandler, Request } from 'express'
|
||||
import { userInfo } from 'os'
|
||||
import { RequestUser } from '../types'
|
||||
import { ModeType } from '../utils'
|
||||
import { ModeType, AuthProviderType } from '../utils'
|
||||
|
||||
const regexUser = /^\/SASjsApi\/user\/[0-9]*$/ // /SASjsApi/user/1
|
||||
|
||||
@@ -27,6 +27,18 @@ export const desktopRestrict: RequestHandler = (req, res, next) => {
|
||||
next()
|
||||
}
|
||||
|
||||
export const ldapRestrict: RequestHandler = (req, res, next) => {
|
||||
const { AUTH_MECHANISM } = process.env
|
||||
|
||||
if (AUTH_MECHANISM === AuthProviderType.LDAP) {
|
||||
return res
|
||||
.status(403)
|
||||
.send(`Not Allowed while AUTH_MECHANISM is '${AuthProviderType.LDAP}'.`)
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
export const desktopUser: RequestUser = {
|
||||
userId: 12345,
|
||||
clientId: 'desktop_app',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import mongoose, { Schema, model, Document, Model } from 'mongoose'
|
||||
import { GroupDetailsResponse } from '../controllers'
|
||||
import User, { IUser } from './User'
|
||||
import { AuthProviderType } from '../utils'
|
||||
const AutoIncrement = require('mongoose-sequence')(mongoose)
|
||||
|
||||
export const PUBLIC_GROUP_NAME = 'Public'
|
||||
@@ -16,6 +17,11 @@ export interface GroupPayload {
|
||||
* @example "This group represents Data Controller Users"
|
||||
*/
|
||||
description: string
|
||||
/**
|
||||
* Identifies the source from which group is created
|
||||
* @example "false"
|
||||
*/
|
||||
authProvider?: AuthProviderType
|
||||
/**
|
||||
* Group should be active or not, defaults to true
|
||||
* @example "true"
|
||||
@@ -46,6 +52,11 @@ const groupSchema = new Schema<IGroupDocument>({
|
||||
type: String,
|
||||
default: 'Group description.'
|
||||
},
|
||||
authProvider: {
|
||||
type: String,
|
||||
enum: AuthProviderType,
|
||||
default: 'internal'
|
||||
},
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import mongoose, { Schema, model, Document, Model } from 'mongoose'
|
||||
const AutoIncrement = require('mongoose-sequence')(mongoose)
|
||||
import bcrypt from 'bcryptjs'
|
||||
import { AuthProviderType } from '../utils'
|
||||
|
||||
export interface UserPayload {
|
||||
/**
|
||||
@@ -17,6 +18,11 @@ export interface UserPayload {
|
||||
* Password for user
|
||||
*/
|
||||
password: string
|
||||
/**
|
||||
* Identifies the source from which user is created
|
||||
* @example "internal"
|
||||
*/
|
||||
authProvider?: AuthProviderType
|
||||
/**
|
||||
* Account should be admin or not, defaults to false
|
||||
* @example "false"
|
||||
@@ -67,6 +73,11 @@ const userSchema = new Schema<IUserDocument>({
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
authProvider: {
|
||||
type: String,
|
||||
enum: AuthProviderType,
|
||||
default: 'internal'
|
||||
},
|
||||
isAdmin: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
|
||||
25
api/src/routes/api/authConfig.ts
Normal file
25
api/src/routes/api/authConfig.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import express from 'express'
|
||||
import { AuthConfigController } from '../../controllers'
|
||||
const authConfigRouter = express.Router()
|
||||
|
||||
authConfigRouter.get('/', async (req, res) => {
|
||||
const controller = new AuthConfigController()
|
||||
try {
|
||||
const response = controller.getDetail()
|
||||
res.send(response)
|
||||
} catch (err: any) {
|
||||
res.status(500).send(err.toString())
|
||||
}
|
||||
})
|
||||
|
||||
authConfigRouter.post('/synchronizeWithLDAP', async (req, res) => {
|
||||
const controller = new AuthConfigController()
|
||||
try {
|
||||
const response = await controller.synchronizeWithLDAP()
|
||||
res.send(response)
|
||||
} catch (err: any) {
|
||||
res.status(500).send(err.toString())
|
||||
}
|
||||
})
|
||||
|
||||
export default authConfigRouter
|
||||
@@ -1,12 +1,17 @@
|
||||
import express from 'express'
|
||||
import { GroupController } from '../../controllers/'
|
||||
import { authenticateAccessToken, verifyAdmin } from '../../middlewares'
|
||||
import {
|
||||
ldapRestrict,
|
||||
authenticateAccessToken,
|
||||
verifyAdmin
|
||||
} from '../../middlewares'
|
||||
import { getGroupValidation, registerGroupValidation } from '../../utils'
|
||||
|
||||
const groupRouter = express.Router()
|
||||
|
||||
groupRouter.post(
|
||||
'/',
|
||||
ldapRestrict,
|
||||
authenticateAccessToken,
|
||||
verifyAdmin,
|
||||
async (req, res) => {
|
||||
@@ -82,6 +87,7 @@ groupRouter.get(
|
||||
|
||||
groupRouter.post(
|
||||
'/:groupId/:userId',
|
||||
ldapRestrict,
|
||||
authenticateAccessToken,
|
||||
verifyAdmin,
|
||||
async (req, res) => {
|
||||
@@ -106,6 +112,7 @@ groupRouter.post(
|
||||
|
||||
groupRouter.delete(
|
||||
'/:groupId/:userId',
|
||||
ldapRestrict,
|
||||
authenticateAccessToken,
|
||||
verifyAdmin,
|
||||
async (req, res) => {
|
||||
@@ -130,6 +137,7 @@ groupRouter.delete(
|
||||
|
||||
groupRouter.delete(
|
||||
'/:groupId',
|
||||
ldapRestrict,
|
||||
authenticateAccessToken,
|
||||
verifyAdmin,
|
||||
async (req, res) => {
|
||||
|
||||
@@ -18,6 +18,7 @@ import clientRouter from './client'
|
||||
import authRouter from './auth'
|
||||
import sessionRouter from './session'
|
||||
import permissionRouter from './permission'
|
||||
import authConfigRouter from './authConfig'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
@@ -43,6 +44,14 @@ router.use(
|
||||
permissionRouter
|
||||
)
|
||||
|
||||
router.use(
|
||||
'/authConfig',
|
||||
desktopRestrict,
|
||||
authenticateAccessToken,
|
||||
verifyAdmin,
|
||||
authConfigRouter
|
||||
)
|
||||
|
||||
router.use(
|
||||
'/',
|
||||
swaggerUi.serve,
|
||||
|
||||
@@ -3,7 +3,8 @@ import { UserController } from '../../controllers/'
|
||||
import {
|
||||
authenticateAccessToken,
|
||||
verifyAdmin,
|
||||
verifyAdminIfNeeded
|
||||
verifyAdminIfNeeded,
|
||||
ldapRestrict
|
||||
} from '../../middlewares'
|
||||
import {
|
||||
deleteUserValidation,
|
||||
@@ -14,18 +15,24 @@ import {
|
||||
|
||||
const userRouter = express.Router()
|
||||
|
||||
userRouter.post('/', authenticateAccessToken, verifyAdmin, async (req, res) => {
|
||||
const { error, value: body } = registerUserValidation(req.body)
|
||||
if (error) return res.status(400).send(error.details[0].message)
|
||||
userRouter.post(
|
||||
'/',
|
||||
ldapRestrict,
|
||||
authenticateAccessToken,
|
||||
verifyAdmin,
|
||||
async (req, res) => {
|
||||
const { error, value: body } = registerUserValidation(req.body)
|
||||
if (error) return res.status(400).send(error.details[0].message)
|
||||
|
||||
const controller = new UserController()
|
||||
try {
|
||||
const response = await controller.createUser(body)
|
||||
res.send(response)
|
||||
} catch (err: any) {
|
||||
res.status(403).send(err.toString())
|
||||
const controller = new UserController()
|
||||
try {
|
||||
const response = await controller.createUser(body)
|
||||
res.send(response)
|
||||
} catch (err: any) {
|
||||
res.status(403).send(err.toString())
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
userRouter.get('/', authenticateAccessToken, async (req, res) => {
|
||||
const controller = new UserController()
|
||||
@@ -70,6 +77,7 @@ userRouter.get('/:userId', authenticateAccessToken, async (req, res) => {
|
||||
|
||||
userRouter.patch(
|
||||
'/by/username/:username',
|
||||
ldapRestrict,
|
||||
authenticateAccessToken,
|
||||
verifyAdminIfNeeded,
|
||||
async (req, res) => {
|
||||
@@ -98,6 +106,7 @@ userRouter.patch(
|
||||
|
||||
userRouter.patch(
|
||||
'/:userId',
|
||||
ldapRestrict,
|
||||
authenticateAccessToken,
|
||||
verifyAdminIfNeeded,
|
||||
async (req, res) => {
|
||||
@@ -120,6 +129,7 @@ userRouter.patch(
|
||||
|
||||
userRouter.delete(
|
||||
'/by/username/:username',
|
||||
ldapRestrict,
|
||||
authenticateAccessToken,
|
||||
verifyAdminIfNeeded,
|
||||
async (req, res) => {
|
||||
@@ -148,6 +158,7 @@ userRouter.delete(
|
||||
|
||||
userRouter.delete(
|
||||
'/:userId',
|
||||
ldapRestrict,
|
||||
authenticateAccessToken,
|
||||
verifyAdminIfNeeded,
|
||||
async (req, res) => {
|
||||
|
||||
@@ -18,6 +18,7 @@ export * from './getTokensFromDB'
|
||||
export * from './instantiateLogger'
|
||||
export * from './isDebugOn'
|
||||
export * from './isPublicRoute'
|
||||
export * from './ldapClient'
|
||||
export * from './zipped'
|
||||
export * from './parseLogToArray'
|
||||
export * from './removeTokensInDB'
|
||||
|
||||
163
api/src/utils/ldapClient.ts
Normal file
163
api/src/utils/ldapClient.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import { createClient, Client } from 'ldapjs'
|
||||
import { ReturnCode } from './verifyEnvVariables'
|
||||
|
||||
export interface LDAPUser {
|
||||
uid: string
|
||||
username: string
|
||||
displayName: string
|
||||
}
|
||||
|
||||
export interface LDAPGroup {
|
||||
name: string
|
||||
members: string[]
|
||||
}
|
||||
|
||||
export class LDAPClient {
|
||||
private ldapClient: Client
|
||||
private static classInstance: LDAPClient | null
|
||||
|
||||
private constructor() {
|
||||
process.logger.info('creating LDAP client')
|
||||
this.ldapClient = createClient({ url: process.env.LDAP_URL as string })
|
||||
|
||||
this.ldapClient.on('error', (error) => {
|
||||
process.logger.error(error.message)
|
||||
})
|
||||
}
|
||||
|
||||
static async init() {
|
||||
if (!LDAPClient.classInstance) {
|
||||
LDAPClient.classInstance = new LDAPClient()
|
||||
|
||||
process.logger.info('binding LDAP client')
|
||||
await LDAPClient.classInstance.bind().catch((error) => {
|
||||
LDAPClient.classInstance = null
|
||||
throw error
|
||||
})
|
||||
}
|
||||
return LDAPClient.classInstance
|
||||
}
|
||||
|
||||
private async bind() {
|
||||
const promise = new Promise<void>((resolve, reject) => {
|
||||
const { LDAP_BIND_DN, LDAP_BIND_PASSWORD } = process.env
|
||||
this.ldapClient.bind(LDAP_BIND_DN!, LDAP_BIND_PASSWORD!, (error) => {
|
||||
if (error) reject(error)
|
||||
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
await promise.catch((error) => {
|
||||
throw new Error(error.message)
|
||||
})
|
||||
}
|
||||
|
||||
async getAllLDAPUsers() {
|
||||
const promise = new Promise<LDAPUser[]>((resolve, reject) => {
|
||||
const { LDAP_USERS_BASE_DN } = process.env
|
||||
const filter = `(objectClass=*)`
|
||||
|
||||
this.ldapClient.search(
|
||||
LDAP_USERS_BASE_DN!,
|
||||
{ filter },
|
||||
(error, result) => {
|
||||
if (error) reject(error)
|
||||
|
||||
const users: LDAPUser[] = []
|
||||
|
||||
result.on('searchEntry', (entry) => {
|
||||
users.push({
|
||||
uid: entry.object.uid as string,
|
||||
username: entry.object.username as string,
|
||||
displayName: entry.object.displayname as string
|
||||
})
|
||||
})
|
||||
|
||||
result.on('end', (result) => {
|
||||
resolve(users)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return await promise
|
||||
.then((res) => res)
|
||||
.catch((error) => {
|
||||
throw new Error(error.message)
|
||||
})
|
||||
}
|
||||
|
||||
async getAllLDAPGroups() {
|
||||
const promise = new Promise<LDAPGroup[]>((resolve, reject) => {
|
||||
const { LDAP_GROUPS_BASE_DN } = process.env
|
||||
|
||||
this.ldapClient.search(LDAP_GROUPS_BASE_DN!, {}, (error, result) => {
|
||||
if (error) reject(error)
|
||||
|
||||
const groups: LDAPGroup[] = []
|
||||
|
||||
result.on('searchEntry', (entry) => {
|
||||
const members =
|
||||
typeof entry.object.memberuid === 'string'
|
||||
? [entry.object.memberuid]
|
||||
: entry.object.memberuid
|
||||
groups.push({
|
||||
name: entry.object.cn as string,
|
||||
members
|
||||
})
|
||||
})
|
||||
|
||||
result.on('end', (result) => {
|
||||
resolve(groups)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return await promise
|
||||
.then((res) => res)
|
||||
.catch((error) => {
|
||||
throw new Error(error.message)
|
||||
})
|
||||
}
|
||||
|
||||
async verifyUser(username: string, password: string) {
|
||||
const promise = new Promise<boolean>((resolve, reject) => {
|
||||
const { LDAP_USERS_BASE_DN } = process.env
|
||||
const filter = `(username=${username})`
|
||||
|
||||
this.ldapClient.search(
|
||||
LDAP_USERS_BASE_DN!,
|
||||
{ filter },
|
||||
(error, result) => {
|
||||
if (error) reject(error)
|
||||
|
||||
const items: any = []
|
||||
|
||||
result.on('searchEntry', (entry) => {
|
||||
items.push(entry.object)
|
||||
})
|
||||
|
||||
result.on('end', (result) => {
|
||||
if (result?.status !== 0 || items.length === 0) return reject()
|
||||
|
||||
// pick the first found
|
||||
const user = items[0]
|
||||
|
||||
this.ldapClient.bind(user.dn, password, (error) => {
|
||||
if (error) return reject(error)
|
||||
|
||||
resolve(true)
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return await promise
|
||||
.then(() => true)
|
||||
.catch(() => {
|
||||
throw new Error('Invalid password.')
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,11 @@ export enum ModeType {
|
||||
Desktop = 'desktop'
|
||||
}
|
||||
|
||||
export enum AuthProviderType {
|
||||
LDAP = 'ldap',
|
||||
Internal = 'internal'
|
||||
}
|
||||
|
||||
export enum ProtocolType {
|
||||
HTTP = 'http',
|
||||
HTTPS = 'https'
|
||||
@@ -64,6 +69,8 @@ export const verifyEnvVariables = (): ReturnCode => {
|
||||
|
||||
errors.push(...verifyExecutablePaths())
|
||||
|
||||
errors.push(...verifyLDAPVariables())
|
||||
|
||||
if (errors.length) {
|
||||
process.logger?.error(
|
||||
`Invalid environment variable(s) provided: \n${errors.join('\n')}`
|
||||
@@ -104,13 +111,24 @@ const verifyMODE = (): string[] => {
|
||||
}
|
||||
|
||||
if (process.env.MODE === ModeType.Server) {
|
||||
const { DB_CONNECT } = process.env
|
||||
const { DB_CONNECT, AUTH_MECHANISM } = process.env
|
||||
|
||||
if (process.env.NODE_ENV !== 'test')
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
if (!DB_CONNECT)
|
||||
errors.push(
|
||||
`- DB_CONNECT is required for PROTOCOL '${ModeType.Server}'`
|
||||
)
|
||||
|
||||
if (AUTH_MECHANISM) {
|
||||
const authMechanismTypes = Object.values(AuthProviderType)
|
||||
if (!authMechanismTypes.includes(AUTH_MECHANISM as AuthProviderType))
|
||||
errors.push(
|
||||
`- AUTH_MECHANISM '${AUTH_MECHANISM}'\n - valid options ${authMechanismTypes}`
|
||||
)
|
||||
} else {
|
||||
process.env.AUTH_MECHANISM = DEFAULTS.AUTH_MECHANISM
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
@@ -280,8 +298,56 @@ const verifyExecutablePaths = () => {
|
||||
return errors
|
||||
}
|
||||
|
||||
const verifyLDAPVariables = () => {
|
||||
const errors: string[] = []
|
||||
const {
|
||||
LDAP_URL,
|
||||
LDAP_BIND_DN,
|
||||
LDAP_BIND_PASSWORD,
|
||||
LDAP_USERS_BASE_DN,
|
||||
LDAP_GROUPS_BASE_DN,
|
||||
MODE,
|
||||
AUTH_MECHANISM
|
||||
} = process.env
|
||||
|
||||
if (MODE === ModeType.Server && AUTH_MECHANISM === AuthProviderType.LDAP) {
|
||||
if (!LDAP_URL) {
|
||||
errors.push(
|
||||
`- LDAP_URL is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
||||
)
|
||||
}
|
||||
|
||||
if (!LDAP_BIND_DN) {
|
||||
errors.push(
|
||||
`- LDAP_BIND_DN is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
||||
)
|
||||
}
|
||||
|
||||
if (!LDAP_BIND_PASSWORD) {
|
||||
errors.push(
|
||||
`- LDAP_BIND_PASSWORD is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
||||
)
|
||||
}
|
||||
|
||||
if (!LDAP_USERS_BASE_DN) {
|
||||
errors.push(
|
||||
`- LDAP_USERS_BASE_DN is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
||||
)
|
||||
}
|
||||
|
||||
if (!LDAP_GROUPS_BASE_DN) {
|
||||
errors.push(
|
||||
`- LDAP_GROUPS_BASE_DN is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
const DEFAULTS = {
|
||||
MODE: ModeType.Desktop,
|
||||
AUTH_MECHANISM: AuthProviderType.Internal,
|
||||
PROTOCOL: ProtocolType.HTTP,
|
||||
PORT: '5000',
|
||||
HELMET_COEP: HelmetCoepType.TRUE,
|
||||
|
||||
Reference in New Issue
Block a user