From b26519ea88d53950c136b28b262ce14817499b99 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Thu, 15 Apr 2021 10:56:11 -0500 Subject: [PATCH] Make sudo access managed via IAM rather than group checkmark --- .../app/resource/auth/Group.resource.js | 4 +-- app/ldap/controllers/Sudo.controller.js | 30 ++++++++++++++----- app/models/auth/User.model.js | 19 +++++++++--- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/app/assets/app/resource/auth/Group.resource.js b/app/assets/app/resource/auth/Group.resource.js index 5a76397..2ed87e1 100644 --- a/app/assets/app/resource/auth/Group.resource.js +++ b/app/assets/app/resource/auth/Group.resource.js @@ -62,7 +62,7 @@ class GroupResource extends CRUDBase { required: true, type: 'text', }, - { + /*{ name: 'Superuser equivalent?', field: 'grants_sudo', type: 'select', @@ -70,7 +70,7 @@ class GroupResource extends CRUDBase { {display: 'Yes', value: true}, {display: 'No', value: false}, ], - }, + },*/ { name: 'Users', field: 'user_ids', diff --git a/app/ldap/controllers/Sudo.controller.js b/app/ldap/controllers/Sudo.controller.js index 4f50c11..2d507e6 100644 --- a/app/ldap/controllers/Sudo.controller.js +++ b/app/ldap/controllers/Sudo.controller.js @@ -26,6 +26,7 @@ class SudoController extends LDAPController { return next(new LDAP.InsufficientAccessRightsError()) } + const iam_targets = this.parse_iam_targets(req.filter) if ( req.scope === 'base' ) { // If scope is base, check if the base DN matches the filter. // If so, return it. Else, return empty. @@ -34,12 +35,12 @@ class SudoController extends LDAPController { const user = await this.get_resource_from_dn(req.dn) // Make sure the user is ldap visible && match the filter - if ( user && user.ldap_visible && req.filter.matches(await user.to_sudo()) ) { + if ( user && user.ldap_visible && req.filter.matches(await user.to_sudo(iam_targets)) ) { // If so, send the object res.send({ dn: user.sudo_dn.format(this.configs.get('ldap:server.format')), - attributes: await user.to_sudo(), + attributes: await user.to_sudo(iam_targets), }) } } else if ( req.scope === 'one' ) { @@ -55,12 +56,12 @@ class SudoController extends LDAPController { if ( req.dn.equals(user.sudo_dn) || user.sudo_dn.parent().equals(req.dn) ) { // Check if the filter matches - if ( req.filter.matches(await user.to_sudo()) ) { + if ( req.filter.matches(await user.to_sudo(iam_targets)) ) { // If so, send the object res.send({ dn: user.sudo_dn.format(this.configs.get('ldap:server.format')), - attributes: await user.to_sudo(), + attributes: await user.to_sudo(iam_targets), }) } } @@ -79,12 +80,12 @@ class SudoController extends LDAPController { if ( req.dn.equals(user.sudo_dn) || req.dn.parentOf(user.sudo_dn) ) { // Check if filter matches - if ( req.filter.matches(await user.to_sudo()) ) { + if ( req.filter.matches(await user.to_sudo(iam_targets)) ) { // If so, send the object res.send({ dn: user.sudo_dn.format(this.configs.get('ldap:server.format')), - attributes: await user.to_sudo(), + attributes: await user.to_sudo(iam_targets), }) } } @@ -98,6 +99,20 @@ class SudoController extends LDAPController { return next() } + parse_iam_targets(filter, target_ids = []) { + if ( Array.isArray(filter?.filters) ) { + for ( const sub_filter of filter.filters ) { + target_ids = [...target_ids, ...this.parse_iam_targets(sub_filter)] + } + } else if ( filter?.attribute ) { + if ( filter.attribute === 'iamtarget' ) { + target_ids.push(filter.value) + } + } + + return target_ids + } + get_cn_from_dn(dn) { try { if ( typeof dn === 'string' ) dn = LDAP.parseDN(dn) @@ -108,8 +123,7 @@ class SudoController extends LDAPController { async get_resource_from_dn(sudo_dn) { const cn = this.get_cn_from_dn(sudo_dn) if ( cn ) { - const user = this.User.findOne({uid: cn.substr(5), ldap_visible: true}) - if ( user && (await user.has_sudo()) ) return user + return this.User.findOne({uid: cn.substr(5), ldap_visible: true}) } } } diff --git a/app/models/auth/User.model.js b/app/models/auth/User.model.js index eecaf8a..cf55acd 100644 --- a/app/models/auth/User.model.js +++ b/app/models/auth/User.model.js @@ -207,14 +207,25 @@ class User extends AuthUser { return groups.some(group => group.grants_sudo) } - async to_sudo() { + async to_sudo(iam_targets = []) { + const Policy = this.models.get('iam:Policy') + const granted = [] + for ( const target of iam_targets ) { + if ( await Policy.check_user_access(this, target, 'sudo') ) { + granted.push(target) + } + } + return { objectClass: ['sudoRole'], cn: `sudo_${this.uid.toLowerCase()}`, sudoUser: this.uid.toLowerCase(), - sudoHost: 'ALL', - sudoRunAs: 'ALL', - sudoCommand: 'ALL', + ...(granted.length ? { + iamtarget: granted, + sudoHost: 'ALL', + sudoRunAs: 'ALL', + sudoCommand: 'ALL', + } : {}) } }