🔒 feature(auth): LDAP Authentication (#2859)

* 🔧 chore: npm install passport-ldapauth

*  feat(auth): add ldap authentication support

* chore: merge conflict fix

---------

Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
Yuichi Oneda 2024-05-29 14:46:20 -07:00 committed by GitHub
parent d5a7806e32
commit a618266905
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 303 additions and 3 deletions

View file

@ -360,6 +360,14 @@ OPENID_REQUIRED_ROLE_PARAMETER_PATH=
OPENID_BUTTON_LABEL= OPENID_BUTTON_LABEL=
OPENID_IMAGE_URL= OPENID_IMAGE_URL=
# LDAP
LDAP_URL=
LDAP_BIND_DN=
LDAP_BIND_CREDENTIALS=
LDAP_USER_SEARCH_BASE=
LDAP_SEARCH_FILTER=mail={{username}}
LDAP_CA_CERT_PATH=
#========================# #========================#
# Email Password Reset # # Email Password Reset #
#========================# #========================#

View file

@ -64,6 +64,11 @@ const userSchema = mongoose.Schema(
unique: true, unique: true,
sparse: true, sparse: true,
}, },
ldapId: {
type: String,
unique: true,
sparse: true,
},
githubId: { githubId: {
type: String, type: String,
unique: true, unique: true,

View file

@ -86,6 +86,7 @@
"passport-github2": "^0.1.12", "passport-github2": "^0.1.12",
"passport-google-oauth20": "^2.0.0", "passport-google-oauth20": "^2.0.0",
"passport-jwt": "^4.0.1", "passport-jwt": "^4.0.1",
"passport-ldapauth": "^3.0.1",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"pino": "^8.12.1", "pino": "^8.12.1",
"sharp": "^0.32.6", "sharp": "^0.32.6",

View file

@ -15,7 +15,7 @@ const AppService = require('./services/AppService');
const noIndex = require('./middleware/noIndex'); const noIndex = require('./middleware/noIndex');
const { isEnabled } = require('~/server/utils'); const { isEnabled } = require('~/server/utils');
const { logger } = require('~/config'); const { logger } = require('~/config');
const { ldapLogin } = require('~/strategies');
const routes = require('./routes'); const routes = require('./routes');
const { PORT, HOST, ALLOW_SOCIAL_LOGIN } = process.env ?? {}; const { PORT, HOST, ALLOW_SOCIAL_LOGIN } = process.env ?? {};
@ -60,6 +60,11 @@ const startServer = async () => {
passport.use(await jwtLogin()); passport.use(await jwtLogin());
passport.use(passportLogin()); passport.use(passportLogin());
// LDAP Auth
if (process.env.LDAP_URL && process.env.LDAP_BIND_DN && process.env.LDAP_USER_SEARCH_BASE) {
passport.use(ldapLogin);
}
if (isEnabled(ALLOW_SOCIAL_LOGIN)) { if (isEnabled(ALLOW_SOCIAL_LOGIN)) {
configureSocialLogins(app); configureSocialLogins(app);
} }

View file

@ -6,6 +6,7 @@ const setHeaders = require('./setHeaders');
const loginLimiter = require('./loginLimiter'); const loginLimiter = require('./loginLimiter');
const validateModel = require('./validateModel'); const validateModel = require('./validateModel');
const requireJwtAuth = require('./requireJwtAuth'); const requireJwtAuth = require('./requireJwtAuth');
const requireLdapAuth = require('./requireLdapAuth');
const uploadLimiters = require('./uploadLimiters'); const uploadLimiters = require('./uploadLimiters');
const registerLimiter = require('./registerLimiter'); const registerLimiter = require('./registerLimiter');
const messageLimiters = require('./messageLimiters'); const messageLimiters = require('./messageLimiters');
@ -29,6 +30,7 @@ module.exports = {
setHeaders, setHeaders,
loginLimiter, loginLimiter,
requireJwtAuth, requireJwtAuth,
requireLdapAuth,
registerLimiter, registerLimiter,
requireLocalAuth, requireLocalAuth,
validateEndpoint, validateEndpoint,

View file

@ -0,0 +1,22 @@
const passport = require('passport');
const requireLdapAuth = (req, res, next) => {
passport.authenticate('ldapauth', (err, user, info) => {
if (err) {
console.log({
title: '(requireLdapAuth) Error at passport.authenticate',
parameters: [{ name: 'error', value: err }],
});
return next(err);
}
if (!user) {
console.log({
title: '(requireLdapAuth) Error: No user',
});
return res.status(422).send(info);
}
req.user = user;
next();
})(req, res, next);
};
module.exports = requireLdapAuth;

View file

@ -25,6 +25,11 @@ afterEach(() => {
delete process.env.DOMAIN_SERVER; delete process.env.DOMAIN_SERVER;
delete process.env.ALLOW_REGISTRATION; delete process.env.ALLOW_REGISTRATION;
delete process.env.ALLOW_SOCIAL_LOGIN; delete process.env.ALLOW_SOCIAL_LOGIN;
delete process.env.LDAP_URL;
delete process.env.LDAP_BIND_DN;
delete process.env.LDAP_BIND_CREDENTIALS;
delete process.env.LDAP_USER_SEARCH_BASE;
delete process.env.LDAP_SEARCH_FILTER;
}); });
//TODO: This works/passes locally but http request tests fail with 404 in CI. Need to figure out why. //TODO: This works/passes locally but http request tests fail with 404 in CI. Need to figure out why.
@ -50,6 +55,11 @@ describe.skip('GET /', () => {
process.env.DOMAIN_SERVER = 'http://test-server.com'; process.env.DOMAIN_SERVER = 'http://test-server.com';
process.env.ALLOW_REGISTRATION = 'true'; process.env.ALLOW_REGISTRATION = 'true';
process.env.ALLOW_SOCIAL_LOGIN = 'true'; process.env.ALLOW_SOCIAL_LOGIN = 'true';
process.env.LDAP_URL = 'Test LDAP URL';
process.env.LDAP_BIND_DN = 'Test LDAP Bind DN';
process.env.LDAP_BIND_CREDENTIALS = 'Test LDAP Bind Credentials';
process.env.LDAP_USER_SEARCH_BASE = 'Test LDAP User Search Base';
process.env.LDAP_SEARCH_FILTER = 'Test LDAP Search Filter';
const response = await request(app).get('/'); const response = await request(app).get('/');
@ -64,6 +74,7 @@ describe.skip('GET /', () => {
openidLoginEnabled: true, openidLoginEnabled: true,
openidLabel: 'Test OpenID', openidLabel: 'Test OpenID',
openidImageUrl: 'http://test-server.com', openidImageUrl: 'http://test-server.com',
ldapLoginEnabled: true,
serverDomain: 'http://test-server.com', serverDomain: 'http://test-server.com',
emailLoginEnabled: 'true', emailLoginEnabled: 'true',
registrationEnabled: 'true', registrationEnabled: 'true',

View file

@ -12,15 +12,24 @@ const {
loginLimiter, loginLimiter,
registerLimiter, registerLimiter,
requireJwtAuth, requireJwtAuth,
requireLdapAuth,
requireLocalAuth, requireLocalAuth,
validateRegistration, validateRegistration,
} = require('../middleware'); } = require('../middleware');
const router = express.Router(); const router = express.Router();
const ldapAuth =
!!process.env.LDAP_URL && !!process.env.LDAP_BIND_DN && !!process.env.LDAP_USER_SEARCH_BASE;
//Local //Local
router.post('/logout', requireJwtAuth, logoutController); router.post('/logout', requireJwtAuth, logoutController);
router.post('/login', loginLimiter, checkBan, requireLocalAuth, loginController); router.post(
'/login',
loginLimiter,
checkBan,
ldapAuth ? requireLdapAuth : requireLocalAuth,
loginController,
);
router.post('/refresh', refreshController); router.post('/refresh', refreshController);
router.post('/register', registerLimiter, checkBan, validateRegistration, registrationController); router.post('/register', registerLimiter, checkBan, validateRegistration, registrationController);
router.post('/requestPasswordReset', resetPasswordRequestController); router.post('/requestPasswordReset', resetPasswordRequestController);

View file

@ -13,6 +13,8 @@ router.get('/', async function (req, res) {
return today.getMonth() === 1 && today.getDate() === 11; return today.getMonth() === 1 && today.getDate() === 11;
}; };
const ldapLoginEnabled =
!!process.env.LDAP_URL && !!process.env.LDAP_BIND_DN && !!process.env.LDAP_USER_SEARCH_BASE;
try { try {
/** @type {TStartupConfig} */ /** @type {TStartupConfig} */
const payload = { const payload = {
@ -30,9 +32,10 @@ router.get('/', async function (req, res) {
!!process.env.OPENID_SESSION_SECRET, !!process.env.OPENID_SESSION_SECRET,
openidLabel: process.env.OPENID_BUTTON_LABEL || 'Continue with OpenID', openidLabel: process.env.OPENID_BUTTON_LABEL || 'Continue with OpenID',
openidImageUrl: process.env.OPENID_IMAGE_URL, openidImageUrl: process.env.OPENID_IMAGE_URL,
ldapLoginEnabled,
serverDomain: process.env.DOMAIN_SERVER || 'http://localhost:3080', serverDomain: process.env.DOMAIN_SERVER || 'http://localhost:3080',
emailLoginEnabled, emailLoginEnabled,
registrationEnabled: isEnabled(process.env.ALLOW_REGISTRATION), registrationEnabled: !ldapLoginEnabled && isEnabled(process.env.ALLOW_REGISTRATION),
socialLoginEnabled: isEnabled(process.env.ALLOW_SOCIAL_LOGIN), socialLoginEnabled: isEnabled(process.env.ALLOW_SOCIAL_LOGIN),
emailEnabled: emailEnabled:
(!!process.env.EMAIL_SERVICE || !!process.env.EMAIL_HOST) && (!!process.env.EMAIL_SERVICE || !!process.env.EMAIL_HOST) &&

View file

@ -5,6 +5,7 @@ const discordLogin = require('./discordStrategy');
const facebookLogin = require('./facebookStrategy'); const facebookLogin = require('./facebookStrategy');
const setupOpenId = require('./openidStrategy'); const setupOpenId = require('./openidStrategy');
const jwtLogin = require('./jwtStrategy'); const jwtLogin = require('./jwtStrategy');
const ldapLogin = require('./ldapStrategy');
module.exports = { module.exports = {
passportLogin, passportLogin,
@ -14,4 +15,5 @@ module.exports = {
jwtLogin, jwtLogin,
facebookLogin, facebookLogin,
setupOpenId, setupOpenId,
ldapLogin,
}; };

View file

@ -0,0 +1,67 @@
const LdapStrategy = require('passport-ldapauth');
const User = require('~/models/User');
const fs = require('fs');
const ldapOptions = {
server: {
url: process.env.LDAP_URL,
bindDN: process.env.LDAP_BIND_DN,
bindCredentials: process.env.LDAP_BIND_CREDENTIALS,
searchBase: process.env.LDAP_USER_SEARCH_BASE,
searchFilter: process.env.LDAP_SEARCH_FILTER || 'mail={{username}}',
searchAttributes: ['displayName', 'mail', 'uid', 'cn', 'name', 'commonname', 'givenName', 'sn'],
...(process.env.LDAP_CA_CERT_PATH && {
tlsOptions: { ca: [fs.readFileSync(process.env.LDAP_CA_CERT_PATH)] },
}),
},
usernameField: 'email',
passwordField: 'password',
};
const ldapLogin = new LdapStrategy(ldapOptions, async (userinfo, done) => {
if (!userinfo) {
return done(null, false, { message: 'Invalid credentials' });
}
try {
const firstName = userinfo.givenName;
const familyName = userinfo.surname || userinfo.sn;
const fullName =
firstName && familyName
? `${firstName} ${familyName}`
: userinfo.cn ||
userinfo.name ||
userinfo.commonname ||
userinfo.displayName ||
userinfo.mail;
const username = userinfo.givenName || userinfo.mail;
let user = await User.findOne({ email: userinfo.mail });
if (user && user.provider !== 'ldap') {
return done(null, false, { message: 'Invalid credentials' });
}
if (!user) {
user = new User({
provider: 'ldap',
ldapId: userinfo.uid,
username,
email: userinfo.mail || '',
emailVerified: true,
name: fullName,
});
} else {
user.provider = 'ldap';
user.ldapId = userinfo.uid;
user.username = username;
user.name = fullName;
}
await user.save();
done(null, user);
} catch (err) {
done(err);
}
});
module.exports = ldapLogin;

View file

@ -21,6 +21,7 @@ const mockStartupConfig = {
openidLoginEnabled: true, openidLoginEnabled: true,
openidLabel: 'Test OpenID', openidLabel: 'Test OpenID',
openidImageUrl: 'http://test-server.com', openidImageUrl: 'http://test-server.com',
ldapLoginEnabled: false,
registrationEnabled: true, registrationEnabled: true,
emailLoginEnabled: true, emailLoginEnabled: true,
socialLoginEnabled: true, socialLoginEnabled: true,

163
package-lock.json generated
View file

@ -94,6 +94,7 @@
"passport-github2": "^0.1.12", "passport-github2": "^0.1.12",
"passport-google-oauth20": "^2.0.0", "passport-google-oauth20": "^2.0.0",
"passport-jwt": "^4.0.1", "passport-jwt": "^4.0.1",
"passport-ldapauth": "^3.0.1",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"pino": "^8.12.1", "pino": "^8.12.1",
"sharp": "^0.32.6", "sharp": "^0.32.6",
@ -9992,6 +9993,14 @@
"resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.14.0.tgz", "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.14.0.tgz",
"integrity": "sha512-+2FW2CcT0K3P+JMR8YG846bmDwplKUTsWgT2ENwdQ1UdVfRk3GQrh6Mi4sTopy30gI8Uau5CEqHTDZ6YvWIUPA==" "integrity": "sha512-+2FW2CcT0K3P+JMR8YG846bmDwplKUTsWgT2ENwdQ1UdVfRk3GQrh6Mi4sTopy30gI8Uau5CEqHTDZ6YvWIUPA=="
}, },
"node_modules/@types/ldapjs": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-2.2.5.tgz",
"integrity": "sha512-Lv/nD6QDCmcT+V1vaTRnEKE8UgOilVv5pHcQuzkU1LcRe4mbHHuUo/KHi0LKrpdHhQY8FJzryF38fcVdeUIrzg==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/mdast": { "node_modules/@types/mdast": {
"version": "3.0.15", "version": "3.0.15",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz",
@ -11144,6 +11153,14 @@
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
"dev": true "dev": true
}, },
"node_modules/asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
"dependencies": {
"safer-buffer": "~2.1.0"
}
},
"node_modules/asn1.js": { "node_modules/asn1.js": {
"version": "5.4.1", "version": "5.4.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
@ -11175,6 +11192,14 @@
"util": "^0.12.5" "util": "^0.12.5"
} }
}, },
"node_modules/assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
"engines": {
"node": ">=0.8"
}
},
"node_modules/async": { "node_modules/async": {
"version": "3.2.5", "version": "3.2.5",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
@ -11506,6 +11531,17 @@
"@babel/core": "^7.0.0" "@babel/core": "^7.0.0"
} }
}, },
"node_modules/backoff": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz",
"integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==",
"dependencies": {
"precond": "0.2"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/bail": { "node_modules/bail": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
@ -14719,6 +14755,14 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/extsprintf": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz",
"integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==",
"engines": [
"node >=0.6.0"
]
},
"node_modules/fast-content-type-parse": { "node_modules/fast-content-type-parse": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz",
@ -19050,6 +19094,58 @@
"langsmith": "dist/cli/main.cjs" "langsmith": "dist/cli/main.cjs"
} }
}, },
"node_modules/ldap-filter": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.3.3.tgz",
"integrity": "sha512-/tFkx5WIn4HuO+6w9lsfxq4FN3O+fDZeO9Mek8dCD8rTUpqzRa766BOBO7BcGkn3X86m5+cBm1/2S/Shzz7gMg==",
"dependencies": {
"assert-plus": "^1.0.0"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/ldapauth-fork": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-5.0.5.tgz",
"integrity": "sha512-LWUk76+V4AOZbny/3HIPQtGPWZyA3SW2tRhsWIBi9imP22WJktKLHV1ofd8Jo/wY7Ve6vAT7FCI5mEn3blZTjw==",
"dependencies": {
"@types/ldapjs": "^2.2.2",
"bcryptjs": "^2.4.0",
"ldapjs": "^2.2.1",
"lru-cache": "^7.10.1"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/ldapauth-fork/node_modules/lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"engines": {
"node": ">=12"
}
},
"node_modules/ldapjs": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-2.3.3.tgz",
"integrity": "sha512-75QiiLJV/PQqtpH+HGls44dXweviFwQ6SiIK27EqzKQ5jU/7UFrl2E5nLdQ3IYRBzJ/AVFJI66u0MZ0uofKYwg==",
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
"dependencies": {
"abstract-logging": "^2.0.0",
"asn1": "^0.2.4",
"assert-plus": "^1.0.0",
"backoff": "^2.5.0",
"ldap-filter": "^0.3.3",
"once": "^1.4.0",
"vasync": "^2.2.0",
"verror": "^1.8.1"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/leven": { "node_modules/leven": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@ -22159,6 +22255,18 @@
"passport-strategy": "^1.0.0" "passport-strategy": "^1.0.0"
} }
}, },
"node_modules/passport-ldapauth": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/passport-ldapauth/-/passport-ldapauth-3.0.1.tgz",
"integrity": "sha512-TRRx3BHi8GC8MfCT9wmghjde/EGeKjll7zqHRRfGRxXbLcaDce2OftbQrFG7/AWaeFhR6zpZHtBQ/IkINdLVjQ==",
"dependencies": {
"ldapauth-fork": "^5.0.1",
"passport-strategy": "^1.0.0"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/passport-local": { "node_modules/passport-local": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
@ -23383,6 +23491,14 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/precond": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
"integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/prelude-ls": { "node_modules/prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -27773,6 +27889,53 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/vasync": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz",
"integrity": "sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==",
"engines": [
"node >=0.6.0"
],
"dependencies": {
"verror": "1.10.0"
}
},
"node_modules/vasync/node_modules/core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
},
"node_modules/vasync/node_modules/verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
"engines": [
"node >=0.6.0"
],
"dependencies": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
},
"node_modules/verror": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
"integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
"dependencies": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
},
"engines": {
"node": ">=0.6.0"
}
},
"node_modules/verror/node_modules/core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
},
"node_modules/vfile": { "node_modules/vfile": {
"version": "5.3.7", "version": "5.3.7",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz",

View file

@ -271,6 +271,7 @@ export type TStartupConfig = {
openidLoginEnabled: boolean; openidLoginEnabled: boolean;
openidLabel: string; openidLabel: string;
openidImageUrl: string; openidImageUrl: string;
ldapLoginEnabled: boolean;
serverDomain: string; serverDomain: string;
emailLoginEnabled: boolean; emailLoginEnabled: boolean;
registrationEnabled: boolean; registrationEnabled: boolean;