UserSource.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. // @flow
  15. import cookie from 'js-cookie'
  16. import Api from '../utils/ApiCaller'
  17. import { servicesUrl, coriolisUrl } from '../constants'
  18. import configLoader from '../utils/Config'
  19. import type { Credentials, User } from '../types/User'
  20. import type { Role, Project, RoleAssignment } from '../types/Project'
  21. class UserModel {
  22. static parseUserData(data: any) {
  23. let newData = {
  24. id: data.token.user.id,
  25. name: data.token.user.name,
  26. email: data.token.user.email,
  27. project: data.token.project,
  28. }
  29. return newData
  30. }
  31. }
  32. class UserSource {
  33. static saveDomainName(domainName: string) {
  34. localStorage.setItem('userDomainName', domainName)
  35. }
  36. static getDomainName(): string {
  37. return localStorage.getItem('userDomainName') || configLoader.config.defaultUserDomain
  38. }
  39. static login(userData: Credentials): Promise<User> {
  40. let auth = {
  41. auth: {
  42. identity: {
  43. methods: ['password'],
  44. password: {
  45. user: {
  46. name: userData.name,
  47. domain: { name: userData.domain },
  48. password: userData.password,
  49. },
  50. },
  51. },
  52. scope: 'unscoped',
  53. },
  54. }
  55. Api.setDefaultHeader('X-Auth-Token', null)
  56. return Api.send({
  57. url: servicesUrl.identity,
  58. method: 'POST',
  59. data: auth,
  60. }).then(response => {
  61. let token = response.headers ? response.headers['X-Subject-Token'] || response.headers['x-subject-token'] : ''
  62. Api.setDefaultHeader('X-Auth-Token', token)
  63. cookie.set('unscopedToken', token, { expires: 30 })
  64. return response.data
  65. })
  66. }
  67. static loginScoped(projectId: string, skipCookie?: boolean): Promise<User> {
  68. let useProjectId = skipCookie ? projectId : cookie.get('projectId') || projectId
  69. let token = cookie.get('unscopedToken')
  70. let auth = {
  71. auth: {
  72. identity: {
  73. methods: ['token'],
  74. token: {
  75. id: token,
  76. },
  77. },
  78. scope: {
  79. project: {
  80. id: useProjectId,
  81. },
  82. },
  83. },
  84. }
  85. Api.setDefaultHeader('X-Auth-Token', null)
  86. return Api.send({
  87. url: servicesUrl.identity,
  88. method: 'POST',
  89. data: auth,
  90. }).then(response => {
  91. let token = response.headers ? response.headers['X-Subject-Token'] || response.headers['x-subject-token'] : ''
  92. let data = UserModel.parseUserData(response.data)
  93. data = { ...data, token }
  94. cookie.set('token', data.token, { expires: 30 })
  95. cookie.set('projectId', data.project.id, { expires: 30 })
  96. Api.setDefaultHeader('X-Auth-Token', data.token)
  97. return data
  98. }).catch(response => {
  99. if (!skipCookie) {
  100. return UserSource.loginScoped(projectId, true)
  101. }
  102. return Promise.reject(response)
  103. })
  104. }
  105. static tokenLogin(): Promise<User> {
  106. let token = cookie.get('token')
  107. let projectId = cookie.get('projectId')
  108. if (token) {
  109. Api.setDefaultHeader('X-Auth-Token', token)
  110. }
  111. if (!token || !projectId) {
  112. return Promise.reject()
  113. }
  114. return Api.send({
  115. url: servicesUrl.identity,
  116. headers: { 'X-Subject-Token': token },
  117. }).then(response => {
  118. let data = UserModel.parseUserData(response.data)
  119. data = { ...data, token }
  120. return data
  121. }).catch(() => {
  122. cookie.remove('token')
  123. Api.setDefaultHeader('X-Auth-Token', null)
  124. return Promise.reject()
  125. })
  126. }
  127. static switchProject(): Promise<void> {
  128. let token = cookie.get('unscopedToken')
  129. if (token) {
  130. cookie.remove('projectId')
  131. return Promise.resolve()
  132. }
  133. return Promise.reject()
  134. }
  135. static logout(): Promise<void> {
  136. let token = cookie.get('token')
  137. let clear = () => {
  138. cookie.remove('token')
  139. window.location.href = '/'
  140. Api.setDefaultHeader('X-Auth-Token', null)
  141. }
  142. return Api.send({
  143. url: servicesUrl.identity,
  144. method: 'DELETE',
  145. headers: { 'X-Subject-Token': token || '' },
  146. }).then(() => {
  147. clear()
  148. }).catch(() => {
  149. clear()
  150. return Promise.reject()
  151. })
  152. }
  153. static getUserInfo(userId: string): Promise<User> {
  154. return Api.get(`${servicesUrl.users}/${userId}`).then(response => response.data.user)
  155. }
  156. static getAllUsers(): Promise<User[]> {
  157. return Api.get(`${servicesUrl.users}`)
  158. .then(response => response.data.users.sort((u1, u2) => u1.name.localeCompare(u2.name)))
  159. }
  160. static update(userId: string, user: User, oldUser: ?User): Promise<User> {
  161. const data = { user: {} }
  162. let oldData = oldUser || {}
  163. if (user.email || oldData.email) {
  164. data.user.email = user.email
  165. }
  166. if (user.description || oldData.description) {
  167. data.user.description = user.description
  168. }
  169. if (user.enabled != null) {
  170. data.user.enabled = user.enabled
  171. }
  172. if (user.name) {
  173. data.user.name = user.name
  174. }
  175. if (user.password) {
  176. data.user.password = user.password
  177. }
  178. if (user.project_id || oldData.project_id) {
  179. data.user.project_id = user.project_id
  180. }
  181. let updatedUser: User
  182. return Api.send({
  183. url: `${servicesUrl.users}/${userId}`,
  184. method: 'PATCH',
  185. data,
  186. }).then(response => {
  187. updatedUser = response.data.user
  188. if (updatedUser.extra) {
  189. updatedUser = {
  190. ...updatedUser,
  191. ...updatedUser.extra,
  192. }
  193. }
  194. return updatedUser
  195. }).then(() => {
  196. // if project id was updated, assign him to that project, if his not already assigned
  197. if (data.user.project_id) {
  198. return this.getProjects(updatedUser.id).then((projects: Project[]) => {
  199. if (projects.find(p => p.id === data.user.project_id)) {
  200. return updatedUser
  201. }
  202. return this.assignUserToProject(updatedUser.id, updatedUser.project_id || 'undefined').then(() => {
  203. return updatedUser
  204. })
  205. })
  206. }
  207. return updatedUser
  208. })
  209. }
  210. static add(user: User): Promise<User> {
  211. let data = { user: {} }
  212. data.user.name = user.name
  213. data.user.password = user.password || ''
  214. data.user.enabled = user.enabled == null ? true : user.enabled
  215. if (user.email) {
  216. data.user.email = user.email
  217. }
  218. if (user.description) {
  219. data.user.description = user.description
  220. }
  221. if (user.project_id) {
  222. data.user.project_id = user.project_id
  223. }
  224. let addedUser: User
  225. return Api.send({
  226. url: `${servicesUrl.users}`,
  227. method: 'POST',
  228. data,
  229. }).then(response => {
  230. addedUser = response.data.user
  231. if (addedUser.extra) {
  232. addedUser = {
  233. ...addedUser,
  234. ...addedUser.extra,
  235. }
  236. }
  237. return addedUser
  238. }).then(() => {
  239. // If the user has a project id set, assign him to that project with admin role
  240. if (addedUser.project_id) {
  241. return this.assignUserToProject(addedUser.id, addedUser.project_id || 'undefined').then(() => {
  242. return addedUser
  243. })
  244. }
  245. return addedUser
  246. })
  247. }
  248. static delete(userId: string): Promise<void> {
  249. return Api.send({
  250. url: `${coriolisUrl}identity/users/${userId}`,
  251. method: 'DELETE',
  252. }).then(() => { })
  253. }
  254. static assignUserToProject(userId: string, projectId: string): Promise<void> {
  255. return this.getMemberRoleId().then((roleId: string) => {
  256. return this.assignUserToProjectWithRole(userId, projectId, roleId)
  257. })
  258. }
  259. static assignUserToProjectWithRole(userId: string, projectId: string, roleId: string): Promise<void> {
  260. return Api.send({
  261. url: `${coriolisUrl}identity/projects/${projectId}/users/${userId}/roles/${roleId}`,
  262. method: 'PUT',
  263. }).then(() => { })
  264. }
  265. static getMemberRoleId(): Promise<string> {
  266. return this.getRoles().then((roles: { id: string, name: string }[]) => {
  267. const role = roles.find(r => r.name === '_member_')
  268. const roleId = role ? role.id : ''
  269. return roleId
  270. })
  271. }
  272. static getAdminRoleId(): Promise<string> {
  273. return this.getRoles().then((roles: { id: string, name: string }[]) => {
  274. const role = roles.find(r => r.name === 'admin')
  275. const roleId = role ? role.id : ''
  276. return roleId
  277. })
  278. }
  279. static getRoles(): Promise<Role[]> {
  280. return Api.get(`${coriolisUrl}identity/roles`).then(response => {
  281. let roles: Role[] = response.data.roles
  282. roles.sort((r1, r2) => r1.name.localeCompare(r2.name))
  283. return roles
  284. })
  285. }
  286. static getProjects(userId: string): Promise<Project[]> {
  287. return Api.get(`${coriolisUrl}identity/role_assignments?include_names`).then(response => {
  288. let assignments: RoleAssignment[] = response.data.role_assignments
  289. let projects: $Shape<Project>[] = assignments
  290. .filter(a => a.user.id === userId)
  291. .filter((a, i, arr) => arr.findIndex(e => e.scope.project.id === a.scope.project.id) === i)
  292. .map(a => a.scope.project)
  293. return projects
  294. })
  295. }
  296. static isAdmin(userId: string): Promise<boolean> {
  297. return Api.send({
  298. url: `${coriolisUrl}identity/role_assignments?include_names`,
  299. quietError: true,
  300. }).then(response => {
  301. let roleAssignments: RoleAssignment[] = response.data.role_assignments
  302. return roleAssignments.filter(a => a.user.id === userId).filter(a => a.role.name === 'admin').length > 0
  303. })
  304. }
  305. }
  306. export default UserSource