Browse Source

Ldap auth

isundil 1 tháng trước cách đây
mục cha
commit
295a944b5b

+ 4 - 2
dist/index.d.ts

@@ -1,12 +1,12 @@
 export interface IAuthenticationHandler {
     tryLogin(username: string, password: string, totp?: string): Promise<boolean | null>;
-    needTotp(username: string): Promise<boolean | null>;
+    needTotp(username: string, password: string): Promise<boolean | null>;
 }
 export declare class AuthenticationLoader {
     private handlers;
     addAuthenticationHandler(authenticationHandler: IAuthenticationHandler): void;
     tryLogin(username: string, password: string, totpCode?: string): Promise<boolean>;
-    needTotp(username: string): Promise<boolean>;
+    needTotp(username: string, password: string): Promise<boolean>;
 }
 import { YesManAuthenticationHandler } from './yesManAuthenticationHandler.js';
 export { YesManAuthenticationHandler };
@@ -14,4 +14,6 @@ import { TotpChecker, ToTpGeneratorOptions, ToTpSecretAndUrl } from './totpCheck
 export { TotpChecker, ToTpGeneratorOptions, ToTpSecretAndUrl };
 import { AccountInformation, EncodePasswordFunction, GetAccountInformationFunction, SqliteAuthenticationHandler } from './sqliteAuthenticationHandler.js';
 export { AccountInformation, EncodePasswordFunction, GetAccountInformationFunction, SqliteAuthenticationHandler };
+import { LdapAuthenticationConfiguration, LdapAuthenticationHandler } from './ldapAuthenticationHandler.js';
+export { LdapAuthenticationConfiguration, LdapAuthenticationHandler };
 //# sourceMappingURL=index.d.ts.map

+ 1 - 1
dist/index.d.ts.map

@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,sBAAsB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAC,IAAI,CAAC,CAAC;IACnF,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAC,IAAI,CAAC,CAAC;CACrD;AAED,qBAAa,oBAAoB;IAC7B,OAAO,CAAC,QAAQ,CAAgC;IAEzC,wBAAwB,CAAC,qBAAqB,EAAE,sBAAsB;IAIhE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAQ5D;AAED,OAAO,EAAE,2BAA2B,EAAE,MAAK,kCAAkC,CAAC;AAC9E,OAAO,EAAE,2BAA2B,EAAE,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAA;AACzJ,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,2BAA2B,EAAE,CAAC"}
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,sBAAsB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAC,IAAI,CAAC,CAAC;IACnF,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAC,IAAI,CAAC,CAAC;CACvE;AAED,qBAAa,oBAAoB;IAC7B,OAAO,CAAC,QAAQ,CAAgC;IAEzC,wBAAwB,CAAC,qBAAqB,EAAE,sBAAsB;IAIhE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAQ9E;AAED,OAAO,EAAE,2BAA2B,EAAE,MAAK,kCAAkC,CAAC;AAC9E,OAAO,EAAE,2BAA2B,EAAE,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAA;AACzJ,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,2BAA2B,EAAE,CAAC;AAClH,OAAO,EAAE,+BAA+B,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC5G,OAAO,EAAE,+BAA+B,EAAE,yBAAyB,EAAE,CAAA"}

+ 4 - 2
dist/index.js

@@ -11,9 +11,9 @@ export class AuthenticationLoader {
         }
         return false;
     }
