Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

Commit

Permalink
feat: add keycloak authentication (#28)
Browse files Browse the repository at this point in the history
* feat: add keycloak integration

* feat: add token to requests

* feat: add user roles to components

* feat: add env vars to dockerfile

* feat: add missing env vars to docker build args

* feat: add console log to catch possible errors

* feat: add log and promise handler to app start

* feat: update onload keycloak init property

* Revert "feat: update onload keycloak init property"

This reverts commit 71c9284.

* feat: add react-keycloak-web dependency and provider

* feat: updated provier do be injected in a better place

* test: disabled failing tests for now

* feat: remove duplicated parameter

* doc: add docker steps to build and run the app

* feat: tests now passing and working

* feat: sonar cloud smells and reports

* feat: add event and token logger to test app behavior

* feat: add pop-up login - partial

* feat: open login popup - partial

* feat: add popup login

* feat: add login provider enum

* feat: remove console log and improve error handling

* feat: handle authenticated condition at header component

* feat: manual login handling

* feat: login without popup

* Empty commit to trigger workflows

* feat: get token from context

* feat: fix header and sample pages styling

* test: BCHeader component test passing

* test: App rendering test passing

* test: userTable component test passing

* feat: app auth provider in the right place

* feat: minor improvements

* feat: new version with check-sso route - testing

* feat: remove console log statements

* feat: add logout component

* feat: restore unrelated files, delete unused files, minor improvements

I decided to restore two files related to Cypress testing since this
is not in the target for now. Also deleted two files related to
Layout, however no Layout is not in use anymore. Also improved
exports of the AuthContext component to have a better reading.

* feat: fix typos and comments

* feat: add env vars to build time

* fix: adding support for env after build

adding env vars support after build

* fix: fixing react-inject-env folder

* feat: add new line to solve lint warning

* feat: add write permission to create env file

Co-authored-by: Paulo Gomes da Cruz Jr <[email protected]>
  • Loading branch information
Ricardo Campos and Paulo Gomes da Cruz Jr authored Dec 2, 2022
1 parent 40be9ec commit ee0bd69
Show file tree
Hide file tree
Showing 38 changed files with 958 additions and 391 deletions.
6 changes: 5 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@
"namedComponents": ["function-declaration", "arrow-function"],
"unnamedComponents": "arrow-function"
}
]
],
"no-shadow": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-shadow": ["error"],
"@typescript-eslint/no-unused-vars": "error"
}
}
15 changes: 15 additions & 0 deletions .github/openshift/deploy.frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ parameters:
- name: REACT_APP_SERVER_URL
description: Server URL address
required: true
- name: REACT_APP_KC_URL
description: KeyCloak Server URL address
required: true
- name: REACT_APP_KC_REALM
description: KeyCloak realm name
required: true
- name: REACT_APP_KC_CLIENT_ID
description: KeyCloak client id
required: true
objects:
- apiVersion: v1
kind: ImageStream
Expand Down Expand Up @@ -94,6 +103,12 @@ objects:
value: ${REACT_APP_NRFESAMPLEAPP_VERSION}
- name: REACT_APP_SERVER_URL
value: ${REACT_APP_SERVER_URL}
- name: REACT_APP_KC_URL
value: ${REACT_APP_KC_URL}
- name: REACT_APP_KC_REALM
value: ${REACT_APP_KC_REALM}
- name: REACT_APP_KC_CLIENT_ID
value: ${REACT_APP_KC_CLIENT_ID}
ports:
- containerPort: 3000
protocol: TCP
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/merge-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ jobs:
oc process -f .github/openshift/deploy.frontend.yml -p ZONE=${{ env.ZONE }} \
-p REACT_APP_NRFESAMPLEAPP_VERSION=test-${{ env.REACT_APP_NRFESAMPLEAPP_VERSION }} \
-p REACT_APP_SERVER_URL=${{ secrets.REACT_APP_SERVER_URL }} \
-p REACT_APP_KC_URL=${{ secrets.REACT_APP_KC_URL }} \
-p REACT_APP_KC_REALM=${{ secrets.REACT_APP_KC_REALM }} \
-p REACT_APP_KC_CLIENT_ID=${{ secrets.REACT_APP_KC_CLIENT_ID }} \
-p PROMOTE=${{ github.repository }}:${{ env.ZONE }}-app | oc apply -f -
# Follow any active rollouts (see deploymentconfigs)
Expand Down Expand Up @@ -354,6 +357,9 @@ jobs:
oc process -f .github/openshift/deploy.frontend.yml -p ZONE=${{ env.ZONE }} \
-p NRFESAMPREACT_APP_NRFESAMPLEAPP_VERSIONLEAPP_VERSION=prod-${{ env.REACT_APP_NRFESAMPLEAPP_VERSION }} \
-p REACT_APP_SERVER_URL=${{ secrets.REACT_APP_SERVER_URL }} \
-p REACT_APP_KC_URL=${{ secrets.REACT_APP_KC_URL }} \
-p REACT_APP_KC_REALM=${{ secrets.REACT_APP_KC_REALM }} \
-p REACT_APP_KC_CLIENT_ID=${{ secrets.REACT_APP_KC_CLIENT_ID }} \
-p PROMOTE=${{ github.repository }}:${{ env.PREV }}-app | oc apply -f -
# Follow any active rollouts (see deploymentconfigs)
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/pr-open.yml
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,9 @@ jobs:
DISABLE_ESLINT_PLUGIN: true
REACT_APP_NRFESAMPLEAPP_VERSION: ${{ env.REACT_APP_NRFESAMPLEAPP_VERSION }}
REACT_APP_SERVER_URL: ${{ secrets.REACT_APP_SERVER_URL }}
REACT_APP_KC_URL: ${{ secrets.REACT_APP_KC_URL }}
REACT_APP_KC_REALM: ${{ secrets.REACT_APP_KC_REALM }}
REACT_APP_KC_CLIENT_ID: ${{ secrets.REACT_APP_KC_CLIENT_ID }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
Expand Down Expand Up @@ -368,6 +371,9 @@ jobs:
oc process -f .github/openshift/deploy.frontend.yml -p ZONE=${{ env.ZONE }} \
-p REACT_APP_NRFESAMPLEAPP_VERSION=${{ env.REACT_APP_NRFESAMPLEAPP_VERSION }} \
-p REACT_APP_SERVER_URL=${{ secrets.REACT_APP_SERVER_URL }} \
-p REACT_APP_KC_URL=${{ secrets.REACT_APP_KC_URL }} \
-p REACT_APP_KC_REALM=${{ secrets.REACT_APP_KC_REALM }} \
-p REACT_APP_KC_CLIENT_ID=${{ secrets.REACT_APP_KC_CLIENT_ID }} \
-p PROMOTE=${{ github.repository }}:${{ env.ZONE }}-app | oc apply -f -
# Follow any active rollouts (see deploymentconfigs)
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@

npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn-error.log*
6 changes: 4 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
FROM node:16-bullseye
LABEL maintainer="Paulo Gomes da Cruz Junior <[email protected]>"

RUN yarn global add serve
RUN yarn global add serve@14.1.2 [email protected]

WORKDIR /app
COPY build/ .

EXPOSE 3000

CMD serve -s .
RUN chmod -R g+w .

CMD react-inject-env set -d . && serve -s .
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,38 @@ To run the unit tests all you need is `yarn test`. For end-to-end test you need
Before writing your first line of code, please take a moment and check out
our [CONTRIBUTING](CONTRIBUTING.md) guide.

## Quick look

But if all you want is to take a quick look on the running service, you can do it by
using Docker.

Note that you'll need these environment variables:
```
REACT_APP_SERVER_URL=<server-url>
REACT_APP_NRFESAMPLEAPP_VERSION=dev
REACT_APP_KC_URL=<keycloak-server-url>
REACT_APP_KC_REALM=<realm-name>
REACT_APP_KC_CLIENT_ID=<client-id>
```

Build the service:
```
docker build -t bcgov/nr-frontend-starting-app:latest \
--build-arg REACT_APP_NRFESAMPLEAPP_VERSION=dev \
--build-arg REACT_APP_SERVER_URL=<server-url> .
```

Then run with:
```
docker run -p 3000:3000 \
-e REACT_APP_NRFESAMPLEAPP_VERSION=dev \
-e REACT_APP_SERVER_URL=<server-url> \
-e REACT_APP_KC_URL=<keycloak-server-url> \
-e REACT_APP_KC_REALM=<realm-name> \
-e REACT_APP_KC_CLIENT_ID=<client-id> \
-t bcgov/nr-frontend-starting-app:latest
```

## Getting help

As mentioned, we're here to help. Feel free to start a conversation
Expand Down
6 changes: 6 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,9 @@ module.exports = {
'lcov'
]
};

