Skip to content

Commit

Permalink
feat: User authentication flow (#395)
Browse files Browse the repository at this point in the history
* Simplify login handler, implement login and logout buttons, add authContext and SSR

* Display user name in PersonalData, require auth on beta home page

* Add auth test helper, fixing tests and Storybook

* Fix beta home tests

* Add hook for fetching user if not from server

* Fix auth API tests

* Style log out button, add Header test

* Add tests for authContext

* Add tests for requireAuth

* Add useUser tests

* Render S&A page authenticated

* Adding login to cypress tests

* Fix Jest tests

* Build new image in Cypress CI

* Add auth to MVP pages, update tests

* Fix Cypress?

* Load MySpace bookmarks using API

* Use client side auth on beta home page

* Fix jest tests

* Use client side auth for all pages

* Add Cypress auth spec, fix linter

* CSS fixes for uppercase name

* Add tests for pages not logged in state
  • Loading branch information
Suzanne Rozier authored Dec 7, 2021
1 parent 3b2b8cd commit 1fd40cc
Show file tree
Hide file tree
Showing 62 changed files with 2,267 additions and 967 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cypress-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # renovate: tag=v2

- name: Docker compose
run: docker-compose -f docker-compose.e2e.yml up -d
run: docker-compose -f docker-compose.e2e.yml up -d --build

- name: Cypress run
uses: cypress-io/github-action@2113e5bc19c45979ba123df6e07256d2aaba9a33 # renovate: tag=v2.11.7
Expand Down
31 changes: 31 additions & 0 deletions docker-compose.e2e.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
version: '3.8'

services:
idp:
container_name: test-saml-idp
image: kristophjunge/test-saml-idp
restart: always
ports:
- '8080:8080'
- '8443:8443'
environment:
SIMPLESAMLPHP_SP_ENTITY_ID: ussf-portal-client
SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE: http://localhost:3000/api/auth/login
SIMPLESAMLPHP_SP_SINGLE_LOGOUT_SERVICE: http://localhost:3000/api/auth/logout/callback
volumes:
- ./users.php:/var/www/simplesamlphp/config/authsources.php

mongo:
container_name: mongo
image: mongo:4.0.0
restart: always
environment:
MONGO_INITDB_DATABASE: cypress-e2e
ports:
- '27017:27017'

redis:
container_name: portal_redis
image: redis:6.0
ports:
- '6379:6379'
119 changes: 119 additions & 0 deletions e2e/cypress/integration/auth.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
describe('The Authentication flow', () => {
describe('access without being logged in', () => {
it('requires a user to be logged in to view MVP pages', () => {
cy.clearCookies()
const mvpRoutes = [
'/',
'/about-us',
'/about-us/accomplishments',
'/news',
'/training-and-education',
'training-and-education/force-multiplier-program',
]

mvpRoutes.forEach((url) => {
cy.visit(url)
cy.url().should('match', /login/)
})
})

it('requires a user to be logged in to view beta pages', () => {
cy.clearCookies()
cy.visit('/joinbeta')

const betaRoutes = ['/', '/sites-and-applications']

betaRoutes.forEach((url) => {
cy.visit(url)
cy.url().should('match', /login/)
})
})
})

describe('logging in', () => {
beforeEach(() => {
cy.intercept('GET', '/api/auth/logout').as('logout')

cy.intercept(
{
method: 'GET',
url: '**/simplesaml/saml2/idp/SingleLogoutService.php*',
},
{
statusCode: 200,
body: 'Logged out',
}
).as('testIDPLogout')
})

it('a user can log into and out of the MVP site', () => {
cy.loginTestIDP()
cy.visit('/')
cy.contains('Manage your life')
cy.url().should('eq', Cypress.config().baseUrl + '/')

cy.contains('Log out').click()
cy.wait('@logout')

cy.visit('/')
cy.url().should('match', /login/)
})

it('a user can log into and out of the beta site', () => {
cy.loginTestIDP()
cy.visit('/joinbeta')

cy.visit('/')
cy.contains('My Space')
cy.url().should('eq', Cypress.config().baseUrl + '/')

cy.contains('Log out').click()
cy.wait('@logout')

cy.visit('/')
cy.url().should('match', /login/)
})
})

describe('access while logged in', () => {
before(() => {
cy.loginTestIDP()
})

beforeEach(() => {
cy.intercept('GET', '/api/auth/user').as('getUser')
cy.preserveLoginCookies()
})

it('can load the user on MVP pages', () => {
const mvpRoutes = [
'/',
'/about-us',
'/about-us/accomplishments',
'/news',
'/training-and-education',
'training-and-education/force-multiplier-program',
]

mvpRoutes.forEach((url) => {
cy.visit(url)
cy.wait('@getUser')
.its('response.statusCode')
.should('be.oneOf', [200, 304])
})
})

it('can load the user on beta pages', () => {
cy.visit('/joinbeta')

const betaRoutes = ['/', '/sites-and-applications']

betaRoutes.forEach((url) => {
cy.visit(url)
cy.wait('@getUser')
.its('response.statusCode')
.should('be.oneOf', [200, 304])
})
})
})
})
15 changes: 5 additions & 10 deletions e2e/cypress/integration/beta.spec.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import logging from '../plugins/logging'
describe('the Beta gate', () => {
before(() => {
cy.loginTestIDP()
})

beforeEach(() => {
cy.injectAxe()
cy.preserveLoginCookies()
})

it('joins and leaves the beta', () => {
// Just in case
cy.clearCookies()

// Start on MVP
cy.visit('/')
cy.contains('Manage your life')

// Join the beta
Expand All @@ -23,8 +22,4 @@ describe('the Beta gate', () => {
// Return to MVP
cy.contains('Manage your life')
})

it('logs any a11y violations', () => {
cy.checkA11y(null, null, logging, { skipFailures: true })
})
})
169 changes: 92 additions & 77 deletions e2e/cypress/integration/mvp.spec.js
Original file line number Diff line number Diff line change
@@ -1,104 +1,119 @@
import logging from '../plugins/logging'
describe('The MVP site', () => {
beforeEach(() => {
cy.visit('/')
cy.injectAxe()
})
describe('logged in pages', () => {
before(() => {
Cypress.Cookies.debug(true)

it('logs any a11y violations', () => {
cy.checkA11y(null, null, logging, { skipFailures: true })
})
cy.loginTestIDP()
})

it('lands on the home page', () => {
cy.contains('Manage your life').click()
cy.url().should('contain', '/#manage-your-life')
// TODO - try visit home page again as part of https://github.com/USSF-ORBIT/ussf-portal-client/issues/404

cy.contains('Work tools').click()
cy.url().should('contain', '/#work-tools')
})
beforeEach(() => {
cy.preserveLoginCookies()
cy.injectAxe()
})

it('contains the expected meta data', () => {
cy.document()
cy.get('head title').should('contain', 'Space Force Portal')
cy.get('head link[rel="canonical"]').should(
'have.attr',
'href',
Cypress.config().baseUrl + '/'
)
})
it('logs any a11y violations', () => {
cy.checkA11y(null, null, logging, { skipFailures: true })
})

it('can navigate to the Training and Education page', () => {
cy.contains('More in Training + Education').click()
cy.url().should('contain', '/training-and-education')
})
it('lands on the home page after logging in', () => {
cy.contains('Manage your life').click()
cy.url().should('contain', '/#manage-your-life')

it('can navigate to the News page', () => {
cy.contains('News').click()
cy.url().should('contain', '/news')
cy.contains('What’s New')
})
cy.contains('Work tools').click()
cy.url().should('contain', '/#work-tools')
})

it('can navigate to the About Us page', () => {
cy.contains('About us').click()
cy.url().should('contain', '/about-us')
cy.contains('About the Space Force')
})
it('contains the expected meta data', () => {
cy.document()
cy.get('head title').should('contain', 'Space Force Portal')

// TODO - Fix as part of https://github.com/USSF-ORBIT/ussf-portal-client/issues/352
/*
cy.get('head link[rel="canonical"]').should(
'have.attr',
'href',
Cypress.config().baseUrl + '/'
)
*/
})

it('can navigate to the Accomplishments page', () => {
cy.contains('About us').click()
cy.url().should('contain', '/about-us')
cy.contains('Our accomplishments').click()
cy.url().should('contain', '/about-us/accomplishments')
cy.contains('Things we’re proud of')
})
it('can navigate to the Training and Education page', () => {
cy.contains('More in Training + Education').click()
cy.url().should('contain', '/training-and-education')
})

it('can navigate to the Training and Education page', () => {
cy.contains('Training and education').click()
cy.url().should('contain', '/training-and-education')
cy.contains('Learn and Grow')
})
it('can navigate to the News page', () => {
cy.contains('News').click()
cy.url().should('contain', '/news')
cy.contains('What’s New')
})

it('can navigate to the Force Multiplier Program page', () => {
cy.contains('Training and education').click()
cy.url().should('contain', '/training-and-education')
cy.contains(
'Start your journey in digital fluency with our Force Multiplier program.'
).click()
cy.url().should(
'contain',
'/training-and-education/force-multiplier-program'
)
cy.contains('Become Digitally Fluent')
})
it('can navigate to the About Us page', () => {
cy.contains('About us').click()
cy.url().should('contain', '/about-us')
cy.contains('About the Space Force')
})

describe('the News page', () => {
beforeEach(() => {
cy.intercept(
'https://www.spaceforce.mil/DesktopModules/ArticleCS/RSS.ashx?ContentType=1&Site=1060&max=10'
).as('getNewsRSS')
it('can navigate to the Accomplishments page', () => {
cy.contains('About us').click()
cy.url().should('contain', '/about-us')
cy.contains('Our accomplishments').click()
cy.url().should('contain', '/about-us/accomplishments')
cy.contains('Things we’re proud of')
})

cy.visit('/news')
it('can navigate to the Training and Education page', () => {
cy.contains('Training and education').click()
cy.url().should('contain', '/training-and-education')
cy.contains('Learn and Grow')
})

it('loads news articles from an RSS feed', () => {
cy.wait(['@getNewsRSS'])
it('can navigate to the Force Multiplier Program page', () => {
cy.contains('Training and education').click()
cy.url().should('contain', '/training-and-education')
cy.contains(
'Start your journey in digital fluency with our Force Multiplier program.'
).click()
cy.url().should(
'contain',
'/training-and-education/force-multiplier-program'
)
cy.contains('Become Digitally Fluent')
})

describe('the News page', () => {
beforeEach(() => {
cy.intercept(
'https://www.spaceforce.mil/DesktopModules/ArticleCS/RSS.ashx?ContentType=1&Site=1060&max=10'
).as('getNewsRSS')

cy.visit('/news')
})

it('loads news articles from an RSS feed', () => {
cy.wait(['@getNewsRSS'])
})
})
})

describe('the Login page', () => {
describe('logged out pages', () => {
beforeEach(() => {
cy.visit('/login')
cy.clearCookies()
})

it('loads the login page', () => {
it('can visit the login page', () => {
cy.visit('/login')
cy.contains('Space Force Portal Login')
})
})

it('can navigate to the Login Notice page', () => {
cy.visit('/login-notice')
cy.contains('Notice')
cy.contains('I agree').click()
cy.contains('Space Force Portal Login')
it('can navigate to the Login Notice page', () => {
cy.visit('/login-notice')
cy.contains('Notice')
cy.contains('I agree').click()
cy.contains('Space Force Portal Login')
})
})
})
Loading

0 comments on commit 1fd40cc

Please sign in to comment.