From 556621e60ead686af60505f0aa6234d039b0f361 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Tue, 17 Mar 2020 14:04:50 +0200 Subject: Meteor 1.8 only in use at Sandstorm. --- .sandstorm-meteor-1.8/ldap.js | 228 ++++++++++++++++++++++------------- .sandstorm-meteor-1.8/oidc_server.js | 116 ++++++++++-------- 2 files changed, 207 insertions(+), 137 deletions(-) diff --git a/.sandstorm-meteor-1.8/ldap.js b/.sandstorm-meteor-1.8/ldap.js index bd2cd1fb..3b963823 100644 --- a/.sandstorm-meteor-1.8/ldap.js +++ b/.sandstorm-meteor-1.8/ldap.js @@ -1,8 +1,7 @@ import ldapjs from 'ldapjs'; import util from 'util'; import Bunyan from 'bunyan'; -import {log_debug, log_info, log_warn, log_error} from './logger'; - +import { log_debug, log_info, log_warn, log_error } from './logger'; export default class LDAP { constructor() { @@ -11,35 +10,66 @@ export default class LDAP { this.connected = false; this.options = { - host : this.constructor.settings_get('LDAP_HOST'), - port : this.constructor.settings_get('LDAP_PORT'), - Reconnect : this.constructor.settings_get('LDAP_RECONNECT'), - timeout : this.constructor.settings_get('LDAP_TIMEOUT'), - connect_timeout : this.constructor.settings_get('LDAP_CONNECT_TIMEOUT'), - idle_timeout : this.constructor.settings_get('LDAP_IDLE_TIMEOUT'), - encryption : this.constructor.settings_get('LDAP_ENCRYPTION'), - ca_cert : this.constructor.settings_get('LDAP_CA_CERT'), - reject_unauthorized : this.constructor.settings_get('LDAP_REJECT_UNAUTHORIZED') || false, - Authentication : this.constructor.settings_get('LDAP_AUTHENTIFICATION'), - Authentication_UserDN : this.constructor.settings_get('LDAP_AUTHENTIFICATION_USERDN'), - Authentication_Password : this.constructor.settings_get('LDAP_AUTHENTIFICATION_PASSWORD'), - Authentication_Fallback : this.constructor.settings_get('LDAP_LOGIN_FALLBACK'), - BaseDN : this.constructor.settings_get('LDAP_BASEDN'), - Internal_Log_Level : this.constructor.settings_get('INTERNAL_LOG_LEVEL'), - User_Authentication : this.constructor.settings_get('LDAP_USER_AUTHENTICATION'), - User_Authentication_Field : this.constructor.settings_get('LDAP_USER_AUTHENTICATION_FIELD'), - User_Attributes : this.constructor.settings_get('LDAP_USER_ATTRIBUTES'), - User_Search_Filter : this.constructor.settings_get('LDAP_USER_SEARCH_FILTER'), - User_Search_Scope : this.constructor.settings_get('LDAP_USER_SEARCH_SCOPE'), - User_Search_Field : this.constructor.settings_get('LDAP_USER_SEARCH_FIELD'), - Search_Page_Size : this.constructor.settings_get('LDAP_SEARCH_PAGE_SIZE'), - Search_Size_Limit : this.constructor.settings_get('LDAP_SEARCH_SIZE_LIMIT'), - group_filter_enabled : this.constructor.settings_get('LDAP_GROUP_FILTER_ENABLE'), - group_filter_object_class : this.constructor.settings_get('LDAP_GROUP_FILTER_OBJECTCLASS'), - group_filter_group_id_attribute : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE'), - group_filter_group_member_attribute: this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE'), - group_filter_group_member_format : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT'), - group_filter_group_name : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_NAME'), + host: this.constructor.settings_get('LDAP_HOST'), + port: this.constructor.settings_get('LDAP_PORT'), + Reconnect: this.constructor.settings_get('LDAP_RECONNECT'), + timeout: this.constructor.settings_get('LDAP_TIMEOUT'), + connect_timeout: this.constructor.settings_get('LDAP_CONNECT_TIMEOUT'), + idle_timeout: this.constructor.settings_get('LDAP_IDLE_TIMEOUT'), + encryption: this.constructor.settings_get('LDAP_ENCRYPTION'), + ca_cert: this.constructor.settings_get('LDAP_CA_CERT'), + reject_unauthorized: + this.constructor.settings_get('LDAP_REJECT_UNAUTHORIZED') || false, + Authentication: this.constructor.settings_get('LDAP_AUTHENTIFICATION'), + Authentication_UserDN: this.constructor.settings_get( + 'LDAP_AUTHENTIFICATION_USERDN', + ), + Authentication_Password: this.constructor.settings_get( + 'LDAP_AUTHENTIFICATION_PASSWORD', + ), + Authentication_Fallback: this.constructor.settings_get( + 'LDAP_LOGIN_FALLBACK', + ), + BaseDN: this.constructor.settings_get('LDAP_BASEDN'), + Internal_Log_Level: this.constructor.settings_get('INTERNAL_LOG_LEVEL'), + User_Authentication: this.constructor.settings_get( + 'LDAP_USER_AUTHENTICATION', + ), + User_Authentication_Field: this.constructor.settings_get( + 'LDAP_USER_AUTHENTICATION_FIELD', + ), + User_Attributes: this.constructor.settings_get('LDAP_USER_ATTRIBUTES'), + User_Search_Filter: this.constructor.settings_get( + 'LDAP_USER_SEARCH_FILTER', + ), + User_Search_Scope: this.constructor.settings_get( + 'LDAP_USER_SEARCH_SCOPE', + ), + User_Search_Field: this.constructor.settings_get( + 'LDAP_USER_SEARCH_FIELD', + ), + Search_Page_Size: this.constructor.settings_get('LDAP_SEARCH_PAGE_SIZE'), + Search_Size_Limit: this.constructor.settings_get( + 'LDAP_SEARCH_SIZE_LIMIT', + ), + group_filter_enabled: this.constructor.settings_get( + 'LDAP_GROUP_FILTER_ENABLE', + ), + group_filter_object_class: this.constructor.settings_get( + 'LDAP_GROUP_FILTER_OBJECTCLASS', + ), + group_filter_group_id_attribute: this.constructor.settings_get( + 'LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE', + ), + group_filter_group_member_attribute: this.constructor.settings_get( + 'LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE', + ), + group_filter_group_member_format: this.constructor.settings_get( + 'LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT', + ), + group_filter_group_name: this.constructor.settings_get( + 'LDAP_GROUP_FILTER_GROUP_NAME', + ), }; } @@ -58,14 +88,13 @@ export default class LDAP { } connectSync(...args) { - if (!this._connectSync) { + if (!this._connectSync) { this._connectSync = Meteor.wrapAsync(this.connectAsync, this); } return this._connectSync(...args); } searchAllSync(...args) { - if (!this._searchAllSync) { this._searchAllSync = Meteor.wrapAsync(this.searchAllAsync, this); } @@ -78,19 +107,19 @@ export default class LDAP { let replied = false; const connectionOptions = { - url : `${this.options.host}:${this.options.port}`, - timeout : this.options.timeout, + url: `${this.options.host}:${this.options.port}`, + timeout: this.options.timeout, connectTimeout: this.options.connect_timeout, - idleTimeout : this.options.idle_timeout, - reconnect : this.options.Reconnect, + idleTimeout: this.options.idle_timeout, + reconnect: this.options.Reconnect, }; if (this.options.Internal_Log_Level !== 'disabled') { connectionOptions.log = new Bunyan({ - name : 'ldapjs', + name: 'ldapjs', component: 'client', - stream : process.stderr, - level : this.options.Internal_Log_Level, + stream: process.stderr, + level: this.options.Internal_Log_Level, }); } @@ -100,10 +129,12 @@ export default class LDAP { if (this.options.ca_cert && this.options.ca_cert !== '') { // Split CA cert into array of strings - const chainLines = this.constructor.settings_get('LDAP_CA_CERT').split('\n'); - let cert = []; - const ca = []; - chainLines.forEach((line) => { + const chainLines = this.constructor + .settings_get('LDAP_CA_CERT') + .split('\n'); + let cert = []; + const ca = []; + chainLines.forEach(line => { cert.push(line); if (line.match(/-END CERTIFICATE-/)) { ca.push(cert.join('\n')); @@ -114,7 +145,7 @@ export default class LDAP { } if (this.options.encryption === 'ssl') { - connectionOptions.url = `ldaps://${connectionOptions.url}`; + connectionOptions.url = `ldaps://${connectionOptions.url}`; connectionOptions.tlsOptions = tlsOptions; } else { connectionOptions.url = `ldap://${connectionOptions.url}`; @@ -127,7 +158,7 @@ export default class LDAP { this.bindSync = Meteor.wrapAsync(this.client.bind, this.client); - this.client.on('error', (error) => { + this.client.on('error', error => { log_error('connection', error); if (replied === false) { replied = true; @@ -171,7 +202,7 @@ export default class LDAP { } }); } else { - this.client.on('connect', (response) => { + this.client.on('connect', response => { log_info('LDAP connected'); this.connected = true; if (replied === false) { @@ -201,7 +232,9 @@ export default class LDAP { } } - const usernameFilter = this.options.User_Search_Field.split(',').map((item) => `(${item}=${username})`); + const usernameFilter = this.options.User_Search_Field.split(',').map( + item => `(${item}=${username})`, + ); if (usernameFilter.length === 0) { log_error('LDAP_LDAP_User_Search_Field not defined'); @@ -215,7 +248,6 @@ export default class LDAP { } bindUserIfNecessary(username, password) { - if (this.domainBinded === true) { return; } @@ -224,7 +256,6 @@ export default class LDAP { return; } - if (!this.options.BaseDN) throw new Error('BaseDN is not provided'); const userDn = `${this.options.User_Authentication_Field}=${username},${this.options.BaseDN}`; @@ -244,23 +275,27 @@ export default class LDAP { log_info('Binding UserDN', this.options.Authentication_UserDN); - this.bindSync(this.options.Authentication_UserDN, this.options.Authentication_Password); + this.bindSync( + this.options.Authentication_UserDN, + this.options.Authentication_Password, + ); this.domainBinded = true; } searchUsersSync(username, page) { this.bindIfNecessary(); const searchOptions = { - filter : this.getUserFilter(username), - scope : this.options.User_Search_Scope || 'sub', + filter: this.getUserFilter(username), + scope: this.options.User_Search_Scope || 'sub', sizeLimit: this.options.Search_Size_Limit, }; - if (!!this.options.User_Attributes) searchOptions.attributes = this.options.User_Attributes.split(','); + if (!!this.options.User_Attributes) + searchOptions.attributes = this.options.User_Attributes.split(','); if (this.options.Search_Page_Size > 0) { searchOptions.paged = { - pageSize : this.options.Search_Page_Size, + pageSize: this.options.Search_Page_Size, pagePause: !!page, }; } @@ -279,7 +314,9 @@ export default class LDAP { getUserByIdSync(id, attribute) { this.bindIfNecessary(); - const Unique_Identifier_Field = this.constructor.settings_get('LDAP_UNIQUE_IDENTIFIER_FIELD').split(','); + const Unique_Identifier_Field = this.constructor + .settings_get('LDAP_UNIQUE_IDENTIFIER_FIELD') + .split(','); let filter; @@ -290,11 +327,13 @@ export default class LDAP { }); } else { const filters = []; - Unique_Identifier_Field.forEach((item) => { - filters.push(new this.ldapjs.filters.EqualityFilter({ - attribute: item, - value : new Buffer(id, 'hex'), - })); + Unique_Identifier_Field.forEach(item => { + filters.push( + new this.ldapjs.filters.EqualityFilter({ + attribute: item, + value: new Buffer(id, 'hex'), + }), + ); }); filter = new this.ldapjs.filters.OrFilter({ filters }); @@ -327,7 +366,7 @@ export default class LDAP { const searchOptions = { filter: this.getUserFilter(username), - scope : this.options.User_Search_Scope || 'sub', + scope: this.options.User_Search_Scope || 'sub', }; log_info('Searching user', username); @@ -341,7 +380,13 @@ export default class LDAP { } if (result.length > 1) { - log_error('Search by username', username, 'returned', result.length, 'records'); + log_error( + 'Search by username', + username, + 'returned', + result.length, + 'records', + ); } return result[0]; @@ -359,9 +404,12 @@ export default class LDAP { } if (this.options.group_filter_group_member_attribute !== '') { - const format_value = ldapUser[this.options.group_filter_group_member_format]; + const format_value = + ldapUser[this.options.group_filter_group_member_format]; if (format_value) { - filter.push(`(${this.options.group_filter_group_member_attribute}=${format_value})`); + filter.push( + `(${this.options.group_filter_group_member_attribute}=${format_value})`, + ); } } @@ -369,7 +417,7 @@ export default class LDAP { const searchOptions = { filter: filter.join('').replace(/#{username}/g, username), - scope : 'sub', + scope: 'sub', }; log_debug('Group list filter LDAP:', searchOptions.filter); @@ -381,13 +429,12 @@ export default class LDAP { } const grp_identifier = this.options.group_filter_group_id_attribute || 'cn'; - const groups = []; - result.map((item) => { + const groups = []; + result.map(item => { groups.push(item[grp_identifier]); }); log_debug(`Groups: ${groups.join(', ')}`); return groups; - } isUserInGroup(username, ldapUser) { @@ -404,20 +451,25 @@ export default class LDAP { } if (this.options.group_filter_group_member_attribute !== '') { - const format_value = ldapUser[this.options.group_filter_group_member_format]; + const format_value = + ldapUser[this.options.group_filter_group_member_format]; if (format_value) { - filter.push(`(${this.options.group_filter_group_member_attribute}=${format_value})`); + filter.push( + `(${this.options.group_filter_group_member_attribute}=${format_value})`, + ); } } if (this.options.group_filter_group_id_attribute !== '') { - filter.push(`(${this.options.group_filter_group_id_attribute}=${this.options.group_filter_group_name})`); + filter.push( + `(${this.options.group_filter_group_id_attribute}=${this.options.group_filter_group_name})`, + ); } filter.push(')'); const searchOptions = { filter: filter.join('').replace(/#{username}/g, username), - scope : 'sub', + scope: 'sub', }; log_debug('Group filter LDAP:', searchOptions.filter); @@ -435,7 +487,7 @@ export default class LDAP { _raw: entry.raw, }; - Object.keys(values._raw).forEach((key) => { + Object.keys(values._raw).forEach(key => { const value = values._raw[key]; if (!['thumbnailPhoto', 'jpegPhoto'].includes(key)) { @@ -458,11 +510,12 @@ export default class LDAP { // Force LDAP idle to wait the record processing this.client._updateIdle(true); page(null, entries, { - end, next: () => { + end, + next: () => { // Reset idle timer this.client._updateIdle(); next && next(); - } + }, }); }; @@ -473,7 +526,7 @@ export default class LDAP { return; } - res.on('error', (error) => { + res.on('error', error => { log_error(error); page(error); return; @@ -481,16 +534,19 @@ export default class LDAP { let entries = []; - const internalPageSize = options.paged && options.paged.pageSize > 0 ? options.paged.pageSize * 2 : 500; + const internalPageSize = + options.paged && options.paged.pageSize > 0 + ? options.paged.pageSize * 2 + : 500; - res.on('searchEntry', (entry) => { + res.on('searchEntry', entry => { entries.push(this.extractLdapEntryData(entry)); if (entries.length >= internalPageSize) { processPage({ entries, title: 'Internal Page', - end : false, + end: false, }); entries = []; } @@ -502,14 +558,14 @@ export default class LDAP { processPage({ entries, title: 'Final Page', - end : true, + end: true, }); } else if (entries.length) { log_info('Page'); processPage({ entries, title: 'Page', - end : false, + end: false, next, }); entries = []; @@ -521,7 +577,7 @@ export default class LDAP { processPage({ entries, title: 'Final Page', - end : true, + end: true, }); entries = []; } @@ -539,7 +595,7 @@ export default class LDAP { return; } - res.on('error', (error) => { + res.on('error', error => { log_error(error); callback(error); return; @@ -547,7 +603,7 @@ export default class LDAP { const entries = []; - res.on('searchEntry', (entry) => { + res.on('searchEntry', entry => { entries.push(this.extractLdapEntryData(entry)); }); @@ -576,7 +632,7 @@ export default class LDAP { } disconnect() { - this.connected = false; + this.connected = false; this.domainBinded = false; log_info('Disconecting'); this.client.unbind(); diff --git a/.sandstorm-meteor-1.8/oidc_server.js b/.sandstorm-meteor-1.8/oidc_server.js index 326238da..91b0e8a4 100644 --- a/.sandstorm-meteor-1.8/oidc_server.js +++ b/.sandstorm-meteor-1.8/oidc_server.js @@ -1,13 +1,12 @@ Oidc = {}; -OAuth.registerService('oidc', 2, null, function (query) { - +OAuth.registerService('oidc', 2, null, function(query) { var debug = process.env.DEBUG || false; var token = getToken(query); if (debug) console.log('XXX: register token:', token); var accessToken = token.access_token || token.id_token; - var expiresAt = (+new Date) + (1000 * parseInt(token.expires_in, 10)); + var expiresAt = +new Date() + 1000 * parseInt(token.expires_in, 10); var userinfo = getUserInfo(accessToken); if (debug) console.log('XXX: userinfo:', userinfo); @@ -22,12 +21,14 @@ OAuth.registerService('oidc', 2, null, function (query) { if (accessToken) { var tokenContent = getTokenContent(accessToken); - var fields = _.pick(tokenContent, getConfiguration().idTokenWhitelistFields); + var fields = _.pick( + tokenContent, + getConfiguration().idTokenWhitelistFields, + ); _.extend(serviceData, fields); } - if (token.refresh_token) - serviceData.refreshToken = token.refresh_token; + if (token.refresh_token) serviceData.refreshToken = token.refresh_token; if (debug) console.log('XXX: serviceData:', serviceData); var profile = {}; @@ -37,88 +38,101 @@ OAuth.registerService('oidc', 2, null, function (query) { return { serviceData: serviceData, - options: { profile: profile } + options: { profile: profile }, }; }); -var userAgent = "Meteor"; +var userAgent = 'Meteor'; if (Meteor.release) { - userAgent += "/" + Meteor.release; + userAgent += '/' + Meteor.release; } -var getToken = function (query) { +var getToken = function(query) { var debug = process.env.DEBUG || false; var config = getConfiguration(); - if(config.tokenEndpoint.includes('https://')){ + if (config.tokenEndpoint.includes('https://')) { var serverTokenEndpoint = config.tokenEndpoint; - }else{ + } else { var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint; } var requestPermissions = config.requestPermissions; var response; try { - response = HTTP.post( - serverTokenEndpoint, - { - headers: { - Accept: 'application/json', - "User-Agent": userAgent - }, - params: { - code: query.code, - client_id: config.clientId, - client_secret: OAuth.openSecret(config.secret), - redirect_uri: OAuth._redirectUri('oidc', config), - grant_type: 'authorization_code', - scope: requestPermissions, - state: query.state - } - } - ); + response = HTTP.post(serverTokenEndpoint, { + headers: { + Accept: 'application/json', + 'User-Agent': userAgent, + }, + params: { + code: query.code, + client_id: config.clientId, + client_secret: OAuth.openSecret(config.secret), + redirect_uri: OAuth._redirectUri('oidc', config), + grant_type: 'authorization_code', + scope: requestPermissions, + state: query.state, + }, + }); } catch (err) { - throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message), - { response: err.response }); + throw _.extend( + new Error( + 'Failed to get token from OIDC ' + + serverTokenEndpoint + + ': ' + + err.message, + ), + { response: err.response }, + ); } if (response.data.error) { // if the http response was a json object with an error attribute - throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + response.data.error); + throw new Error( + 'Failed to complete handshake with OIDC ' + + serverTokenEndpoint + + ': ' + + response.data.error, + ); } else { if (debug) console.log('XXX: getToken response: ', response.data); return response.data; } }; -var getUserInfo = function (accessToken) { +var getUserInfo = function(accessToken) { var debug = process.env.DEBUG || false; var config = getConfiguration(); // Some userinfo endpoints use a different base URL than the authorization or token endpoints. // This logic allows the end user to override the setting by providing the full URL to userinfo in their config. - if (config.userinfoEndpoint.includes("https://")) { + if (config.userinfoEndpoint.includes('https://')) { var serverUserinfoEndpoint = config.userinfoEndpoint; } else { var serverUserinfoEndpoint = config.serverUrl + config.userinfoEndpoint; } var response; try { - response = HTTP.get( - serverUserinfoEndpoint, - { - headers: { - "User-Agent": userAgent, - "Authorization": "Bearer " + accessToken - } - } - ); + response = HTTP.get(serverUserinfoEndpoint, { + headers: { + 'User-Agent': userAgent, + Authorization: 'Bearer ' + accessToken, + }, + }); } catch (err) { - throw _.extend(new Error("Failed to fetch userinfo from OIDC " + serverUserinfoEndpoint + ": " + err.message), - {response: err.response}); + throw _.extend( + new Error( + 'Failed to fetch userinfo from OIDC ' + + serverUserinfoEndpoint + + ': ' + + err.message, + ), + { response: err.response }, + ); } if (debug) console.log('XXX: getUserInfo response: ', response.data); return response.data; }; -var getConfiguration = function () { +var getConfiguration = function() { var config = ServiceConfiguration.configurations.findOne({ service: 'oidc' }); if (!config) { throw new ServiceConfiguration.ConfigError('Service oidc not configured.'); @@ -126,7 +140,7 @@ var getConfiguration = function () { return config; }; -var getTokenContent = function (token) { +var getTokenContent = function(token) { var content = null; if (token) { try { @@ -137,13 +151,13 @@ var getTokenContent = function (token) { var signed = parts[0] + '.' + parts[1]; } catch (err) { this.content = { - exp: 0 + exp: 0, }; } } return content; -} +}; -Oidc.retrieveCredential = function (credentialToken, credentialSecret) { +Oidc.retrieveCredential = function(credentialToken, credentialSecret) { return OAuth.retrieveCredential(credentialToken, credentialSecret); }; -- cgit v1.2.3-1-g7c22