1import React, { PureComponent } from 'react';
2import { connect, ConnectedProps } from 'react-redux';
3import { NavModel } from '@grafana/data';
4import { Alert, Button, LegacyForms } from '@grafana/ui';
5const { FormField } = LegacyForms;
6import { getNavModel } from 'app/core/selectors/navModel';
7import config from 'app/core/config';
8import Page from 'app/core/components/Page/Page';
9import { LdapConnectionStatus } from './LdapConnectionStatus';
10import { LdapSyncInfo } from './LdapSyncInfo';
11import { LdapUserInfo } from './LdapUserInfo';
12import {
13  AppNotificationSeverity,
14  LdapError,
15  LdapUser,
16  StoreState,
17  SyncInfo,
18  LdapConnectionInfo,
19  AccessControlAction,
20} from 'app/types';
21import {
22  loadLdapState,
23  loadLdapSyncStatus,
24  loadUserMapping,
25  clearUserError,
26  clearUserMappingInfo,
27} from '../state/actions';
28import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
29import { contextSrv } from 'app/core/core';
30
31interface OwnProps extends GrafanaRouteComponentProps<{}, { username?: string }> {
32  navModel: NavModel;
33  ldapConnectionInfo: LdapConnectionInfo;
34  ldapUser?: LdapUser;
35  ldapSyncInfo?: SyncInfo;
36  ldapError?: LdapError;
37  userError?: LdapError;
38}
39
40interface State {
41  isLoading: boolean;
42}
43
44export class LdapPage extends PureComponent<Props, State> {
45  state = {
46    isLoading: true,
47  };
48
49  async componentDidMount() {
50    const { clearUserMappingInfo, queryParams } = this.props;
51    await clearUserMappingInfo();
52    await this.fetchLDAPStatus();
53
54    if (queryParams.username) {
55      await this.fetchUserMapping(queryParams.username);
56    }
57
58    this.setState({ isLoading: false });
59  }
60
61  async fetchLDAPStatus() {
62    const { loadLdapState, loadLdapSyncStatus } = this.props;
63    return Promise.all([loadLdapState(), loadLdapSyncStatus()]);
64  }
65
66  async fetchUserMapping(username: string) {
67    const { loadUserMapping } = this.props;
68    return await loadUserMapping(username);
69  }
70
71  search = (event: any) => {
72    event.preventDefault();
73    const username = event.target.elements['username'].value;
74    if (username) {
75      this.fetchUserMapping(username);
76    }
77  };
78
79  onClearUserError = () => {
80    this.props.clearUserError();
81  };
82
83  render() {
84    const { ldapUser, userError, ldapError, ldapSyncInfo, ldapConnectionInfo, navModel, queryParams } = this.props;
85    const { isLoading } = this.state;
86    const canReadLDAPUser = contextSrv.hasPermission(AccessControlAction.LDAPUsersRead);
87
88    return (
89      <Page navModel={navModel}>
90        <Page.Contents isLoading={isLoading}>
91          <>
92            {ldapError && ldapError.title && (
93              <div className="gf-form-group">
94                <Alert title={ldapError.title} severity={AppNotificationSeverity.Error}>
95                  {ldapError.body}
96                </Alert>
97              </div>
98            )}
99
100            <LdapConnectionStatus ldapConnectionInfo={ldapConnectionInfo} />
101
102            {config.licenseInfo.hasLicense && ldapSyncInfo && <LdapSyncInfo ldapSyncInfo={ldapSyncInfo} />}
103
104            {canReadLDAPUser && (
105              <>
106                <h3 className="page-heading">Test user mapping</h3>
107                <div className="gf-form-group">
108                  <form onSubmit={this.search} className="gf-form-inline">
109                    <FormField
110                      label="Username"
111                      labelWidth={8}
112                      inputWidth={30}
113                      type="text"
114                      id="username"
115                      name="username"
116                      defaultValue={queryParams.username}
117                    />
118                    <Button type="submit">Run</Button>
119                  </form>
120                </div>
121                {userError && userError.title && (
122                  <div className="gf-form-group">
123                    <Alert
124                      title={userError.title}
125                      severity={AppNotificationSeverity.Error}
126                      onRemove={this.onClearUserError}
127                    >
128                      {userError.body}
129                    </Alert>
130                  </div>
131                )}
132                {ldapUser && <LdapUserInfo ldapUser={ldapUser} showAttributeMapping={true} />}
133              </>
134            )}
135          </>
136        </Page.Contents>
137      </Page>
138    );
139  }
140}
141
142const mapStateToProps = (state: StoreState) => ({
143  navModel: getNavModel(state.navIndex, 'ldap'),
144  ldapConnectionInfo: state.ldap.connectionInfo,
145  ldapUser: state.ldap.user,
146  ldapSyncInfo: state.ldap.syncInfo,
147  userError: state.ldap.userError,
148  ldapError: state.ldap.ldapError,
149});
150
151const mapDispatchToProps = {
152  loadLdapState,
153  loadLdapSyncStatus,
154  loadUserMapping,
155  clearUserError,
156  clearUserMappingInfo,
157};
158
159const connector = connect(mapStateToProps, mapDispatchToProps);
160type Props = OwnProps & ConnectedProps<typeof connector>;
161
162export default connector(LdapPage);
163