it's no longer needed to use stunnel ;-)master
parent
5e5005cf6b
commit
3f600c664f
@ -0,0 +1,21 @@
|
|||||||
|
import 'mocha';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { GoogleLDAPAuth } from '../../src/auth/GoogleLDAPAuth';
|
||||||
|
|
||||||
|
describe('test google ldap auth', function() {
|
||||||
|
this.timeout(10000);
|
||||||
|
it('authenticate against ldap server', async () => {
|
||||||
|
const auth = new GoogleLDAPAuth({
|
||||||
|
base: 'dc=hokify,dc=com',
|
||||||
|
tlsOptions: {
|
||||||
|
key: fs.readFileSync('./ldap.gsuite.hokify.com.40567.key'),
|
||||||
|
cert: fs.readFileSync('./ldap.gsuite.hokify.com.40567.crt')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await auth.authenticate('username', 'password');
|
||||||
|
|
||||||
|
expect(result).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,18 @@
|
|||||||
|
import 'mocha';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { IMAPAuth } from '../../src/auth/IMAPAuth';
|
||||||
|
|
||||||
|
describe('test imap auth', () => {
|
||||||
|
it('authenticate against imap server', async () => {
|
||||||
|
const auth = new IMAPAuth({
|
||||||
|
host: 'imap.gmail.com',
|
||||||
|
port: 993,
|
||||||
|
useSecureTransport: true,
|
||||||
|
validHosts: ['gmail.com']
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await auth.authenticate('username', 'password');
|
||||||
|
|
||||||
|
expect(result).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,23 @@
|
|||||||
|
import 'mocha';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { LDAPAuth } from '../../src/auth/LDAPAuth';
|
||||||
|
|
||||||
|
describe('test ldap auth', function() {
|
||||||
|
this.timeout(10000);
|
||||||
|
it('authenticate against ldap server', async () => {
|
||||||
|
const auth = new LDAPAuth({
|
||||||
|
url: 'ldaps://ldap.google.com:636',
|
||||||
|
base: 'dc=hokify,dc=com',
|
||||||
|
tlsOptions: {
|
||||||
|
servername: 'ldap.google.com',
|
||||||
|
key: fs.readFileSync('./ldap.gsuite.hokify.com.40567.key'),
|
||||||
|
cert: fs.readFileSync('./ldap.gsuite.hokify.com.40567.crt'),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await auth.authenticate('username', 'password');
|
||||||
|
|
||||||
|
expect(result).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,18 @@
|
|||||||
|
import 'mocha';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { SMTPAuth } from '../../src/auth/SMTPAuth';
|
||||||
|
|
||||||
|
describe('test smtp auth', () => {
|
||||||
|
it('authenticate against smtp server', async () => {
|
||||||
|
const auth = new SMTPAuth({
|
||||||
|
host: 'smtp.gmail.com',
|
||||||
|
port: 465,
|
||||||
|
useSecureTransport: true,
|
||||||
|
validHosts: ['gmail.com']
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await auth.authenticate('username', 'password');
|
||||||
|
|
||||||
|
expect(result).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"include": ["__tests__/*.ts", "*.ts", "../src/**/*.ts"]
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,25 @@
|
|||||||
|
import * as NodeCache from 'node-cache';
|
||||||
|
import { IAuthentication } from './types/Authentication';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this is just a simple abstraction to provide
|
||||||
|
* an application layer for caching credentials
|
||||||
|
*/
|
||||||
|
export class Authentication implements IAuthentication {
|
||||||
|
cache = new NodeCache();
|
||||||
|
|
||||||
|
constructor(private authenticator: IAuthentication) {}
|
||||||
|
|
||||||
|
async authenticate(username: string, password: string): Promise<boolean> {
|
||||||
|
const cacheKey = `usr:${username}|pwd:${password}`;
|
||||||
|
const fromCache = this.cache.get(cacheKey) as undefined | boolean;
|
||||||
|
if (fromCache !== undefined) {
|
||||||
|
return fromCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
const authResult = await this.authenticator.authenticate(username, password);
|
||||||
|
this.cache.set(cacheKey, authResult, 86400); // cache for one day
|
||||||
|
|
||||||
|
return authResult;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
import * as imaps from 'imap-simple';
|
||||||
|
import { IAuthentication } from '../types/Authentication';
|
||||||
|
|
||||||
|
interface IIMAPAuthOptions {
|
||||||
|
host: string;
|
||||||
|
port?: number;
|
||||||
|
useSecureTransport?: boolean;
|
||||||
|
validHosts?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IMAPAuth implements IAuthentication {
|
||||||
|
private host: string;
|
||||||
|
|
||||||
|
private port = 143;
|
||||||
|
|
||||||
|
private useSecureTransport = false;
|
||||||
|
|
||||||
|
private validHosts?: string[];
|
||||||
|
|
||||||
|
constructor(config: IIMAPAuthOptions) {
|
||||||
|
this.host = config.host;
|
||||||
|
if (config.port !== undefined) {
|
||||||
|
this.port = config.port;
|
||||||
|
}
|
||||||
|
if (config.useSecureTransport !== undefined) {
|
||||||
|
this.useSecureTransport = config.useSecureTransport;
|
||||||
|
}
|
||||||
|
if (config.validHosts !== undefined) {
|
||||||
|
this.validHosts = config.validHosts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async authenticate(username: string, password: string) {
|
||||||
|
if (this.validHosts) {
|
||||||
|
const domain = username.split('@').pop();
|
||||||
|
if (!domain || !this.validHosts.includes(domain)) {
|
||||||
|
console.info('invalid or no domain in username', username, domain);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let success = false;
|
||||||
|
try {
|
||||||
|
const connection = await imaps.connect({
|
||||||
|
imap: {
|
||||||
|
host: this.host,
|
||||||
|
port: this.port,
|
||||||
|
tls: this.useSecureTransport,
|
||||||
|
user: username,
|
||||||
|
password,
|
||||||
|
tlsOptions: {
|
||||||
|
servername: this.host // SNI (needs to be set for gmail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
connection.end();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('imap auth failed', err);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
import * as LdapAuth from 'ldapauth-fork';
|
||||||
|
import { IAuthentication } from '../types/Authentication';
|
||||||
|
|
||||||
|
interface ILDAPAuthOptions {
|
||||||
|
/** ldap url
|
||||||
|
* e.g. ldaps://ldap.google.com
|
||||||
|
*/
|
||||||
|
url: string;
|
||||||
|
/** base DN
|
||||||
|
* e.g. 'dc=hokify,dc=com', */
|
||||||
|
base: string;
|
||||||
|
/** tls options
|
||||||
|
* e.g. {
|
||||||
|
key: fs.readFileSync('ldap.gsuite.hokify.com.40567.key'),
|
||||||
|
cert: fs.readFileSync('ldap.gsuite.hokify.com.40567.crt'),
|
||||||
|
servername: 'ldap.google.com'
|
||||||
|
} */
|
||||||
|
tlsOptions?: any;
|
||||||
|
/**
|
||||||
|
* searchFilter
|
||||||
|
*/
|
||||||
|
searchFilter?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LDAPAuth implements IAuthentication {
|
||||||
|
private ldap: LdapAuth;
|
||||||
|
|
||||||
|
constructor(options: ILDAPAuthOptions) {
|
||||||
|
this.ldap = new LdapAuth({
|
||||||
|
url: options.url,
|
||||||
|
searchBase: options.base,
|
||||||
|
tlsOptions: options.tlsOptions,
|
||||||
|
searchFilter: options.searchFilter || '(uid={{username}})',
|
||||||
|
reconnect: true
|
||||||
|
});
|
||||||
|
this.ldap.on('error', function(err) {
|
||||||
|
console.error('LdapAuth: ', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async authenticate(username: string, password: string) {
|
||||||
|
// console.log('AUTH', this.ldap);
|
||||||
|
const authResult: boolean = await new Promise((resolve, reject) => {
|
||||||
|
this.ldap.authenticate(username, password, function(err, user) {
|
||||||
|
if (err) {
|
||||||
|
resolve(false);
|
||||||
|
console.error('ldap error', err);
|
||||||
|
// reject(err);
|
||||||
|
}
|
||||||
|
if (user) resolve(user);
|
||||||
|
else reject();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return !!authResult;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
import { SMTPClient } from 'smtp-client';
|
||||||
|
import { IAuthentication } from '../types/Authentication';
|
||||||
|
|
||||||
|
interface ISMTPAuthOptions {
|
||||||
|
host: string;
|
||||||
|
port?: number;
|
||||||
|
useSecureTransport?: boolean;
|
||||||
|
validHosts?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SMTPAuth implements IAuthentication {
|
||||||
|
private host: string;
|
||||||
|
|
||||||
|
private port = 25;
|
||||||
|
|
||||||
|
private useSecureTransport = false;
|
||||||
|
|
||||||
|
private validHosts?: string[];
|
||||||
|
|
||||||
|
constructor(options: ISMTPAuthOptions) {
|
||||||
|
this.host = options.host;
|
||||||
|
|
||||||
|
if (options.port !== undefined) {
|
||||||
|
this.port = options.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.useSecureTransport !== undefined) {
|
||||||
|
this.useSecureTransport = options.useSecureTransport;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.validHosts !== undefined) {
|
||||||
|
this.validHosts = options.validHosts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async authenticate(username: string, password: string) {
|
||||||
|
if (this.validHosts) {
|
||||||
|
const domain = username.split('@').pop();
|
||||||
|
if (!domain || !this.validHosts.includes(domain)) {
|
||||||
|
console.info('invalid or no domain in username', username, domain);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const s = new SMTPClient({
|
||||||
|
host: this.host,
|
||||||
|
port: this.port,
|
||||||
|
secure: this.useSecureTransport,
|
||||||
|
tlsOptions: {
|
||||||
|
servername: this.host // SNI (needs to be set for gmail)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let success = false;
|
||||||
|
try {
|
||||||
|
await s.connect();
|
||||||
|
await s.greet({ hostname: 'mx.domain.com' }); // runs EHLO command or HELO as a fallback
|
||||||
|
await s.authPlain({ username, password }); // authenticates a user
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
s.close(); // runs QUIT command
|
||||||
|
} catch (err) {
|
||||||
|
console.error('imap auth failed', err);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
import { IAuthentication } from '../types/Authentication';
|
||||||
|
|
||||||
|
interface IStaticAuthOtions {
|
||||||
|
validCrentials: {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StaticAuth implements IAuthentication {
|
||||||
|
private validCredentials: { username: string; password: string }[];
|
||||||
|
|
||||||
|
constructor(options: IStaticAuthOtions) {
|
||||||
|
this.validCredentials = options.validCrentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
async authenticate(username: string, password: string) {
|
||||||
|
return !!this.validCredentials.find(
|
||||||
|
credential => credential.username === username && credential.password === password
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,9 @@
|
|||||||
{
|
{
|
||||||
"extends": "./tsconfig.json",
|
"extends": "./tsconfig.json",
|
||||||
"include": [
|
"include": ["src/**/*.ts", "*.js", "*.ts", "__tests__/**/*.ts"],
|
||||||
"src/**/*.ts",
|
"exclude": ["node_modules"],
|
||||||
"*.js"
|
"compilerOptions": {
|
||||||
],
|
"allowJs": true,
|
||||||
"compilerOptions": {
|
"checkJs": true
|
||||||
"allowJs": true,
|
}
|
||||||
"checkJs": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in new issue