mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 20:02:29 +01:00
login: add SAML single-sign-on support (#169)
This adds SAML single-sign-on support. Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com> Co-authored-by: Rohit Yadav <rohit@apache.org>
This commit is contained in:
parent
e03c332d56
commit
e40df4d25d
@ -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 => {
|
||||
|
||||
@ -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' })
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user