Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -1742,14 +1742,14 @@ describe('SAMLAuthenticationProvider', () => {
});

mockOptions.client.asInternalUser.transport.request.mockResolvedValue({
access_token: 'some-token',
refresh_token: 'some-refresh-token',
access_token: 'essu_dev_some-token',
refresh_token: 'essu_dev_some-refresh-token',
realm: ELASTIC_CLOUD_SSO_REALM_NAME,
authentication: mockUser,
});
mockOptions.uiam?.getUserProfileGrant.mockReturnValue({
type: 'uiamAccessToken',
accessToken: 'some-token',
accessToken: 'essu_dev_some-token',
sharedSecret: 'some-secret',
});
mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient);
Expand Down Expand Up @@ -1777,20 +1777,20 @@ describe('SAMLAuthenticationProvider', () => {
AuthenticationResult.redirectTo('/test-base-path/some-path#some-app', {
userProfileGrant: {
type: 'uiamAccessToken',
accessToken: 'some-token',
accessToken: 'essu_dev_some-token',
sharedSecret: 'some-secret',
},
state: {
accessToken: 'some-token',
refreshToken: 'some-refresh-token',
accessToken: 'essu_dev_some-token',
refreshToken: 'essu_dev_some-refresh-token',
realm: ELASTIC_CLOUD_SSO_REALM_NAME,
},
user: mockUser,
})
);

expect(mockOptions.uiam?.getUserProfileGrant).toHaveBeenCalledTimes(1);
expect(mockOptions.uiam?.getUserProfileGrant).toHaveBeenCalledWith('some-token');
expect(mockOptions.uiam?.getUserProfileGrant).toHaveBeenCalledWith('essu_dev_some-token');
expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({
method: 'POST',
path: '/_security/saml/authenticate',
Expand All @@ -1807,8 +1807,8 @@ describe('SAMLAuthenticationProvider', () => {
it('properly constructs authentication headers when UIAM is enabled.', async () => {
const request = httpServerMock.createKibanaRequest({ headers: {} });
const state = {
accessToken: 'some-valid-token',
refreshToken: 'some-valid-refresh-token',
accessToken: 'essu_dev_some-valid-token',
refreshToken: 'essu_dev_some-valid-refresh-token',
realm: ELASTIC_CLOUD_SSO_REALM_NAME,
};
const authorization = `Bearer ${state.accessToken}`;
Expand Down Expand Up @@ -1841,8 +1841,8 @@ describe('SAMLAuthenticationProvider', () => {

it('fails if token invalidation fails.', async () => {
const request = httpServerMock.createKibanaRequest();
const accessToken = 'x-saml-token';
const refreshToken = 'x-saml-refresh-token';
const accessToken = 'essu_dev_x-saml-token';
const refreshToken = 'essu_dev_x-saml-refresh-token';

const failureReason = new errors.ResponseError(
securityMock.createApiResponse({ statusCode: 500, body: {} })
Expand All @@ -1859,17 +1859,17 @@ describe('SAMLAuthenticationProvider', () => {

expect(mockOptions.uiam?.invalidateSessionTokens).toHaveBeenCalledTimes(1);
expect(mockOptions.uiam?.invalidateSessionTokens).toHaveBeenCalledWith(
'x-saml-token',
'x-saml-refresh-token'
'essu_dev_x-saml-token',
'essu_dev_x-saml-refresh-token'
);

expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled();
});

it('redirects to `loggedOut` URL.', async () => {
const request = httpServerMock.createKibanaRequest();
const accessToken = 'x-saml-token';
const refreshToken = 'x-saml-refresh-token';
const accessToken = 'essu_dev_x-saml-token';
const refreshToken = 'essu_dev_x-saml-refresh-token';

await expect(
provider.logout(request, {
Expand All @@ -1881,8 +1881,8 @@ describe('SAMLAuthenticationProvider', () => {

expect(mockOptions.uiam?.invalidateSessionTokens).toHaveBeenCalledTimes(1);
expect(mockOptions.uiam?.invalidateSessionTokens).toHaveBeenCalledWith(
'x-saml-token',
'x-saml-refresh-token'
'essu_dev_x-saml-token',
'essu_dev_x-saml-refresh-token'
);

expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled();
Expand All @@ -1893,8 +1893,8 @@ describe('SAMLAuthenticationProvider', () => {
it('succeeds if token from the state is expired, but has been successfully refreshed.', async () => {
const request = httpServerMock.createKibanaRequest();
const state = {
accessToken: 'expired-token',
refreshToken: 'valid-refresh-token',
accessToken: 'essu_dev_expired-token',
refreshToken: 'essu_dev_valid-refresh-token',
realm: 'cloud-saml-kibana',
};

Expand All @@ -1903,27 +1903,27 @@ describe('SAMLAuthenticationProvider', () => {
);

mockOptions.uiam?.refreshSessionTokens.mockResolvedValue({
accessToken: 'new-access-token',
refreshToken: 'new-refresh-token',
accessToken: 'essu_dev_new-access-token',
refreshToken: 'essu_dev_new-refresh-token',
});

mockOptions.uiam?.getUserProfileGrant.mockReturnValue({
accessToken: 'new-access-token',
accessToken: 'essu_dev_new-access-token',
sharedSecret: 'some-secret',
type: 'uiamAccessToken',
});

await expect(provider.authenticate(request, state)).resolves.toEqual(
AuthenticationResult.succeeded(mockUser, {
authHeaders: { authorization: 'Bearer new-access-token' },
authHeaders: { authorization: 'Bearer essu_dev_new-access-token' },
userProfileGrant: {
accessToken: 'new-access-token',
accessToken: 'essu_dev_new-access-token',
sharedSecret: 'some-secret',
type: 'uiamAccessToken',
},
state: {
accessToken: 'new-access-token',
refreshToken: 'new-refresh-token',
accessToken: 'essu_dev_new-access-token',
refreshToken: 'essu_dev_new-refresh-token',
realm: 'cloud-saml-kibana',
},
})
Expand All @@ -1933,13 +1933,14 @@ describe('SAMLAuthenticationProvider', () => {
expect(mockOptions.uiam?.refreshSessionTokens).toHaveBeenCalledWith(state.refreshToken);

expect(request.headers).not.toHaveProperty('authorization');
expect(request.headers).not.toHaveProperty(ES_CLIENT_AUTHENTICATION_HEADER);
});

it('fails if token from the state is expired, refresh attempt failed, and displays error from UIAM', async () => {
const request = httpServerMock.createKibanaRequest({ headers: {} });
const state = {
accessToken: 'expired-token',
refreshToken: 'invalid-refresh-token',
accessToken: 'essu_dev_expired-token',
refreshToken: 'essu_dev_invalid-refresh-token',
realm: 'cloud-saml-kibana',
};
const authorization = `Bearer ${state.accessToken}`;
Expand All @@ -1963,6 +1964,227 @@ describe('SAMLAuthenticationProvider', () => {
});

expect(request.headers).not.toHaveProperty('authorization');
expect(request.headers).not.toHaveProperty(ES_CLIENT_AUTHENTICATION_HEADER);
});
});
});

describe('UIAM mode with ES native tokens', () => {
beforeEach(() => {
mockUser = mockAuthenticatedUser({
authentication_provider: { type: 'saml', name: ELASTIC_CLOUD_SSO_REALM_NAME },
});
mockOptions = mockAuthenticationProviderOptions({
name: ELASTIC_CLOUD_SSO_REALM_NAME,
uiam: true,
});

mockOptions.client.asInternalUser.transport.request.mockResolvedValue({
access_token: 'x_essu_dev_some-token',
refresh_token: 'x_essu_dev_some-refresh-token',
realm: ELASTIC_CLOUD_SSO_REALM_NAME,
authentication: mockUser,
});
mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient);

provider = new SAMLAuthenticationProvider(mockOptions, {
realm: ELASTIC_CLOUD_SSO_REALM_NAME,
});
});

describe('`login` method', () => {
it('properly constructs ES native user profile activate grant when UIAM is enabled.', async () => {
const request = httpServerMock.createKibanaRequest();
await expect(
provider.login(
request,
{ type: SAMLLogin.LoginWithSAMLResponse, samlResponse: mockSAMLSet1.samlResponse },
{
requestIdMap: {
[mockSAMLSet1.requestId]: { redirectURL: '/test-base-path/some-path#some-app' },
},
realm: ELASTIC_CLOUD_SSO_REALM_NAME,
}
)
).resolves.toEqual(
AuthenticationResult.redirectTo('/test-base-path/some-path#some-app', {
userProfileGrant: { type: 'accessToken', accessToken: 'x_essu_dev_some-token' },
state: {
accessToken: 'x_essu_dev_some-token',
refreshToken: 'x_essu_dev_some-refresh-token',
realm: ELASTIC_CLOUD_SSO_REALM_NAME,
},
user: mockUser,
})
);

expect(mockOptions.uiam?.getUserProfileGrant).not.toHaveBeenCalled();
expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({
method: 'POST',
path: '/_security/saml/authenticate',
body: {
ids: [mockSAMLSet1.requestId],
content: mockSAMLSet1.samlResponse,
realm: ELASTIC_CLOUD_SSO_REALM_NAME,
},
});
});
});

describe('`authenticate` method', () => {
it('properly constructs authentication headers only with ES native access token when UIAM is enabled.', async () => {
const request = httpServerMock.createKibanaRequest({ headers: {} });
const state = {
accessToken: 'x_essu_dev_some-valid-token',
refreshToken: 'x_essu_dev_some-valid-refresh-token',
realm: ELASTIC_CLOUD_SSO_REALM_NAME,
};
const authorization = `Bearer ${state.accessToken}`;

await expect(provider.authenticate(request, state)).resolves.toEqual(
AuthenticationResult.succeeded(mockUser, { authHeaders: { authorization } })
);

expect(mockOptions.client.asScoped).toHaveBeenCalledWith({ headers: { authorization } });

expect(request.headers).not.toHaveProperty('authorization');
expect(request.headers).not.toHaveProperty(ES_CLIENT_AUTHENTICATION_HEADER);
});
});

describe('`logout` method', () => {
it('returns `notHandled` if state is not presented or does not include access token.', async () => {
const request = httpServerMock.createKibanaRequest();

await expect(provider.logout(request)).resolves.toEqual(
DeauthenticationResult.notHandled()
);

expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled();
});

it('fails if token invalidation fails.', async () => {
const request = httpServerMock.createKibanaRequest();
const accessToken = 'x_essu_dev_x-saml-token';
const refreshToken = 'x_essu_dev_x-saml-refresh-token';

const failureReason = new errors.ResponseError(
securityMock.createApiResponse({ statusCode: 500, body: {} })
);
mockOptions.client.asInternalUser.transport.request.mockRejectedValue(failureReason);

await expect(
provider.logout(request, {
accessToken,
refreshToken,
realm: ELASTIC_CLOUD_SSO_REALM_NAME,
})
).resolves.toEqual(DeauthenticationResult.failed(failureReason));

expect(mockOptions.uiam?.invalidateSessionTokens).not.toHaveBeenCalled();

expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1);
expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({
method: 'POST',
path: '/_security/saml/logout',
body: { token: accessToken, refresh_token: refreshToken },
});
});

it('redirects to `loggedOut` URL.', async () => {
const request = httpServerMock.createKibanaRequest();
const accessToken = 'x_essu_dev_x-saml-token';
const refreshToken = 'x_essu_dev_x-saml-refresh-token';

mockOptions.client.asInternalUser.transport.request.mockResolvedValue({ redirect: null });

await expect(
provider.logout(request, {
accessToken,
refreshToken,
realm: 'test-realm',
})
).resolves.toEqual(DeauthenticationResult.redirectTo(mockOptions.urls.loggedOut(request)));

expect(mockOptions.uiam?.invalidateSessionTokens).not.toHaveBeenCalled();

expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1);
expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({
method: 'POST',
path: '/_security/saml/logout',
body: { token: accessToken, refresh_token: refreshToken },
});
});
});

describe('refresh token handling', () => {
it('succeeds if token from the state is expired, but has been successfully refreshed.', async () => {
const request = httpServerMock.createKibanaRequest();
const state = {
accessToken: 'x_essu_dev_expired-token',
refreshToken: 'x_essu_dev_valid-refresh-token',
realm: 'cloud-saml-kibana',
};

mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValueOnce(
new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} }))
);

mockOptions.tokens.refresh.mockResolvedValue({
accessToken: 'x_essu_dev_new-access-token',
refreshToken: 'x_essu_dev_new-refresh-token',
authenticationInfo: mockUser,
});

await expect(provider.authenticate(request, state)).resolves.toEqual(
AuthenticationResult.succeeded(mockUser, {
authHeaders: { authorization: 'Bearer x_essu_dev_new-access-token' },
state: {
accessToken: 'x_essu_dev_new-access-token',
refreshToken: 'x_essu_dev_new-refresh-token',
realm: 'cloud-saml-kibana',
},
})
);

expect(mockOptions.uiam?.refreshSessionTokens).not.toHaveBeenCalled();

expect(mockOptions.tokens.refresh).toHaveBeenCalledTimes(1);
expect(mockOptions.tokens.refresh).toHaveBeenCalledWith(state.refreshToken);

expect(request.headers).not.toHaveProperty('authorization');
expect(request.headers).not.toHaveProperty(ES_CLIENT_AUTHENTICATION_HEADER);
});

it('fails if token from the state is expired, refresh attempt failed, and displays error from UIAM', async () => {
const request = httpServerMock.createKibanaRequest({ headers: {} });
const state = {
accessToken: 'x_essu_dev_expired-token',
refreshToken: 'x_essu_dev_invalid-refresh-token',
realm: 'cloud-saml-kibana',
};
const authorization = `Bearer ${state.accessToken}`;

mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue(
new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} }))
);

const refreshFailureReason = new Boom.Boom('Authentication failed');
mockOptions.tokens.refresh.mockRejectedValue(refreshFailureReason);

await expect(provider.authenticate(request, state)).resolves.toEqual(
AuthenticationResult.failed(refreshFailureReason as any)
);

expect(mockOptions.uiam?.refreshSessionTokens).not.toHaveBeenCalled();

expect(mockOptions.tokens.refresh).toHaveBeenCalledTimes(1);
expect(mockOptions.tokens.refresh).toHaveBeenCalledWith(state.refreshToken);

expect(mockOptions.client.asScoped).toHaveBeenCalledWith({ headers: { authorization } });

expect(request.headers).not.toHaveProperty('authorization');
expect(request.headers).not.toHaveProperty(ES_CLIENT_AUTHENTICATION_HEADER);
});
});
});
Expand Down
Loading
Loading