/* Copyright (C) 2017 Cloudbase Solutions SRL This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ // @flow import cookie from 'js-cookie' import Api from '../utils/ApiCaller' import { servicesUrl, coriolisUrl } from '../constants' import configLoader from '../utils/Config' import type { Credentials, User } from '../types/User' import type { Role, Project, RoleAssignment } from '../types/Project' class UserModel { static parseUserData(data: any) { let newData = { id: data.token.user.id, name: data.token.user.name, email: data.token.user.email, project: data.token.project, } return newData } } class UserSource { static saveDomainName(domainName: string) { localStorage.setItem('userDomainName', domainName) } static getDomainName(): string { return localStorage.getItem('userDomainName') || configLoader.config.defaultUserDomain } static login(userData: Credentials): Promise { let auth = { auth: { identity: { methods: ['password'], password: { user: { name: userData.name, domain: { name: userData.domain }, password: userData.password, }, }, }, scope: 'unscoped', }, } Api.setDefaultHeader('X-Auth-Token', null) return Api.send({ url: servicesUrl.identity, method: 'POST', data: auth, }).then(response => { let token = response.headers ? response.headers['X-Subject-Token'] || response.headers['x-subject-token'] : '' Api.setDefaultHeader('X-Auth-Token', token) cookie.set('unscopedToken', token, { expires: 30 }) return response.data }) } static loginScoped(projectId: string, skipCookie?: boolean): Promise { let useProjectId = skipCookie ? projectId : cookie.get('projectId') || projectId let token = cookie.get('unscopedToken') let auth = { auth: { identity: { methods: ['token'], token: { id: token, }, }, scope: { project: { id: useProjectId, }, }, }, } Api.setDefaultHeader('X-Auth-Token', null) return Api.send({ url: servicesUrl.identity, method: 'POST', data: auth, }).then(response => { let token = response.headers ? response.headers['X-Subject-Token'] || response.headers['x-subject-token'] : '' let data = UserModel.parseUserData(response.data) data = { ...data, token } cookie.set('token', data.token, { expires: 30 }) cookie.set('projectId', data.project.id, { expires: 30 }) Api.setDefaultHeader('X-Auth-Token', data.token) return data }).catch(response => { if (!skipCookie) { return UserSource.loginScoped(projectId, true) } return Promise.reject(response) }) } static tokenLogin(): Promise { let token = cookie.get('token') let projectId = cookie.get('projectId') if (token) { Api.setDefaultHeader('X-Auth-Token', token) } if (!token || !projectId) { return Promise.reject() } return Api.send({ url: servicesUrl.identity, headers: { 'X-Subject-Token': token }, }).then(response => { let data = UserModel.parseUserData(response.data) data = { ...data, token } return data }).catch(() => { cookie.remove('token') Api.setDefaultHeader('X-Auth-Token', null) return Promise.reject() }) } static switchProject(): Promise { let token = cookie.get('unscopedToken') if (token) { cookie.remove('projectId') return Promise.resolve() } return Promise.reject() } static logout(): Promise { let token = cookie.get('token') let clear = () => { cookie.remove('token') window.location.href = '/' Api.setDefaultHeader('X-Auth-Token', null) } return Api.send({ url: servicesUrl.identity, method: 'DELETE', headers: { 'X-Subject-Token': token || '' }, }).then(() => { clear() }).catch(() => { clear() return Promise.reject() }) } static getUserInfo(userId: string): Promise { return Api.get(`${servicesUrl.users}/${userId}`).then(response => response.data.user) } static getAllUsers(): Promise { return Api.get(`${servicesUrl.users}`) .then(response => response.data.users.sort((u1, u2) => u1.name.localeCompare(u2.name))) } static update(userId: string, user: User, oldUser: ?User): Promise { const data = { user: {} } let oldData = oldUser || {} if (user.email || oldData.email) { data.user.email = user.email } if (user.description || oldData.description) { data.user.description = user.description } if (user.enabled != null) { data.user.enabled = user.enabled } if (user.name) { data.user.name = user.name } if (user.password) { data.user.password = user.password } if (user.project_id || oldData.project_id) { data.user.project_id = user.project_id } let updatedUser: User return Api.send({ url: `${servicesUrl.users}/${userId}`, method: 'PATCH', data, }).then(response => { updatedUser = response.data.user if (updatedUser.extra) { updatedUser = { ...updatedUser, ...updatedUser.extra, } } return updatedUser }).then(() => { // if project id was updated, assign him to that project, if his not already assigned if (data.user.project_id) { return this.getProjects(updatedUser.id).then((projects: Project[]) => { if (projects.find(p => p.id === data.user.project_id)) { return updatedUser } return this.assignUserToProject(updatedUser.id, updatedUser.project_id || 'undefined').then(() => { return updatedUser }) }) } return updatedUser }) } static add(user: User): Promise { let data = { user: {} } data.user.name = user.name data.user.password = user.password || '' data.user.enabled = user.enabled == null ? true : user.enabled if (user.email) { data.user.email = user.email } if (user.description) { data.user.description = user.description } if (user.project_id) { data.user.project_id = user.project_id } let addedUser: User return Api.send({ url: `${servicesUrl.users}`, method: 'POST', data, }).then(response => { addedUser = response.data.user if (addedUser.extra) { addedUser = { ...addedUser, ...addedUser.extra, } } return addedUser }).then(() => { // If the user has a project id set, assign him to that project with admin role if (addedUser.project_id) { return this.assignUserToProject(addedUser.id, addedUser.project_id || 'undefined').then(() => { return addedUser }) } return addedUser }) } static delete(userId: string): Promise { return Api.send({ url: `${coriolisUrl}identity/users/${userId}`, method: 'DELETE', }).then(() => { }) } static assignUserToProject(userId: string, projectId: string): Promise { return this.getMemberRoleId().then((roleId: string) => { return this.assignUserToProjectWithRole(userId, projectId, roleId) }) } static assignUserToProjectWithRole(userId: string, projectId: string, roleId: string): Promise { return Api.send({ url: `${coriolisUrl}identity/projects/${projectId}/users/${userId}/roles/${roleId}`, method: 'PUT', }).then(() => { }) } static getMemberRoleId(): Promise { return this.getRoles().then((roles: { id: string, name: string }[]) => { const role = roles.find(r => r.name === '_member_') const roleId = role ? role.id : '' return roleId }) } static getAdminRoleId(): Promise { return this.getRoles().then((roles: { id: string, name: string }[]) => { const role = roles.find(r => r.name === 'admin') const roleId = role ? role.id : '' return roleId }) } static getRoles(): Promise { return Api.get(`${coriolisUrl}identity/roles`).then(response => { let roles: Role[] = response.data.roles roles.sort((r1, r2) => r1.name.localeCompare(r2.name)) return roles }) } static getProjects(userId: string): Promise { return Api.get(`${coriolisUrl}identity/role_assignments?include_names`).then(response => { let assignments: RoleAssignment[] = response.data.role_assignments let projects: $Shape[] = assignments .filter(a => a.user.id === userId) .filter((a, i, arr) => arr.findIndex(e => e.scope.project.id === a.scope.project.id) === i) .map(a => a.scope.project) return projects }) } static isAdmin(userId: string): Promise { return Api.send({ url: `${coriolisUrl}identity/role_assignments?include_names`, quietError: true, }).then(response => { let roleAssignments: RoleAssignment[] = response.data.role_assignments return roleAssignments.filter(a => a.user.id === userId).filter(a => a.role.name === 'admin').length > 0 }) } } export default UserSource