Skip to content
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

Add infrastructure #3

Merged
merged 12 commits into from
Dec 22, 2023
Merged
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
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
site
?
_site
_site
node_modules/
build/
package-lock.json
yarn.lock
template
build
.secrets
tools/
10 changes: 10 additions & 0 deletions api/ping/ping.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 1.0
provider:
name: openfaas
gateway: http://127.0.0.1:8080
functions:
ping:
lang: python3-http
handler: ./ping
image: host.docker.internal:5000/ping:1.0

Empty file added api/ping/ping/__init__.py
Empty file.
11 changes: 11 additions & 0 deletions api/ping/ping/handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import json

def handle(event, context):
"""handle a request to the function
Args:
req (str): request body
"""
return {
"statusCode": 200,
"body": str(event.body)
}
Empty file added api/ping/ping/requirements.txt
Empty file.
2 changes: 2 additions & 0 deletions app/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
**/node_modules/**
*/yarn.lock
12 changes: 12 additions & 0 deletions app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM node:21-alpine3.17 as builder

ADD . /workdir

WORKDIR /workdir

RUN yarn install && yarn run build


FROM nginx:mainline-alpine
ADD nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /workdir/build /usr/share/nginx/html
24 changes: 24 additions & 0 deletions app/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
worker_processes 5; ## Default: 1

events {
worker_connections 4096; ## Default: 1024
}

http {
include /etc/nginx/mime.types;
server {
listen 80;
listen [::]:80;

root /usr/share/nginx/html;
index index.html index.htm;

location (.*)/config.js$ {
try_files $uri $uri/ /config.js;
}

location / {
try_files $uri $uri/ /index.html;
}
}
}
49 changes: 49 additions & 0 deletions app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "mm-potal",
"version": "0.1.0",
"private": true,
"homepage": "/app",
"dependencies": {
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"oidc-client-ts": "^2.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.20.1",
"react-scripts": "5.0.1",
"typescript": "^4.4.2",
"web-vitals": "^2.1.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11"
}
}
9 changes: 9 additions & 0 deletions app/public/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
config = {
'serverUrl': 'http://localhost/api',
'applicationRoot':'http://localhost/app/',
'authentication': {
'clientId': 'mm-portal',
'serviceUrl': 'http://localhost:8080/realms/master/',
'scope': 'openid profile email phone roles'
}
}
13 changes: 13 additions & 0 deletions app/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">

<head>
<script src="config.js"></script>
</head>

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

</html>
52 changes: 52 additions & 0 deletions app/src/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import './index.css';
import SecureRoute from './components/secureRoute'
import Home from './features/home/view/content';
import About from './features/about/view/content';
import NavigationMenu from './components/navigationbar';
import TitleBar from './components/titlebar';
import { SettingsService } from './common/services/settingsService';
import { AuthenticationService } from './common/services/authenticationService';
import { ApplicationContext } from './common/utilities/applicationContext';
import { SigninCallback } from './components/signinCallback';

const App = () => {
const context = new AppContext();
return (
<ApplicationContext.Provider value={context}>
<div className="App">
<Router basename='/app'>
<NavigationMenu />
<TitleBar />
<Routes>
<Route path="/" element={<SecureRoute><Home /></SecureRoute>} />
<Route path="signin-callback" element={<SigninCallback />} />
<Route path="about" element={<About />} />
</Routes>
</Router>
</div>
</ApplicationContext.Provider >
);
};

export default App;

class AppContext {
private settingsService: SettingsService | undefined;
private authenticationService: AuthenticationService | undefined;

public getAuthenticationService(): AuthenticationService {
if (!this.authenticationService) {
this.authenticationService = new AuthenticationService(this.getSettingsService().getAuthenticationSettings());
}
return this.authenticationService;
}

public getSettingsService(): SettingsService {
if (!this.settingsService) {
this.settingsService = new SettingsService();
}
return this.settingsService;
}
}
54 changes: 54 additions & 0 deletions app/src/common/services/authenticationService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Log, User, UserManager } from 'oidc-client-ts';
import { AuthenticationSettings } from '../settings/AuthenticationSettings';
import { JsonHelper } from '../utilities/jsonHelper';
import { Roles } from '../utilities/roles';

export class AuthenticationService {
public userManager: UserManager;

constructor(settings: AuthenticationSettings) {
const oidcSettings = {
authority: settings.serviceUrl,
client_id: settings.clientId,
redirect_uri: settings.applicationRoot + `signin-callback`,
response_type: 'code',
scope: settings.scope
};
this.userManager = new UserManager(oidcSettings);

Log.setLogger(console);
Log.setLevel(Log.INFO);
}

public getUser(): Promise<User | null> {
return this.userManager.getUser();
}

public login(): Promise<void> {
return this.userManager.signinRedirect();
}

public renewToken(): Promise<User | null> {
return this.userManager.signinSilent();
}

public logout(): Promise<void> {
return this.userManager.signoutRedirect({ state: (window.location.pathname + window.location.search).substring(1) });
}

public handleSignin(): Promise<User> {
return this.userManager.signinRedirectCallback();
}

public async getUserRoles(): Promise<string[]> {
var user = await this.getUser();
if (!user) {
throw new ReferenceError("User is not authenticated");
}
const claims = JsonHelper.parseJwt(user.access_token);
console.log("Claims", claims);
const allowedRoles = [Roles.admin, Roles.user]
const roles = (claims.roles as string[]).filter(r => allowedRoles.includes(r));
return roles;
}
}
18 changes: 18 additions & 0 deletions app/src/common/services/settingsService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { AuthenticationSettings } from '../settings/AuthenticationSettings';

export class SettingsService {
public getAuthenticationSettings(): AuthenticationSettings {
let settings = new AuthenticationSettings();

settings.clientId = (window as any).config.authentication.clientId;
settings.serviceUrl = (window as any).config.authentication.serviceUrl;
settings.scope = (window as any).config.authentication.clientScope;
settings.applicationRoot = (window as any).config.applicationRoot;

return settings;
}

public getServerUrl(): string {
return (window as any).config.serverUrl;
}
}
6 changes: 6 additions & 0 deletions app/src/common/settings/AuthenticationSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class AuthenticationSettings {
clientId: string
serviceUrl: string
scope: string
applicationRoot: string
}
14 changes: 14 additions & 0 deletions app/src/common/utilities/applicationContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as React from 'react';
import { AuthenticationService } from '../services/authenticationService';
import { SettingsService } from '../services/settingsService';

export interface IApplicationContext {
getAuthenticationService(): AuthenticationService;
getSettingsService(): SettingsService;
}

export const ApplicationContext = React.createContext({} as IApplicationContext);

export function useApplicationContext(): IApplicationContext {
return React.useContext(ApplicationContext);
}
Loading