import Component from '@ember/component' import { all as PromiseAll } from 'rsvp'; import { inject as service } from '@ember/service'; import { get, set, setProperties } from '@ember/object'; import layout from './template'; import $ from 'jquery'; import { isEmpty } from '@ember/utils'; import { computed } from '@ember/object'; import { isArray } from '@ember/array'; const USER = 'user'; const ADMIN = 'admin'; const RESTRICTED_ADMIN = 'restricted-admin'; const BASE = 'user-base'; const LOGIN_ACCESS_ROLES = [ { name: USER, translationKey: 'formGlobalRoles.mode.user.label', descriptionTranslationKey: 'formGlobalRoles.mode.user.detail', }, { name: ADMIN, translationKey: 'formGlobalRoles.mode.admin.label', descriptionTranslationKey: 'formGlobalRoles.mode.admin.detail', }, { name: RESTRICTED_ADMIN, translationKey: 'formGlobalRoles.mode.restrictedAdmin.label', descriptionTranslationKey: 'formGlobalRoles.mode.restrictedAdmin.detail', }, { name: BASE, translationKey: 'formGlobalRoles.mode.userBase.label', descriptionTranslationKey: 'formGlobalRoles.mode.userBase.detail', }, ]; export default Component.extend({ settings: service(), globalStore: service(), layout, user: null, groupPrincipalId: null, mode: null, allRoles: null, _boundSave: null, _boundValidator: null, init() { this._super(...arguments); set(this, 'allRoles', this.globalStore.all('globalRole')); this.initRoles(); this.initBindings(); }, actions: { toggle(e) { const $target = $(e.target); const $row = $target.closest('.input-group'); const check = $('input[type =checkbox]', $row)[0]; if ( check && e.target !== check && e.target.tagName !== 'LABEL' ) { check.click(); } } }, baseRoles: computed('allRoles.[]', function() { const { allRoles } = this; return [ADMIN, RESTRICTED_ADMIN, USER, BASE].map((r) => allRoles.findBy('id', r)).compact(); }), make(role) { const out = this.globalStore.createRecord({ type: 'globalRoleBinding', globalRoleId: get(role, 'id'), }); if (!isEmpty(this.user)) { set(out, 'userId', this.user.id); } if (!isEmpty(this.groupPrincipalId)) { set(out, 'groupPrincipalId', this.groupPrincipalId); } return out; }, save() { set(this, 'errors', null); const allRoles = [...this.baseRoles, ...this.additionalRoles, ...this.userRoles]; // all active non-existant roles remapped to an array of the role resources for the save const add = allRoles.filterBy('active').filterBy('existing', false).map((r) => this.make(r.role)); // all inactive existing roles remapped to an array of the role resources for the remove save const remove = allRoles.filterBy('active', false).filterBy('existing').map((r) => r.existing); return PromiseAll(add.map((x) => x.save())).then(() => { return PromiseAll(remove.map((x) => x.delete())).then(() => { return true; }); }); }, initBindings() { if (!isEmpty(this.setSave)) { set(this, '_boundSave', this.save.bind(this)); this.setSave(this._boundSave); } if (!isEmpty(this.setGRValidator)) { set(this, '_boundValidator', this.confirmUserCanLogIn.bind(this)); this.setGRValidator(this._boundValidator); } }, initBaseRoles() { set(this, 'baseRoles', this.baseRoles.map(this.roleMapper.bind(this))) }, initRoles() { setProperties(this, { baseRoles: this.baseRoles.map(this.roleMapper.bind(this)), additionalRoles: this.allRoles.filterBy('isHidden', false).filterBy('builtin').filter((role) => role.id !== 'restricted-admin').map(this.roleMapper.bind(this)), userRoles: this.allRoles.filterBy('isHidden', false).filterBy('builtin', false).map(this.roleMapper.bind(this)) }); if (this.mode === 'new') { this.populateDefaultRoles(); } }, roleMapper(role) { const { user, groupGlobalRoleBindings = [] } = this; const usersGlobalRoleBindings = []; let hasTranslation = true; if (!isEmpty(user)) { usersGlobalRoleBindings.pushObjects(user.globalRoleBindings.filterBy('groupPrincipalId', null)); } else { usersGlobalRoleBindings.pushObjects(groupGlobalRoleBindings.slice()); } const binding = usersGlobalRoleBindings.findBy('globalRole', role) || false; let translationKey = null; let descriptionTranslationKey = null; if ( this.baseRoles.findBy('id', role.id) ) { let roleMatch = LOGIN_ACCESS_ROLES.findBy('name', role.id); ({ translationKey, descriptionTranslationKey } = roleMatch); } else { hasTranslation = false; } return { role, translationKey, descriptionTranslationKey, hasTranslation, active: !!binding, existing: binding, } }, confirmUserCanLogIn() { const allRoles = [...this.baseRoles, ...this.additionalRoles, ...this.userRoles]; let roles = allRoles.filterBy('active').map((r) => r.role); let allRolesRules = []; roles.forEach((r) => { if (!isEmpty(r.rules)) { allRolesRules.pushObjects(r.rules); } }); let matchingRules = allRolesRules.filter((rule) => { if (this.isRuleValid(rule)) { return rule; } return false; }); if (isEmpty(matchingRules)) { return false; } return true; }, isRuleValid(rule) { if (( rule.resources || [] ).any(resourceValidator) && ( rule.apiGroups || [] ).any(apiGroupValidator) && verbsValidator(( rule.verbs || [] ))) { return true; } return false; function resourceValidator(resource) { const resourcesRequiredForLogin = ['*', 'preferences', 'settings', 'features']; // console.log(`resourceValidator status: `, resourcesRequiredForLogin.includes(resource), resource); return resourcesRequiredForLogin.includes(resource); } function apiGroupValidator(apiGroup) { const apiGroupsRequiredForLogin = ['*', 'management.cattle.io']; // console.log(`apiGroupsRequiredForLogin status: `, apiGroupsRequiredForLogin.includes(apiGroup), apiGroup); return apiGroupsRequiredForLogin.includes(apiGroup); } function verbsValidator(verbs) { const restrictedTarget = ['get', 'list', 'watch']; const verbsRequiredForLogin = ['*', ...restrictedTarget]; if (isArray(verbs) && verbs.length > 1) { // console.log(`verbsRequiredForLogin status 1: `, restrictedTarget.every((rt) => verbs.includes(rt)), verbs); return restrictedTarget.every((rt) => verbs.includes(rt)); } // console.log(`verbsRequiredForLogin status 2: `, verbsRequiredForLogin.includes(verbs), verbs); return verbsRequiredForLogin.includes(verbs.firstObject); } }, populateDefaultRoles() { [...this.baseRoles, ...this.additionalRoles, ...this.userRoles].forEach((r) => { if (!isEmpty(r.role)) { if (r.role.id === 'user' || r.role.newUserDefault) { set(r, 'active', true); } } }); } });