UserSessions.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import { css } from '@emotion/css';
  2. import { withI18n, withI18nProps } from '@lingui/react';
  3. import React, { PureComponent } from 'react';
  4. import { ConfirmButton, ConfirmModal, Button } from '@grafana/ui';
  5. import { contextSrv } from 'app/core/core';
  6. import { AccessControlAction, UserSession } from 'app/types';
  7. interface Props extends withI18nProps {
  8. sessions: UserSession[];
  9. onSessionRevoke: (id: number) => void;
  10. onAllSessionsRevoke: () => void;
  11. }
  12. interface State {
  13. showLogoutModal: boolean;
  14. }
  15. class BaseUserSessions extends PureComponent<Props, State> {
  16. forceAllLogoutButton = React.createRef<HTMLButtonElement>();
  17. state: State = {
  18. showLogoutModal: false,
  19. };
  20. showLogoutConfirmationModal = () => {
  21. this.setState({ showLogoutModal: true });
  22. };
  23. dismissLogoutConfirmationModal = () => {
  24. this.setState({ showLogoutModal: false }, () => {
  25. this.forceAllLogoutButton.current?.focus();
  26. });
  27. };
  28. onSessionRevoke = (id: number) => {
  29. return () => {
  30. this.props.onSessionRevoke(id);
  31. };
  32. };
  33. onAllSessionsRevoke = () => {
  34. this.setState({ showLogoutModal: false });
  35. this.props.onAllSessionsRevoke();
  36. };
  37. render() {
  38. const { sessions, i18n } = this.props;
  39. const { showLogoutModal } = this.state;
  40. const logoutFromAllDevicesClass = css`
  41. margin-top: 0.8rem;
  42. `;
  43. const canLogout = contextSrv.hasPermission(AccessControlAction.UsersLogout);
  44. return (
  45. <>
  46. <h3 className="page-heading">Sessions</h3>
  47. <div className="gf-form-group">
  48. <div className="gf-form">
  49. <table className="filter-table form-inline">
  50. <thead>
  51. <tr>
  52. <th>Last seen</th>
  53. <th>Logged on</th>
  54. <th>IP address</th>
  55. <th colSpan={2}>Browser and OS</th>
  56. </tr>
  57. </thead>
  58. <tbody>
  59. {sessions &&
  60. sessions.map((session, index) => (
  61. <tr key={`${session.id}-${index}`}>
  62. <td>{session.isActive ? 'Now' : session.seenAt}</td>
  63. <td>{i18n.date(session.createdAt, { dateStyle: 'long' })}</td>
  64. <td>{session.clientIp}</td>
  65. <td>{`${session.browser} on ${session.os} ${session.osVersion}`}</td>
  66. <td>
  67. <div className="pull-right">
  68. {canLogout && (
  69. <ConfirmButton
  70. confirmText="Confirm logout"
  71. confirmVariant="destructive"
  72. onConfirm={this.onSessionRevoke(session.id)}
  73. autoFocus
  74. >
  75. Force logout
  76. </ConfirmButton>
  77. )}
  78. </div>
  79. </td>
  80. </tr>
  81. ))}
  82. </tbody>
  83. </table>
  84. </div>
  85. <div className={logoutFromAllDevicesClass}>
  86. {canLogout && sessions.length > 0 && (
  87. <Button variant="secondary" onClick={this.showLogoutConfirmationModal} ref={this.forceAllLogoutButton}>
  88. Force logout from all devices
  89. </Button>
  90. )}
  91. <ConfirmModal
  92. isOpen={showLogoutModal}
  93. title="Force logout from all devices"
  94. body="Are you sure you want to force logout from all devices?"
  95. confirmText="Force logout"
  96. onConfirm={this.onAllSessionsRevoke}
  97. onDismiss={this.dismissLogoutConfirmationModal}
  98. />
  99. </div>
  100. </div>
  101. </>
  102. );
  103. }
  104. }
  105. export const UserSessions = withI18n()(BaseUserSessions);