You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2020/02/19 12:22:52 UTC
[cloudstack-primate] branch master updated: login: add SAML
single-sign-on support (#169)
This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack-primate.git
The following commit(s) were added to refs/heads/master by this push:
new 71b7412 login: add SAML single-sign-on support (#169)
71b7412 is described below
commit 71b7412d69744ffd5d9ac2a018c6f321c162826a
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Wed Feb 19 17:52:46 2020 +0530
login: add SAML single-sign-on support (#169)
This adds SAML single-sign-on support.
Signed-off-by: Rohit Yadav <ro...@shapeblue.com>
Co-authored-by: Rohit Yadav <ro...@apache.org>
---
src/permission.js | 2 ++
src/utils/request.js | 17 +++++++-----
src/views/auth/Login.vue | 72 ++++++++++++++++++++++++++++++++++--------------
3 files changed, 64 insertions(+), 27 deletions(-)
diff --git a/src/permission.js b/src/permission.js
index bf43720..2bbc789 100644
--- a/src/permission.js
+++ b/src/permission.js
@@ -22,6 +22,7 @@ import store from './store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
+import message from 'ant-design-vue/es/message'
import notification from 'ant-design-vue/es/notification'
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
import { ACCESS_TOKEN } from '@/store/mutation-types'
@@ -41,6 +42,7 @@ router.beforeEach((to, from, next) => {
NProgress.done()
} else {
if (Object.keys(store.getters.apis).length === 0) {
+ message.loading('Discovering features...', 5)
store
.dispatch('GetInfo')
.then(apis => {
diff --git a/src/utils/request.js b/src/utils/request.js
index 235793a..f28958f 100644
--- a/src/utils/request.js
+++ b/src/utils/request.js
@@ -29,15 +29,18 @@ const service = axios.create({
})
const err = (error) => {
- if (error.response) {
- console.log('error has occurred')
- console.log(error)
+ const response = error.response
+ if (response) {
+ console.log(response)
const token = Vue.ls.get(ACCESS_TOKEN)
- if (error.response.status === 403) {
- const data = error.response.data
+ if (response.status === 403) {
+ const data = response.data
notification.error({ message: 'Forbidden', description: data.message })
}
- if (error.response.status === 401) {
+ if (response.status === 401) {
+ if (response.config && response.config.params && ['listIdps'].includes(response.config.params.command)) {
+ return
+ }
notification.error({ message: 'Unauthorized', description: 'Authorization verification failed' })
if (token) {
store.dispatch('Logout').then(() => {
@@ -47,7 +50,7 @@ const err = (error) => {
})
}
}
- if (error.response.status === 404) {
+ if (response.status === 404) {
notification.error({ message: 'Not Found', description: 'Resource not found' })
this.$router.push({ path: '/exception/404' })
}
diff --git a/src/views/auth/Login.vue b/src/views/auth/Login.vue
index 1bc6ec0..2e6f019 100644
--- a/src/views/auth/Login.vue
+++ b/src/views/auth/Login.vue
@@ -28,11 +28,12 @@
size="large"
:tabBarStyle="{ textAlign: 'center', borderBottom: 'unset' }"
@change="handleTabClick"
+ :animated="false"
>
- <a-tab-pane key="tab1">
+ <a-tab-pane key="cs">
<span slot="tab">
<a-icon type="safety" />
- <b>CloudStack Login</b>
+ Portal Login
</span>
<a-form-item>
<a-input
@@ -78,11 +79,18 @@
</a-form-item>
</a-tab-pane>
- <a-tab-pane key="tab2" disabled>
+ <a-tab-pane key="saml" :disabled="idps.length === 0">
<span slot="tab">
<a-icon type="audit" />
- <b>SAML</b>
+ Single-Sign-On
</span>
+ <a-form-item>
+ <a-select v-decorator="['idp', { initialValue: selectedIdp } ]">
+ <a-select-option v-for="(idp, idx) in idps" :key="idx" :value="idp.id">
+ {{ idp.orgName }}
+ </a-select-option>
+ </a-select>
+ </a-form-item>
</a-tab-pane>
</a-tabs>
@@ -100,14 +108,18 @@
</template>
<script>
+import { api } from '@/api'
import { mapActions } from 'vuex'
+import config from '@/config/settings'
export default {
components: {
},
data () {
return {
- customActiveKey: 'tab1',
+ idps: [],
+ selectedIdp: '',
+ customActiveKey: 'cs',
loginBtn: false,
loginType: 0,
form: this.$form.createForm(this),
@@ -120,8 +132,19 @@ export default {
},
created () {
},
+ mounted () {
+ this.fetchData()
+ },
methods: {
...mapActions(['Login', 'Logout']),
+ fetchData () {
+ api('listIdps').then(response => {
+ if (response) {
+ this.idps = response.listidpsresponse.idp || []
+ this.selectedIdp = this.idps[0].id || ''
+ }
+ })
+ },
// handler
handleUsernameOrEmail (rule, value, callback) {
const { state } = this
@@ -148,24 +171,33 @@ export default {
state.loginBtn = true
- const validateFieldsKey = customActiveKey === 'tab1' ? ['username', 'password', 'domain'] : ['mobile', 'captcha']
+ const validateFieldsKey = customActiveKey === 'cs' ? ['username', 'password', 'domain'] : ['idp']
validateFields(validateFieldsKey, { force: true }, (err, values) => {
if (!err) {
- const loginParams = { ...values }
- delete loginParams.username
- loginParams[!state.loginType ? 'email' : 'username'] = values.username
- loginParams.password = values.password
- loginParams.domain = values.domain
- if (!loginParams.domain) {
- loginParams.domain = '/'
+ if (customActiveKey === 'cs') {
+ const loginParams = { ...values }
+ delete loginParams.username
+ loginParams[!state.loginType ? 'email' : 'username'] = values.username
+ loginParams.password = values.password
+ loginParams.domain = values.domain
+ if (!loginParams.domain) {
+ loginParams.domain = '/'
+ }
+ Login(loginParams)
+ .then((res) => this.loginSuccess(res))
+ .catch(err => this.requestFailed(err))
+ .finally(() => {
+ state.loginBtn = false
+ })
+ } else if (customActiveKey === 'saml') {
+ state.loginBtn = false
+ var samlUrl = config.apiBase + '?command=samlSso'
+ if (values.idp) {
+ samlUrl += ('&idpid=' + values.idp)
+ }
+ window.location.href = samlUrl
}
- Login(loginParams)
- .then((res) => this.loginSuccess(res))
- .catch(err => this.requestFailed(err))
- .finally(() => {
- state.loginBtn = false
- })
} else {
setTimeout(() => {
state.loginBtn = false
@@ -174,7 +206,6 @@ export default {
})
},
loginSuccess (res) {
- this.$message.loading('Login Successful. Discovering Features...', 5)
this.$router.push({ path: '/dashboard' }).catch(() => {})
},
requestFailed (err) {
@@ -204,6 +235,7 @@ export default {
}
button.login-button {
+ margin-top: 8px;
padding: 0 15px;
font-size: 16px;
height: 40px;