snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 1s
This commit is contained in:
@@ -0,0 +1,316 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// ***************************************************************
|
||||
// - [#] indicates a test step (e.g. #. Go to a page)
|
||||
// - [*] indicates an assertion (e.g. * Check the title)
|
||||
// - Use element ID when selecting an element. Create one if none.
|
||||
// ***************************************************************
|
||||
|
||||
// Stage: @prod
|
||||
// Group: @channels @enterprise @saml
|
||||
// Skip: @headless @electron @firefox // run on Chrome (headed) only
|
||||
|
||||
import type {UserCollection} from '@/support/okta_commands';
|
||||
import users from '@/fixtures/saml_users.json';
|
||||
|
||||
//Manual Setup required: Follow the instructions mentioned in the mattermost/platform-private/config/saml-okta-setup.txt file
|
||||
context('Okta', () => {
|
||||
const loginButtonText = 'SAML';
|
||||
|
||||
const regular1 = users.regulars['samluser-1'];
|
||||
const guest1 = users.guests['samlguest-1'];
|
||||
const guest2 = users.guests['samlguest-2'];
|
||||
const admin1 = users.admins['samladmin-1'];
|
||||
const admin2 = users.admins['samladmin-2'];
|
||||
|
||||
const {
|
||||
oktaBaseUrl,
|
||||
oktaMMAppName,
|
||||
oktaMMEntityId,
|
||||
} = Cypress.expose();
|
||||
const idpUrl = `${oktaBaseUrl}/app/${oktaMMAppName}/${oktaMMEntityId}/sso/saml`;
|
||||
const idpMetadataUrl = `${oktaBaseUrl}/app/${oktaMMEntityId}/sso/saml/metadata`;
|
||||
|
||||
const newConfig = {
|
||||
SamlSettings: {
|
||||
Enable: true,
|
||||
EnableSyncWithLdap: false,
|
||||
EnableSyncWithLdapIncludeAuth: false,
|
||||
Verify: true,
|
||||
Encrypt: true,
|
||||
SignRequest: true,
|
||||
IdpURL: idpUrl,
|
||||
IdpDescriptorURL: `http://www.okta.com/${oktaMMEntityId}`,
|
||||
IdpMetadataURL: idpMetadataUrl,
|
||||
ServiceProviderIdentifier: `${Cypress.config('baseUrl')}/login/sso/saml`,
|
||||
AssertionConsumerServiceURL: `${Cypress.config('baseUrl')}/login/sso/saml`,
|
||||
SignatureAlgorithm: 'RSAwithSHA1',
|
||||
CanonicalAlgorithm: 'Canonical1.0',
|
||||
IdpCertificateFile: 'saml-idp.crt',
|
||||
PublicCertificateFile: 'saml-public.crt',
|
||||
PrivateKeyFile: 'saml-private.key',
|
||||
IdAttribute: '',
|
||||
GuestAttribute: '',
|
||||
EnableAdminAttribute: false,
|
||||
AdminAttribute: '',
|
||||
FirstNameAttribute: '',
|
||||
LastNameAttribute: '',
|
||||
EmailAttribute: 'Email',
|
||||
UsernameAttribute: 'Username',
|
||||
LoginButtonText: loginButtonText,
|
||||
},
|
||||
GuestAccountsSettings: {
|
||||
Enable: true,
|
||||
},
|
||||
};
|
||||
|
||||
// TestSettings type is not exported from saml_commands; uses SAMLUser | null for user field
|
||||
let testSettings: any;
|
||||
|
||||
//Note: the assumption is that this test suite runs on a clean setup (empty DB) which would ensure that the users are not present in the Mattermost instance beforehand
|
||||
describe('SAML Login flow', () => {
|
||||
before(() => {
|
||||
// * Check if server has license for SAML
|
||||
cy.apiRequireLicenseForFeature('SAML');
|
||||
|
||||
// # Get certificates status and upload as necessary
|
||||
cy.apiGetSAMLCertificateStatus().then((resp) => {
|
||||
const data = resp.body;
|
||||
|
||||
if (!data.idp_certificate_file) {
|
||||
cy.apiUploadSAMLIDPCert('saml-idp.crt');
|
||||
}
|
||||
|
||||
if (!data.public_certificate_file) {
|
||||
cy.apiUploadSAMLPublicCert('saml-public.crt');
|
||||
}
|
||||
|
||||
if (!data.private_key_file) {
|
||||
cy.apiUploadSAMLPrivateKey('saml-private.key');
|
||||
}
|
||||
});
|
||||
|
||||
// # Check SAML metadata if working properly
|
||||
cy.apiGetMetadataFromIdp(idpMetadataUrl);
|
||||
|
||||
cy.oktaAddUsers(users as unknown as UserCollection);
|
||||
cy.apiUpdateConfig(newConfig).then(({config}) => {
|
||||
cy.setTestSettings(loginButtonText, config).then((_response) => {
|
||||
testSettings = _response;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Saml login new and existing MM regular user', () => {
|
||||
cy.apiAdminLogin();
|
||||
|
||||
testSettings.user = regular1;
|
||||
|
||||
//login new user
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.skipOrCreateTeam(testSettings, oktaUserId).then(() => {
|
||||
cy.doSamlLogout(testSettings).then(() => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//login existing user
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.doSamlLogout(testSettings).then(() => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Saml login new and existing MM guest user(userType=Guest)', () => {
|
||||
cy.apiAdminLogin();
|
||||
|
||||
testSettings.user = guest1;
|
||||
newConfig.SamlSettings.GuestAttribute = 'UserType=Guest';
|
||||
|
||||
cy.apiUpdateConfig(newConfig).then(() => {
|
||||
//login new user
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.skipOrCreateTeam(testSettings, oktaUserId).then(() => {
|
||||
cy.doLogoutFromSignUp().then(() => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//login existing user
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.doLogoutFromSignUp().then(() => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Saml login new and existing MM guest(isGuest=true)', () => {
|
||||
cy.apiAdminLogin();
|
||||
|
||||
testSettings.user = guest2;
|
||||
newConfig.SamlSettings.GuestAttribute = 'IsGuest=true';
|
||||
|
||||
cy.apiUpdateConfig(newConfig).then(() => {
|
||||
//login new user
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.skipOrCreateTeam(testSettings, oktaUserId).then(() => {
|
||||
cy.doLogoutFromSignUp().then(() => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//login existing user
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.doLogoutFromSignUp().then(() => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Saml login new and existing MM admin(userType=Admin)', () => {
|
||||
cy.apiAdminLogin();
|
||||
|
||||
testSettings.user = admin1;
|
||||
newConfig.SamlSettings.EnableAdminAttribute = true;
|
||||
newConfig.SamlSettings.AdminAttribute = 'UserType=Admin';
|
||||
|
||||
cy.apiUpdateConfig(newConfig).then(() => {
|
||||
//login new user
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.skipOrCreateTeam(testSettings, oktaUserId).then(() => {
|
||||
cy.doSamlLogout(testSettings).then(() => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//login existing user
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.doSamlLogout(testSettings).then(() => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Saml login new and existing MM admin(isAdmin=true)', () => {
|
||||
cy.apiAdminLogin();
|
||||
testSettings.user = admin2;
|
||||
newConfig.SamlSettings.EnableAdminAttribute = true;
|
||||
newConfig.SamlSettings.AdminAttribute = 'IsAdmin=true';
|
||||
|
||||
cy.apiUpdateConfig(newConfig).then(() => {
|
||||
//login new user
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.skipOrCreateTeam(testSettings, oktaUserId).then(() => {
|
||||
cy.doSamlLogout(testSettings).then(() => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.doSamlLogout(testSettings).then(() => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Saml login invited Guest user to a team', () => {
|
||||
cy.apiAdminLogin();
|
||||
testSettings.user = regular1;
|
||||
|
||||
//login as a regular user - generate an invite link
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.skipOrCreateTeam(testSettings, oktaUserId).then((teamName: string) => {
|
||||
testSettings.teamName = teamName;
|
||||
|
||||
//get invite link
|
||||
cy.getInvitePeopleLink(testSettings).then((inviteUrl) => {
|
||||
//logout regular1
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogout(testSettings).then(() => {
|
||||
testSettings.user = guest1;
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((_oktaUserId) => {
|
||||
cy.visit(inviteUrl as unknown as string).then(() => {
|
||||
cy.oktaDeleteSession(_oktaUserId);
|
||||
|
||||
//login the guest
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.doLogoutFromSignUp();
|
||||
cy.oktaDeleteSession(_oktaUserId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// ***************************************************************
|
||||
// - [#] indicates a test step (e.g. #. Go to a page)
|
||||
// - [*] indicates an assertion (e.g. * Check the title)
|
||||
// - Use element ID when selecting an element. Create one if none.
|
||||
// ***************************************************************
|
||||
|
||||
// Stage: @prod
|
||||
// Group: @channels @enterprise @saml
|
||||
// Skip: @headless @electron @firefox // run on Chrome (headed) only
|
||||
|
||||
import type {UserCollection} from '@/support/okta_commands';
|
||||
import users from '@/fixtures/saml_users.json';
|
||||
|
||||
//Manual Setup required: Follow the instructions mentioned in the mattermost/platform-private/config/saml-okta-setup.txt file
|
||||
context('LDAP SAML - Automated Tests (SAML TESTS)', () => {
|
||||
const loginButtonText = 'SAML';
|
||||
|
||||
const regular1 = users.regulars['samluser-1'];
|
||||
|
||||
const {
|
||||
oktaBaseUrl,
|
||||
oktaMMAppName,
|
||||
oktaMMEntityId,
|
||||
} = Cypress.expose();
|
||||
const idpUrl = `${oktaBaseUrl}/app/${oktaMMAppName}/${oktaMMEntityId}/sso/saml`;
|
||||
const idpMetadataUrl = `${oktaBaseUrl}/app/${oktaMMEntityId}/sso/saml/metadata`;
|
||||
|
||||
const newConfig = {
|
||||
SamlSettings: {
|
||||
Enable: true,
|
||||
EnableSyncWithLdap: false,
|
||||
EnableSyncWithLdapIncludeAuth: false,
|
||||
Verify: true,
|
||||
Encrypt: true,
|
||||
SignRequest: true,
|
||||
IdpURL: idpUrl,
|
||||
IdpDescriptorURL: `http://www.okta.com/${oktaMMEntityId}`,
|
||||
IdpMetadataURL: idpMetadataUrl,
|
||||
ServiceProviderIdentifier: `${Cypress.config('baseUrl')}/login/sso/saml`,
|
||||
AssertionConsumerServiceURL: `${Cypress.config('baseUrl')}/login/sso/saml`,
|
||||
SignatureAlgorithm: 'RSAwithSHA1',
|
||||
CanonicalAlgorithm: 'Canonical1.0',
|
||||
IdpCertificateFile: 'saml-idp.crt',
|
||||
PublicCertificateFile: 'saml-public.crt',
|
||||
PrivateKeyFile: 'saml-private.key',
|
||||
IdAttribute: '',
|
||||
GuestAttribute: '',
|
||||
EnableAdminAttribute: false,
|
||||
AdminAttribute: '',
|
||||
FirstNameAttribute: '',
|
||||
LastNameAttribute: '',
|
||||
EmailAttribute: 'Email',
|
||||
UsernameAttribute: 'Username',
|
||||
LoginButtonText: loginButtonText,
|
||||
},
|
||||
GuestAccountsSettings: {
|
||||
Enable: true,
|
||||
},
|
||||
};
|
||||
|
||||
// TestSettings type is not exported from saml_commands; uses SAMLUser | null for user field
|
||||
let testSettings: any;
|
||||
|
||||
//Note: the assumption is that this test suite runs on a clean setup (empty DB) which would ensure that the users are not present in the Mattermost instance beforehand
|
||||
describe('LDAP SAML - Automated Tests (SAML TESTS)', () => {
|
||||
before(() => {
|
||||
// * Check if server has license for SAML
|
||||
cy.apiRequireLicenseForFeature('SAML');
|
||||
|
||||
// # Get certificates status and upload as necessary
|
||||
cy.apiGetSAMLCertificateStatus().then((resp) => {
|
||||
const data = resp.body;
|
||||
|
||||
if (!data.idp_certificate_file) {
|
||||
cy.apiUploadSAMLIDPCert('saml-idp.crt');
|
||||
}
|
||||
|
||||
if (!data.public_certificate_file) {
|
||||
cy.apiUploadSAMLPublicCert('saml-public.crt');
|
||||
}
|
||||
|
||||
if (!data.private_key_file) {
|
||||
cy.apiUploadSAMLPrivateKey('saml-private.key');
|
||||
}
|
||||
});
|
||||
|
||||
// # Check SAML metadata if working properly
|
||||
cy.apiGetMetadataFromIdp(idpMetadataUrl);
|
||||
|
||||
cy.oktaAddUsers(users as unknown as UserCollection);
|
||||
cy.apiUpdateConfig(newConfig).then(({config}) => {
|
||||
cy.setTestSettings(loginButtonText, config).then((_response) => {
|
||||
testSettings = _response;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T3012 - Check SAML Metadata without Enable Encryption', () => {
|
||||
cy.apiAdminLogin();
|
||||
const test1Settings = {
|
||||
...newConfig,
|
||||
SamlSettings: {
|
||||
...newConfig.SamlSettings,
|
||||
Encrypt: false,
|
||||
PublicCertificateFile: '',
|
||||
PrivateKeyFile: '',
|
||||
},
|
||||
};
|
||||
cy.apiUpdateConfig(test1Settings).then(() => {
|
||||
const baseUrl = Cypress.config('baseUrl');
|
||||
cy.request(`${baseUrl}/api/v4/saml/metadata`).then((resp) => {
|
||||
expect(resp.status).to.eq(200);
|
||||
expect(resp.headers['content-type']).to.eq('application/xml');
|
||||
expect(resp.body).to.contain('<?xml version');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T3280 - SAML Login Audit', () => {
|
||||
cy.apiAdminLogin();
|
||||
|
||||
cy.apiUpdateConfig(newConfig).then(() => {
|
||||
testSettings.user = regular1;
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.skipOrCreateTeam(testSettings, oktaUserId).then(() => {
|
||||
cy.uiOpenProfileModal('Security');
|
||||
cy.findByTestId('viewAccessHistory').click();
|
||||
cy.findByTestId('auditTableBody').find('td').
|
||||
each(($el) => {
|
||||
cy.wrap($el).
|
||||
invoke('text').
|
||||
then((text) => {
|
||||
if (text.includes('Saml obtained user')) {
|
||||
expect(text).to.contains('Saml obtained user');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T3281 - SAML Signature Algorithm using RSAwithSHA256', () => {
|
||||
cy.apiAdminLogin();
|
||||
const test1Settings = {
|
||||
...newConfig,
|
||||
SamlSettings: {
|
||||
...newConfig.SamlSettings,
|
||||
SignatureAlgorithm: 'RSAwithSHA256',
|
||||
},
|
||||
};
|
||||
cy.apiUpdateConfig(test1Settings).then(() => {
|
||||
testSettings.user = regular1;
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.skipOrCreateTeam(testSettings, oktaUserId);
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('SAML Signature Algorithm using RSAwithSHA512', () => {
|
||||
cy.apiAdminLogin();
|
||||
const test1Settings = {
|
||||
...newConfig,
|
||||
SamlSettings: {
|
||||
...newConfig.SamlSettings,
|
||||
SignatureAlgorithm: 'RSAwithSHA512',
|
||||
},
|
||||
};
|
||||
cy.apiUpdateConfig(test1Settings).then(() => {
|
||||
testSettings.user = regular1;
|
||||
cy.oktaGetOrCreateUser(testSettings.user).then((oktaUserId) => {
|
||||
cy.oktaDeleteSession(oktaUserId);
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
cy.doOktaLogin(testSettings.user).then(() => {
|
||||
cy.skipOrCreateTeam(testSettings, oktaUserId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,212 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// ***************************************************************
|
||||
// - [#] indicates a test step (e.g. # Go to a page)
|
||||
// - [*] indicates an assertion (e.g. * Check the title)
|
||||
// - Use element ID when selecting an element. Create one if none.
|
||||
// ***************************************************************
|
||||
|
||||
// Group: @channels @enterprise @saml
|
||||
|
||||
import * as TIMEOUTS from '@/fixtures/timeouts';
|
||||
import {getRandomId} from '@/utils';
|
||||
|
||||
// assumes that E20 license is uploaded
|
||||
// Update config.mk to make sure docker images for openldap and keycloak
|
||||
// - assumes openldap docker available on config default http://localhost:389
|
||||
// - assumes keycloak docker - uses api to update
|
||||
// assumes the CYPRESS_* variables are set (CYPRESS_keycloakBaseUrl / CYPRESS_keycloakAppName)
|
||||
// requires {"chromeWebSecurity": false}
|
||||
// copy ./mattermost-server/build/docker/keycloak/keycloak.crt -> ./mattermost-webapp/e2e/cypress/tests/fixtures/keycloak.crt
|
||||
describe('SAML Guest', () => {
|
||||
const loginButtonText = 'SAML';
|
||||
|
||||
const guestUser = {
|
||||
username: 'guest.test',
|
||||
password: 'Password1',
|
||||
email: 'guest.test@mmtest.com',
|
||||
firstname: 'Guest',
|
||||
lastname: 'OneSaml',
|
||||
ldapfirstname: 'Guest',
|
||||
ldaplastname: 'OneSaml',
|
||||
keycloakId: '',
|
||||
};
|
||||
const userFilter = `username=${guestUser.username}`;
|
||||
const keycloakBaseUrl = Cypress.expose('keycloakBaseUrl') || 'http://localhost:8484';
|
||||
const keycloakAppName = Cypress.expose('keycloakAppName') || 'mattermost';
|
||||
const idpUrl = `${keycloakBaseUrl}/auth/realms/${keycloakAppName}/protocol/saml`;
|
||||
const idpDescriptorUrl = `${keycloakBaseUrl}/auth/realms/${keycloakAppName}`;
|
||||
|
||||
const newConfig = {
|
||||
GuestAccountsSettings: {
|
||||
Enable: true,
|
||||
},
|
||||
SamlSettings: {
|
||||
Enable: true,
|
||||
EnableSyncWithLdap: false,
|
||||
EnableSyncWithLdapIncludeAuth: false,
|
||||
Verify: true,
|
||||
Encrypt: false,
|
||||
SignRequest: false,
|
||||
IdpURL: idpUrl,
|
||||
IdpDescriptorURL: idpDescriptorUrl,
|
||||
IdpMetadataURL: '',
|
||||
ServiceProviderIdentifier: `${Cypress.config('baseUrl')}/login/sso/saml`,
|
||||
AssertionConsumerServiceURL: `${Cypress.config('baseUrl')}/login/sso/saml`,
|
||||
SignatureAlgorithm: 'RSAwithSHA256',
|
||||
CanonicalAlgorithm: 'Canonical1.0',
|
||||
IdpCertificateFile: 'saml-idp.crt',
|
||||
PublicCertificateFile: '',
|
||||
PrivateKeyFile: '',
|
||||
IdAttribute: 'username',
|
||||
GuestAttribute: '',
|
||||
EnableAdminAttribute: false,
|
||||
AdminAttribute: '',
|
||||
FirstNameAttribute: 'firstName',
|
||||
LastNameAttribute: 'lastName',
|
||||
EmailAttribute: 'email',
|
||||
UsernameAttribute: 'username',
|
||||
LoginButtonText: loginButtonText,
|
||||
},
|
||||
};
|
||||
|
||||
// TestSettings type is not exported from saml_commands; uses SAMLUser | null for user field
|
||||
let testSettings: any;
|
||||
|
||||
before(() => {
|
||||
// * Check if server has license for SAML
|
||||
cy.apiRequireLicenseForFeature('SAML');
|
||||
|
||||
// # Upload certificate, overwrite existing
|
||||
cy.apiUploadSAMLIDPCert('keycloak.crt');
|
||||
|
||||
// # Update Configs
|
||||
cy.apiUpdateConfig(newConfig).then(({config}) => {
|
||||
cy.setTestSettings(loginButtonText, config).then((_response) => {
|
||||
testSettings = _response;
|
||||
cy.keycloakResetUsers([guestUser]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T1423_1 - SAML Guest Setting disabled if Guest Access is turned off', () => {
|
||||
// # Visit saml settings
|
||||
cy.visit('/admin_console/authentication/saml');
|
||||
|
||||
// # Turn on Guest Attribute Filter
|
||||
cy.findByTestId('SamlSettings.GuestAttributeinput').clear().type('username=e2etest.one');
|
||||
|
||||
// # Save SAML Settings
|
||||
cy.findByText('Save').click().wait(TIMEOUTS.ONE_SEC);
|
||||
|
||||
// # Visit Guest Access settings
|
||||
cy.visit('/admin_console/authentication/guest_access');
|
||||
|
||||
// # Turn off Guest Access
|
||||
cy.findByTestId('GuestAccountsSettings.Enablefalse').check();
|
||||
|
||||
// # Save Guest Access Settings
|
||||
cy.findByText('Save').click().wait(TIMEOUTS.ONE_SEC);
|
||||
|
||||
// # Handle confirmation model
|
||||
cy.findByText('Save and Disable Guest Access').click().wait(TIMEOUTS.ONE_SEC);
|
||||
|
||||
// # Visit saml settings
|
||||
cy.visit('/admin_console/authentication/saml');
|
||||
|
||||
// * verify Guest Attribute is disabled.
|
||||
cy.findByTestId('SamlSettings.GuestAttributeinput').should('be.disabled');
|
||||
});
|
||||
|
||||
it('MM-T1423_2 - SAML User will login as member', () => {
|
||||
const testConfig = {
|
||||
...newConfig,
|
||||
GuestAccountsSettings: {
|
||||
Enable: false,
|
||||
},
|
||||
};
|
||||
cy.apiAdminLogin().then(() => {
|
||||
cy.apiUpdateConfig(testConfig);
|
||||
});
|
||||
|
||||
testSettings.user = guestUser;
|
||||
|
||||
// # MM Login via SAML
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
// # Login to Keycloak
|
||||
cy.doKeycloakLogin(testSettings.user).then(() => {
|
||||
// # Create team if no membership
|
||||
cy.skipOrCreateTeam(testSettings, getRandomId()).then(() => {
|
||||
// * check the user is member, if can create public channel
|
||||
cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
|
||||
cy.get('#showNewChannel button').should('exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T1426_1 - User logged in as member, filter does not match', () => {
|
||||
const testConfig = {
|
||||
...newConfig,
|
||||
GuestAccountsSettings: {
|
||||
...newConfig.GuestAccountsSettings,
|
||||
Enable: true,
|
||||
},
|
||||
SamlSettings: {
|
||||
...newConfig.SamlSettings,
|
||||
GuestAttribute: 'username=Wrong',
|
||||
},
|
||||
};
|
||||
cy.apiAdminLogin().then(() => {
|
||||
cy.apiUpdateConfig(testConfig);
|
||||
});
|
||||
|
||||
testSettings.user = guestUser;
|
||||
|
||||
// # MM Login via SAML
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
// # Login to Keycloak
|
||||
cy.doKeycloakLogin(testSettings.user).then(() => {
|
||||
// # Create team if no membership
|
||||
cy.skipOrCreateTeam(testSettings, getRandomId()).then(() => {
|
||||
// * check the user is member, if can create public channel
|
||||
cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
|
||||
cy.get('#showNewChannel button').should('exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T1426_2 - User logged in as guest, correct filter', () => {
|
||||
const testConfig = {
|
||||
...newConfig,
|
||||
GuestAccountsSettings: {
|
||||
...newConfig.GuestAccountsSettings,
|
||||
Enable: true,
|
||||
},
|
||||
SamlSettings: {
|
||||
...newConfig.SamlSettings,
|
||||
GuestAttribute: userFilter,
|
||||
},
|
||||
};
|
||||
cy.apiAdminLogin().then(() => {
|
||||
cy.apiUpdateConfig(testConfig);
|
||||
});
|
||||
|
||||
testSettings.user = guestUser;
|
||||
|
||||
// # MM Login via SAML
|
||||
cy.doSamlLogin(testSettings).then(() => {
|
||||
// # Login to Keycloak
|
||||
cy.doKeycloakLogin(testSettings.user).then(() => {
|
||||
// # Create team if no membership
|
||||
cy.skipOrCreateTeam(testSettings, getRandomId()).then(() => {
|
||||
// * check the user is guest, cannot create public channel
|
||||
cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {AdminConfig} from '@mattermost/types/config';
|
||||
|
||||
// ***************************************************************
|
||||
// - [#] indicates a test step (e.g. # Go to a page)
|
||||
// - [*] indicates an assertion (e.g. * Check the title)
|
||||
// - Use element ID when selecting an element. Create one if none.
|
||||
// ***************************************************************
|
||||
|
||||
// Stage: @prod
|
||||
// Group: @channels @enterprise @saml
|
||||
|
||||
/**
|
||||
* Note: This test requires Enterprise license to be uploaded
|
||||
*/
|
||||
const testSamlMetadataUrl = 'http://test_saml_metadata_url';
|
||||
const testSamlMetadataSuccessUrl = 'http://test_saml_metadata_success_url';
|
||||
const testIdpURL = 'http://test_idp_url';
|
||||
const testIdpDescriptorURL = 'http://test_idp_descriptor_url';
|
||||
const testFetchedIdpURL = 'http://test_fetched_idp_url';
|
||||
const testFetchedIdpDescriptorURL = 'http://test_fetched_idp_descriptor_url';
|
||||
const testIdpPublicCertificate = 'MIICozCCAYsCBgGNzWfMwjANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDDAptYXR0ZXJtb3N0';
|
||||
const getSamlMetadataErrorMessage = 'SAML Metadata URL did not connect and pull data successfully';
|
||||
const getSamlMetadataSuccessMessage = 'SAML Metadata retrieved successfully. Two fields and one certificate have been updated';
|
||||
|
||||
let config: AdminConfig;
|
||||
|
||||
describe('SystemConsole->SAML 2.0 - Get Metadata from Idp Flow', () => {
|
||||
before(() => {
|
||||
// * Check if server has license for SAML
|
||||
cy.apiRequireLicenseForFeature('SAML');
|
||||
|
||||
cy.apiUpdateConfig({
|
||||
SamlSettings: {
|
||||
Enable: true,
|
||||
AssertionConsumerServiceURL: Cypress.config('baseUrl') + '/login/sso/saml',
|
||||
ServiceProviderIdentifier: Cypress.config('baseUrl') + '/login/sso/saml',
|
||||
IdpMetadataURL: '',
|
||||
IdpURL: testIdpURL,
|
||||
IdpDescriptorURL: testIdpDescriptorURL,
|
||||
},
|
||||
}).then((data) => {
|
||||
({config} = data);
|
||||
});
|
||||
|
||||
//make sure we can navigate to SAML settings
|
||||
cy.visit('/admin_console/authentication/saml');
|
||||
cy.get('.admin-console__header').should('be.visible').and('have.text', 'SAML 2.0');
|
||||
});
|
||||
|
||||
it('fail to fetch metadata from Idp Metadata Url', () => {
|
||||
// * Verify that the metadata Url textbox is enabled and empty
|
||||
cy.findByTestId('SamlSettings.IdpMetadataURLinput').
|
||||
scrollIntoView().should('be.visible').and('be.enabled').and('have.text', '');
|
||||
|
||||
// * Verify that the Get Metadata Url fetch button is disabled
|
||||
cy.get('#getSamlMetadataFromIDPButton').find('button').should('be.visible').and('be.disabled');
|
||||
|
||||
// # Type in the metadata Url in the metadata Url textbox
|
||||
cy.findByTestId('SamlSettings.IdpMetadataURLinput').
|
||||
scrollIntoView().should('be.visible').
|
||||
focus().type(testSamlMetadataUrl);
|
||||
|
||||
// # Click on the Get SAML Metadata Button
|
||||
cy.get('#getSamlMetadataFromIDPButton button').click();
|
||||
|
||||
// * Verify that we get the right error message
|
||||
cy.get('#getSamlMetadataFromIDPButton').should('be.visible').contains(getSamlMetadataErrorMessage);
|
||||
|
||||
// * Verify that the IdpURL textbox content has not been updated
|
||||
cy.findByTestId('SamlSettings.IdpURLinput').then((elem) => {
|
||||
Cypress.$(elem).val() === config.SamlSettings.IdpURL;
|
||||
});
|
||||
|
||||
// * Verify that the IdpDescriptorURL textbox content has not been updated
|
||||
cy.findByTestId('SamlSettings.IdpDescriptorURL').then((elem) => {
|
||||
Cypress.$(elem).val() === config.SamlSettings.IdpDescriptorURL;
|
||||
});
|
||||
|
||||
// * Verify that the IdpDescriptorURL textbox content has been updated
|
||||
cy.findByTestId('SamlSettings.ServiceProviderIdentifier').then((elem) => {
|
||||
Cypress.$(elem).val() === config.SamlSettings.ServiceProviderIdentifier;
|
||||
});
|
||||
|
||||
// * Verify that we can successfully save the settings (we have not affected previous state)
|
||||
cy.get('#saveSetting').click();
|
||||
});
|
||||
|
||||
it('fetches metadata and sets the IdP certificate from Idp Metadata Url', () => {
|
||||
cy.apiUpdateConfig({
|
||||
SamlSettings: {
|
||||
Enable: true,
|
||||
IdpMetadataURL: testSamlMetadataSuccessUrl,
|
||||
IdpURL: testIdpURL,
|
||||
IdpDescriptorURL: testIdpDescriptorURL,
|
||||
AssertionConsumerServiceURL: Cypress.config('baseUrl') + '/login/sso/saml',
|
||||
ServiceProviderIdentifier: Cypress.config('baseUrl') + '/login/sso/saml',
|
||||
},
|
||||
});
|
||||
|
||||
cy.visit('/admin_console/authentication/saml');
|
||||
|
||||
cy.intercept('POST', '**/api/v4/saml/metadatafromidp', (req) => {
|
||||
req.reply({
|
||||
statusCode: 200,
|
||||
body: {
|
||||
idp_url: testFetchedIdpURL,
|
||||
idp_descriptor_url: testFetchedIdpDescriptorURL,
|
||||
idp_public_certificate: testIdpPublicCertificate,
|
||||
},
|
||||
});
|
||||
}).as('getSamlMetadataFromIdp');
|
||||
|
||||
cy.intercept('POST', '**/api/v4/saml/certificate/idp', (req) => {
|
||||
expect(req.headers['content-type']).to.eq('application/x-pem-file');
|
||||
expect(req.body).to.eq(testIdpPublicCertificate);
|
||||
|
||||
req.reply({
|
||||
statusCode: 200,
|
||||
body: {status: 'OK'},
|
||||
});
|
||||
}).as('setSamlIdpCertificateFromMetadata');
|
||||
|
||||
// # Click on the Get SAML Metadata Button
|
||||
cy.get('#getSamlMetadataFromIDPButton button').scrollIntoView().should('be.visible').and('be.enabled').click();
|
||||
|
||||
// * Verify that the metadata and certificate endpoints are called
|
||||
cy.wait('@getSamlMetadataFromIdp');
|
||||
cy.wait('@setSamlIdpCertificateFromMetadata');
|
||||
|
||||
// * Verify that the IdP URL fields have been updated
|
||||
cy.findByTestId('SamlSettings.IdpURLinput').should('have.value', testFetchedIdpURL);
|
||||
cy.findByTestId('SamlSettings.IdpDescriptorURLinput').should('have.value', testFetchedIdpDescriptorURL);
|
||||
|
||||
// * Verify that the success message reflects the updated fields and certificate
|
||||
cy.get('#getSamlMetadataFromIDPButton').should('be.visible').contains(getSamlMetadataSuccessMessage);
|
||||
|
||||
// * Verify that the IdP certificate row shows the remove certificate view
|
||||
cy.contains('.remove-filename', 'saml-idp.crt').should('be.visible');
|
||||
cy.contains('button', 'Remove Identity Provider Certificate').should('be.visible');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user