actions.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. import { debounce } from 'lodash';
  2. import { dateTimeFormatTimeAgo } from '@grafana/data';
  3. import { featureEnabled, getBackendSrv, locationService } from '@grafana/runtime';
  4. import config from 'app/core/config';
  5. import { contextSrv } from 'app/core/core';
  6. import { accessControlQueryParam } from 'app/core/utils/accessControl';
  7. import { ThunkResult, LdapUser, UserSession, UserDTO, AccessControlAction, UserFilter } from 'app/types';
  8. import {
  9. userAdminPageLoadedAction,
  10. userProfileLoadedAction,
  11. userOrgsLoadedAction,
  12. userSessionsLoadedAction,
  13. userAdminPageFailedAction,
  14. ldapConnectionInfoLoadedAction,
  15. ldapSyncStatusLoadedAction,
  16. userMappingInfoLoadedAction,
  17. userMappingInfoFailedAction,
  18. clearUserMappingInfoAction,
  19. clearUserErrorAction,
  20. ldapFailedAction,
  21. usersFetched,
  22. queryChanged,
  23. pageChanged,
  24. filterChanged,
  25. usersFetchBegin,
  26. usersFetchEnd,
  27. } from './reducers';
  28. // UserAdminPage
  29. export function loadAdminUserPage(userId: number): ThunkResult<void> {
  30. return async (dispatch) => {
  31. try {
  32. dispatch(userAdminPageLoadedAction(false));
  33. await dispatch(loadUserProfile(userId));
  34. await dispatch(loadUserOrgs(userId));
  35. await dispatch(loadUserSessions(userId));
  36. if (config.ldapEnabled && featureEnabled('ldapsync')) {
  37. await dispatch(loadLdapSyncStatus());
  38. }
  39. dispatch(userAdminPageLoadedAction(true));
  40. } catch (error) {
  41. console.error(error);
  42. const userError = {
  43. title: error.data.message,
  44. body: error.data.error,
  45. };
  46. dispatch(userAdminPageFailedAction(userError));
  47. }
  48. };
  49. }
  50. export function loadUserProfile(userId: number): ThunkResult<void> {
  51. return async (dispatch) => {
  52. const user = await getBackendSrv().get(`/api/users/${userId}`, accessControlQueryParam());
  53. dispatch(userProfileLoadedAction(user));
  54. };
  55. }
  56. export function updateUser(user: UserDTO): ThunkResult<void> {
  57. return async (dispatch) => {
  58. await getBackendSrv().put(`/api/users/${user.id}`, user);
  59. dispatch(loadAdminUserPage(user.id));
  60. };
  61. }
  62. export function setUserPassword(userId: number, password: string): ThunkResult<void> {
  63. return async (dispatch) => {
  64. const payload = { password };
  65. await getBackendSrv().put(`/api/admin/users/${userId}/password`, payload);
  66. dispatch(loadAdminUserPage(userId));
  67. };
  68. }
  69. export function disableUser(userId: number): ThunkResult<void> {
  70. return async (dispatch) => {
  71. await getBackendSrv().post(`/api/admin/users/${userId}/disable`);
  72. locationService.push('/admin/users');
  73. };
  74. }
  75. export function enableUser(userId: number): ThunkResult<void> {
  76. return async (dispatch) => {
  77. await getBackendSrv().post(`/api/admin/users/${userId}/enable`);
  78. dispatch(loadAdminUserPage(userId));
  79. };
  80. }
  81. export function deleteUser(userId: number): ThunkResult<void> {
  82. return async (dispatch) => {
  83. await getBackendSrv().delete(`/api/admin/users/${userId}`);
  84. locationService.push('/admin/users');
  85. };
  86. }
  87. export function updateUserPermissions(userId: number, isGrafanaAdmin: boolean): ThunkResult<void> {
  88. return async (dispatch) => {
  89. const payload = { isGrafanaAdmin };
  90. await getBackendSrv().put(`/api/admin/users/${userId}/permissions`, payload);
  91. dispatch(loadAdminUserPage(userId));
  92. };
  93. }
  94. export function loadUserOrgs(userId: number): ThunkResult<void> {
  95. return async (dispatch) => {
  96. const orgs = await getBackendSrv().get(`/api/users/${userId}/orgs`);
  97. dispatch(userOrgsLoadedAction(orgs));
  98. };
  99. }
  100. export function addOrgUser(user: UserDTO, orgId: number, role: string): ThunkResult<void> {
  101. return async (dispatch) => {
  102. const payload = {
  103. loginOrEmail: user.login,
  104. role: role,
  105. };
  106. await getBackendSrv().post(`/api/orgs/${orgId}/users/`, payload);
  107. dispatch(loadAdminUserPage(user.id));
  108. };
  109. }
  110. export function updateOrgUserRole(userId: number, orgId: number, role: string): ThunkResult<void> {
  111. return async (dispatch) => {
  112. const payload = { role };
  113. await getBackendSrv().patch(`/api/orgs/${orgId}/users/${userId}`, payload);
  114. dispatch(loadAdminUserPage(userId));
  115. };
  116. }
  117. export function deleteOrgUser(userId: number, orgId: number): ThunkResult<void> {
  118. return async (dispatch) => {
  119. await getBackendSrv().delete(`/api/orgs/${orgId}/users/${userId}`);
  120. dispatch(loadAdminUserPage(userId));
  121. };
  122. }
  123. export function loadUserSessions(userId: number): ThunkResult<void> {
  124. return async (dispatch) => {
  125. if (!contextSrv.hasPermission(AccessControlAction.UsersAuthTokenList)) {
  126. return;
  127. }
  128. const tokens = await getBackendSrv().get(`/api/admin/users/${userId}/auth-tokens`);
  129. tokens.reverse();
  130. const sessions = tokens.map((session: UserSession) => {
  131. return {
  132. id: session.id,
  133. isActive: session.isActive,
  134. seenAt: dateTimeFormatTimeAgo(session.seenAt),
  135. createdAt: session.createdAt,
  136. clientIp: session.clientIp,
  137. browser: session.browser,
  138. browserVersion: session.browserVersion,
  139. os: session.os,
  140. osVersion: session.osVersion,
  141. device: session.device,
  142. };
  143. });
  144. dispatch(userSessionsLoadedAction(sessions));
  145. };
  146. }
  147. export function revokeSession(tokenId: number, userId: number): ThunkResult<void> {
  148. return async (dispatch) => {
  149. const payload = { authTokenId: tokenId };
  150. await getBackendSrv().post(`/api/admin/users/${userId}/revoke-auth-token`, payload);
  151. dispatch(loadUserSessions(userId));
  152. };
  153. }
  154. export function revokeAllSessions(userId: number): ThunkResult<void> {
  155. return async (dispatch) => {
  156. await getBackendSrv().post(`/api/admin/users/${userId}/logout`);
  157. dispatch(loadUserSessions(userId));
  158. };
  159. }
  160. // LDAP user actions
  161. export function loadLdapSyncStatus(): ThunkResult<void> {
  162. return async (dispatch) => {
  163. // Available only in enterprise
  164. const canReadLDAPStatus = contextSrv.hasPermission(AccessControlAction.LDAPStatusRead);
  165. if (featureEnabled('ldapsync') && canReadLDAPStatus) {
  166. const syncStatus = await getBackendSrv().get(`/api/admin/ldap-sync-status`);
  167. dispatch(ldapSyncStatusLoadedAction(syncStatus));
  168. }
  169. };
  170. }
  171. export function syncLdapUser(userId: number): ThunkResult<void> {
  172. return async (dispatch) => {
  173. await getBackendSrv().post(`/api/admin/ldap/sync/${userId}`);
  174. dispatch(loadAdminUserPage(userId));
  175. };
  176. }
  177. // LDAP debug page
  178. export function loadLdapState(): ThunkResult<void> {
  179. return async (dispatch) => {
  180. if (!contextSrv.hasPermission(AccessControlAction.LDAPStatusRead)) {
  181. return;
  182. }
  183. try {
  184. const connectionInfo = await getBackendSrv().get(`/api/admin/ldap/status`);
  185. dispatch(ldapConnectionInfoLoadedAction(connectionInfo));
  186. } catch (error) {
  187. error.isHandled = true;
  188. const ldapError = {
  189. title: error.data.message,
  190. body: error.data.error,
  191. };
  192. dispatch(ldapFailedAction(ldapError));
  193. }
  194. };
  195. }
  196. export function loadUserMapping(username: string): ThunkResult<void> {
  197. return async (dispatch) => {
  198. try {
  199. const response = await getBackendSrv().get(`/api/admin/ldap/${encodeURIComponent(username)}`);
  200. const { name, surname, email, login, isGrafanaAdmin, isDisabled, roles, teams } = response;
  201. const userInfo: LdapUser = {
  202. info: { name, surname, email, login },
  203. permissions: { isGrafanaAdmin, isDisabled },
  204. roles,
  205. teams,
  206. };
  207. dispatch(userMappingInfoLoadedAction(userInfo));
  208. } catch (error) {
  209. error.isHandled = true;
  210. const userError = {
  211. title: error.data.message,
  212. body: error.data.error,
  213. };
  214. dispatch(clearUserMappingInfoAction());
  215. dispatch(userMappingInfoFailedAction(userError));
  216. }
  217. };
  218. }
  219. export function clearUserError(): ThunkResult<void> {
  220. return (dispatch) => {
  221. dispatch(clearUserErrorAction());
  222. };
  223. }
  224. export function clearUserMappingInfo(): ThunkResult<void> {
  225. return (dispatch) => {
  226. dispatch(clearUserErrorAction());
  227. dispatch(clearUserMappingInfoAction());
  228. };
  229. }
  230. // UserListAdminPage
  231. const getFilters = (filters: UserFilter[]) => {
  232. return filters
  233. .map((filter) => {
  234. if (Array.isArray(filter.value)) {
  235. return filter.value.map((v) => `${filter.name}=${v.value}`).join('&');
  236. }
  237. return `${filter.name}=${filter.value}`;
  238. })
  239. .join('&');
  240. };
  241. export function fetchUsers(): ThunkResult<void> {
  242. return async (dispatch, getState) => {
  243. try {
  244. const { perPage, page, query, filters } = getState().userListAdmin;
  245. const result = await getBackendSrv().get(
  246. `/api/users/search?perpage=${perPage}&page=${page}&query=${query}&${getFilters(filters)}`
  247. );
  248. dispatch(usersFetched(result));
  249. } catch (error) {
  250. usersFetchEnd();
  251. console.error(error);
  252. }
  253. };
  254. }
  255. const fetchUsersWithDebounce = debounce((dispatch) => dispatch(fetchUsers()), 500);
  256. export function changeQuery(query: string): ThunkResult<void> {
  257. return async (dispatch) => {
  258. dispatch(usersFetchBegin());
  259. dispatch(queryChanged(query));
  260. fetchUsersWithDebounce(dispatch);
  261. };
  262. }
  263. export function changeFilter(filter: UserFilter): ThunkResult<void> {
  264. return async (dispatch) => {
  265. dispatch(usersFetchBegin());
  266. dispatch(filterChanged(filter));
  267. fetchUsersWithDebounce(dispatch);
  268. };
  269. }
  270. export function changePage(page: number): ThunkResult<void> {
  271. return async (dispatch) => {
  272. dispatch(usersFetchBegin());
  273. dispatch(pageChanged(page));
  274. dispatch(fetchUsers());
  275. };
  276. }