Skip to content

Commit

Permalink
Disable and update user forms if OIDC is enabled (#2859)
Browse files Browse the repository at this point in the history
* Send oicd is enabled value to frontend

* Create users function to check sso is enabled

* Add sso differences in the users views

* Add sso differences in profile views

* Disable password update requested notification

* Correct typo and remove test debug

* Move sso auth logic to auth folder

* Format mix.exs file

* Disable oidc by default in dev
  • Loading branch information
arbulu89 authored and EMaksy committed Aug 19, 2024
1 parent a1e1f10 commit 2912d0d
Show file tree
Hide file tree
Showing 24 changed files with 389 additions and 136 deletions.
2 changes: 2 additions & 0 deletions assets/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ module.exports = {
config: {
checksServiceBaseUrl: '',
suseManagerEnabled: true,
adminUsername: 'admin',
oidcEnabled: false,
aTestVariable: 123,
},
},
Expand Down
5 changes: 5 additions & 0 deletions assets/js/lib/auth/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { getFromConfig } from '@lib/config';

const OIDC_ENABLED = getFromConfig('oidcEnabled') || false;

export const isSingleSignOnEnabled = () => OIDC_ENABLED;
7 changes: 7 additions & 0 deletions assets/js/lib/auth/config.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { isSingleSignOnEnabled } from './config';

describe('auth config', () => {
it('should check if single sign on is enabled', () => {
expect(isSingleSignOnEnabled()).toBe(false);
});
});
4 changes: 2 additions & 2 deletions assets/js/lib/model/users.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { isAdmin } from './users';

describe('users', () => {
it('should check if a user is admin', () => {
const admin = adminUser.build();
const admin = adminUser.build({ username: 'admin' });
expect(isAdmin(admin)).toBe(true);

const user = userFactory.build({ id: 2 });
const user = userFactory.build({ username: 'other' });
expect(isAdmin(user)).toBe(false);
});
});
115 changes: 62 additions & 53 deletions assets/js/pages/Profile/ProfileForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ function ProfileForm({
disableForm,
passwordModalOpen = false,
totpBoxOpen = false,
singleSignOnEnabled = false,
toggleTotpBox = noop,
togglePasswordModal = noop,
onSave = noop,
Expand Down Expand Up @@ -94,6 +95,7 @@ function ProfileForm({
setFullName(value);
setFullNameError(null);
}}
disabled={singleSignOnEnabled}
/>
{fullNameErrorState && errorMessage(fullNameErrorState)}
</div>
Expand All @@ -108,61 +110,66 @@ function ProfileForm({
setEmailAddress(value);
setEmailAddressError(null);
}}
disabled={singleSignOnEnabled}
/>
{emailAddressErrorState && errorMessage(emailAddressErrorState)}
</div>
<Label className="col-start-1 col-span-1">Username</Label>
<div className="col-start-2 col-span-3">
<Input value={username} aria-label="username" disabled />
</div>
<Label className="col-start-1 col-span-1">Password</Label>
<div className="col-start-2 col-span-3">
<Button
onClick={togglePasswordModal}
type="primary-white"
disabled={loading || disableForm}
>
Change Password
</Button>
</div>

<Label
className="col-start-1 col-span-1"
info="Setup a multi-factor TOTP authentication besides your password to increase security
for your account."
>
Authenticator App
</Label>
<div className="col-start-2 col-span-3">
<div className="inline-flex">
<Switch
selected={totpEnabled}
onChange={toggleTotp}
disabled={loading || disableForm || totpBoxOpen}
/>
{totpBoxOpen && (
<span
className="ml-5 cursor-pointer text-jungle-green-500 hover:opacity-75"
onClick={() => toggleTotpBox(false)}
aria-hidden="true"
{!singleSignOnEnabled && (
<>
<Label className="col-start-1 col-span-1">Password</Label>
<div className="col-start-2 col-span-3">
<Button
onClick={togglePasswordModal}
type="primary-white"
disabled={loading || disableForm || singleSignOnEnabled}
>
Cancel
</span>
)}
</div>
Change Password
</Button>
</div>

{totpBoxOpen && (
<Label
className="col-start-1 col-span-1"
info="Setup a multi-factor TOTP authentication besides your password to increase security
for your account."
>
Authenticator App
</Label>
<div className="col-start-2 col-span-3">
<TotpEnrollementBox
errors={errors}
qrData={totpQrData}
secret={totpSecret}
loading={loading}
verifyTotp={onVerifyTotp}
/>
<div className="inline-flex">
<Switch
selected={totpEnabled}
onChange={toggleTotp}
disabled={loading || disableForm || totpBoxOpen}
/>
{totpBoxOpen && (
<span
className="ml-5 cursor-pointer text-jungle-green-500 hover:opacity-75"
onClick={() => toggleTotpBox(false)}
aria-hidden="true"
>
Cancel
</span>
)}
</div>

{totpBoxOpen && (
<div className="col-start-2 col-span-3">
<TotpEnrollementBox
errors={errors}
qrData={totpQrData}
secret={totpSecret}
loading={loading}
verifyTotp={onVerifyTotp}
/>
</div>
)}
</div>
)}
</div>
</>
)}

<Label className="col-start-1 col-span-1">Permissions</Label>
<div className="col-start-2 col-span-3">
Expand All @@ -174,15 +181,17 @@ function ProfileForm({
/>
</div>
</div>
<div className="flex flex-row w-80 space-x-2 mt-5">
<Button
disabled={loading || disableForm}
type="default-fit"
onClick={onSaveClicked}
>
Save
</Button>
</div>
{!singleSignOnEnabled && (
<div className="flex flex-row w-80 space-x-2 mt-5">
<Button
disabled={loading || disableForm}
type="default-fit"
onClick={onSaveClicked}
>
Save
</Button>
</div>
)}
</div>
<Modal
title="Disable TOTP"
Expand Down
11 changes: 11 additions & 0 deletions assets/js/pages/Profile/ProfileForm.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ export default {
type: 'text',
},
},
singleSignOnEnabled: {
description: 'Single sign on login is enabled',
control: { type: 'boolean' },
},
},
render: (args) => (
<ContainerWrapper>
Expand Down Expand Up @@ -129,3 +133,10 @@ export const WithErrors = {
],
},
};

export const SingleSignOnEnabled = {
args: {
...Default.args,
singleSignOnEnabled: true,
},
};
55 changes: 55 additions & 0 deletions assets/js/pages/Profile/ProfileForm.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -305,4 +305,59 @@ describe('ProfileForm', () => {
);
expect(screen.getByText('Error validating totp code')).toBeVisible();
});

describe('Single sign on', () => {
it('should disable fullname, email and username fields', () => {
const { username, fullname, email, abilities } = profileFactory.build();

render(
<ProfileForm
fullName={fullname}
emailAddress={email}
username={username}
abilities={abilities}
singleSignOnEnabled
/>
);

expect(screen.getByLabelText('fullname')).toBeDisabled();
expect(screen.getByLabelText('email')).toBeDisabled();
expect(screen.getByLabelText('username')).toBeDisabled();
expect(screen.getByLabelText('permissions')).toBeDisabled();
});

it('should remove password and totp fields', () => {
const { username, fullname, email, abilities } = profileFactory.build();

render(
<ProfileForm
fullName={fullname}
emailAddress={email}
username={username}
abilities={abilities}
singleSignOnEnabled
/>
);

expect(screen.queryByText('Password')).not.toBeInTheDocument();
expect(screen.queryByText('Authenticator App')).not.toBeInTheDocument();
expect(screen.getByText('Permissions')).toBeVisible();
});

it('should remove save button', () => {
const { username, fullname, email, abilities } = profileFactory.build();

render(
<ProfileForm
fullName={fullname}
emailAddress={email}
username={username}
abilities={abilities}
singleSignOnEnabled
/>
);

expect(screen.queryByText('Save')).not.toBeInTheDocument();
});
});
});
2 changes: 2 additions & 0 deletions assets/js/pages/Profile/ProfilePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { toast } from 'react-hot-toast';
import { useDispatch } from 'react-redux';
import PageHeader from '@common/PageHeader';
import { isAdmin } from '@lib/model/users';
import { isSingleSignOnEnabled } from '@lib/auth/config';
import ProfileForm from '@pages/Profile/ProfileForm';
import {
getUserProfile,
Expand Down Expand Up @@ -162,6 +163,7 @@ function ProfilePage() {
toggleTotpBox={setTotpBoxOpen}
loading={loading || saving}
disableForm={isDefaultAdmin}
singleSignOnEnabled={isSingleSignOnEnabled()}
onSave={updateProfile}
onEnableTotp={totpInitiateEnrolling}
onVerifyTotp={verifyTotpEnrollment}
Expand Down
6 changes: 6 additions & 0 deletions assets/js/pages/Users/CreateUserPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { toast } from 'react-hot-toast';

import BackButton from '@common/BackButton';
import PageHeader from '@common/PageHeader';
import NotFound from '@pages/NotFound';

import { isSingleSignOnEnabled } from '@lib/auth/config';
import { listAbilities } from '@lib/api/abilities';
import { createUser } from '@lib/api/users';

Expand Down Expand Up @@ -55,6 +57,10 @@ function CreateUserPage() {
navigate('/users');
};

if (isSingleSignOnEnabled()) {
return <NotFound />;
}

useEffect(() => {
fetchAbilities(setAbilities);
}, []);
Expand Down
14 changes: 13 additions & 1 deletion assets/js/pages/Users/CreateUserPage.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { faker } from '@faker-js/faker';
import * as router from 'react-router';

import { networkClient } from '@lib/network';

import * as authConfig from '@lib/auth/config';
import { abilityFactory, userFactory } from '@lib/test-utils/factories/users';

import CreateUserPage from './CreateUserPage';
Expand Down Expand Up @@ -144,4 +144,16 @@ describe('CreateUserPage', () => {
await user.click(screen.getByRole('button', { name: 'Create' }));
expect(toast.error).toHaveBeenCalledWith(toastMessage);
});

describe('Single sign on', () => {
it('should redirect to not found page', async () => {
jest.spyOn(authConfig, 'isSingleSignOnEnabled').mockReturnValue(true);

render(<CreateUserPage />);

expect(
screen.getByText('the page is in another castle', { exact: false })
).toBeVisible();
});
});
});
3 changes: 3 additions & 0 deletions assets/js/pages/Users/EditUserPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import Banner from '@common/Banners/Banner';
import PageHeader from '@common/PageHeader';

import { isAdmin } from '@lib/model/users';
import { isSingleSignOnEnabled } from '@lib/auth/config';

import { editUser, getUser } from '@lib/api/users';

import { fetchAbilities } from './CreateUserPage';
Expand Down Expand Up @@ -123,6 +125,7 @@ function EditUserPage() {
onSave={onEditUser}
onCancel={onCancel}
editing
singleSignOnEnabled={isSingleSignOnEnabled()}
/>
</div>
);
Expand Down
Loading

0 comments on commit 2912d0d

Please sign in to comment.