• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / gui / 1113439055

19 Dec 2023 09:01PM UTC coverage: 82.752% (-17.2%) from 99.964%
1113439055

Pull #4258

gitlab-ci

mender-test-bot
chore: Types update

Signed-off-by: Mender Test Bot <mender@northern.tech>
Pull Request #4258: chore: Types update

4326 of 6319 branches covered (0.0%)

8348 of 10088 relevant lines covered (82.75%)

189.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.57
/src/js/components/settings/organization/organization.js
1
// Copyright 2017 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14
import React, { useCallback, useEffect, useState } from 'react';
15
import CopyToClipboard from 'react-copy-to-clipboard';
16
import { useDispatch, useSelector } from 'react-redux';
17

18
// material ui
19
import { FileCopy as CopyPasteIcon } from '@mui/icons-material';
20
import { Button, Checkbox, Collapse, FormControlLabel, List } from '@mui/material';
21
import { makeStyles } from 'tss-react/mui';
22

23
import copy from 'copy-to-clipboard';
24
import moment from 'moment';
25

26
import { setSnackbar } from '../../../actions/appActions';
27
import {
28
  changeSamlConfig,
29
  deleteSamlConfig,
30
  downloadLicenseReport,
31
  getSamlConfigs,
32
  getUserOrganization,
33
  storeSamlConfig
34
} from '../../../actions/organizationActions';
35
import { TIMEOUTS } from '../../../constants/appConstants';
36
import { createFileDownload, toggle } from '../../../helpers';
37
import { getCurrentSession, getFeatures, getIsEnterprise, getIsPreview, getOrganization, getUserRoles } from '../../../selectors';
38
import ExpandableAttribute from '../../common/expandable-attribute';
39
import { HELPTOOLTIPS, MenderHelpTooltip } from '../../helptips/helptooltips';
40
import Billing from './billing';
41
import OrganizationSettingsItem, { maxWidth } from './organizationsettingsitem';
42
import { SAMLConfig } from './samlconfig';
43

44
const useStyles = makeStyles()(theme => ({
13✔
45
  copyNotification: { height: 30, padding: 15 },
46
  deviceLimitBar: { backgroundColor: theme.palette.grey[500], margin: '15px 0' },
47
  ssoToggle: { width: `calc(${maxWidth}px + ${theme.spacing(4)})` },
48
  tenantInfo: { marginTop: 11, paddingBottom: 3, 'span': { marginLeft: theme.spacing(0.5), color: theme.palette.text.disabled } },
49
  tenantToken: { width: `calc(${maxWidth}px - ${theme.spacing(4)})` },
50
  tokenTitle: { paddingRight: 10 },
51
  tokenExplanation: { margin: '1em 0' }
52
}));
53

54
export const OrgHeader = () => {
5✔
55
  const { classes } = useStyles();
14✔
56
  return (
14✔
57
    <div className="flexbox center-aligned">
58
      <div className={classes.tokenTitle}>Organization token</div>
59
      <MenderHelpTooltip id={HELPTOOLTIPS.tenantToken.id} disableHoverListener={false} placement="top" />
60
    </div>
61
  );
62
};
63

64
export const CopyTextToClipboard = ({ onCopy, token }) => {
5✔
65
  const [copied, setCopied] = useState(false);
23✔
66
  const { classes } = useStyles();
23✔
67

68
  const onCopied = () => {
23✔
69
    setCopied(true);
×
70
    onCopy();
×
71
    setTimeout(() => setCopied(false), TIMEOUTS.fiveSeconds);
×
72
  };
73

74
  return (
23✔
75
    <div>
76
      <CopyToClipboard text={token} onCopy={onCopied}>
77
        <Button startIcon={<CopyPasteIcon />}>Copy to clipboard</Button>
78
      </CopyToClipboard>
79
      <div className={classes.copyNotification}>{copied && <span className="green fadeIn">Copied to clipboard.</span>}</div>
23!
80
    </div>
81
  );
82
};
83

