Skip to content

Commit

Permalink
Add single sign on logic
Browse files Browse the repository at this point in the history
  • Loading branch information
martinlagler committed Mar 11, 2024
1 parent b3c927d commit 627346e
Show file tree
Hide file tree
Showing 24 changed files with 794 additions and 103 deletions.
3 changes: 3 additions & 0 deletions config/packages/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ security:
authentication_required_handler: sulu_security.two_factor_authentication_required_handler
success_handler: sulu_security.two_factor_authentication_success_handler
failure_handler: sulu_security.two_factor_authentication_failure_handler
access_token:
token_handler: sulu_security.single_sign_on_token_handler
token_extractors: sulu_security.single_sign_on_token_extractor

# website:
# pattern: ^/
Expand Down
5 changes: 0 additions & 5 deletions config/packages/sulu_security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ sulu_security:
force:
pattern: '/@sulu\.io$/'

single_sign_on:
providers:
'sulu.io':
dsn: 'openid://%env(resolve:SULU_OPEN_ID_CLIENT_ID)%:%env(resolve:SULU_OPEN_ID_CLIENT_SECRET)%@%env(resolve:SULU_OPEN_ID_ENDPOINT)%'

when@test:
sulu_security:
two_factor:
Expand Down
6 changes: 6 additions & 0 deletions config/routes/sulu_admin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,9 @@ sulu_audience_targeting_api:
type: rest
resource: "@SuluAudienceTargetingBundle/Resources/config/routing_api.yml"
prefix: /admin/api

sulu_admin_single_sign_on:
path: /openid
controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::redirectAction
defaults:
route: sulu_admin
7 changes: 6 additions & 1 deletion src/Sulu/Bundle/AdminBundle/Controller/AdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ class AdminController
*/
private $passwordInfoTranslationKey;

/**
* @var bool
*/
private $hasSingleSignOnProvider;

public function __construct(
UrlGeneratorInterface $urlGenerator,
TokenStorageInterface $tokenStorage,
Expand Down Expand Up @@ -198,7 +203,7 @@ public function __construct(
?bool $collaborationEnabled = null,
?string $passwordPattern = null,
?string $passwordInfoTranslationKey = null,
?bool $hasSingleSignOnProvider = false
bool $hasSingleSignOnProvider = false
) {
$this->urlGenerator = $urlGenerator;
$this->tokenStorage = $tokenStorage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default class Input<T: ?string | ?number> extends React.PureComponent<Inp
skin: 'default',
type: 'text',
valid: true,
autoFocus: false,

Check failure on line 22 in src/Sulu/Bundle/AdminBundle/Resources/js/components/Input/Input.js

View workflow job for this annotation

GitHub Actions / Node Lint

Default prop types declarations should be sorted alphabetically
};

setInputRef = (ref: ?ElementRef<'input'>) => {
Expand Down Expand Up @@ -86,6 +87,7 @@ export default class Input<T: ?string | ?number> extends React.PureComponent<Inp
min,
max,
step,
autoFocus,
} = this.props;

const inputContainerClass = classNames(
Expand Down Expand Up @@ -159,6 +161,7 @@ export default class Input<T: ?string | ?number> extends React.PureComponent<Inp
ref={inputRef ? this.setInputRef : undefined}
step={step}
type={type}
autoFocus={autoFocus}

Check failure on line 164 in src/Sulu/Bundle/AdminBundle/Resources/js/components/Input/Input.js

View workflow job for this annotation

GitHub Actions / Node Lint

Props should be sorted alphabetically
value={value == null ? '' : value}
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const mockUserStoreContact = jest.fn();
const mockUserStoreUser = jest.fn();
const mockUserStoreGetPersistentSetting = jest.fn().mockReturnValue(0);
const mockUserStoreSetPersistentSetting = jest.fn();
const mockUserStoreHasSingleSignOn = jest.fn().mockReturnValue(false);

jest.mock('../../../stores/userStore', () => {
return new class {
Expand All @@ -54,6 +55,10 @@ jest.mock('../../../stores/userStore', () => {
return mockUserStoreContact();
}

hasSingleSignOn() {
return mockUserStoreHasSingleSignOn();
}

getPersistentSetting(value) {
return mockUserStoreGetPersistentSetting(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class LoginForm extends React.Component<Props> {
/>
</label>
)}
{!this.props.hasSingleSignOn || (this.props.hasSingleSignOn && this.props.hasOnlyPassword) && (
{(!this.props.hasSingleSignOn || (this.props.hasSingleSignOn && this.props.hasOnlyPassword)) && (

Check failure on line 122 in src/Sulu/Bundle/AdminBundle/Resources/js/containers/Login/LoginForm.js

View workflow job for this annotation

GitHub Actions / Node Lint

This line has a length of 121. Maximum allowed is 120
<label className={inputFieldClass}>
<div className={formStyles.labelText}>
{translate('sulu_admin.password')}
Expand All @@ -131,22 +131,23 @@ class LoginForm extends React.Component<Props> {
type="password"
valid={!this.props.error}
value={this.password}
autoFocus={this.props.hasOnlyPassword}

Check failure on line 134 in src/Sulu/Bundle/AdminBundle/Resources/js/containers/Login/LoginForm.js

View workflow job for this annotation

GitHub Actions / Node Lint

Props should be sorted alphabetically
/>
</label>
)}
<div className={formStyles.buttons}>
<Button onClick={this.props.onChangeForm} skin="link">
{translate('sulu_admin.forgot_password')}
</Button>
<Button
disabled={this.submitButtonDisabled}
loading={this.props.loading}
skin="primary"
type="submit"
>
{translate('sulu_admin.login')}
</Button>
</div>
<div className={formStyles.buttons}>
<Button onClick={this.props.onChangeForm} skin="link">
{translate('sulu_admin.forgot_password')}
</Button>
<Button
disabled={this.submitButtonDisabled}
loading={this.props.loading}
skin="primary"
type="submit"
>
{translate('sulu_admin.login')}
</Button>
</div>
</fieldset>
</form>
</Fragment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const mockUserStoreTwoFactorError = jest.fn();
const mockUserStoreSetResetSuccess = jest.fn();
const mockUserStoreLoading = jest.fn().mockReturnValue(false);
const mockUserStoreForgotPasswordSuccess = jest.fn().mockReturnValue(false);
const mockUserStoreHasJsonLogin = jest.fn().mockReturnValue(false);
const mockUserStoreHasSingleSignOn = jest.fn().mockReturnValue(false);

jest.mock('../../../stores/userStore', () => {
return new class {
Expand Down Expand Up @@ -66,6 +68,14 @@ jest.mock('../../../stores/userStore', () => {
return mockUserStoreSetResetSuccess(value);
}

hasSingleSignOn() {
return mockUserStoreHasSingleSignOn();
}

get hasJsonLogin() {
return mockUserStoreHasJsonLogin();
}

get loading() {
return mockUserStoreLoading();
}
Expand Down Expand Up @@ -247,3 +257,25 @@ test('Should not call the submit handler of the reset password view with not mat
expect(router.reset).not.toBeCalled();
});
});

test('Should render the Login with only username/email', () => {
const router = new Router();
mockUserStoreHasSingleSignOn.mockReturnValueOnce(true);

const loginForm = mount(
<Login initialized={true} onLoginSuccess={jest.fn()} router={router} />
);

expect(loginForm.render()).toMatchSnapshot()
});

test('Should render the Login with only password', () => {
const router = new Router();
mockUserStoreHasJsonLogin.mockReturnValue(true);

const loginForm = mount(
<Login initialized={true} onLoginSuccess={jest.fn()} router={router} />
);

expect(loginForm.render()).toMatchSnapshot()
});
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,204 @@ exports[`Should render the Login with forgot password with success 1`] = `
</div>
`;

exports[`Should render the Login with only password 1`] = `
<div
class="login"
>
<div
class="loginContainer"
>
<div
class="formContainer"
>
<div
class="logoContainer"
>
<span
aria-label="su-sulu"
class="su-sulu"
/>
</div>
<div
class="header"
>
sulu_admin.welcome
</div>
<form
class="form"
>
<fieldset>
<label
class="inputField"
>
<div
class="labelText"
>
sulu_admin.password
</div>
<div
class="input default left"
>
<div
class="prependedContainer default"
>
<span
aria-label="su-lock"
class="su-lock icon default"
/>
</div>
<input
autocomplete="current-password"
type="password"
value=""
/>
</div>
</label>
<div
class="buttons"
>
<button
class="button link hasText"
type="button"
>
<span
class="buttonText"
>
sulu_admin.forgot_password
</span>
</button>
<button
class="button primary hasText"
disabled=""
type="submit"
>
<span
class="buttonText"
>
sulu_admin.login
</span>
</button>
</div>
</fieldset>
</form>
</div>
<div
class="backLinkContainer"
>
<a
class="backLink"
href="/"
>
<span
aria-label="su-angle-left"
class="su-angle-left backLinkIcon"
/>
sulu_admin.back_to_website
</a>
</div>
</div>
</div>
`;

exports[`Should render the Login with only username/email 1`] = `
<div
class="login"
>
<div
class="loginContainer"
>
<div
class="formContainer"
>
<div
class="logoContainer"
>
<span
aria-label="su-sulu"
class="su-sulu"
/>
</div>
<div
class="header"
>
sulu_admin.welcome
</div>
<form
class="form"
>
<fieldset>
<label
class="inputField"
>
<div
class="labelText"
>
sulu_admin.username_or_email
</div>
<div
class="input default left"
>
<div
class="prependedContainer default"
>
<span
aria-label="su-user"
class="su-user icon default"
/>
</div>
<input
autocomplete="username"
type="text"
value=""
/>
</div>
</label>
<div
class="buttons"
>
<button
class="button link hasText"
type="button"
>
<span
class="buttonText"
>
sulu_admin.forgot_password
</span>
</button>
<button
class="button primary hasText"
disabled=""
type="submit"
>
<span
class="buttonText"
>
sulu_admin.login
</span>
</button>
</div>
</fieldset>
</form>
</div>
<div
class="backLinkContainer"
>
<a
class="backLink"
href="/"
>
<span
aria-label="su-angle-left"
class="su-angle-left backLinkIcon"
/>
sulu_admin.back_to_website
</a>
</div>
</div>
</div>
`;

exports[`Should render the Login with reset password view 1`] = `
<div
class="login"
Expand Down
Loading

0 comments on commit 627346e

Please sign in to comment.