-    async needTotp(username) {
+    async needTotp(username, password) {
         for (let i of this.handlers) {
-            const result = await i.needTotp(username);
+            const result = await i.needTotp(username, password);
             if (result !== null)
                 return result;
         }
@@ -26,4 +26,6 @@ import { TotpChecker } from './totpChecker.js';
 export { TotpChecker };
 import { SqliteAuthenticationHandler } from './sqliteAuthenticationHandler.js';
 export { SqliteAuthenticationHandler };
+import { LdapAuthenticationHandler } from './ldapAuthenticationHandler.js';
+export { LdapAuthenticationHandler };
 //# sourceMappingURL=index.js.map

+ 1 - 1
dist/index.js.map

@@ -1 +1 @@
-{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,oBAAoB;IACrB,QAAQ,GAA6B,EAAE,CAAC;IAEzC,wBAAwB,CAAC,qBAA6C;QACzE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAC9C,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAC,QAAgB,EAAE,QAAgB,EAAE,QAAiB;QACvE,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC9D,IAAI,MAAM,KAAK,IAAI;gBACf,OAAO,MAAM,CAAC;QACtB,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAC,QAAgB;QAClC,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC1C,IAAI,MAAM,KAAK,IAAI;gBACf,OAAO,MAAM,CAAC;QACtB,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;CACJ;AAED,OAAO,EAAE,2BAA2B,EAAE,MAAK,kCAAkC,CAAC;AAC9E,OAAO,EAAE,2BAA2B,EAAE,CAAC;AACvC,OAAO,EAAE,WAAW,EAA0C,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,WAAW,EAA0C,CAAC;AAC/D,OAAO,EAA6E,2BAA2B,EAAE,MAAM,kCAAkC,CAAA;AACzJ,OAAO,EAA6E,2BAA2B,EAAE,CAAC"}
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,oBAAoB;IACrB,QAAQ,GAA6B,EAAE,CAAC;IAEzC,wBAAwB,CAAC,qBAA6C;QACzE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAC9C,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAC,QAAgB,EAAE,QAAgB,EAAE,QAAiB;QACvE,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC9D,IAAI,MAAM,KAAK,IAAI;gBACf,OAAO,MAAM,CAAC;QACtB,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAC,QAAgB,EAAE,QAAgB;QACpD,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACpD,IAAI,MAAM,KAAK,IAAI;gBACf,OAAO,MAAM,CAAC;QACtB,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;CACJ;AAED,OAAO,EAAE,2BAA2B,EAAE,MAAK,kCAAkC,CAAC;AAC9E,OAAO,EAAE,2BAA2B,EAAE,CAAC;AACvC,OAAO,EAAE,WAAW,EAA0C,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,WAAW,EAA0C,CAAC;AAC/D,OAAO,EAA6E,2BAA2B,EAAE,MAAM,kCAAkC,CAAA;AACzJ,OAAO,EAA6E,2BAA2B,EAAE,CAAC;AAClH,OAAO,EAAmC,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC5G,OAAO,EAAmC,yBAAyB,EAAE,CAAA"}

+ 16 - 0
dist/ldapAuthenticationHandler.d.ts

@@ -0,0 +1,16 @@
+import { IAuthenticationHandler } from "./index.js";
+export interface LdapAuthenticationConfiguration {
+    ldapUrl: string;
+    bindDnField: string;
+    bindRoot: string;
+    usernameField: string;
+    totpField: string | null;
+}
+export declare class LdapAuthenticationHandler implements IAuthenticationHandler {
+    private configuration;
+    constructor(configuration: LdapAuthenticationConfiguration);
+    private tryBind;
+    tryLogin(username: string, password: string, totp?: string): Promise<boolean | null>;
+    needTotp(username: string, password: string): Promise<boolean | null>;
+}
+//# sourceMappingURL=ldapAuthenticationHandler.d.ts.map

+ 1 - 0
dist/ldapAuthenticationHandler.d.ts.map

@@ -0,0 +1 @@
+{"version":3,"file":"ldapAuthenticationHandler.d.ts","sourceRoot":"","sources":["../src/ldapAuthenticationHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGpD,MAAM,WAAW,+BAA+B;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,GAAC,IAAI,CAAC;CAC1B;AAOD,qBAAa,yBAA0B,YAAW,sBAAsB;IACpE,OAAO,CAAC,aAAa,CAAkC;gBAEpC,aAAa,EAAE,+BAA+B;YAInD,OAAO;IAyCR,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAMpF,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;CAMrF"}

+ 60 - 0
dist/ldapAuthenticationHandler.js

@@ -0,0 +1,60 @@
+import { Client } from "ldapts";
+import { TotpChecker } from "./totpChecker.js";
+export class LdapAuthenticationHandler {
+    configuration;
+    constructor(configuration) {
+        this.configuration = configuration;
+    }
+    async tryBind(username, password) {
+        if (!username || !password)
+            return null;
+        const client = new Client({
+            url: this.configuration.ldapUrl,
+            timeout: 0,
+            connectTimeout: 0,
+            tlsOptions: {
+                minVersion: 'TLSv1.2',
+            },
+            strictDN: true,
+        });
+        const bindDn = `${this.configuration.bindDnField}=${username},${this.configuration.bindRoot}`;
+        let totp = null;
+        try {
+            await client.bind(bindDn, password);
+            if (this.configuration.totpField) {
+                const data = await client.search(bindDn);
+                let totpData = data.searchEntries[0]?.[this.configuration.totpField];
+                if (typeof totpData === "string")
+                    totp = totpData;
+                if (Array.isArray(totpData))
+                    totp = totpData.join("");
+                else
+                    totp = totpData.toString("utf8");
+            }
+        }
+        catch (ex) {
+            console.error(ex);
+            return null;
+        }
+        finally {
+            client.unbind();
+        }
+        return {
+            username: username,
+            totp: totp
+        };
+    }
+    async tryLogin(username, password, totp) {
+        const account = await this.tryBind(username, password);
+        if (!account)
+            return null;
+        return TotpChecker.ValidateTotp(account.totp, totp);
+    }
+    async needTotp(username, password) {
+        const account = await this.tryBind(username, password);
+        if (!account)
+            return null;
+        return !!account.totp;
+    }
+}
+//# sourceMappingURL=ldapAuthenticationHandler.js.map

+ 1 - 0
dist/ldapAuthenticationHandler.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"ldapAuthenticationHandler.js","sourceRoot":"","sources":["../src/ldapAuthenticationHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAe/C,MAAM,OAAO,yBAAyB;IAC1B,aAAa,CAAkC;IAEvD,YAAmB,aAA8C;QAC7D,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACvC,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,QAAgB,EAAE,QAAgB;QACpD,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ;YACtB,OAAO,IAAI,CAAC;QAChB,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC;YACtB,GAAG,EAAE,IAAI,CAAC,aAAa,CAAC,OAAO;YAC/B,OAAO,EAAE,CAAC;YACV,cAAc,EAAE,CAAC;YACjB,UAAU,EAAE;gBACR,UAAU,EAAE,SAAS;aACxB;YACD,QAAQ,EAAE,IAAI;SACjB,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,WAAW,IAAI,QAAQ,IAAI,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;QAC9F,IAAI,IAAI,GAAgB,IAAI,CAAC;QAE7B,IAAI,CAAC;YACD,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YACpC,IAAI,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,CAAC;gBAC/B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACzC,IAAI,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;gBACrE,IAAI,OAAO,QAAQ,KAAK,QAAQ;oBAC5B,IAAI,GAAG,QAAQ,CAAC;gBACpB,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;oBACvB,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;;oBAEzB,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACzC,CAAC;QACL,CAAC;QACD,OAAO,EAAE,EAAE,CAAC;YACR,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAClB,OAAO,IAAI,CAAC;QAChB,CAAC;gBACO,CAAC;YACL,MAAM,CAAC,MAAM,EAAE,CAAC;QACpB,CAAC;QACD,OAA6B;YACzB,QAAQ,EAAE,QAAQ;YAClB,IAAI,EAAE,IAAI;SACb,CAAC;IACN,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAC,QAAgB,EAAE,QAAgB,EAAE,IAAa;QACnE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACvD,IAAI,CAAC,OAAO;YACR,OAAO,IAAI,CAAC;QAChB,OAAO,WAAW,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACxD,CAAC;IACM,KAAK,CAAC,QAAQ,CAAC,QAAgB,EAAE,QAAgB;QACpD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACvD,IAAI,CAAC,OAAO;YACR,OAAO,IAAI,CAAC;QAChB,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IAC1B,CAAC;CACJ"}

+ 1 - 1
dist/sqliteAuthenticationHandler.d.ts

@@ -10,7 +10,7 @@ export declare class SqliteAuthenticationHandler implements IAuthenticationHandl
     passwordEncoder: EncodePasswordFunction;
     getAccountInformation: GetAccountInformationFunction;
     constructor(getAccountInformationFunction: GetAccountInformationFunction, passwordEncoder: EncodePasswordFunction);
-    needTotp(username: string): Promise<boolean | null>;
+    needTotp(username: string, _password: string): Promise<boolean | null>;
     tryLogin(username: string, password: string, totp?: string): Promise<boolean | null>;
 }
 //# sourceMappingURL=sqliteAuthenticationHandler.d.ts.map

+ 1 - 1
dist/sqliteAuthenticationHandler.d.ts.map

@@ -1 +1 @@
-{"version":3,"file":"sqliteAuthenticationHandler.d.ts","sourceRoot":"","sources":["../src/sqliteAuthenticationHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGpD,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,GAAC,IAAI,CAAC;CAC3B;AAED,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,GAAC,IAAI,CAAC,CAAC,CAAC;AACrG,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;AAEpE,qBAAa,2BAA4B,YAAW,sBAAsB;IACtE,eAAe,EAAE,sBAAsB,CAAC;IACxC,qBAAqB,EAAE,6BAA6B,CAAC;gBAElC,6BAA6B,EAAE,6BAA6B,EAAE,eAAe,EAAE,sBAAsB;IAK3G,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAOnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;CAapG"}
+{"version":3,"file":"sqliteAuthenticationHandler.d.ts","sourceRoot":"","sources":["../src/sqliteAuthenticationHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGpD,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,GAAC,IAAI,CAAC;CAC3B;AAED,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,GAAC,IAAI,CAAC,CAAC,CAAC;AACrG,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;AAEpE,qBAAa,2BAA4B,YAAW,sBAAsB;IACtE,eAAe,EAAE,sBAAsB,CAAC;IACxC,qBAAqB,EAAE,6BAA6B,CAAC;gBAElC,6BAA6B,EAAE,6BAA6B,EAAE,eAAe,EAAE,sBAAsB;IAK3G,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAOtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;CASpG"}

+ 2 - 6
dist/sqliteAuthenticationHandler.js

@@ -6,7 +6,7 @@ export class SqliteAuthenticationHandler {
         this.passwordEncoder = passwordEncoder;
         this.getAccountInformation = getAccountInformationFunction;
     }
-    async needTotp(username) {
+    async needTotp(username, _password) {
         const accountInformation = await this.getAccountInformation(username);
         if (!accountInformation)
             return null;
@@ -17,12 +17,8 @@ export class SqliteAuthenticationHandler {
         if (!accountInformation)
             return null;
         password = this.passwordEncoder(password);
-        if (accountInformation.passwordEncoded !== password ||
-            (accountInformation.totpSecret && !totp) ||
-            (!accountInformation.totpSecret && totp))
+        if (accountInformation.passwordEncoded !== password)
             return false;
-        if (!accountInformation.totpSecret && !totp)
-            return true;
         return TotpChecker.ValidateTotp(accountInformation.totpSecret, totp);
     }
 }

+ 1 - 1
dist/sqliteAuthenticationHandler.js.map

@@ -1 +1 @@
-{"version":3,"file":"sqliteAuthenticationHandler.js","sourceRoot":"","sources":["../src/sqliteAuthenticationHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAW/C,MAAM,OAAO,2BAA2B;IACpC,eAAe,CAAyB;IACxC,qBAAqB,CAAgC;IAErD,YAAmB,6BAA4D,EAAE,eAAuC;QACpH,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,qBAAqB,GAAG,6BAA6B,CAAC;IAC/D,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAC,QAAgB;QAClC,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QACtE,IAAI,CAAC,kBAAkB;YACnB,OAAO,IAAI,CAAC;QAChB,OAAO,CAAC,CAAC,kBAAkB,CAAC,UAAU,CAAC;IAC3C,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAC,QAAgB,EAAE,QAAgB,EAAE,IAAa;QACnE,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QACtE,IAAI,CAAC,kBAAkB;YACnB,OAAO,IAAI,CAAC;QAChB,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,kBAAkB,CAAC,eAAe,KAAK,QAAQ;YAC/C,CAAC,kBAAkB,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC;YACxC,CAAC,CAAC,kBAAkB,CAAC,UAAU,IAAI,IAAI,CAAC;YACxC,OAAO,KAAK,CAAC;QACjB,IAAI,CAAC,kBAAkB,CAAC,UAAU,IAAI,CAAC,IAAI;YACvC,OAAO,IAAI,CAAC;QAChB,OAAO,WAAW,CAAC,YAAY,CAAC,kBAAkB,CAAC,UAAW,EAAE,IAAK,CAAC,CAAC;IAC3E,CAAC;CACJ"}
+{"version":3,"file":"sqliteAuthenticationHandler.js","sourceRoot":"","sources":["../src/sqliteAuthenticationHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAW/C,MAAM,OAAO,2BAA2B;IACpC,eAAe,CAAyB;IACxC,qBAAqB,CAAgC;IAErD,YAAmB,6BAA4D,EAAE,eAAuC;QACpH,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,qBAAqB,GAAG,6BAA6B,CAAC;IAC/D,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAC,QAAgB,EAAE,SAAiB;QACrD,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QACtE,IAAI,CAAC,kBAAkB;YACnB,OAAO,IAAI,CAAC;QAChB,OAAO,CAAC,CAAC,kBAAkB,CAAC,UAAU,CAAC;IAC3C,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAC,QAAgB,EAAE,QAAgB,EAAE,IAAa;QACnE,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QACtE,IAAI,CAAC,kBAAkB;YACnB,OAAO,IAAI,CAAC;QAChB,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,kBAAkB,CAAC,eAAe,KAAK,QAAQ;YAC/C,OAAO,KAAK,CAAC;QACjB,OAAO,WAAW,CAAC,YAAY,CAAC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACzE,CAAC;CACJ"}

+ 1 - 1
dist/totpChecker.d.ts

@@ -11,7 +11,7 @@ export interface ToTpSecretAndUrl {
     secret: string;
 }
 export declare class TotpChecker {
-    static ValidateTotp(_totpSecret: string, _code: string): Promise<boolean>;
+    static ValidateTotp(totpSecret: string | null, code: string | undefined): Promise<boolean>;
     static EncodeBase32(input: Buffer): string;
     static GenerateCode(optionsOrIssuer: ToTpGeneratorOptions | string): ToTpSecretAndUrl;
 }

+ 1 - 1
dist/totpChecker.d.ts.map

@@ -1 +1 @@
-{"version":3,"file":"totpChecker.d.ts","sourceRoot":"","sources":["../src/totpChecker.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,GAAC,SAAS,GAAC,SAAS,CAAA;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAClB;AAID,qBAAa,WAAW;WACA,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;WAIxE,YAAY,CAAC,KAAK,EAAE,MAAM;WAO1B,YAAY,CAAC,eAAe,EAAE,oBAAoB,GAAC,MAAM,GAAG,gBAAgB;CAa7F"}
+{"version":3,"file":"totpChecker.d.ts","sourceRoot":"","sources":["../src/totpChecker.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,GAAC,SAAS,GAAC,SAAS,CAAA;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAClB;AAID,qBAAa,WAAW;WACA,YAAY,CAAC,UAAU,EAAE,MAAM,GAAC,IAAI,EAAE,IAAI,EAAE,MAAM,GAAC,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;WAQrF,YAAY,CAAC,KAAK,EAAE,MAAM;WAO1B,YAAY,CAAC,eAAe,EAAE,oBAAoB,GAAC,MAAM,GAAG,gBAAgB;CAa7F"}

+ 5 - 1
dist/totpChecker.js

@@ -1,7 +1,11 @@
 import crypto from 'crypto';
 const RFC_4648 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
 export class TotpChecker {
-    static async ValidateTotp(_totpSecret, _code) {
+    static async ValidateTotp(totpSecret, code) {
+        if (!totpSecret && !code)
+            return true;
+        if ((!totpSecret && code) || (totpSecret && !code))
+            return false;
         return true;
     }
     static EncodeBase32(input) {

+ 1 - 1
dist/totpChecker.js.map

@@ -1 +1 @@
-{"version":3,"file":"totpChecker.js","sourceRoot":"","sources":["../src/totpChecker.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAgB5B,MAAM,QAAQ,GAAG,kCAAkC,CAAC;AAEpD,MAAM,OAAO,WAAW;IACb,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,WAAmB,EAAE,KAAa;QAC/D,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,MAAM,CAAC,YAAY,CAAC,KAAa;QACpC,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,IAAI,CAAC,IAAI,KAAK;YACf,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAC/C,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAEM,MAAM,CAAC,YAAY,CAAC,eAA4C;QACnE,IAAI,OAAO,GAAyB,OAAO,eAAe,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAC,MAAM,EAAE,eAAe,EAAC,CAAC,CAAC,CAAC,eAAe,CAAC;QACtH,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QACrC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;QACtC,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC;QACjD,OAAO,CAAC,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;QACpE,OAAO,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;QAClD,MAAM,SAAS,GAAG,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;QACrF,OAAO;YACH,GAAG,EAAE,kBAAkB,OAAO,CAAC,MAAM,WAAW,OAAO,CAAC,MAAM,WAAW,SAAS,WAAW,OAAO,CAAC,MAAM,WAAW,OAAO,CAAC,MAAM,cAAc,OAAO,CAAC,SAAS,EAAE;YACrK,MAAM,EAAE,SAAS;SACpB,CAAC;IACN,CAAC;CACJ"}
+{"version":3,"file":"totpChecker.js","sourceRoot":"","sources":["../src/totpChecker.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAgB5B,MAAM,QAAQ,GAAG,kCAAkC,CAAC;AAEpD,MAAM,OAAO,WAAW;IACb,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,UAAuB,EAAE,IAAsB;QAC5E,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI;YACpB,OAAO,IAAI,CAAC;QAChB,IAAI,CAAC,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC;YAC9C,OAAO,KAAK,CAAA;QAChB,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,MAAM,CAAC,YAAY,CAAC,KAAa;QACpC,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,IAAI,CAAC,IAAI,KAAK;YACf,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAC/C,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAEM,MAAM,CAAC,YAAY,CAAC,eAA4C;QACnE,IAAI,OAAO,GAAyB,OAAO,eAAe,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAC,MAAM,EAAE,eAAe,EAAC,CAAC,CAAC,CAAC,eAAe,CAAC;QACtH,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QACrC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;QACtC,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC;QACjD,OAAO,CAAC,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;QACpE,OAAO,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;QAClD,MAAM,SAAS,GAAG,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;QACrF,OAAO;YACH,GAAG,EAAE,kBAAkB,OAAO,CAAC,MAAM,WAAW,OAAO,CAAC,MAAM,WAAW,SAAS,WAAW,OAAO,CAAC,MAAM,WAAW,OAAO,CAAC,MAAM,cAAc,OAAO,CAAC,SAAS,EAAE;YACrK,MAAM,EAAE,SAAS;SACpB,CAAC;IACN,CAAC;CACJ"}

+ 1 - 1
dist/yesManAuthenticationHandler.d.ts

@@ -3,6 +3,6 @@ export declare class YesManAuthenticationHandler implements IAuthenticationHandl
     private useTotp;
     constructor(useTotp: boolean);
     tryLogin(username: string, password: string, totp?: string): Promise<boolean | null>;
-    needTotp(username: string): Promise<boolean | null>;
+    needTotp(username: string, _password: string): Promise<boolean | null>;
 }
 //# sourceMappingURL=yesManAuthenticationHandler.d.ts.map

+ 1 - 1
dist/yesManAuthenticationHandler.d.ts.map

@@ -1 +1 @@
-{"version":3,"file":"yesManAuthenticationHandler.d.ts","sourceRoot":"","sources":["../src/yesManAuthenticationHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGpD,qBAAa,2BAA4B,YAAW,sBAAsB;IACtE,OAAO,CAAC,OAAO,CAAU;gBAEN,OAAO,EAAE,OAAO;IAG5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAWpF,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;CAG7D"}
+{"version":3,"file":"yesManAuthenticationHandler.d.ts","sourceRoot":"","sources":["../src/yesManAuthenticationHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGpD,qBAAa,2BAA4B,YAAW,sBAAsB;IACtE,OAAO,CAAC,OAAO,CAAU;gBAEN,OAAO,EAAE,OAAO;IAG5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAOpF,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;CAGhF"}

+ 2 - 6
dist/yesManAuthenticationHandler.js

@@ -9,13 +9,9 @@ export class YesManAuthenticationHandler {
             return Promise.resolve(null);
         if (!password)
             return Promise.resolve(false);
-        if ((this.useTotp && !totp) || (!this.useTotp && totp))
-            return Promise.resolve(false);
-        if (!totp)
-            return Promise.resolve(true);
-        return TotpChecker.ValidateTotp(TotpChecker.EncodeBase32(Buffer.from(username)), totp);
+        return TotpChecker.ValidateTotp(this.useTotp ? TotpChecker.EncodeBase32(Buffer.from(username)) : null, totp);
     }
-    needTotp(username) {
+    needTotp(username, _password) {
         return Promise.resolve(username ? this.useTotp : null);
     }
 }

+ 1 - 1
dist/yesManAuthenticationHandler.js.map

@@ -1 +1 @@
-{"version":3,"file":"yesManAuthenticationHandler.js","sourceRoot":"","sources":["../src/yesManAuthenticationHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,MAAM,OAAO,2BAA2B;IAC5B,OAAO,CAAU;IAEzB,YAAmB,OAAgB;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;IACM,QAAQ,CAAC,QAAgB,EAAE,QAAgB,EAAE,IAAa;QAC7D,IAAI,CAAC,QAAQ;YACT,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,QAAQ;YACT,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;YAClD,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI;YACL,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACjC,OAAO,WAAW,CAAC,YAAY,CAAC,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3F,CAAC;IACM,QAAQ,CAAC,QAAgB;QAC5B,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC;CACJ"}
+{"version":3,"file":"yesManAuthenticationHandler.js","sourceRoot":"","sources":["../src/yesManAuthenticationHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,MAAM,OAAO,2BAA2B;IAC5B,OAAO,CAAU;IAEzB,YAAmB,OAAgB;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;IACM,QAAQ,CAAC,QAAgB,EAAE,QAAgB,EAAE,IAAa;QAC7D,IAAI,CAAC,QAAQ;YACT,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,QAAQ;YACT,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAClC,OAAO,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACjH,CAAC;IACM,QAAQ,CAAC,QAAgB,EAAE,SAAiB;QAC/C,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC;CACJ"}

+ 3 - 0
package.json

@@ -17,5 +17,8 @@
     "npx": "^10.2.2",
     "ts-node": "^10.9.2",
     "typescript": "^5.9.3"
+  },
+  "dependencies": {
+    "ldapts": "^8.0.9"
   }
 }

+ 5 - 4
src/index.ts

@@ -1,7 +1,7 @@
 
 export interface IAuthenticationHandler {
     tryLogin(username: string, password: string, totp?: string): Promise<boolean|null>;
-    needTotp(username: string): Promise<boolean|null>;
+    needTotp(username: string, password: string): Promise<boolean|null>;
 }
 
 export class AuthenticationLoader {
@@ -20,9 +20,9 @@ export class AuthenticationLoader {
         return false;
     }
 
-    public async needTotp(username: string): Promise<boolean> {
+    public async needTotp(username: string, password: string): Promise<boolean> {
         for (let i of this.handlers) {
-            const result = await i.needTotp(username);
+            const result = await i.needTotp(username, password);
             if (result !== null)
                 return result;
         }
@@ -36,4 +36,5 @@ import { TotpChecker, ToTpGeneratorOptions, ToTpSecretAndUrl } from './totpCheck
 export { TotpChecker, ToTpGeneratorOptions, ToTpSecretAndUrl };
 import { AccountInformation, EncodePasswordFunction, GetAccountInformationFunction, SqliteAuthenticationHandler } from './sqliteAuthenticationHandler.js'
 export { AccountInformation, EncodePasswordFunction, GetAccountInformationFunction, SqliteAuthenticationHandler };
-
+import { LdapAuthenticationConfiguration, LdapAuthenticationHandler } from './ldapAuthenticationHandler.js';
+export { LdapAuthenticationConfiguration, LdapAuthenticationHandler }

+ 78 - 0
src/ldapAuthenticationHandler.ts

@@ -0,0 +1,78 @@
+import { Client } from "ldapts";
+import { IAuthenticationHandler } from "./index.js";
+import { TotpChecker } from "./totpChecker.js";
+
+export interface LdapAuthenticationConfiguration {
+    ldapUrl: string;
+    bindDnField: string;
+    bindRoot: string;
+    usernameField: string;
+    totpField: string|null;
+}
+
+interface AccountInformations {
+    username: string;
+    totp: string|null;
+}
+
+export class LdapAuthenticationHandler implements IAuthenticationHandler {
+    private configuration: LdapAuthenticationConfiguration;
+
+    public constructor(configuration: LdapAuthenticationConfiguration) {
+        this.configuration = configuration;
+    }
+
+    private async tryBind(username: string, password: string): Promise<AccountInformations|null> {
+        if (!username || !password)
+            return null;
+        const client = new Client({
+            url: this.configuration.ldapUrl,
+            timeout: 0,
+            connectTimeout: 0,
+            tlsOptions: {
+                minVersion: 'TLSv1.2',
+            },
+            strictDN: true,
+        });
+        const bindDn = `${this.configuration.bindDnField}=${username},${this.configuration.bindRoot}`;
+        let totp: string|null = null;
+
+        try {
+            await client.bind(bindDn, password);
+            if (this.configuration.totpField) {
+                const data = await client.search(bindDn);
+                let totpData = data.searchEntries[0]?.[this.configuration.totpField];
+                if (typeof totpData === "string")
+                    totp = totpData;
+                if (Array.isArray(totpData))
+                    totp = totpData.join("");
+                else
+                    totp = totpData.toString("utf8");
+            }
+        }
+        catch (ex) {
+            console.error(ex);
+            return null;
+        }
+        finally {
+            client.unbind();
+        }
+        return <AccountInformations> {
+            username: username,
+            totp: totp
+        };
+    }
+
+    public async tryLogin(username: string, password: string, totp?: string): Promise<boolean | null> {
+        const account = await this.tryBind(username, password);
+        if (!account)
+            return null;
+        return TotpChecker.ValidateTotp(account.totp, totp);
+    }
+    public async needTotp(username: string, password: string): Promise<boolean | null> {
+        const account = await this.tryBind(username, password);
+        if (!account)
+            return null;
+        return !!account.totp;
+    }
+}

+ 3 - 7
src/sqliteAuthenticationHandler.ts

@@ -19,7 +19,7 @@ export class SqliteAuthenticationHandler implements IAuthenticationHandler {
         this.getAccountInformation = getAccountInformationFunction;
     }
 
-    public async needTotp(username: string): Promise<boolean | null> {
+    public async needTotp(username: string, _password: string): Promise<boolean | null> {
         const accountInformation = await this.getAccountInformation(username);
         if (!accountInformation)
             return null;
@@ -31,12 +31,8 @@ export class SqliteAuthenticationHandler implements IAuthenticationHandler {
         if (!accountInformation)
             return null;
         password = this.passwordEncoder(password);
-        if (accountInformation.passwordEncoded !== password || 
-            (accountInformation.totpSecret && !totp) ||
-            (!accountInformation.totpSecret && totp))
+        if (accountInformation.passwordEncoded !== password)
             return false;
-        if (!accountInformation.totpSecret && !totp)
-            return true;
-        return TotpChecker.ValidateTotp(accountInformation.totpSecret!, totp!);
+        return TotpChecker.ValidateTotp(accountInformation.totpSecret, totp);
     }
 }

+ 5 - 1
src/totpChecker.ts

@@ -18,7 +18,11 @@ export interface ToTpSecretAndUrl {
 const RFC_4648 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
 
 export class TotpChecker {
-    public static async ValidateTotp(_totpSecret: string, _code: string): Promise<boolean> {
+    public static async ValidateTotp(totpSecret: string|null, code: string|undefined): Promise<boolean> {
+        if (!totpSecret && !code)
+            return true;
+        if ((!totpSecret && code) || (totpSecret && !code))
+            return false
         return true;
     }
 

+ 2 - 6
src/yesManAuthenticationHandler.ts

@@ -12,13 +12,9 @@ export class YesManAuthenticationHandler implements IAuthenticationHandler {
             return Promise.resolve(null);
         if (!password)
             return Promise.resolve(false);
-        if ((this.useTotp && !totp) || (!this.useTotp && totp))
-            return Promise.resolve(false);
-        if (!totp)
-            return Promise.resolve(true);
-        return TotpChecker.ValidateTotp(TotpChecker.EncodeBase32(Buffer.from(username)), totp);
+        return TotpChecker.ValidateTotp(this.useTotp ? TotpChecker.EncodeBase32(Buffer.from(username)) : null, totp);
     }
-    public needTotp(username: string): Promise<boolean | null> {
+    public needTotp(username: string, _password: string): Promise<boolean | null> {
         return Promise.resolve(username ? this.useTotp : null);
     }
 }