process.env = Object.assign(process.env, {
REACT_APP_KC_URL: 'https://dev.any-keycloak-server.com/auth',
REACT_APP_KC_REALM: 'default',
REACT_APP_KC_CLIENT_ID: 'test-client-id'
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@typescript-eslint/parser": "5.0.0",
"eslint-config-airbnb-typescript": "^17.0.0",
"jest": "^29.0.0",
"keycloak-js": "^19.0.3",
"node-sass": "^7.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
39 changes: 20 additions & 19 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<!--

<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Expand All @@ -23,10 +21,13 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>NR Sample App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
<title>NR Sample App</title>
<script src="/env.js"></script>
</head>

<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>

</html>
55 changes: 46 additions & 9 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,64 @@
/* eslint-disable no-console */
import React from 'react';
import {
BrowserRouter, Routes, Route
} from 'react-router-dom';

import './custom.scss';

import Layout from './layout/PublicLayout';
import Home from './views/Home';
import Landing from './views/Landing';
import Form from './views/Form';
import Table from './views/Table';
import Home from './views/Home';
import ProtectedRoute from './routes/ProtectedRoute';
import { useAuth } from './contexts/AuthContext';
import SilentCheckSso from './components/SilentCheckSso';
import Logout from './components/Logout';

/**
* Create an app structure conaining all the routes.
*
* @returns {JSX.Element} instance of the app ready to use.
*/
const App: React.FC = () => {
const { signed } = useAuth();

function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="/form" element={<Form />} />
<Route path="/table" element={<Table />} />
</Route>
<Route path="/" element={<Landing />} />
<Route path="/silent-check-sso" element={<SilentCheckSso />} />
<Route path="/logout" element={<Logout />} />

<Route
path="/home"
element={(
<ProtectedRoute signed={signed}>
<Home />
</ProtectedRoute>
)}
/>

<Route
path="/form"
element={(
<ProtectedRoute signed={signed}>
<Form />
</ProtectedRoute>
)}
/>

<Route
path="/table"
element={(
<ProtectedRoute signed={signed}>
<Table />
</ProtectedRoute>
)}
/>
</Routes>
</BrowserRouter>
);
}
};

export default App;
7 changes: 6 additions & 1 deletion src/__test__/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from '../App';
import { AuthProvider } from '../contexts/AuthContext';

test('renders app', () => {
render(<App />);
render(
<AuthProvider>
<App />
</AuthProvider>
);
});
25 changes: 18 additions & 7 deletions src/__test__/components/BCHeader.test.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
/* eslint-disable no-undef */
import { render } from '@testing-library/react';
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { render } from '@testing-library/react';
import renderer from 'react-test-renderer';
import BCHeader from '../../components/BCHeader';
import { AuthProvider } from '../../contexts/AuthContext';

describe('the Header component', () => {
it('should have the correct title', () => {
const { getByTestId } = render(<BCHeader />);
const { getByTestId } = render(
<BrowserRouter>
<AuthProvider>
<BCHeader />
</AuthProvider>
</BrowserRouter>
);

// Carbon Header component uses &nbsp; to break the space between the prefix
// (BC Gov's) and the title (NR Sample App), so it's necessary to use \xA0
// to validate correctly
expect(getByTestId('header-name').textContent).toBe('BC Gov\'s\xA0NR Sample App');
expect(getByTestId('header-name').textContent).toBe("BC Gov's NR Sample App");
});

it('should match the snapshot', () => {
const tree = renderer
.create(<BCHeader />)
.create(
<BrowserRouter>
<AuthProvider>
<BCHeader />
</AuthProvider>
</BrowserRouter>
)
.toJSON();
expect(tree).toMatchSnapshot();
});
Expand Down
14 changes: 11 additions & 3 deletions src/__test__/components/UserTable.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/* eslint-disable no-undef */
import { render } from '@testing-library/react';
import React from 'react';

import { render } from '@testing-library/react';
import renderer from 'react-test-renderer';
import UserTable from '../../components/UserTable';
import SampleUser from '../../types/SampleUser';
import { AuthProvider } from '../../contexts/AuthContext';

describe('the UserTable component', () => {
const users: SampleUser[] = [{
Expand All @@ -17,7 +19,9 @@ describe('the UserTable component', () => {

it('should have the correct headers', () => {
const { getByTestId } = render(
<UserTable elements={users} deleteFn={() => {}} headers={tableHeaders} />
<AuthProvider>
<UserTable elements={users} deleteFn={() => {}} headers={tableHeaders} />
</AuthProvider>
);
tableHeaders.forEach((element, i) => {
expect(getByTestId(`header-${element}-${i}`).textContent).toBe(element);
Expand All @@ -26,7 +30,11 @@ describe('the UserTable component', () => {

it('should match the snapshot', () => {
const tree = renderer
.create(<UserTable elements={users} deleteFn={() => {}} headers={tableHeaders} />)
.create(
<AuthProvider>
<UserTable elements={users} deleteFn={() => {}} headers={tableHeaders} />
</AuthProvider>
)
.toJSON();
expect(tree).toMatchSnapshot();
});
Expand Down
Loading

0 comments on commit ee0bd69

Please sign in to comment.