Skip to content

Commit

Permalink
fix: re-add attributes env var for ldap
Browse files Browse the repository at this point in the history
  • Loading branch information
SeDemal committed Apr 29, 2024
1 parent 6c385bd commit d2cd7a8
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 14 deletions.
6 changes: 4 additions & 2 deletions src/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const numberSchema = z
.string()
.regex(/\d*/)
.transform((value) => (value === undefined ? undefined : Number(value)))
.optional()
.optional();

const portSchema = z
.string()
Expand Down Expand Up @@ -77,6 +77,7 @@ const env = createEnv({
AUTH_LDAP_BASE: z.string(),
AUTH_LDAP_SEARCH_SCOPE: z.enum(['base', 'one', 'sub']).default('base'),
AUTH_LDAP_USERNAME_ATTRIBUTE: z.string().default('uid'),
AUTH_LDAP_USER_MAIL_ATTRIBUTE: z.string().default('mail'),
AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG: z.string().optional(),
AUTH_LDAP_GROUP_CLASS: z.string().default('groupOfUniqueNames'),
AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE: z.string().default('member'),
Expand All @@ -98,7 +99,7 @@ const env = createEnv({
AUTH_OIDC_OWNER_GROUP: z.string().default('admin'),
AUTH_OIDC_AUTO_LOGIN: zodParsedBoolean(),
AUTH_OIDC_SCOPE_OVERWRITE: z.string().default('openid email profile groups'),
AUTH_OIDC_TIMEOUT: numberSchema.default(3500)
AUTH_OIDC_TIMEOUT: numberSchema.default(3500),
}
: {}),
},
Expand Down Expand Up @@ -144,6 +145,7 @@ const env = createEnv({
AUTH_LDAP_BASE: process.env.AUTH_LDAP_BASE,
AUTH_LDAP_SEARCH_SCOPE: process.env.AUTH_LDAP_SEARCH_SCOPE?.toLowerCase(),
AUTH_LDAP_USERNAME_ATTRIBUTE: process.env.AUTH_LDAP_USERNAME_ATTRIBUTE,
AUTH_LDAP_USER_MAIL_ATTRIBUTE: process.env.AUTH_LDAP_USER_MAIL_ATTRIBUTE,
AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG: process.env.AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG,
AUTH_LDAP_GROUP_CLASS: process.env.AUTH_LDAP_GROUP_CLASS,
AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE: process.env.AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE,
Expand Down
42 changes: 30 additions & 12 deletions src/utils/auth/ldap.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Consola from 'consola';
import ldap from 'ldapjs';
import Credentials from 'next-auth/providers/credentials';
import { z } from 'zod';
import { env } from '~/env';
import { signInSchema } from '~/validations/user';

Expand Down Expand Up @@ -61,7 +62,6 @@ const ldapSearch = async <
reject('error: ' + err.message);
});
res.on('searchEntry', (entry) => {
console.log(entry.pojo);
results.push(
entry.pojo.attributes.reduce<Record<string, string | string[]>>(
(obj, attr) => {
Expand Down Expand Up @@ -99,26 +99,34 @@ export default Credentials({
Consola.log(`user ${data.name} is trying to log in using LDAP. Connecting to LDAP server...`);
const client = await ldapLogin(env.AUTH_LDAP_BIND_DN, env.AUTH_LDAP_BIND_PASSWORD);

Consola.log(`Connection established. Logging in User...`);
Consola.log(`Connection established. Searching User...`);
const ldapUser = (
await ldapSearch(client, env.AUTH_LDAP_BASE, {
filter: env.AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG ? `(&(${env.AUTH_LDAP_USERNAME_ATTRIBUTE}=${data.name})${env.AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG})` : `(${env.AUTH_LDAP_USERNAME_ATTRIBUTE}=${data.name})`,
filter: env.AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG
? `(&(${env.AUTH_LDAP_USERNAME_ATTRIBUTE}=${data.name})${env.AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG})`
: `(${env.AUTH_LDAP_USERNAME_ATTRIBUTE}=${data.name})`,
scope: env.AUTH_LDAP_SEARCH_SCOPE,
// as const for inference
attributes: ['uid', 'mail'] as const,
attributes: [
env.AUTH_LDAP_USERNAME_ATTRIBUTE,
env.AUTH_LDAP_USER_MAIL_ATTRIBUTE,
] as const,
})
)[0];

if (!ldapUser) throw new Error('User not found in LDAP');

Consola.log(`User logged in. Retrieving groups...`);
Consola.log(`User found. Logging in...`);
await ldapLogin(ldapUser.dn, data.password).then((client) => client.destroy());

Consola.log(`User logged in. Retrieving groups...`);
const userGroups = (
await ldapSearch(client, env.AUTH_LDAP_BASE, {
filter: `(&(objectclass=${env.AUTH_LDAP_GROUP_CLASS})(${
filter: `(&(objectClass=${env.AUTH_LDAP_GROUP_CLASS})(${
env.AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE
}=${ldapUser[env.AUTH_LDAP_GROUP_MEMBER_USER_ATTRIBUTE as 'dn' | 'uid']})${env.AUTH_LDAP_GROUP_FILTER_EXTRA_ARG ?? ''})`,
}=${ldapUser[env.AUTH_LDAP_GROUP_MEMBER_USER_ATTRIBUTE as 'dn' | 'uid']})${
env.AUTH_LDAP_GROUP_FILTER_EXTRA_ARG ?? ''
})`,
scope: env.AUTH_LDAP_SEARCH_SCOPE,
// as const for inference
attributes: 'cn',
Expand All @@ -129,15 +137,25 @@ export default Credentials({

Consola.log(`user ${data.name} successfully authorized`);

let user = await adapter.getUserByEmail!(ldapUser.mail);
try {
z.string().email().parse(ldapUser[env.AUTH_LDAP_USER_MAIL_ATTRIBUTE]);
} catch {
throw new Error(
`Invalid or non-existing Email. Not Supported: "${
ldapUser[env.AUTH_LDAP_USER_MAIL_ATTRIBUTE] ?? ' '
}"`
);
}

let user = await adapter.getUserByEmail!(ldapUser[env.AUTH_LDAP_USER_MAIL_ATTRIBUTE]);
const isAdmin = userGroups.includes(env.AUTH_LDAP_ADMIN_GROUP);
const isOwner = userGroups.includes(env.AUTH_LDAP_OWNER_GROUP);

if (!user) {
// CreateUser will create settings in event
user = await adapter.createUser({
name: ldapUser.uid,
email: ldapUser.mail,
user = adapter.createUser({
name: ldapUser[env.AUTH_LDAP_USERNAME_ATTRIBUTE],
email: ldapUser[env.AUTH_LDAP_USER_MAIL_ATTRIBUTE],
emailVerified: new Date(), // assume ldap email is verified
isAdmin: isAdmin,
isOwner: isOwner,
Expand All @@ -156,7 +174,7 @@ export default Credentials({

return {
id: user?.id || ldapUser.dn,
name: user?.name || ldapUser.uid,
name: user?.name || ldapUser[env.AUTH_LDAP_USERNAME_ATTRIBUTE],
isAdmin: isAdmin,
isOwner: isOwner,
};
Expand Down

0 comments on commit d2cd7a8

Please sign in to comment.