-
Notifications
You must be signed in to change notification settings - Fork 4
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
DCJ-654: Sign-in via OIDC #2667
Changes from 31 commits
db33dbc
386d0f2
456cb7f
9bf40f3
d79a57e
924b8ee
becd2c8
1115338
907746f
a3eaeba
2e4e93b
7deecc4
df09f44
dfa721a
59cd7be
20dad74
25d21fd
f2bb354
28b4231
c5c67a0
1c1288b
e100a0f
14176ab
a45492f
b035073
a0b9fba
18e7b13
8944f48
9e5f497
3c59ed3
ad2d8ed
54f025b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,48 @@ | ||
/* eslint-disable no-undef */ | ||
|
||
import {OidcBroker} from '../../../src/libs/auth/oidcBroker'; | ||
import {Auth} from '../../../src/libs/auth/auth'; | ||
import {Config} from '../../../src/libs/config'; | ||
import {GoogleIS} from '../../../src/libs/googleIS'; | ||
import {OAuth2} from '../../../src/libs/ajax/OAuth2'; | ||
import {Storage} from '../../../src/libs/storage'; | ||
import { v4 as uuid } from 'uuid'; | ||
import {v4 as uuid} from 'uuid'; | ||
import {mockOidcUser} from './mockOidcUser'; | ||
|
||
describe('Auth', function () { | ||
describe('Auth Failure', function () { | ||
it('Sign In error throws expected message', async function () { | ||
cy.stub(OidcBroker, 'signIn').returns(null); | ||
cy.on('fail', (err) => { | ||
return err.message !== Auth.signInError(); | ||
}); | ||
Auth.signIn().then(() => { | ||
expect(Storage.getOidcUser()).to.be.null; | ||
expect(Storage.userIsLogged()).to.be.false; | ||
}); | ||
}); | ||
}); | ||
|
||
describe('Auth Success', function () { | ||
// Intercept configuration calls | ||
beforeEach(async () => { | ||
beforeEach(() => { | ||
cy.intercept({ | ||
method: 'GET', | ||
url: '/config.json', | ||
hostname: 'localhost', | ||
}, {'env': 'ci'}); | ||
cy.stub(OAuth2, 'getConfig').returns({ | ||
'authorityEndpoint': 'authorityEndpoint', | ||
'authorityEndpoint': Cypress.config().baseUrl, | ||
'clientId': 'clientId' | ||
}); | ||
await Auth.initialize(); | ||
Auth.initialize(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function still seems to be async, does it need to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was initially confused by this. Cypress gives you a warning if you use
|
||
}); | ||
|
||
it('Sign In stores the current user', async function () { | ||
cy.stub(OidcBroker, 'signIn').returns(mockOidcUser); | ||
await Auth.signIn(); | ||
expect(Storage.getOidcUser()).to.not.be.empty; | ||
expect(Storage.userIsLogged()).to.be.true; | ||
}); | ||
|
||
it('Sign Out Clears the session when called', async function () { | ||
cy.stub(Config, 'getGoogleClientId').returns('12345'); | ||
cy.stub(GoogleIS, 'revokeAccessToken'); | ||
Storage.setUserIsLogged(true); | ||
Storage.setAnonymousId(uuid()); | ||
Storage.setData('key', 'val'); | ||
|
@@ -38,4 +57,5 @@ describe('Auth', function () { | |
expect(Storage.getData('key')).to.be.null; | ||
expect(Storage.getEnv()).to.be.null; | ||
}); | ||
|
||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import {OidcUser} from "../../../src/libs/auth/oidcBroker"; | ||
|
||
export const mockOidcUser: OidcUser = { | ||
access_token: '', | ||
get expires_in(): number | undefined { | ||
return undefined; | ||
}, | ||
session_state: undefined, | ||
state: undefined, | ||
token_type: '', | ||
get expired(): boolean | undefined { | ||
return undefined; | ||
}, | ||
get scopes(): string[] { | ||
return []; | ||
}, | ||
toStorageString(): string { | ||
return ''; | ||
}, | ||
profile: { | ||
jti: undefined, | ||
nbf: undefined, | ||
sub: undefined, | ||
iss: '', | ||
aud: '', | ||
exp: 0, | ||
iat: 0 | ||
} | ||
}; |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,113 @@ | ||
/* eslint-disable no-undef */ | ||
|
||
import React from 'react'; | ||
import { mount } from 'cypress/react'; | ||
import {mount} from 'cypress/react18'; | ||
import SignInButton from '../../../src/components/SignInButton'; | ||
import { Config } from '../../../src/libs/config'; | ||
import {User} from '../../../src/libs/ajax/User'; | ||
import {Auth} from '../../../src/libs/auth/auth'; | ||
import {Storage} from '../../../src/libs/storage'; | ||
import {Metrics} from '../../../src/libs/ajax/Metrics'; | ||
import {StackdriverReporter} from '../../../src/libs/stackdriverReporter'; | ||
import {ToS} from '../../../src/libs/ajax/ToS'; | ||
import {mockOidcUser} from '../Auth/mockOidcUser'; | ||
|
||
const signInText = 'Sign-in'; | ||
const signInText = 'Sign In'; | ||
|
||
// Note that we do not want to click the signin button | ||
// in tests as that would trigger an auth-flow we cannot | ||
// replicate in a test environment. | ||
describe('Sign In Component', function() { | ||
it('Sign In Button Loads when client id is valid', function () { | ||
const duosUser = { | ||
displayName: 'display name', | ||
email: '[email protected]', | ||
roles: [{ | ||
name: 'Admin' | ||
}] | ||
}; | ||
|
||
const userStatus = { | ||
'adminEnabled': true, | ||
'enabled': true, | ||
'inAllUsersGroup': true, | ||
'inGoogleProxyGroup': true, | ||
'tosAccepted': true | ||
}; | ||
|
||
const notAcceptedUserStatus = Object.assign({}, userStatus, {'tosAccepted': false}); | ||
|
||
describe('Sign In: Component Loads', function () { | ||
|
||
it('Sign In Button Loads', function () { | ||
cy.viewport(600, 300); | ||
mount(<SignInButton history={undefined}/>); | ||
cy.contains(signInText).should('exist'); | ||
}); | ||
|
||
it('Sign In: On Success', function () { | ||
cy.viewport(600, 300); | ||
cy.stub(Auth, 'signIn').returns(Promise.resolve(mockOidcUser)); | ||
cy.stub(User, 'getMe').returns(duosUser); | ||
cy.stub(StackdriverReporter, 'report'); | ||
cy.stub(Metrics, 'identify'); | ||
cy.stub(Metrics, 'syncProfile'); | ||
cy.stub(Metrics, 'captureEvent'); | ||
cy.stub(ToS, 'getStatus').returns(userStatus); | ||
mount(<SignInButton history={[]}/>); | ||
cy.get('button').click().then(() => { | ||
expect(Storage.getCurrentUser()).to.deep.equal(duosUser); | ||
expect(Storage.getAnonymousId()).to.not.be.null; | ||
expect(StackdriverReporter.report).to.not.be.called; | ||
expect(Metrics.identify).to.be.called; | ||
expect(Metrics.syncProfile).to.be.called; | ||
expect(Metrics.captureEvent).to.be.called; | ||
}); | ||
}); | ||
|
||
it('Sign In: No Roles Error Reporter Is Called', function () { | ||
const bareUser = {email: '[email protected]'}; | ||
cy.viewport(600, 300); | ||
// Load the client id from perf so we can have a valid button | ||
cy.readFile('config/alpha.json').then((config) => { | ||
const clientId = config.clientId; | ||
cy.stub(Config, 'getGoogleClientId').returns(clientId); | ||
mount(<SignInButton />); | ||
cy.contains(signInText).should('exist'); | ||
cy.stub(Auth, 'signIn').returns(Promise.resolve(mockOidcUser)); | ||
cy.stub(User, 'getMe').returns(bareUser); | ||
cy.stub(StackdriverReporter, 'report'); | ||
cy.stub(Metrics, 'identify'); | ||
cy.stub(Metrics, 'syncProfile'); | ||
cy.stub(Metrics, 'captureEvent'); | ||
cy.stub(ToS, 'getStatus').returns(userStatus); | ||
mount(<SignInButton history={[]}/>); | ||
cy.get('button').click().then(() => { | ||
expect(StackdriverReporter.report).to.be.called; | ||
}); | ||
}); | ||
it('Spinner loads when client id is empty', function () { | ||
|
||
it('Sign In: Redirects to ToS if not accepted', function () { | ||
cy.viewport(600, 300); | ||
cy.stub(Config, 'getGoogleClientId').returns(''); | ||
mount(<SignInButton />); | ||
cy.contains(signInText).should('not.exist'); | ||
cy.stub(Auth, 'signIn').returns(Promise.resolve(mockOidcUser)); | ||
cy.stub(User, 'getMe').returns(duosUser); | ||
cy.stub(ToS, 'getStatus').returns(notAcceptedUserStatus); | ||
cy.stub(Metrics, 'identify'); | ||
cy.stub(Metrics, 'syncProfile'); | ||
cy.stub(Metrics, 'captureEvent'); | ||
let history = []; | ||
mount(<SignInButton history={history}/>); | ||
cy.get('button').click().then(() => { | ||
expect(history).to.not.be.empty; | ||
expect(history[0].includes('tos_acceptance')).to.be.true; | ||
}); | ||
}); | ||
|
||
it('Sign In: Registers user if not found and redirects to ToS', function () { | ||
cy.viewport(600, 300); | ||
cy.stub(Auth, 'signIn').returns(Promise.resolve(mockOidcUser)); | ||
// Simulate user not found | ||
cy.stub(User, 'getMe').throws(); | ||
cy.stub(User, 'registerUser').returns(duosUser); | ||
cy.stub(ToS, 'getStatus').returns(notAcceptedUserStatus); | ||
cy.stub(Metrics, 'identify'); | ||
cy.stub(Metrics, 'syncProfile'); | ||
cy.stub(Metrics, 'captureEvent'); | ||
let history = []; | ||
mount(<SignInButton history={history}/>); | ||
cy.get('button').click().then(() => { | ||
expect(User.registerUser).to.be.called; | ||
expect(history).to.not.be.empty; | ||
expect(history[0].includes('tos_acceptance')).to.be.true; | ||
}); | ||
}); | ||
|
||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought alpha didn't exist anymore? Why do we need this config?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This config file name is not important and is, unfortunately, an artifact of our build image process. Originally, it did reference real environment variables. I'll change this in a separate PR/ticket.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created https://broadworkbench.atlassian.net/browse/DCJ-678 to cover this bit of tech debt.