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

mendersoftware / gui / 897326496

pending completion
897326496

Pull #3752

gitlab-ci

mzedel
chore(e2e): made use of shared timeout & login checking values to remove code duplication

Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #3752: chore(e2e-tests): slightly simplified log in test + separated log out test

4395 of 6392 branches covered (68.76%)

8060 of 9780 relevant lines covered (82.41%)

126.17 hits per line

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

69.44
/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 { connect } from 'react-redux';
17

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

23
import moment from 'moment';
24

25
import { setSnackbar } from '../../../actions/appActions';
26
import {
27
  changeSamlConfig,
28
  deleteSamlConfig,
29
  downloadLicenseReport,
30
  getSamlConfigs,
31
  getUserOrganization,
32
  storeSamlConfig
33
} from '../../../actions/organizationActions';
34
import { TIMEOUTS } from '../../../constants/appConstants';
35
import { createFileDownload, toggle, versionCompare } from '../../../helpers';
36
import { getTenantCapabilities, getUserRoles } from '../../../selectors';
37
import ExpandableAttribute from '../../common/expandable-attribute';
38
import { MenderTooltipClickable } from '../../common/mendertooltip';
39
import Billing from './billing';
40
import OrganizationSettingsItem, { maxWidth } from './organizationsettingsitem';
41
import { SAMLConfig } from './samlconfig';
42

43
const useStyles = makeStyles()(theme => ({
10✔
44
  copyNotification: { height: 30, padding: 15 },
45
  deviceLimitBar: { backgroundColor: theme.palette.grey[500], margin: '15px 0' },
46
  ssoToggle: { width: `calc(${maxWidth}px + ${theme.spacing(4)})` },
47
  tenantToken: { width: `calc(${maxWidth}px - ${theme.spacing(4)})` },
48
  tokenTitle: { paddingRight: 10 },
49
  tokenExplanation: { margin: '1em 0' }
50
}));
51

52
export const OrgHeader = () => {
6✔
53
  const { classes } = useStyles();
5✔
54
  return (
5✔
55
    <div className="flexbox center-aligned">
56
      <div className={classes.tokenTitle}>Organization token</div>
57
      <MenderTooltipClickable
58
        disableHoverListener={false}
59
        placement="top"
60
        title={
61
          <>
62
            <h3>Organization token</h3>
63
            <p className={classes.tokenExplanation}>
64
              This token is unique for your organization and ensures that only devices that you own are able to connect to your account.
65
            </p>
66
          </>
67
        }
68
      >
69
        <InfoIcon />
70
      </MenderTooltipClickable>
71
    </div>
72
  );
73
};
74

75
export const CopyTextToClipboard = ({ token }) => {
6✔
76
  const [copied, setCopied] = useState(false);
11✔
77
  const { classes } = useStyles();
11✔
78

79
  const onCopied = () => {
11✔
80
    setCopied(true);
×
81
    setTimeout(() => setCopied(false), TIMEOUTS.fiveSeconds);
×
82
  };
83

84
  return (
11✔
85
    <div>
86
      <CopyToClipboard text={token} onCopy={onCopied}>
87
        <Button startIcon={<CopyPasteIcon />}>Copy to clipboard</Button>
88
      </CopyToClipboard>
89
      <div className={classes.copyNotification}>{copied && <span className="green fadeIn">Copied to clipboard.</span>}</div>
11!
90
    </div>
91
  );
92
};
93