84
export const Organization = () => {
5✔
85
  const [hasSingleSignOn, setHasSingleSignOn] = useState(false);
15✔
86
  const [isConfiguringSSO, setIsConfiguringSSO] = useState(false);
14✔
87
  const [isResettingSSO, setIsResettingSSO] = useState(false);
14✔
88
  const [showTokenWarning, setShowTokenWarning] = useState(false);
14✔
89
  const isEnterprise = useSelector(getIsEnterprise);
14✔
90
  const { isAdmin } = useSelector(getUserRoles);
14✔
91
  const canPreview = useSelector(getIsPreview);
14✔
92
  const { isHosted } = useSelector(getFeatures);
14✔
93
  const org = useSelector(getOrganization);
14✔
94
  const samlConfigs = useSelector(state => state.organization.samlConfigs);
26✔
95
  const dispatch = useDispatch();
14✔
96
  const { token } = useSelector(getCurrentSession);
14✔
97

98
  const { classes } = useStyles();
14✔
99

100
  useEffect(() => {
14✔
101
    dispatch(getUserOrganization());
3✔
102
  }, [dispatch]);
103

104
  useEffect(() => {
14✔
105
    if (isEnterprise) {
3!
106
      dispatch(getSamlConfigs());
3✔
107
    }
108
  }, [dispatch, isEnterprise]);
109

110
  useEffect(() => {
14✔
111
    setHasSingleSignOn(!!samlConfigs.length);
6✔
112
    setIsConfiguringSSO(!!samlConfigs.length);
6✔
113
  }, [samlConfigs.length]);
114

115
  const dispatchedSetSnackbar = useCallback((...args) => dispatch(setSnackbar(...args)), [dispatch]);
14✔
116

117
  const onSSOClick = () => {
14✔
118
    if (hasSingleSignOn) {
3!
119
      setIsConfiguringSSO(false);
3✔
120
      return setIsResettingSSO(true);
3✔
121
    }
122
    setIsConfiguringSSO(toggle);
×
123
  };
124

125
  const onCancelSSOSettings = () => {
14✔
126
    setIsResettingSSO(false);
×
127
    setIsConfiguringSSO(hasSingleSignOn);
×
128
  };
129

130
  const onSaveSSOSettings = useCallback(
14✔
131
    (id, fileContent) => {
132
      if (isResettingSSO) {
3!
133
        return dispatch(deleteSamlConfig(samlConfigs[0])).then(() => setIsResettingSSO(false));
3✔
134
      }
135
      if (id) {
×
136
        return dispatch(changeSamlConfig({ id, config: fileContent }));
×
137
      }
138
      return dispatch(storeSamlConfig(fileContent));
×
139
    },
140
    [isResettingSSO, dispatch, samlConfigs]
141
  );
142

143
  const onTokenExpansion = useCallback(() => setShowTokenWarning(true), []);
14✔
144

145
  const onDownloadReportClick = () =>
14✔
146
    dispatch(downloadLicenseReport()).then(report => createFileDownload(report, `Mender-license-report-${moment().format(moment.HTML5_FMT.DATE)}`, token));
×
147

148
  const onTenantInfoClick = () => {
14✔
149
    copy(`Organization: ${org.name}, Tenant ID: ${org.id}`);
×
150
    setSnackbar('Copied to clipboard');
×
151
  };
152

153
  return (
14✔
154
    <div className="margin-top-small">
155
      <h2 className="margin-top-small">Organization and billing</h2>
156
      <List>
157
        <OrganizationSettingsItem
158
          title="Organization name"
159
          content={{
160
            action: { action: onTenantInfoClick, internal: true },
161
            description: (
162
              <div className={`clickable ${classes.tenantInfo}`} onClick={onTenantInfoClick}>
163
                {org.name}
164
                <span>({org.id})</span>
165
              </div>
166
            )
167
          }}
168
        />
169
        <OrganizationSettingsItem
170
          title={<OrgHeader />}
171
          content={{}}
172
          secondary={
173
            <>
174
              <ExpandableAttribute
175
                className={classes.tenantToken}
176
                component="div"
177
                disableGutters
178
                dividerDisabled
179
                key="org_token"
180
                onExpansion={onTokenExpansion}
181
                secondary={org.tenant_token}
182
                textClasses={{ secondary: 'inventory-text tenant-token-text' }}
183
              />
184
              {showTokenWarning && (
14!
185
                <p className="warning">
186
                  <b>Important</b>
187
                  <br />
188
                  Do not share your organization token with others. Treat this token like a password, as it can be used to request authorization for new
189
                  devices.
190
                </p>
191
              )}
192
            </>
193
          }
194
          sideBarContent={<CopyTextToClipboard onCopy={onTokenExpansion} token={org.tenant_token} />}
195
        />
196
      </List>
197
      {isEnterprise && isAdmin && (
42✔
198
        <div className="flexbox center-aligned">
199
          <FormControlLabel
200
            className={`margin-bottom-small ${classes.ssoToggle}`}
201
            control={<Checkbox checked={!isResettingSSO && (hasSingleSignOn || isConfiguringSSO)} onChange={onSSOClick} />}
30✔
202
            label="Enable SAML single sign-on"
203
          />
204
          {isResettingSSO && !isConfiguringSSO && (
22✔
205
            <>
206
              <Button onClick={onCancelSSOSettings}>Cancel</Button>
207
              <Button onClick={onSaveSSOSettings} disabled={!hasSingleSignOn} variant="contained">
208
                Save
209
              </Button>
210
            </>
211
          )}
212
        </div>
213
      )}
214
      <Collapse className="margin-left-large" in={isConfiguringSSO}>
215
        <SAMLConfig configs={samlConfigs} onSave={onSaveSSOSettings} onCancel={onCancelSSOSettings} setSnackbar={dispatchedSetSnackbar} token={token} />
216
      </Collapse>
217
      {isHosted && <Billing />}
28✔
218
      {(canPreview || !isHosted) && isEnterprise && isAdmin && (
28!
219
        <Button className="margin-top" onClick={onDownloadReportClick} variant="contained">
220
          Download license report
221
        </Button>
222
      )}
223
    </div>
224
  );
225
};
226

227
export default Organization;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc