feat: add more auth providers and cleanup google auth
it's no longer needed to use stunnel ;-)
This commit is contained in:
parent
5e5005cf6b
commit
3f600c664f
112
README.md
112
README.md
@ -14,6 +14,33 @@ This is a first implementation draft, which is currently only working with a nod
|
||||
|
||||
CONTRIBUTIONS WELCOME! If you are willing to help, just open a PR or contact me via bug system or simon.tretter@hokify.com.
|
||||
|
||||
## Motivation
|
||||
|
||||
### Why not Freeradius?
|
||||
|
||||
There are several reasons why I started implementing this radius server in node js. We are using
|
||||
freeradius right now, but have several issues which are hard to tackle due to the reason that freeradius
|
||||
is a complex software and supports many uses cases. It is also written in C++ and uses threads behind the scene.
|
||||
Therefore it's not easy to extend or modify it, or even bring new feature in.
|
||||
The idea of this project is to make a super simple node radius server, which is async by default. No complex
|
||||
thread handling, no other fancy thing. The basic goal is to make WPA2 authenticiation easy again.
|
||||
|
||||
### 802.11x protocol in node
|
||||
|
||||
Another motivation is that it is very exciting to see how wireless protocols have evolved, and see
|
||||
how a implementation like TTLS works.
|
||||
|
||||
### Few alternatives (only non-free ones like Jumpcloud...)
|
||||
|
||||
Furthermore there are few alternatives out there, e.g. jumpcloud is non-free and I couldn't find many others.
|
||||
|
||||
### Vision
|
||||
|
||||
As soon as I understood the TTLS PAP Tunnel approach, I had this vision of making Wlan Authentification easy
|
||||
for everyone. Why limit it to something "complex" like LDAP and co. This library aims to make it easy for everyone
|
||||
to implement either their own authentication mechanismus (e.g. against a database), or provides some mechansimns
|
||||
out of the box (e.g. imap, static, ldap,..).
|
||||
|
||||
## Installation
|
||||
|
||||
npm install
|
||||
@ -28,7 +55,7 @@ you need:
|
||||
2. Optional: Create your own SSL certificate (e.g. self signed via npm run create-certificate)
|
||||
3. Check config.js and adapt to your needs
|
||||
|
||||
- configure authentication (passport config), e.g. for LDAP
|
||||
- configure authentication e.g. for LDAP
|
||||
|
||||
```js
|
||||
var config = {
|
||||
@ -46,15 +73,86 @@ var config = {
|
||||
4. Install und build server: npm install && npm run build
|
||||
5. Start server "npm run start"
|
||||
|
||||
## Authentications
|
||||
## Configuration
|
||||
|
||||
right now only one simple ldap implementation is done,
|
||||
the idea is though to use [passport.js](http://www.passportjs.org/) as authentication provider,
|
||||
therefore it would be possible to use the radius server with your email provider authentication or any
|
||||
other auth mechanismus you use (well everything with no 2factor or anything else that requries an extra step).
|
||||
see config.js in root
|
||||
|
||||
|
||||
|
||||
### Authentications
|
||||
|
||||
#### Google LDAP
|
||||
|
||||
google ldap optimized authenticiation implementaiton
|
||||
|
||||
#### LDAP
|
||||
|
||||
ldap authentication
|
||||
|
||||
```typescript
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
#### IMAP
|
||||
|
||||
imap authenticiation
|
||||
|
||||
```typescript
|
||||
interface IIMAPAuthOptions {
|
||||
host: string;
|
||||
port?: number;
|
||||
useSecureTransport?: boolean;
|
||||
validHosts?: string[];
|
||||
}
|
||||
```
|
||||
|
||||
#### SMTP
|
||||
|
||||
smtp authenticiation
|
||||
|
||||
```typescript
|
||||
interface ISMTPAuthOptions {
|
||||
host: string;
|
||||
port?: number;
|
||||
useSecureTransport?: boolean;
|
||||
validHosts?: string[];
|
||||
}
|
||||
```
|
||||
|
||||
#### Static Auth
|
||||
|
||||
static authenticiation
|
||||
|
||||
```typescript
|
||||
interface IStaticAuthOtions {
|
||||
validCrentials: {
|
||||
username: string;
|
||||
password: string;
|
||||
}[];
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
You need to specify at least a radius password and the base DN for LDAP:
|
||||
Ensure you have installed latest node version and run:
|
||||
|
||||
npm run start
|
||||
|
0
tests/.gitignore → __tests__/.gitignore
vendored
0
tests/.gitignore → __tests__/.gitignore
vendored
21
__tests__/auth/google-ldap.test.ts
Normal file
21
__tests__/auth/google-ldap.test.ts
Normal file
@ -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);
|
||||
});
|
||||
});
|
18
__tests__/auth/imap.test.ts
Normal file
18
__tests__/auth/imap.test.ts
Normal file
@ -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);
|
||||
});
|
||||
});
|
23
__tests__/auth/ldap.test.ts
Normal file
23
__tests__/auth/ldap.test.ts
Normal file
@ -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);
|
||||
});
|
||||
});
|
18
__tests__/auth/smtp.test.ts
Normal file
18
__tests__/auth/smtp.test.ts
Normal file
@ -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);
|
||||
});
|
||||
});
|
7
__tests__/tsconfig.json
Normal file
7
__tests__/tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
|
||||
},
|
||||
"include": ["__tests__/*.ts", "*.ts", "../src/**/*.ts"]
|
||||
}
|
48
config.js
48
config.js
@ -19,20 +19,46 @@ module.exports = {
|
||||
]
|
||||
},
|
||||
|
||||
// authentication
|
||||
authentication: 'ldap',
|
||||
// GoogleLDAPAuth (optimized for google auth)
|
||||
authentication: 'GoogleLDAPAuth',
|
||||
authenticationOptions: {
|
||||
url: 'ldap://127.0.0.1:1636',
|
||||
base: 'dc=hokify,dc=com',
|
||||
tlsOptions2: {
|
||||
tlsOptions: {
|
||||
key: fs.readFileSync('ldap.gsuite.hokify.com.40567.key'),
|
||||
cert: fs.readFileSync('ldap.gsuite.hokify.com.40567.crt'),
|
||||
|
||||
// This is necessary only if using the client certificate authentication.
|
||||
requestCert: true,
|
||||
|
||||
// This is necessary only if the client uses the self-signed certificate.
|
||||
ca: [fs.readFileSync('ldap.gsuite.hokify.com.40567.key')]
|
||||
cert: fs.readFileSync('ldap.gsuite.hokify.com.40567.crt')
|
||||
}
|
||||
}
|
||||
|
||||
/** LDAP AUTH
|
||||
authentication: 'LDAPAuth',
|
||||
authenticationOptions: {
|
||||
url: 'ldaps://ldap.google.com',
|
||||
base: 'dc=hokify,dc=com',
|
||||
tlsOptions: {
|
||||
key: fs.readFileSync('ldap.gsuite.hokify.com.40567.key'),
|
||||
cert: fs.readFileSync('ldap.gsuite.hokify.com.40567.crt'),
|
||||
servername: 'ldap.google.com'
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/** IMAP AUTH
|
||||
authentication: 'IMAPAuth',
|
||||
authenticationOptions: {
|
||||
host: 'imap.gmail.com',
|
||||
port: 993,
|
||||
useSecureTransport: true,
|
||||
validHosts: ['hokify.com']
|
||||
}
|
||||
*/
|
||||
|
||||
/** SMTP AUTH
|
||||
authentication: 'IMAPAuth',
|
||||
authenticationOptions: {
|
||||
host: 'smtp.gmail.com',
|
||||
port: 465,
|
||||
useSecureTransport: true,
|
||||
validHosts: ['gmail.com']
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
899
package-lock.json
generated
899
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@ -1,34 +1,41 @@
|
||||
{
|
||||
"name": "radius-server",
|
||||
"description": "radius server for google LDAP and TTLT",
|
||||
"description": "radius server for google LDAP and TTLS",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"debug": "DEBUG=radius:* ../node/node dist/app.js",
|
||||
"start": "../node/node dist/app.js",
|
||||
"build": "tsc",
|
||||
"dev": "ts-node src/app.ts",
|
||||
"test-ttls-pap": "tests/eapol_test -c tests/ttls-pap.conf -s testing123",
|
||||
"test": "mocha -r ts-node/register __tests__/**/*.test.ts",
|
||||
"test-ttls-pap": "__tests__/eapol_test -c __tests__/ttls-pap.conf -s testing123",
|
||||
"test-radtest": "radtest -x user pwd localhost 1812 testing123",
|
||||
"create-certificate": "sh ./ssl/create.sh && sh ./ssl/sign.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"imap-simple": "^4.3.0",
|
||||
"ldapauth-fork": "^4.3.1",
|
||||
"ldapjs": "^1.0.2",
|
||||
"md5": "^2.2.1",
|
||||
"native-duplexpair": "^1.0.0",
|
||||
"node-cache": "^5.1.0",
|
||||
"passport-ldapauth": "^2.1.3",
|
||||
"radius": "~1.1.4",
|
||||
"ts-node": "^8.6.2",
|
||||
"type-cacheable": "^4.0.0",
|
||||
"yargs": "~15.1.0"
|
||||
"yargs": "~15.1.0",
|
||||
"smtp-client": "^0.3.1"
|
||||
},
|
||||
"license": "GPLv3",
|
||||
"devDependencies": {
|
||||
"@types/ldapjs": "^1.0.5",
|
||||
"@types/radius": "0.0.28",
|
||||
"@hokify/eslint-config": "^0.2.4",
|
||||
"@types/chai": "^4.2.9",
|
||||
"@types/ldapjs": "^1.0.5",
|
||||
"@types/mocha": "^7.0.1",
|
||||
"@types/radius": "0.0.28",
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^6.8.0",
|
||||
"mocha": "^7.0.1",
|
||||
"prettier": "^1.19.1",
|
||||
"typescript": "^3.8.2"
|
||||
}
|
||||
|
30
src/app.ts
30
src/app.ts
@ -1,24 +1,32 @@
|
||||
import { GoogleLDAPAuth } from './auth/google-ldap';
|
||||
import { UDPServer } from './server/UDPServer';
|
||||
import { RadiusService } from './radius/RadiusService';
|
||||
|
||||
import * as config from '../config';
|
||||
import { Authentication } from './auth';
|
||||
import { IAuthentication } from './types/Authentication';
|
||||
|
||||
console.log(`Listener Port: ${config.port || 1812}`);
|
||||
console.log(`RADIUS Secret: ${config.secret}`);
|
||||
console.log(`Auth Mode: ${config.authentication}`);
|
||||
|
||||
// const ldap = new LDAPAuth({url: 'ldap://ldap.google.com', base: 'dc=hokify,dc=com', uid: 'uid', tlsOptions});
|
||||
|
||||
const ldap = new GoogleLDAPAuth(
|
||||
config.authenticationOptions.url,
|
||||
config.authenticationOptions.base
|
||||
);
|
||||
|
||||
const server = new UDPServer(config.port);
|
||||
const radiusService = new RadiusService(config.secret, ldap);
|
||||
|
||||
(async () => {
|
||||
/* configure auth mechansim */
|
||||
let auth: IAuthentication;
|
||||
try {
|
||||
const AuthMechanismus = (await import(`./auth/${config.authentication}`))[
|
||||
config.authentication
|
||||
];
|
||||
auth = new AuthMechanismus(config.authenticationOptions);
|
||||
} catch (err) {
|
||||
console.error('cannot load auth mechanismus', config.authentication);
|
||||
throw err;
|
||||
}
|
||||
// start radius server
|
||||
const authentication = new Authentication(auth);
|
||||
|
||||
const server = new UDPServer(config.port);
|
||||
const radiusService = new RadiusService(config.secret, authentication);
|
||||
|
||||
server.on('message', async (msg, rinfo) => {
|
||||
const response = await radiusService.handleMessage(msg);
|
||||
|
||||
|
25
src/auth.ts
Normal file
25
src/auth.ts
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -1,26 +1,45 @@
|
||||
import * as NodeCache from 'node-cache';
|
||||
|
||||
import { Client, createClient } from 'ldapjs';
|
||||
import debug from 'debug';
|
||||
import * as tls from 'tls';
|
||||
import { IAuthentication } from '../types/Authentication';
|
||||
|
||||
const usernameFields = ['posixUid', 'mail'];
|
||||
|
||||
const log = debug('radius:auth:ldap');
|
||||
const log = debug('radius:auth:google-ldap');
|
||||
// TLS:
|
||||
// https://github.com/ldapjs/node-ldapjs/issues/307
|
||||
|
||||
interface IGoogleLDAPAuthOptions {
|
||||
/** 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')
|
||||
} */
|
||||
tlsOptions: tls.TlsOptions;
|
||||
}
|
||||
|
||||
export class GoogleLDAPAuth implements IAuthentication {
|
||||
cache = new NodeCache();
|
||||
private ldap: Client;
|
||||
|
||||
ldap: Client;
|
||||
private lastDNsFetch: Date;
|
||||
|
||||
lastDNsFetch: Date;
|
||||
private allValidDNsCache: { [key: string]: string };
|
||||
|
||||
allValidDNsCache: { [key: string]: string };
|
||||
private base: string;
|
||||
|
||||
constructor(private url: string, private base: string, tlsOptions?) {
|
||||
this.ldap = createClient({ url, tlsOptions }).on('error', error => {
|
||||
constructor(config: IGoogleLDAPAuthOptions) {
|
||||
this.base = config.base;
|
||||
|
||||
this.ldap = createClient({
|
||||
url: 'ldaps://ldap.google.com:636',
|
||||
tlsOptions: {
|
||||
...config.tlsOptions,
|
||||
servername: 'ldap.google.com'
|
||||
}
|
||||
}).on('error', error => {
|
||||
console.error('Error in ldap', error);
|
||||
});
|
||||
|
||||
@ -74,12 +93,6 @@ export class GoogleLDAPAuth implements IAuthentication {
|
||||
}
|
||||
|
||||
async authenticate(username: string, password: string, count = 0, forceFetching = false) {
|
||||
const cacheKey = `usr:${username}|pwd:${password}`;
|
||||
const fromCache = this.cache.get(cacheKey);
|
||||
if (fromCache !== undefined) {
|
||||
return fromCache;
|
||||
}
|
||||
|
||||
const cacheValidTime = new Date();
|
||||
cacheValidTime.setHours(cacheValidTime.getHours() - 12);
|
||||
|
||||
@ -100,14 +113,14 @@ export class GoogleLDAPAuth implements IAuthentication {
|
||||
if (!dnsFetched && !forceFetching) {
|
||||
return this.authenticate(username, password, count, true);
|
||||
}
|
||||
console.error(`invalid username, not found in DN: ${username}`);
|
||||
console.error(`invalid username, not found in DN: ${username}`, this.allValidDNsCache);
|
||||
return false;
|
||||
}
|
||||
|
||||
const authResult: boolean = await new Promise((resolve, reject) => {
|
||||
this.ldap.bind(dn, password, (err, res) => {
|
||||
if (err) {
|
||||
if (err && (err as any).stack && (err as any).stack.includes(`${this.url} closed`)) {
|
||||
if (err && (err as any).stack && (err as any).stack.includes(`ldap.google.com closed`)) {
|
||||
count++;
|
||||
// wait 1 second to give the ldap error handler time to reconnect
|
||||
setTimeout(() => resolve(this.authenticate(dn, password)), 2000);
|
||||
@ -123,8 +136,6 @@ export class GoogleLDAPAuth implements IAuthentication {
|
||||
});
|
||||
});
|
||||
|
||||
this.cache.set(cacheKey, authResult, 86400);
|
||||
|
||||
return authResult;
|
||||
return !!authResult;
|
||||
}
|
||||
}
|
64
src/auth/IMAPAuth.ts
Normal file
64
src/auth/IMAPAuth.ts
Normal file
@ -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;
|
||||
}
|
||||
}
|
57
src/auth/LDAPAuth.ts
Normal file
57
src/auth/LDAPAuth.ts
Normal file
@ -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;
|
||||
}
|
||||
}
|
68
src/auth/SMTPAuth.ts
Normal file
68
src/auth/SMTPAuth.ts
Normal file
@ -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;
|
||||
}
|
||||
}
|
22
src/auth/StaticAuth.ts
Normal file
22
src/auth/StaticAuth.ts
Normal file
@ -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,3 +1,6 @@
|
||||
// https://tools.ietf.org/html/rfc5281 TTLS v0
|
||||
// https://tools.ietf.org/html/draft-funk-eap-ttls-v1-00 TTLS v1 (not implemented)
|
||||
|
||||
/* eslint-disable no-bitwise */
|
||||
import * as tls from 'tls';
|
||||
import * as NodeCache from 'node-cache';
|
||||
|
@ -38,7 +38,7 @@ export class UDPServer extends events.EventEmitter implements IServer {
|
||||
|
||||
// retry up to MAX_RETRIES to send this message,
|
||||
// we automatically retry if there is no confirmation (=any incoming message from client)
|
||||
// if expectAcknowledgment (e.g. Access-Accept or Access-Reject) is set, we do not retry
|
||||
// if expectAcknowledgment is false (e.g. Access-Accept or Access-Reject), we do not retry
|
||||
const identifierForRetry = `${address}:${port}`;
|
||||
if (expectAcknowledgment && retried < UDPServer.MAX_RETRIES) {
|
||||
this.timeout[identifierForRetry] = setTimeout(sendResponse, 600 * (retried + 1));
|
||||
|
@ -1,11 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"*.js"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true
|
||||
}
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["src/**/*.ts", "*.js", "*.ts", "__tests__/**/*.ts"],
|
||||
"exclude": ["node_modules"],
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user