94
export const Organization = ({
6✔
95
  canPreview,
96
  changeSamlConfig,
97
  deleteSamlConfig,
98
  downloadLicenseReport,
99
  getSamlConfigs,
100
  getUserOrganization,
101
  isAdmin,
102
  isEnterprise,
103
  isHosted,
104
  org,
105
  samlConfigs,
106
  setSnackbar,
107
  storeSamlConfig
108
}) => {
109
  const [hasSingleSignOn, setHasSingleSignOn] = useState(false);
4✔
110
  const [isResettingSSO, setIsResettingSSO] = useState(false);
4✔
111
  const [isConfiguringSSO, setIsConfiguringSSO] = useState(false);
4✔
112

113
  const { classes } = useStyles();
4✔
114

115
  useEffect(() => {
4✔
116
    getUserOrganization();
2✔
117
  }, []);
118

119
  useEffect(() => {
4✔
120
    if (isEnterprise) {
2!
121
      getSamlConfigs();
2✔
122
    }
123
  }, [isEnterprise]);
124

125
  useEffect(() => {
4✔
126
    setHasSingleSignOn(!!samlConfigs.length);
2✔
127
    setIsConfiguringSSO(!!samlConfigs.length);
2✔
128
  }, [samlConfigs]);
129

130
  const onSSOClick = () => {
4✔
131
    if (hasSingleSignOn) {
1!
132
      setIsConfiguringSSO(false);
×
133
      return setIsResettingSSO(true);
×
134
    }
135
    setIsConfiguringSSO(toggle);
1✔
136
  };
137

138
  const onCancelSSOSettings = () => {
4✔
139
    setIsResettingSSO(false);
1✔
140
    setIsConfiguringSSO(hasSingleSignOn);
1✔
141
  };
142

143
  const onSaveSSOSettings = useCallback(
4✔
144
    (id, fileContent) => {
145
      if (isResettingSSO) {
×
146
        return deleteSamlConfig(samlConfigs[0]).then(() => setIsResettingSSO(false));
×
147
      }
148
      if (id) {
×
149
        return changeSamlConfig({ id, config: fileContent });
×
150
      }
151
      return storeSamlConfig(fileContent);
×
152
    },
153
    [isResettingSSO, changeSamlConfig, deleteSamlConfig, storeSamlConfig]
154
  );
155

156
  const onDownloadReportClick = () =>
4✔
157
    downloadLicenseReport().then(report => createFileDownload(report, `Mender-license-report-${moment().format(moment.HTML5_FMT.DATE)}`));
×
158

159
  return (
4✔
160
    <div className="margin-top-small">
161
      <h2 className="margin-top-small">Organization and billing</h2>
162
      <List>
163
        <OrganizationSettingsItem title="Organization name" content={{ action: { internal: true }, description: org.name }} />
164
        <OrganizationSettingsItem
165
          title={<OrgHeader />}
166
          content={{}}
167
          secondary={
168
            <ExpandableAttribute
169
              className={classes.tenantToken}
170
              component="div"
171
              disableGutters
172
              dividerDisabled
173
              key="org_token"
174
              secondary={org.tenant_token}
175
              textClasses={{ secondary: 'inventory-text tenant-token-text' }}
176
            />
177
          }
178
          sideBarContent={<CopyTextToClipboard token={org.tenant_token} />}
179
        />
180
      </List>
181
      {isEnterprise && isAdmin && (
12✔
182
        <div className="flexbox center-aligned">
183
          <FormControlLabel
184
            className={`margin-bottom-small ${classes.ssoToggle}`}
185
            control={<Checkbox checked={!isResettingSSO && (hasSingleSignOn || isConfiguringSSO)} onChange={onSSOClick} />}
12✔
186
            label="Enable SAML single sign-on"
187
          />
188
          {isResettingSSO && !isConfiguringSSO && (
4!
189
            <>
190
              <Button onClick={onCancelSSOSettings}>Cancel</Button>
191
              <Button onClick={onSaveSSOSettings} disabled={!hasSingleSignOn} variant="contained">
192
                Save
193
              </Button>
194
            </>
195
          )}
196
        </div>
197
      )}
198
      <Collapse className="margin-left-large" in={isConfiguringSSO}>
199
        <SAMLConfig configs={samlConfigs} onSave={onSaveSSOSettings} onCancel={onCancelSSOSettings} setSnackbar={setSnackbar} />
200
      </Collapse>
201
      {isHosted && <Billing />}
8✔
202
      {(canPreview || !isHosted) && isEnterprise && isAdmin && (
8!
203
        <Button className="margin-top" onClick={onDownloadReportClick} variant="contained">
204
          Download license report
205
        </Button>
206
      )}
207
    </div>
208
  );
209
};
210

211
const actionCreators = { changeSamlConfig, deleteSamlConfig, downloadLicenseReport, getSamlConfigs, getUserOrganization, setSnackbar, storeSamlConfig };
6✔
212

213
const mapStateToProps = state => {
6✔
214
  const { isEnterprise } = getTenantCapabilities(state);
2✔
215
  const { isAdmin } = getUserRoles(state);
2✔
216
  return {
2✔
217
    canPreview: versionCompare(state.app.versionInformation.Integration, 'next') > -1,
218
    hasReporting: state.app.features.hasReporting,
219
    isAdmin,
220
    isEnterprise,
221
    isHosted: state.app.features.isHosted,
222
    org: state.organization.organization,
223
    samlConfigs: state.organization.samlConfigs
224
  };
225
};
226

227
export default connect(mapStateToProps, actionCreators)(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