Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): Improve saml test connection function #5899

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions packages/cli/src/sso/saml/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ export class SamlUrls {

static readonly initSSO = '/initsso';

static readonly restInitSSO = this.samlRESTRoot + this.initSSO;

static readonly acs = '/acs';

static readonly restAcs = this.samlRESTRoot + this.acs;
Expand All @@ -17,9 +15,9 @@ export class SamlUrls {

static readonly configTest = '/config/test';

static readonly configToggleEnabled = '/config/toggle';
static readonly configTestReturn = '/config/test/return';

static readonly restConfig = this.samlRESTRoot + this.config;
static readonly configToggleEnabled = '/config/toggle';

static readonly defaultRedirect = '/';

Expand Down
16 changes: 12 additions & 4 deletions packages/cli/src/sso/saml/routes/saml.controller.ee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import type { PostBindingContext } from 'samlify/types/src/entity';
import { isSamlLicensedAndEnabled } from '../samlHelpers';
import type { SamlLoginBinding } from '../types';
import { AuthenticatedRequest } from '@/requests';
import { getServiceProviderEntityId, getServiceProviderReturnUrl } from '../serviceProvider.ee';
import {
getServiceProviderConfigTestReturnUrl,
getServiceProviderEntityId,
getServiceProviderReturnUrl,
} from '../serviceProvider.ee';

@RestController('/sso/saml')
export class SamlController {
Expand Down Expand Up @@ -100,6 +104,10 @@ export class SamlController {
private async acsHandler(req: express.Request, res: express.Response, binding: SamlLoginBinding) {
const loginResult = await this.samlService.handleSamlLogin(req, binding);
if (loginResult) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (req.body.RelayState && req.body.RelayState === getServiceProviderConfigTestReturnUrl()) {
return res.status(202).send(loginResult.attributes);
}
if (loginResult.authenticatedUser) {
// Only sign in user if SAML is enabled, otherwise treat as test connection
if (isSamlLicensedAndEnabled()) {
Expand Down Expand Up @@ -134,11 +142,11 @@ export class SamlController {
*/
@Get(SamlUrls.configTest, { middlewares: [samlLicensedOwnerMiddleware] })
async configTestGet(req: AuthenticatedRequest, res: express.Response) {
return this.handleInitSSO(res);
return this.handleInitSSO(res, getServiceProviderConfigTestReturnUrl());
}

private async handleInitSSO(res: express.Response) {
const result = this.samlService.getLoginRequestUrl();
private async handleInitSSO(res: express.Response, relayState?: string) {
const result = this.samlService.getLoginRequestUrl(relayState);
if (result?.binding === 'redirect') {
return result.context.context;
} else if (result?.binding === 'post') {
Expand Down
28 changes: 17 additions & 11 deletions packages/cli/src/sso/saml/saml.service.ee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ import {
setSamlLoginLabel,
updateUserFromSamlAttributes,
} from './samlHelpers';
import type { Settings } from '../../databases/entities/Settings';
import type { Settings } from '@/databases/entities/Settings';
import axios from 'axios';
import https from 'https';
import type { SamlLoginBinding } from './types';
import type { BindingContext, PostBindingContext } from 'samlify/types/src/entity';
import { validateMetadata, validateResponse } from './samlValidator';
import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper';

@Service()
export class SamlService {
Expand All @@ -48,6 +49,7 @@ export class SamlService {
loginLabel: 'SAML',
wantAssertionsSigned: true,
wantMessageSigned: true,
relayState: getInstanceBaseUrl(),
signatureConfig: {
prefix: 'ds',
location: {
Expand Down Expand Up @@ -92,36 +94,40 @@ export class SamlService {
return getServiceProviderInstance(this._samlPreferences);
}

getLoginRequestUrl(binding?: SamlLoginBinding): {
getLoginRequestUrl(
relayState?: string,
binding?: SamlLoginBinding,
): {
binding: SamlLoginBinding;
context: BindingContext | PostBindingContext;
} {
if (binding === undefined) binding = this._samlPreferences.loginBinding ?? 'redirect';
if (binding === 'post') {
return {
binding,
context: this.getPostLoginRequestUrl(),
context: this.getPostLoginRequestUrl(relayState),
};
} else {
return {
binding,
context: this.getRedirectLoginRequestUrl(),
context: this.getRedirectLoginRequestUrl(relayState),
};
}
}

private getRedirectLoginRequestUrl(): BindingContext {
const loginRequest = this.getServiceProviderInstance().createLoginRequest(
this.getIdentityProviderInstance(),
'redirect',
);
private getRedirectLoginRequestUrl(relayState?: string): BindingContext {
const sp = this.getServiceProviderInstance();
sp.entitySetting.relayState = relayState ?? getInstanceBaseUrl();
const loginRequest = sp.createLoginRequest(this.getIdentityProviderInstance(), 'redirect');
//TODO:SAML: debug logging
LoggerProxy.debug(loginRequest.context);
return loginRequest;
}

private getPostLoginRequestUrl(): PostBindingContext {
const loginRequest = this.getServiceProviderInstance().createLoginRequest(
private getPostLoginRequestUrl(relayState?: string): PostBindingContext {
const sp = this.getServiceProviderInstance();
sp.entitySetting.relayState = relayState ?? getInstanceBaseUrl();
const loginRequest = sp.createLoginRequest(
this.getIdentityProviderInstance(),
'post',
) as PostBindingContext;
Expand Down
5 changes: 5 additions & 0 deletions packages/cli/src/sso/saml/serviceProvider.ee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export function getServiceProviderReturnUrl(): string {
return getInstanceBaseUrl() + SamlUrls.restAcs;
}

export function getServiceProviderConfigTestReturnUrl(): string {
return getInstanceBaseUrl() + SamlUrls.configTestReturn;
}

// TODO:SAML: make these configurable for the end user
export function getServiceProviderInstance(prefs: SamlPreferences): ServiceProviderInstance {
if (serviceProviderInstance === undefined) {
Expand All @@ -24,6 +28,7 @@ export function getServiceProviderInstance(prefs: SamlPreferences): ServiceProvi
wantAssertionsSigned: prefs.wantAssertionsSigned,
wantMessageSigned: prefs.wantMessageSigned,
signatureConfig: prefs.signatureConfig,
relayState: prefs.relayState,
nameIDFormat: ['urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'],
assertionConsumerService: [
{
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/sso/saml/types/samlPreferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,8 @@ export class SamlPreferences {
action: 'after',
},
};

@IsString()
@IsOptional()
relayState?: string = '';
}