|
@@ -16,6 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
import { observable, action } from 'mobx'
|
|
import { observable, action } from 'mobx'
|
|
|
import type { User, Credentials } from '../types/User'
|
|
import type { User, Credentials } from '../types/User'
|
|
|
|
|
+import type { Project } from '../types/Project'
|
|
|
import UserSource from '../sources/UserSource'
|
|
import UserSource from '../sources/UserSource'
|
|
|
import projectStore from './ProjectStore'
|
|
import projectStore from './ProjectStore'
|
|
|
import notificationStore from '../stores/NotificationStore'
|
|
import notificationStore from '../stores/NotificationStore'
|
|
@@ -23,70 +24,89 @@ import notificationStore from '../stores/NotificationStore'
|
|
|
/**
|
|
/**
|
|
|
* This is the authentication / authorization flow:
|
|
* This is the authentication / authorization flow:
|
|
|
* 1. Post username and password unscoped login. Set unscoped token in cookies.
|
|
* 1. Post username and password unscoped login. Set unscoped token in cookies.
|
|
|
- * 2. Post unscoped token with project id. Set scoped token and project id in cookies.
|
|
|
|
|
- * 3. Get token login on subsequent app reloads to retrieve the user info.
|
|
|
|
|
|
|
+ * 2. Get user details with unscoped token to see if he has project id
|
|
|
|
|
+ * 3. Post unscoped token with project id (either from his details or from cookies). Set scoped token and project id in cookies.
|
|
|
|
|
+ * 4. Get token login on subsequent app reloads to retrieve the user info.
|
|
|
*
|
|
*
|
|
|
* After token expiration, the app is redirected to login page.
|
|
* After token expiration, the app is redirected to login page.
|
|
|
*/
|
|
*/
|
|
|
class UserStore {
|
|
class UserStore {
|
|
|
- @observable user: ?User = null
|
|
|
|
|
|
|
+ @observable loggedUser: ?User = null
|
|
|
|
|
+ @observable users: User[] = []
|
|
|
@observable loading: boolean = false
|
|
@observable loading: boolean = false
|
|
|
@observable loginFailedResponse: any = null
|
|
@observable loginFailedResponse: any = null
|
|
|
|
|
+ @observable userDetails: ?User = null
|
|
|
|
|
+ @observable userDetailsLoading: boolean = false
|
|
|
|
|
+ @observable updating: boolean = false
|
|
|
|
|
+ @observable loggedIn: boolean = false
|
|
|
|
|
+ @observable projects: Project[] = []
|
|
|
|
|
+ @observable allUsersLoading: boolean = false
|
|
|
|
|
|
|
|
@action login(creds: Credentials): Promise<void> {
|
|
@action login(creds: Credentials): Promise<void> {
|
|
|
this.loading = true
|
|
this.loading = true
|
|
|
- this.user = null
|
|
|
|
|
|
|
+ this.loggedUser = null
|
|
|
this.loginFailedResponse = null
|
|
this.loginFailedResponse = null
|
|
|
|
|
|
|
|
- return UserSource.login(creds).then(() => {
|
|
|
|
|
- return this.loginScoped()
|
|
|
|
|
- }).then((user: User) => {
|
|
|
|
|
|
|
+ return UserSource.login(creds).then((auth: any) => {
|
|
|
|
|
+ this.loggedUser = { id: auth.token.user.id, email: '', name: '', project: { id: '', name: '' } }
|
|
|
|
|
+ return this.getLoggedUserInfo()
|
|
|
|
|
+ }).then(() => {
|
|
|
|
|
+ return this.loginScoped(this.loggedUser ? this.loggedUser.project_id : '', true)
|
|
|
|
|
+ }).then(() => {
|
|
|
|
|
+ return this.isAdmin()
|
|
|
|
|
+ }).then(() => {
|
|
|
this.loading = false
|
|
this.loading = false
|
|
|
|
|
+ this.loggedIn = true
|
|
|
notificationStore.notify('Signed in', 'success')
|
|
notificationStore.notify('Signed in', 'success')
|
|
|
- this.user = user
|
|
|
|
|
- this.getUserInfo(user)
|
|
|
|
|
}).catch((reason) => {
|
|
}).catch((reason) => {
|
|
|
this.loading = false
|
|
this.loading = false
|
|
|
this.loginFailedResponse = reason
|
|
this.loginFailedResponse = reason
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @action loginScoped(projectId?: string): Promise<User> {
|
|
|
|
|
- return new Promise((resolve) => {
|
|
|
|
|
- const sourceLoginScoped = () => {
|
|
|
|
|
- UserSource.loginScoped(projectId || projectStore.projects[0].id).then((user: User) => {
|
|
|
|
|
- this.user = { ...user, scoped: true }
|
|
|
|
|
- resolve(user)
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ @action loginScoped(projectId?: string, skipProjectCookie?: boolean): Promise<User> {
|
|
|
|
|
+ return projectStore.getProjects().then(() => {
|
|
|
|
|
+ let projects = projectStore.projects.filter(p => p.enabled)
|
|
|
|
|
+ if (projects.length === 0) {
|
|
|
|
|
+ return Promise.reject({ status: 500, message: 'There are no projects assigned to user.' })
|
|
|
}
|
|
}
|
|
|
- if (projectStore.projects && projectStore.projects.length) {
|
|
|
|
|
- sourceLoginScoped()
|
|
|
|
|
- } else {
|
|
|
|
|
- projectStore.getProjects().then(() => {
|
|
|
|
|
- sourceLoginScoped()
|
|
|
|
|
- })
|
|
|
|
|
|
|
+
|
|
|
|
|
+ let project = projects.find(p => p.id === projectId)
|
|
|
|
|
+ let id = (project && project.id) || projects[0].id
|
|
|
|
|
+ return UserSource.loginScoped(id, Boolean(id && skipProjectCookie))
|
|
|
|
|
+ }).then((user: User) => {
|
|
|
|
|
+ if (!this.loggedUser) {
|
|
|
|
|
+ return Promise.reject('No Logged in user')
|
|
|
}
|
|
}
|
|
|
|
|
+ this.loggedUser.scoped = true
|
|
|
|
|
+ this.loggedUser.project = user.project
|
|
|
|
|
+ return this.loggedUser
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @action getUserInfo(user: User): Promise<void> {
|
|
|
|
|
- return UserSource.getUserInfo(user).then((userData: User) => {
|
|
|
|
|
- this.user = { ...this.user, ...userData }
|
|
|
|
|
- }).catch(reason => {
|
|
|
|
|
- console.error('Error while getting user data', reason)
|
|
|
|
|
- notificationStore.notify('Error while getting user data', 'error')
|
|
|
|
|
|
|
+ @action getLoggedUserInfo(): Promise<void> {
|
|
|
|
|
+ if (!this.loggedUser) {
|
|
|
|
|
+ return Promise.reject('No logged-in user')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return UserSource.getUserInfo(this.loggedUser.id).then((userData: User) => {
|
|
|
|
|
+ this.loggedUser = { ...this.loggedUser, ...userData, isAdmin: false }
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@action tokenLogin(): Promise<void> {
|
|
@action tokenLogin(): Promise<void> {
|
|
|
- this.user = null
|
|
|
|
|
|
|
+ this.loggedUser = null
|
|
|
this.loading = true
|
|
this.loading = true
|
|
|
|
|
|
|
|
return UserSource.tokenLogin().then(user => {
|
|
return UserSource.tokenLogin().then(user => {
|
|
|
- this.loading = false
|
|
|
|
|
- this.user = { ...this.user, ...user }
|
|
|
|
|
|
|
+ this.loggedUser = { ...this.loggedUser, ...user }
|
|
|
notificationStore.notify('Signed in', 'success')
|
|
notificationStore.notify('Signed in', 'success')
|
|
|
- this.getUserInfo(user)
|
|
|
|
|
|
|
+ return this.getLoggedUserInfo()
|
|
|
|
|
+ }).then(() => {
|
|
|
|
|
+ return this.isAdmin()
|
|
|
|
|
+ }).then(() => {
|
|
|
|
|
+ this.loading = false
|
|
|
|
|
+ this.loggedIn = true
|
|
|
}).catch(() => {
|
|
}).catch(() => {
|
|
|
this.loading = false
|
|
this.loading = false
|
|
|
})
|
|
})
|
|
@@ -94,26 +114,130 @@ class UserStore {
|
|
|
|
|
|
|
|
@action switchProject(projectId: string): Promise<void> {
|
|
@action switchProject(projectId: string): Promise<void> {
|
|
|
notificationStore.notify('Switching projects')
|
|
notificationStore.notify('Switching projects')
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
|
|
- UserSource.switchProject().then(() => {
|
|
|
|
|
- return this.loginScoped(projectId)
|
|
|
|
|
- }).then(() => {
|
|
|
|
|
- resolve()
|
|
|
|
|
- }).catch(reason => {
|
|
|
|
|
- console.error('Error switching projects', reason)
|
|
|
|
|
- notificationStore.notify('Error switching projects')
|
|
|
|
|
- this.logout()
|
|
|
|
|
- reject()
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ return UserSource.switchProject().then(() => {
|
|
|
|
|
+ return this.loginScoped(projectId)
|
|
|
|
|
+ }).then(() => {
|
|
|
|
|
+ return this.isAdmin()
|
|
|
|
|
+ }).catch(reason => {
|
|
|
|
|
+ console.error('Error switching projects', reason)
|
|
|
|
|
+ notificationStore.notify('Error switching projects')
|
|
|
|
|
+ this.logout()
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@action logout(): Promise<void> {
|
|
@action logout(): Promise<void> {
|
|
|
|
|
+ this.loggedIn = false
|
|
|
|
|
+
|
|
|
return UserSource.logout().catch(reason => {
|
|
return UserSource.logout().catch(reason => {
|
|
|
console.log('Error logging out', reason)
|
|
console.log('Error logging out', reason)
|
|
|
notificationStore.notify('Error logging out')
|
|
notificationStore.notify('Error logging out')
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ @action getAllUsers(showLoading?: boolean): Promise<void> {
|
|
|
|
|
+ if (showLoading) this.allUsersLoading = true
|
|
|
|
|
+
|
|
|
|
|
+ return UserSource.getAllUsers().then(users => {
|
|
|
|
|
+ this.users = users
|
|
|
|
|
+ this.allUsersLoading = false
|
|
|
|
|
+ }).catch(() => {
|
|
|
|
|
+ this.allUsersLoading = false
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @action getUserInfo(userId: string): Promise<void> {
|
|
|
|
|
+ this.userDetailsLoading = true
|
|
|
|
|
+
|
|
|
|
|
+ return UserSource.getUserInfo(userId).then(user => {
|
|
|
|
|
+ this.userDetails = user
|
|
|
|
|
+ this.userDetailsLoading = false
|
|
|
|
|
+ }).catch(() => {
|
|
|
|
|
+ this.userDetailsLoading = false
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @action isAdmin(): Promise<void> {
|
|
|
|
|
+ if (!this.loggedUser) {
|
|
|
|
|
+ return Promise.resolve()
|
|
|
|
|
+ }
|
|
|
|
|
+ this.loggedUser.isAdmin = false
|
|
|
|
|
+ return UserSource.isAdmin(this.loggedUser.id).then(isAdmin => {
|
|
|
|
|
+ if (this.loggedUser) {
|
|
|
|
|
+ this.loggedUser.isAdmin = isAdmin
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(() => {
|
|
|
|
|
+ if (window.location.href.indexOf('#/project') > -1 || window.location.href.indexOf('#/user') > -1) {
|
|
|
|
|
+ window.location.href = '#/'
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @action clearUserDetails() {
|
|
|
|
|
+ this.userDetailsLoading = false
|
|
|
|
|
+ this.userDetails = null
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @action update(userId: string, user: User): Promise<void> {
|
|
|
|
|
+ this.updating = true
|
|
|
|
|
+
|
|
|
|
|
+ return UserSource.update(userId, user, this.userDetails).then((user: User) => {
|
|
|
|
|
+ this.userDetails = user
|
|
|
|
|
+ this.updating = false
|
|
|
|
|
+ if (this.loggedUser && this.loggedUser.id === userId) {
|
|
|
|
|
+ this.loggedUser.name = user.name
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(() => {
|
|
|
|
|
+ this.updating = false
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @action assignUserToProject(userId: string, projectId: string): Promise<void> {
|
|
|
|
|
+ this.updating = true
|
|
|
|
|
+
|
|
|
|
|
+ return UserSource.assignUserToProject(userId, projectId).then(() => {
|
|
|
|
|
+ this.updating = false
|
|
|
|
|
+ }).catch(() => { this.updating = false })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @action assignUserToProjectWithRole(userId: string, projectId: string, roleId: string): Promise<void> {
|
|
|
|
|
+ return UserSource.assignUserToProjectWithRole(userId, projectId, roleId)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @action add(user: User): Promise<?User> {
|
|
|
|
|
+ this.updating = true
|
|
|
|
|
+
|
|
|
|
|
+ return UserSource.add(user).then((user: User) => {
|
|
|
|
|
+ if (!this.users.find(u => u.id === user.id)) {
|
|
|
|
|
+ this.users = [
|
|
|
|
|
+ ...this.users,
|
|
|
|
|
+ user,
|
|
|
|
|
+ ]
|
|
|
|
|
+ this.users.sort((a, b) => a.name.localeCompare(b.name))
|
|
|
|
|
+ }
|
|
|
|
|
+ this.updating = false
|
|
|
|
|
+ return user
|
|
|
|
|
+ }).catch((ex) => {
|
|
|
|
|
+ console.error(ex)
|
|
|
|
|
+ this.updating = false
|
|
|
|
|
+ return null
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @action delete(userId: string): Promise<void> {
|
|
|
|
|
+ return UserSource.delete(userId).then(() => {
|
|
|
|
|
+ this.users = this.users.filter(u => u.id === userId)
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @action getProjects(userId: string): Promise<void> {
|
|
|
|
|
+ return UserSource.getProjects(userId).then((projects: Project[]) => {
|
|
|
|
|
+ this.projects = projects
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @action clearProjects() {
|
|
|
|
|
+ this.projects = []
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
export default new UserStore()
|
|
export default new UserStore()
|