From 9dfdef991b37895eb0e3fff7c2baa1498141f9c9 Mon Sep 17 00:00:00 2001 From: Nancy Butler <42977925+mantis-toboggan-md@users.noreply.github.com> Date: Wed, 30 Dec 2020 12:21:41 -0700 Subject: [PATCH 01/18] openldap saml linting, button cb errors --- assets/translations/en-us.yaml | 113 +++++++++--- config/product/auth.js | 8 + edit/auth/ldap/config.vue | 192 ++++++++++++++++++++ edit/auth/ldap/index.vue | 226 ++++++++++++++++++++++++ edit/auth/saml.vue | 271 +++++++++++++++++++++++++++++ plugins/steve/resource-instance.js | 1 - 6 files changed, 782 insertions(+), 29 deletions(-) create mode 100644 edit/auth/ldap/config.vue create mode 100644 edit/auth/ldap/index.vue create mode 100644 edit/auth/saml.vue diff --git a/assets/translations/en-us.yaml b/assets/translations/en-us.yaml index 14ec509802..996783f8e8 100644 --- a/assets/translations/en-us.yaml +++ b/assets/translations/en-us.yaml @@ -103,49 +103,106 @@ suffix: ############################## authConfig: accessMode: - label: Configure who should be able to login and use {vendor} - unrestricted: "Allow any valid user" - restricted: "Allow members of clusters and projects, plus authorized users & groups" - required: "Restrict access to only the auhorized users & groups" + label: 'Configure who should be able to login and use {vendor}' + required: Restrict access to only the auhorized users & groups + restricted: 'Allow members of clusters and projects, plus authorized users & groups' + unrestricted: Allow any valid user allowedPrincipalIds: title: Authorized Users & Groups - associatedWarning: "Note: The {provider} user you authenticate as will be associated as an alternate way to login to the {vendor} user you are currently logged in as ({username})." - stateBanner: - enabled: "{provider} is currently enabled." - disabled: "{provider} is currently disabled." + associatedWarning: 'Note: The {provider} user you authenticate as will be associated as an alternate way to login to the {vendor} user you are currently logged in as ({username}).' github: - target: - label: Which version of GitHub do you want to use? - public: Public GitHub.com - private: A private installation of GitHub Enterprise - host: - label: GitHub Enterprise Host - placeholder: e.g. github.mycompany.example clientId: label: Client ID clientSecret: label: Client Secret form: + app: + label: Application name + value: 'Anything you like, e.g. My {vendor}' + calllback: + label: Authorization callback URL + description: + label: Description + value: 'Optional, can be left blank' + homepage: + label: Homepage URL + instruction: 'Fill in the form with these values:' prefix: |-
  • Click here to go to GitHub application settings in a new window.
  • Click on the "OAuth Apps" tab.
  • Click the "New OAuth App" button.
  • - instruction: "Fill in the form with these values:" - app: - label: Application name - value: Anything you like, e.g. My {vendor} - homepage: - label: Homepage URL - description: - label: Description - value: Optional, can be left blank - calllback: - label: Authorization callback URL suffix: |-
  • Click "Register application"
  • Copy and paste the Client ID and Client Secret of your newly created OAuth app into the fields below
  • - - + host: + label: GitHub Enterprise Host + placeholder: e.g. github.mycompany.example + target: + label: Which version of GitHub do you want to use? + private: A private installation of GitHub Enterprise + public: Public GitHub.com + ldap: + freeipa: Configure a FreeIPA server + activedirectory: Configure an Active Directory account + openldap: Configure an OpenLDAP server + cert: Certificate + disabledStatusBitmask: Disabled Status Bitmask + groupDNAttribute: Group DN Attribute + groupMemberMappingAttribute: Group Member Mapping Attribute + groupMemberUserAttribute: Group Member User Attribute + groupSearchBase: + label: Group Search Base + placeholder: 'ou=groups,dc=mycompany,dc=com' + hostname: Hostname/IP + loginAttribute: Login Attribute + nameAttribute: Name Attribute + nestedGroupMembership: + label: Nested Group Membership + options: + direct: Search only direct group memberships + nested: Search direct and nested group memberships + objectClass: Object Class + password: Password + port: Port + customizeSchema: Customize Schema + users: Users + groups: Groups + searchAttribute: Search Attribute + searchFilter: Search Filter + serverConnectionTimeout: Server Connection Timeout + serviceAccountDN: Service Account Distinguished Name + serviceAccountPassword: Service Account Password + serviceAccountInfo: Rancher needs a service account that has read-only access to all of the domains that will be able to login, so that we can determine what groups a user is a member of when they make a request with an API key. + starttls: + label: Start TLS + tip: Upgrades non-encrypted connections by wrapping with TLS during the connection process. Can not be used in conjunction with TLS. + tls: TLS + userEnabledAttribute: User Enabled Attribute + userMemberAttribute: User Member Attribute + userSearchBase: + label: User Search Base + placeholder: 'e.g. ou=users,dc=mycompany,dc=com' + username: Username + usernameAttribute: Username Attribute + saml: + UID: UID Field + adfs: Configure an AD FS account + api: Rancher API Host + cert: Certificate + displayName: Display Name Field + groups: Groups Field + key: Private Key + keycloak: Configure a Keycloak account + metadata: Metadata XML + okta: Configure an Okta account + ping: Configure a Ping account + shibboleth: Congiure a Shibboleth account + showLdap: Configure an OpenLDAP Server + userName: User Name Field + stateBanner: + disabled: '{provider} is currently disabled.' + enabled: '{provider} is currently enabled.' + testAndEnable: Test and Enable Authentication diff --git a/config/product/auth.js b/config/product/auth.js index c858829a86..1fe7997151 100644 --- a/config/product/auth.js +++ b/config/product/auth.js @@ -42,6 +42,14 @@ export function init(store) { }); componentForType(`${ MANAGEMENT.AUTH_CONFIG }/github`, 'auth/github'); + componentForType(`${ MANAGEMENT.AUTH_CONFIG }/openldap`, 'auth/ldap/index'); + componentForType(`${ MANAGEMENT.AUTH_CONFIG }/freeipa`, 'auth/ldap/index'); + componentForType(`${ MANAGEMENT.AUTH_CONFIG }/activedirectory`, 'auth/ldap/index'); + componentForType(`${ MANAGEMENT.AUTH_CONFIG }/ping`, 'auth/saml'); + componentForType(`${ MANAGEMENT.AUTH_CONFIG }/shibboleth`, 'auth/saml'); + componentForType(`${ MANAGEMENT.AUTH_CONFIG }/okta`, 'auth/saml'); + componentForType(`${ MANAGEMENT.AUTH_CONFIG }/keycloak`, 'auth/saml'); + componentForType(`${ MANAGEMENT.AUTH_CONFIG }/adfs`, 'auth/saml'); basicType([ 'config', diff --git a/edit/auth/ldap/config.vue b/edit/auth/ldap/config.vue new file mode 100644 index 0000000000..f3c05bd38c --- /dev/null +++ b/edit/auth/ldap/config.vue @@ -0,0 +1,192 @@ + + + diff --git a/edit/auth/ldap/index.vue b/edit/auth/ldap/index.vue new file mode 100644 index 0000000000..ae465e70aa --- /dev/null +++ b/edit/auth/ldap/index.vue @@ -0,0 +1,226 @@ + + + diff --git a/edit/auth/saml.vue b/edit/auth/saml.vue new file mode 100644 index 0000000000..da809ef30c --- /dev/null +++ b/edit/auth/saml.vue @@ -0,0 +1,271 @@ + + + diff --git a/plugins/steve/resource-instance.js b/plugins/steve/resource-instance.js index 614f8915d1..f08ba5a6de 100644 --- a/plugins/steve/resource-instance.js +++ b/plugins/steve/resource-instance.js @@ -747,7 +747,6 @@ export default { if ( !opt.url ) { opt.url = this.actionLinkFor(actionName); } - opt.method = 'post'; opt.data = body; From b5a761ebc6dfa12483c31c6fc39ac8a8bd8f82ca Mon Sep 17 00:00:00 2001 From: Nancy Butler <42977925+mantis-toboggan-md@users.noreply.github.com> Date: Mon, 11 Jan 2021 08:46:45 -0700 Subject: [PATCH 02/18] google oauth, auth mixin --- assets/translations/en-us.yaml | 39 ++++ config/product/auth.js | 1 + edit/auth/github.vue | 57 +----- edit/auth/googleoauth.vue | 210 ++++++++++++++++++++++ edit/auth/ldap/config.vue | 22 ++- edit/auth/ldap/index.vue | 135 +++----------- edit/auth/saml.vue | 97 +--------- mixins/auth.js | 116 ++++++++++++ models/management.cattle.io.authconfig.js | 22 +++ store/auth.js | 5 +- store/github.js | 1 - 11 files changed, 443 insertions(+), 262 deletions(-) create mode 100644 edit/auth/googleoauth.vue create mode 100644 mixins/auth.js diff --git a/assets/translations/en-us.yaml b/assets/translations/en-us.yaml index 996783f8e8..9116b3609b 100644 --- a/assets/translations/en-us.yaml +++ b/assets/translations/en-us.yaml @@ -141,6 +141,45 @@ authConfig: label: Which version of GitHub do you want to use? private: A private installation of GitHub Enterprise public: Public GitHub.com + googleoauth: + adminEmail: Admin Email + domain: Domain + oauthCredentials: + label: OAuth Credentials + tip: The OAuth Credentials JSON can be found in the Google API developers console. + serviceAccountCredentials: + label: Service Account Credentials + tip: The Service Account Credentials JSON can be found in the service accounts section of the Google API developers console. + steps: + 1: + title: 'Step One: For standard Google, click here to go applications settings in a new window' + body: |- + + 2: + title: 'Step Two: Navigate to the "Credentials" tab to create your OAuth client ID' + body: |- + + 3: + title: 'Step Three: Create Service Account credentials' + body: |- + Follow this guide to:
    + ldap: freeipa: Configure a FreeIPA server activedirectory: Configure an Active Directory account diff --git a/config/product/auth.js b/config/product/auth.js index 1fe7997151..4201fb2661 100644 --- a/config/product/auth.js +++ b/config/product/auth.js @@ -50,6 +50,7 @@ export function init(store) { componentForType(`${ MANAGEMENT.AUTH_CONFIG }/okta`, 'auth/saml'); componentForType(`${ MANAGEMENT.AUTH_CONFIG }/keycloak`, 'auth/saml'); componentForType(`${ MANAGEMENT.AUTH_CONFIG }/adfs`, 'auth/saml'); + componentForType(`${ MANAGEMENT.AUTH_CONFIG }/googleoauth`, 'auth/googleoauth'); basicType([ 'config', diff --git a/edit/auth/github.vue b/edit/auth/github.vue index 7ee6052b79..60f18aa089 100644 --- a/edit/auth/github.vue +++ b/edit/auth/github.vue @@ -10,7 +10,7 @@ import AsyncButton from '@/components/AsyncButton'; import CopyToClipboardText from '@/components/CopyToClipboardText.vue'; import AllowedPrincipals from '@/components/auth/AllowedPrincipals'; import { NORMAN, MANAGEMENT } from '@/config/types'; -import { addObject, findBy } from '@/utils/array'; +import { findBy } from '@/utils/array'; const NAME = 'github'; @@ -105,6 +105,14 @@ export default { AUTH_CONFIG() { return MANAGEMENT.AUTH_CONFIG; }, + + toSave() { + return { + enabled: true, + githubConfig: this.model, + description: 'Enable GitHub', + }; + } }, watch: { @@ -139,53 +147,6 @@ export default { } }, - async save(btnCb) { - this.errors = []; - - const wasEnabled = this.model.enabled; - - try { - if ( !wasEnabled ) { - const code = await this.$store.dispatch('auth/test', { provider: NAME, body: this.model }); - - this.model.enabled = true; - - await this.model.doAction('testAndApply', { - code, - enabled: true, - githubConfig: this.model, - description: 'Enable GitHub', - }); - - // Reload principals to get the new ones from GitHub - this.principals = await this.$store.dispatch('rancher/findAll', { - type: NORMAN.PRINCIPAL, - opt: { url: '/v3/principals', force: true } - }); - - this.model.accessMode = 'restricted'; - this.model.allowedPrincipalIds = this.model.allowedPrincipalIds || []; - - if ( this.me && !this.model.allowedPrincipalIds.includes(this.me.id) ) { - addObject(this.model.allowedPrincipalIds, this.me.id); - } - } - - await this.model.save(); - - await this.reloadModel(); - - btnCb(true); - - if ( wasEnabled ) { - this.done(); - } - } catch (err) { - this.errors = [err]; - btnCb(false); - } - }, - async disable(btnCb) { try { const clone = await this.$store.dispatch(`rancher/clone`, { resource: this.model }); diff --git a/edit/auth/googleoauth.vue b/edit/auth/googleoauth.vue new file mode 100644 index 0000000000..c20549f100 --- /dev/null +++ b/edit/auth/googleoauth.vue @@ -0,0 +1,210 @@ + + + diff --git a/edit/auth/ldap/config.vue b/edit/auth/ldap/config.vue index f3c05bd38c..c1c0f3b70f 100644 --- a/edit/auth/ldap/config.vue +++ b/edit/auth/ldap/config.vue @@ -49,6 +49,16 @@ export default { watch: { hostname(neu, old) { this.value.servers[0] = neu; + }, + 'model.starttls'(neu) { + if (neu) { + this.model.tls = false; + } + }, + 'model.tls'(neu) { + if (neu) { + this.model.starttls = false; + } } } @@ -70,12 +80,14 @@ export default { -
    -
    - - +
    diff --git a/edit/auth/ldap/index.vue b/edit/auth/ldap/index.vue index ae465e70aa..68f2a7ac06 100644 --- a/edit/auth/ldap/index.vue +++ b/edit/auth/ldap/index.vue @@ -1,13 +1,12 @@ diff --git a/edit/auth/saml.vue b/edit/auth/saml.vue index da809ef30c..157d4425e1 100644 --- a/edit/auth/saml.vue +++ b/edit/auth/saml.vue @@ -1,6 +1,7 @@ diff --git a/mixins/auth.js b/mixins/auth.js new file mode 100644 index 0000000000..d25c166e28 --- /dev/null +++ b/mixins/auth.js @@ -0,0 +1,116 @@ +import { NORMAN, MANAGEMENT } from '@/config/types'; +import { addObject, findBy } from '@/utils/array'; + +export default { + async fetch() { + const NAME = this.$route.params.id; + const originalModel = await this.$store.dispatch('rancher/find', { + type: NORMAN.AUTH_CONFIG, + id: NAME, + opt: { url: `/v3/${ NORMAN.AUTH_CONFIG }/${ NAME }`, force: true } + }); + + const serverUrl = await this.$store.dispatch('management/find', { + type: MANAGEMENT.SETTING, + id: 'server-url', + opt: { url: `/v1/{ MANAGEMENT.SETTING }/server-url` } + }); + + if ( serverUrl ) { + this.serverSetting = serverUrl.value; + } + + this.model = await this.$store.dispatch(`rancher/clone`, { resource: originalModel }); + if (NAME === 'shibboleth' && !this.model.openLdapConfig) { + this.model.openLdapConfig = {}; + this.showLdap = false; + } + }, + + computed: { + me() { + const out = findBy(this.principals, 'me', true); + + return out; + }, + + serverUrl() { + if ( this.serverSetting ) { + return this.serverSetting; + } else if ( process.client ) { + return window.location.origin; + } + + return ''; + }, + + principal() { + return this.$store.getters['rancher/byId'](NORMAN.PRINCIPAL, this.$store.getters['auth/principalId']) || {}; + }, + + displayName() { + return this.t(`model.authConfig.provider.${ this.NAME }`); + }, + + NAME() { + return this.$route.params.id; + }, + + AUTH_CONFIG() { + return MANAGEMENT.AUTH_CONFIG; + } + }, + + methods: { + async save(btnCb) { + this.errors = []; + const wasEnabled = this.model.enabled; + let obj = this.toSave; + + if (!obj) { + obj = this.model; + } + + try { + if ( !wasEnabled ) { + this.model.enabled = true; + + if (this.model.id === 'googleoauth' || this.model.id === 'github') { + const code = await this.$store.dispatch('auth/test', { provider: this.model.id, body: this.model }); + + this.model.enabled = true; + obj.code = code; + } + + await this.model.doAction('testAndApply', obj); + + // Reload principals to get the new ones from the provider + this.principals = await this.$store.dispatch('rancher/findAll', { + type: NORMAN.PRINCIPAL, + opt: { url: '/v3/principals', force: true } + }); + + this.model.allowedPrincipalIds = this.model.allowedPrincipalIds || []; + if ( this.me && !this.model.allowedPrincipalIds.includes(this.me.id) ) { + addObject(this.model.allowedPrincipalIds, this.me.id); + } + } + + if (this.model.configType === 'oauth') { + await this.model.save(); + await this.reloadModel(); + } + + btnCb(true); + if ( wasEnabled ) { + this.done(); + } + this.$router.applyQuery( { mode: 'view' } ); + } catch (err) { + this.errors = [err]; + btnCb(false); + this.model.enabled = wasEnabled; + } + }, + }, +}; diff --git a/models/management.cattle.io.authconfig.js b/models/management.cattle.io.authconfig.js index bc28e13bc3..e449772d72 100644 --- a/models/management.cattle.io.authconfig.js +++ b/models/management.cattle.io.authconfig.js @@ -1,4 +1,5 @@ import { insertAt } from '@/utils/array'; +import { set } from '@/utils/object'; const configType = { activedirectory: 'ldap', @@ -61,5 +62,26 @@ export default { await this.save(); this.currentRouter().push({ name: 'c-cluster-auth-config' }); }; + }, + + applyDefaults() { + return () => { + switch (this.configType) { + case 'saml': + if (this.id === 'shibboleth' && !this.openLdapConfig) { + this.openLdapConfig = {}; + set(this, 'openLdapConfig', {}); + } + break; + case 'ldap': + set(this, 'servers', []); + set(this, 'accessMode', 'unrestricted'); + set(this, 'starttls', false); + + break; + default: + break; + } + }; } }; diff --git a/store/auth.js b/store/auth.js index 07a356bb89..7f9d7f6b0b 100644 --- a/store/auth.js +++ b/store/auth.js @@ -5,7 +5,8 @@ import { open, popupWindowOptions } from '@/utils/window'; import { BACK_TO, SPA, AUTH_TEST, _FLAGGED, GITHUB_SCOPE, GITHUB_NONCE, GITHUB_REDIRECT } from '@/config/query-params'; -import { BASE_SCOPES } from '@/store/github'; + +export const BASE_SCOPES = { github: ['read:org'], googleoauth: ['email'] }; const KEY = 'rc_nonce'; @@ -143,7 +144,7 @@ export const actions = { const fromQuery = unescape(parseUrl(redirectUrl).query?.[GITHUB_SCOPE] || ''); const scopes = fromQuery.split(/[, ]+/).filter(x => !!x); - addObjects(scopes, BASE_SCOPES); + addObjects(scopes, BASE_SCOPES[provider]); if ( opt.scopes ) { addObjects(scopes, opt.scopes); diff --git a/store/github.js b/store/github.js index fafb3e9520..b0f0a4a889 100644 --- a/store/github.js +++ b/store/github.js @@ -5,7 +5,6 @@ import { GITHUB_REPOS, GITHUB_SCOPES, _DATE } from '@/config/local-storage'; const API_BASE = 'https://api.github.com/'; -export const BASE_SCOPES = ['read:org']; export const EXTENDED_SCOPES = ['repo']; export const DOCKERFILE = /^Dockerfile(\..*)?$/i; From 09f6228d5ca23eabb03eb0ae56828c616d9bff59 Mon Sep 17 00:00:00 2001 From: Nancy Butler <42977925+mantis-toboggan-md@users.noreply.github.com> Date: Tue, 12 Jan 2021 09:45:21 -0700 Subject: [PATCH 03/18] fix saml enable and ldap disable --- assets/translations/en-us.yaml | 1 + edit/auth/github.vue | 28 +--------- edit/auth/googleoauth.vue | 2 +- edit/auth/ldap/config.vue | 17 +++--- edit/auth/ldap/index.vue | 14 +++-- edit/auth/saml.vue | 13 ++++- mixins/auth.js | 65 ++++++++++++++++++++--- models/management.cattle.io.authconfig.js | 3 +- 8 files changed, 93 insertions(+), 50 deletions(-) diff --git a/assets/translations/en-us.yaml b/assets/translations/en-us.yaml index 9116b3609b..3b98fb3dcd 100644 --- a/assets/translations/en-us.yaml +++ b/assets/translations/en-us.yaml @@ -184,6 +184,7 @@ authConfig: freeipa: Configure a FreeIPA server activedirectory: Configure an Active Directory account openldap: Configure an OpenLDAP server + defaultLoginDomain: Default Login Domain cert: Certificate disabledStatusBitmask: Disabled Status Bitmask groupDNAttribute: Group DN Attribute diff --git a/edit/auth/github.vue b/edit/auth/github.vue index 60f18aa089..73ab05c201 100644 --- a/edit/auth/github.vue +++ b/edit/auth/github.vue @@ -121,18 +121,6 @@ export default { }, methods: { - async reloadModel() { - this.originalModel = await this.$store.dispatch('rancher/find', { - type: NORMAN.AUTH_CONFIG, - id: NAME, - opt: { url: `/v3/${ NORMAN.AUTH_CONFIG }/${ NAME }`, force: true } - }); - - this.model = await this.$store.dispatch(`rancher/clone`, { resource: this.originalModel }); - - return this.model; - }, - updateHost() { const match = this.targetUrl.match(/^(((https?):)?\/\/)?([^/]+)(\/.*)?$/); @@ -146,20 +134,6 @@ export default { this.model.hostname = match[4] || 'github.com'; } }, - - async disable(btnCb) { - try { - const clone = await this.$store.dispatch(`rancher/clone`, { resource: this.model }); - - clone.enabled = false; - await clone.save(); - await this.reloadModel(); - btnCb(true); - } catch (err) { - this.errors = [err]; - btnCb(false); - } - } }, }; @@ -180,7 +154,7 @@ export default { @finish="save" @cancel="done" > -