11'use strict'
22
33import atna from 'atna-audit'
4- import basicAuth from 'basic-auth'
5- import crypto from 'crypto'
64import logger from 'winston'
75import os from 'os'
86
97import * as auditing from '../auditing'
108import * as authorisation from './authorisation'
11- import { UserModelAPI } from '../model/users '
12- import { caseInsensitiveRegex , logAndSetResponse } from '../utils'
9+ import passport from '../passport '
10+ import { logAndSetResponse } from '../utils'
1311import { config } from '../config'
1412import {
1513 BASIC_AUTH_TYPE ,
@@ -36,108 +34,26 @@ const auditingExemptPaths = [
3634 / \/ l o g s /
3735]
3836
39- const isUndefOrEmpty = string => string == null || string === ''
40-
4137async function authenticateBasic ( ctx ) {
42- const credentials = basicAuth ( ctx )
43- if ( credentials == null ) {
44- // No basic auth details found
45- return null
46- }
47- const { name : email , pass : password } = credentials
48- const user = await UserModelAPI . findOne ( {
49- email : caseInsensitiveRegex ( email )
50- } )
51- if ( user == null ) {
52- // not authenticated - user not found
53- ctx . throw (
54- 401 ,
55- `No user exists for ${ email } , denying access to API, request originated from ${ ctx . request . host } ` ,
56- { email}
57- )
58- }
38+ // Basic auth using middleware
39+ await passport . authenticate ( 'basic' , function ( err , user ) {
40+ if ( user ) {
41+ ctx . req . user = user
42+ ctx . body = 'User Authenticated Successfully'
43+ ctx . status = 200
44+ }
45+ } ) ( ctx , ( ) => { } )
5946
60- const hash = crypto . createHash ( user . passwordAlgorithm )
61- hash . update ( user . passwordSalt )
62- hash . update ( password )
63- if ( user . passwordHash !== hash . digest ( 'hex' ) ) {
64- // not authenticated - password mismatch
65- ctx . throw (
66- 401 ,
67- `Password did not match expected value, denying access to API, the request was made by ${ email } from ${ ctx . request . host } ` ,
68- { email}
69- )
70- }
71- return user
47+ return ctx . req . user || null
7248}
7349
50+ /**
51+ * @deprecated
52+ */
7453async function authenticateToken ( ctx ) {
75- const { header} = ctx . request
76- const email = header [ 'auth-username' ]
77- const authTS = header [ 'auth-ts' ]
78- const authSalt = header [ 'auth-salt' ]
79- const authToken = header [ 'auth-token' ]
54+ await passport . authenticate ( 'token' ) ( ctx , ( ) => { } )
8055
81- // if any of the required headers aren't present
82- if (
83- isUndefOrEmpty ( email ) ||
84- isUndefOrEmpty ( authTS ) ||
85- isUndefOrEmpty ( authSalt ) ||
86- isUndefOrEmpty ( authToken )
87- ) {
88- ctx . throw (
89- 401 ,
90- `API request made by ${ email } from ${ ctx . request . host } is missing required API authentication headers, denying access` ,
91- { email}
92- )
93- }
94-
95- // check if request is recent
96- const requestDate = new Date ( Date . parse ( authTS ) )
97-
98- const authWindowSeconds =
99- config . api . authWindowSeconds != null ? config . api . authWindowSeconds : 10
100- const to = new Date ( )
101- to . setSeconds ( to . getSeconds ( ) + authWindowSeconds )
102- const from = new Date ( )
103- from . setSeconds ( from . getSeconds ( ) - authWindowSeconds )
104-
105- if ( requestDate < from || requestDate > to ) {
106- // request expired
107- ctx . throw (
108- 401 ,
109- `API request made by ${ email } from ${ ctx . request . host } has expired, denying access` ,
110- { email}
111- )
112- }
113-
114- const user = await UserModelAPI . findOne ( {
115- email : caseInsensitiveRegex ( email )
116- } )
117- if ( user == null ) {
118- // not authenticated - user not found
119- ctx . throw (
120- 401 ,
121- `No user exists for ${ email } , denying access to API, request originated from ${ ctx . request . host } ` ,
122- { email}
123- )
124- }
125-
126- const hash = crypto . createHash ( 'sha512' )
127- hash . update ( user . passwordHash )
128- hash . update ( authSalt )
129- hash . update ( authTS )
130-
131- if ( authToken !== hash . digest ( 'hex' ) ) {
132- // not authenticated - token mismatch
133- ctx . throw (
134- 401 ,
135- `API token did not match expected value, denying access to API, the request was made by ${ email } from ${ ctx . request . host } ` ,
136- { email}
137- )
138- }
139-
140- return user
56+ return ctx . req . user || null
14157}
14258
14359function getEnabledAuthenticationTypesFromConfig ( config ) {
@@ -160,20 +76,26 @@ function getEnabledAuthenticationTypesFromConfig(config) {
16076 return [ ]
16177}
16278
163- function isAuthenticationTypeEnabled ( type ) {
79+ export function isAuthenticationTypeEnabled ( type ) {
16480 return getEnabledAuthenticationTypesFromConfig ( config ) . includes ( type )
16581}
16682
16783async function authenticateRequest ( ctx ) {
168- let user
169- // First attempt basic authentication if enabled
170- if ( user == null && isAuthenticationTypeEnabled ( 'basic' ) ) {
171- user = await authenticateBasic ( ctx )
84+ let user = null
85+
86+ // First attempt local authentication if enabled
87+ if ( ctx . req . user ) {
88+ user = ctx . req . user
17289 }
173- // Otherwise try token based authentication if enabled
174- if ( user == null && isAuthenticationTypeEnabled ( 'token' ) ) {
90+ // Otherwise try token based authentication if enabled (@deprecated)
91+ if ( user == null ) {
17592 user = await authenticateToken ( ctx )
17693 }
94+ // Otherwise try basic based authentication if enabled
95+ if ( user == null ) {
96+ // Basic auth using middleware
97+ user = await authenticateBasic ( ctx )
98+ }
17799 // User could not be authenticated
178100 if ( user == null ) {
179101 const enabledTypes =
@@ -195,9 +117,50 @@ function handleAuditResponse(err) {
195117}
196118
197119export async function authenticate ( ctx , next ) {
198- let user
199120 try {
200- user = await authenticateRequest ( ctx )
121+ // Authenticate Request either by basic or local or token
122+ const user = await authenticateRequest ( ctx )
123+
124+ if ( ctx . isAuthenticated ( ) ) {
125+ // Set the user on the context for consumption by other middleware
126+ ctx . authenticated = user
127+
128+ // Deal with paths exempt from audit
129+ if ( ctx . path === '/transactions' ) {
130+ if (
131+ ! ctx . query . filterRepresentation ||
132+ ctx . query . filterRepresentation !== 'full'
133+ ) {
134+ // exempt from auditing success
135+ return next ( )
136+ }
137+ } else {
138+ for ( const pathTest of auditingExemptPaths ) {
139+ if ( pathTest . test ( ctx . path ) ) {
140+ // exempt from auditing success
141+ return next ( )
142+ }
143+ }
144+ }
145+ // Send an auth success audit event
146+ let audit = atna . construct . userLoginAudit (
147+ atna . constants . OUTCOME_SUCCESS ,
148+ himSourceID ,
149+ os . hostname ( ) ,
150+ ctx . authenticated . email ,
151+ ctx . authenticated . groups . join ( ',' ) ,
152+ ctx . authenticated . groups . join ( ',' )
153+ )
154+ audit = atna . construct . wrapInSyslog ( audit )
155+ auditing . sendAuditEvent ( audit , handleAuditResponse )
156+
157+ return next ( )
158+ } else {
159+ ctx . throw (
160+ 401 ,
161+ `Denying access for an API request from ${ ctx . request . host } `
162+ )
163+ }
201164 } catch ( err ) {
202165 // Handle authentication errors
203166 if ( err . status === 401 ) {
@@ -210,7 +173,7 @@ export async function authenticate(ctx, next) {
210173 atna . constants . OUTCOME_SERIOUS_FAILURE ,
211174 himSourceID ,
212175 os . hostname ( ) ,
213- err . email
176+ `Unknown with ip ${ ctx . request . ip } `
214177 )
215178 audit = atna . construct . wrapInSyslog ( audit )
216179 auditing . sendAuditEvent ( audit , handleAuditResponse )
@@ -219,41 +182,6 @@ export async function authenticate(ctx, next) {
219182 // Rethrow other errors
220183 throw err
221184 }
222-
223- // Set the user on the context for consumption by other middleware
224- ctx . authenticated = user
225-
226- // Deal with paths exempt from audit
227- if ( ctx . path === '/transactions' ) {
228- if (
229- ! ctx . query . filterRepresentation ||
230- ctx . query . filterRepresentation !== 'full'
231- ) {
232- // exempt from auditing success
233- return next ( )
234- }
235- } else {
236- for ( const pathTest of auditingExemptPaths ) {
237- if ( pathTest . test ( ctx . path ) ) {
238- // exempt from auditing success
239- return next ( )
240- }
241- }
242- }
243-
244- // Send an auth success audit event
245- let audit = atna . construct . userLoginAudit (
246- atna . constants . OUTCOME_SUCCESS ,
247- himSourceID ,
248- os . hostname ( ) ,
249- user . email ,
250- user . groups . join ( ',' ) ,
251- user . groups . join ( ',' )
252- )
253- audit = atna . construct . wrapInSyslog ( audit )
254- auditing . sendAuditEvent ( audit , handleAuditResponse )
255-
256- return next ( )
257185}
258186
259187export async function getEnabledAuthenticationTypes ( ctx , next ) {
0 commit comments