UserSource.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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 '../config'
  18. import type { Credentials, User } from '../types/User'
  19. import type { Role, Project, RoleAssignment } from '../types/Project'
  20. class UserModel {
  21. static parseUserData(data: any) {
  22. let newData = {
  23. id: data.token.user.id,
  24. name: data.token.user.name,
  25. email: data.token.user.email,
  26. project: data.token.project,
  27. }
  28. return newData
  29. }
  30. }
  31. class UserSource {
  32. static login(userData: Credentials): Promise<User> {
  33. let auth = {
  34. auth: {
  35. identity: {
  36. methods: ['password'],
  37. password: {
  38. user: {
  39. name: userData.name,
  40. domain: { name: 'default' },
  41. password: userData.password,
  42. },
  43. },
  44. },
  45. scope: 'unscoped',
  46. },
  47. }
  48. Api.setDefaultHeader('X-Auth-Token', null)
  49. return new Promise((resolve, reject) => {
  50. Api.send({
  51. url: servicesUrl.identity,
  52. method: 'POST',
  53. data: auth,
  54. }).then((response) => {
  55. let token = response.headers ? response.headers['X-Subject-Token'] || response.headers['x-subject-token'] : ''
  56. Api.setDefaultHeader('X-Auth-Token', token)
  57. cookie.set('unscopedToken', token, { expires: 30 })
  58. resolve(response.data)
  59. }).catch(reject)
  60. })
  61. }
  62. static loginScoped(projectId: string, skipCookie?: boolean): Promise<User> {
  63. let useProjectId = skipCookie ? projectId : cookie.get('projectId') || projectId
  64. let token = cookie.get('unscopedToken')
  65. let auth = {
  66. auth: {
  67. identity: {
  68. methods: ['token'],
  69. token: {
  70. id: token,
  71. },
  72. },
  73. scope: {
  74. project: {
  75. id: useProjectId,
  76. },
  77. },
  78. },
  79. }
  80. Api.setDefaultHeader('X-Auth-Token', null)
  81. return new Promise((resolve, reject) => {
  82. Api.send({
  83. url: servicesUrl.identity,
  84. method: 'POST',
  85. data: auth,
  86. }).then((response) => {
  87. let token = response.headers ? response.headers['X-Subject-Token'] || response.headers['x-subject-token'] : ''
  88. let data = UserModel.parseUserData(response.data)
  89. data = { ...data, token }
  90. cookie.set('token', data.token, { expires: 30 })
  91. cookie.set('projectId', data.project.id, { expires: 30 })
  92. Api.setDefaultHeader('X-Auth-Token', data.token)
  93. resolve(data)
  94. }, response => {
  95. if (!skipCookie) {
  96. UserSource.loginScoped(projectId, true).then(resolve, reject)
  97. } else {
  98. reject(response)
  99. }
  100. }).catch(reject)
  101. })
  102. }
  103. static tokenLogin(): Promise<User> {
  104. let token = cookie.get('token')
  105. let projectId = cookie.get('projectId')
  106. if (token) {
  107. Api.setDefaultHeader('X-Auth-Token', token)
  108. }
  109. return new Promise((resolve, reject) => {
  110. if (!token || !projectId) {
  111. reject()
  112. return
  113. }
  114. 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. resolve(data)
  121. }).catch(() => {
  122. cookie.remove('token')
  123. Api.setDefaultHeader('X-Auth-Token', null)
  124. reject()
  125. })
  126. })
  127. }
  128. static switchProject(): Promise<void> {
  129. let token = cookie.get('unscopedToken')
  130. return new Promise((resolve, reject) => {
  131. if (token) {
  132. cookie.remove('projectId')
  133. resolve()
  134. } else {
  135. reject()
  136. }
  137. })
  138. }
  139. static logout(): Promise<void> {
  140. let token = cookie.get('token')
  141. return new Promise((resolve, reject) => {
  142. Api.send({
  143. url: servicesUrl.identity,
  144. method: 'DELETE',
  145. headers: { 'X-Subject-Token': token || '' },
  146. }).then(() => {
  147. cookie.remove('token')
  148. window.location.href = '/'
  149. resolve()
  150. }).catch(() => {
  151. cookie.remove('token')
  152. window.location.href = '/'
  153. reject()
  154. })
  155. Api.setDefaultHeader('X-Auth-Token', null)
  156. })
  157. }
  158. static getUserInfo(userId: string): Promise<User> {
  159. return Api.get(`${servicesUrl.users}/${userId}`).then(response => {
  160. return response.data.user
  161. })
  162. }
  163. static getAllUsers(): Promise<User[]> {
  164. return Api.get(`${servicesUrl.users}`).then(response => {
  165. return response.data.users.sort((u1, u2) => u1.name.localeCompare(u2.name))
  166. })
  167. }
  168. static update(userId: string, user: User, oldUser: ?User): Promise<User> {
  169. const data = { user: {} }
  170. let oldData = oldUser || {}
  171. if (user.email || oldData.email) {
  172. data.user.email = user.email
  173. }
  174. if (user.description || oldData.description) {
  175. data.user.description = user.description
  176. }
  177. if (user.enabled !== undefined && user.enabled !== null) {
  178. data.user.enabled = user.enabled
  179. }
  180. if (user.name) {
  181. data.user.name = user.name
  182. }
  183. if (user.password) {
  184. data.user.password = user.password
  185. }
  186. if (user.project_id || oldData.project_id) {
  187. data.user.project_id = user.project_id
  188. }
  189. let updatedUser: User
  190. return Api.send({
  191. url: `${servicesUrl.users}/${userId}`,
  192. method: 'PATCH',
  193. data,
  194. }).then(response => {
  195. updatedUser = response.data.user
  196. if (updatedUser.extra) {
  197. updatedUser = {
  198. ...updatedUser,
  199. ...updatedUser.extra,
  200. }
  201. }
  202. return updatedUser
  203. }).then(() => {
  204. // if project id was updated, assign him to that project, if his not already assigned
  205. if (data.user.project_id) {
  206. return this.getProjects(updatedUser.id).then((projects: Project[]) => {
  207. if (projects.find(p => p.id === data.user.project_id)) {
  208. return updatedUser
  209. }
  210. return this.assignUserToProject(updatedUser.id, updatedUser.project_id || 'undefined').then(() => {
  211. return updatedUser
  212. })
  213. })
  214. }
  215. return updatedUser
  216. })
  217. }
  218. static add(user: User): Promise<User> {
  219. let data = { user: {} }
  220. data.user.name = user.name
  221. data.user.password = user.password || ''
  222. data.user.enabled = user.enabled === null || user.enabled === undefined ? true : user.enabled
  223. if (user.email) {
  224. data.user.email = user.email
  225. }
  226. if (user.description) {
  227. data.user.description = user.description
  228. }
  229. if (user.project_id) {
  230. data.user.project_id = user.project_id
  231. }
  232. let addedUser: User
  233. return Api.send({
  234. url: `${servicesUrl.users}`,
  235. method: 'POST',
  236. data,
  237. }).then(response => {
  238. addedUser = response.data.user
  239. if (addedUser.extra) {
  240. addedUser = {
  241. ...addedUser,
  242. ...addedUser.extra,
  243. }
  244. }
  245. return addedUser
  246. }).then(() => {
  247. // If the user has a project id set, assign him to that project with admin role
  248. if (addedUser.project_id) {
  249. return this.assignUserToProject(addedUser.id, addedUser.project_id || 'undefined').then(() => {
  250. return addedUser
  251. })
  252. }
  253. return addedUser
  254. })
  255. }
  256. static delete(userId: string): Promise<void> {
  257. return Api.send({
  258. url: `${coriolisUrl}identity/users/${userId}`,
  259. method: 'DELETE',
  260. }).then(() => { })
  261. }
  262. static assignUserToProject(userId: string, projectId: string): Promise<void> {
  263. return this.getMemberRoleId().then((roleId: string) => {
  264. return this.assignUserToProjectWithRole(userId, projectId, roleId)
  265. })
  266. }
  267. static assignUserToProjectWithRole(userId: string, projectId: string, roleId: string): Promise<void> {
  268. return Api.send({
  269. url: `${coriolisUrl}identity/projects/${projectId}/users/${userId}/roles/${roleId}`,
  270. method: 'PUT',
  271. }).then(() => { })
  272. }
  273. static getMemberRoleId(): Promise<string> {
  274. return this.getRoles().then((roles: { id: string, name: string }[]) => {
  275. const role = roles.find(r => r.name === '_member_')
  276. const roleId = role ? role.id : ''
  277. return roleId
  278. })
  279. }
  280. static getAdminRoleId(): Promise<string> {
  281. return this.getRoles().then((roles: { id: string, name: string }[]) => {
  282. const role = roles.find(r => r.name === 'admin')
  283. const roleId = role ? role.id : ''
  284. return roleId
  285. })
  286. }
  287. static getRoles(): Promise<Role[]> {
  288. return Api.get(`${coriolisUrl}identity/roles`).then(response => {
  289. let roles: Role[] = response.data.roles
  290. roles.sort((r1, r2) => r1.name.localeCompare(r2.name))
  291. return roles
  292. })
  293. }
  294. static getProjects(userId: string): Promise<Project[]> {
  295. return Api.get(`${coriolisUrl}identity/role_assignments?include_names`).then(response => {
  296. let assignments: RoleAssignment[] = response.data.role_assignments
  297. let projects: $Shape<Project>[] = assignments
  298. .filter(a => a.user.id === userId)
  299. .filter((a, i, arr) => arr.findIndex(e => e.scope.project.id === a.scope.project.id) === i)
  300. .map(a => a.scope.project)
  301. return projects
  302. })
  303. }
  304. static isAdmin(userId: string): Promise<boolean> {
  305. return Api.send({
  306. url: `${coriolisUrl}identity/role_assignments?include_names`,
  307. quietError: true,
  308. }).then(response => {
  309. let roleAssignments: RoleAssignment[] = response.data.role_assignments
  310. return roleAssignments.filter(a => a.user.id === userId).filter(a => a.role.name === 'admin').length > 0
  311. })
  312. }
  313. }
  314. export default UserSource