Skip to content
Snippets Groups Projects
Unverified Commit 460dcf79 authored by Marco Emilio "sphakka" Poleggi's avatar Marco Emilio "sphakka" Poleggi
Browse files

Frontend: fix for CORS with dynmaic host probing. K8s: backend uses NodePort. More documentation

parent fbc3a788
No related branches found
No related tags found
No related merge requests found
all: all:
hosts: hosts:
testserver:
ansible_ssh_host: '<your-VM-IP>'
# aliases
tfserver:
testserver:
project-web-sso: project-web-sso:
testserver: ansible_ssh_host: '<your-VM-IP>'
...@@ -20,7 +20,7 @@ spec: ...@@ -20,7 +20,7 @@ spec:
spec: spec:
containers: containers:
- name: backend - name: backend
image: web-sso-backend:latest image: <your-Docker-Hub-username>/web-sso-backend:latest
ports: ports:
- containerPort: 8000 - containerPort: 8000
# @TODO: declare env from config map # @TODO: declare env from config map
...@@ -37,10 +37,11 @@ kind: Service ...@@ -37,10 +37,11 @@ kind: Service
metadata: metadata:
name: backend-service name: backend-service
spec: spec:
type: NodePort
selector: selector:
app: web-sso app: web-sso
tier: backend tier: backend
ports: ports:
- port: 8000 - port: 8000
targetPort: 8000 targetPort: 8000
type: ClusterIP nodePort: 30080
...@@ -20,6 +20,20 @@ ...@@ -20,6 +20,20 @@
background-color: white; background-color: white;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.form-group {
margin-bottom: 15px;
}
input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
label {
display: block;
margin-bottom: 5px;
} }
.button { .button {
display: inline-block; display: inline-block;
...@@ -44,19 +58,29 @@ ...@@ -44,19 +58,29 @@
<body> <body>
<div class="container"> <div class="container">
<h1 class="welcome-text">Welcome to Dashboard</h1> <h1 class="welcome-text">Welcome to Dashboard</h1>
<p>You are successfully logged in!</p> <p>You are logged in!</p>
<button onclick="logout()" class="button">Logout</button> <button onclick="logout()" class="button">Logout</button>
<button onclick="unenroll()" class="button">Remove account</button>
<form id="unenrollForm">
<div class="form-group">
<label for="password">Password:</label>
<input type="password" id="password" name="password">
</div>
<button type="submit" class="button">Remove account</button>
</form>
</div> </div>
<script> <script>
/* Get email from URL or localStorage (you should implement proper state /* Get email from URL or localStorage (you should implement proper state
management) */ management) */
const email = localStorage.getItem('userEmail'); const email = localStorage.getItem('userEmail');
console.log(email); /* The backend is supposed to run in the same host */
const backendHost = window.location.hostname;
async function logout() { async function logout() {
try { try {
const response = await fetch('http://localhost:8000/logout', { const response = await fetch(
'http://' + backendHost + ':8000/logout', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...@@ -76,15 +100,22 @@ ...@@ -76,15 +100,22 @@
alert('Error during logout. ' + error); alert('Error during logout. ' + error);
} }
} }
async function unenroll() {
document.getElementById('unenrollForm').addEventListener(
'submit', async (e) => {
e.preventDefault();
const password = document.getElementById('password').value;
try { try {
const response = await fetch('http://localhost:8000/unenroll', { const response = await fetch(
'http://' + backendHost + ':8000/unenroll', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ "email": email }), body: JSON.stringify({ email, password }),
}); }
);
const data = await response.json(); const data = await response.json();
if (data.status === 'OK:UNENROLLED') { if (data.status === 'OK:UNENROLLED') {
...@@ -98,6 +129,7 @@ ...@@ -98,6 +129,7 @@
alert('Error during unenroll. ' + error); alert('Error during unenroll. ' + error);
} }
} }
);
</script> </script>
</body> </body>
</html> </html>
...@@ -65,7 +65,7 @@ ...@@ -65,7 +65,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="password">Password:</label> <label for="password">Password:</label>
<input type="password" id="password" name="password" required> <input type="password" id="password" name="password">
</div> </div>
<button type="submit" class="button">Login</button> <button type="submit" class="button">Login</button>
</form> </form>
...@@ -73,22 +73,28 @@ ...@@ -73,22 +73,28 @@
</div> </div>
<script> <script>
document.getElementById('loginForm').addEventListener('submit', async (e) => { document.getElementById('loginForm').addEventListener(
'submit', async (e) => {
e.preventDefault(); e.preventDefault();
const email = document.getElementById('email').value; const email = document.getElementById('email').value;
const password = document.getElementById('password').value; const password = document.getElementById('password').value;
/* The backend is supposed to run in the same host */
const backendHost = window.location.hostname;
try { try {
const response = await fetch('http://localhost:8000/login', { const response = await fetch(
'http://' + backendHost + ':8000/login', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ email, password }), body: JSON.stringify({ email, password }),
}); }
);
const data = await response.json(); const data = await response.json();
if (data.status === 'OK:LOGGED_IN') { if (data.status === 'OK:LOGGED_IN' ||
data.status === 'OK:SESSION_EXISTS') {
localStorage.setItem('userEmail', email); localStorage.setItem('userEmail', email);
window.location.href = 'dashboard.html'; window.location.href = 'dashboard.html';
} else if (data.status === 'OK:NEED_PASSWORD') { } else if (data.status === 'OK:NEED_PASSWORD') {
...@@ -100,7 +106,8 @@ ...@@ -100,7 +106,8 @@
} catch (error) { } catch (error) {
alert('Error during login. ' + error); alert('Error during login. ' + error);
} }
}); }
);
</script> </script>
</body> </body>
</html> </html>
...@@ -73,19 +73,24 @@ ...@@ -73,19 +73,24 @@
</div> </div>
<script> <script>
document.getElementById('signupForm').addEventListener('submit', async (e) => { document.getElementById('signupForm').addEventListener(
'submit', async (e) => {
e.preventDefault(); e.preventDefault();
const email = document.getElementById('email').value; const email = document.getElementById('email').value;
const password = document.getElementById('password').value; const password = document.getElementById('password').value;
/* The backend is supposed to run in the same host */
const backendHost = window.location.hostname;
try { try {
const response = await fetch('http://localhost:8000/enroll', { const response = await fetch(
'http://' + backendHost + ':8000/enroll', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ email, password }), body: JSON.stringify({ email, password }),
}); }
);
const data = await response.json(); const data = await response.json();
if (data.status === 'OK:ENROLLED') { if (data.status === 'OK:ENROLLED') {
...@@ -97,7 +102,8 @@ ...@@ -97,7 +102,8 @@
} catch (error) { } catch (error) {
alert('Error during sign up. ' + error); alert('Error during sign up. ' + error);
} }
}); }
);
</script> </script>
</body> </body>
</html> </html>
...@@ -357,8 +357,12 @@ map: ...@@ -357,8 +357,12 @@ map:
version you developed in version you developed in
[Lab-Ansible](https://gitedu.hesge.ch/lsds/teaching/bachelor/cloud-and-deployment/lab-ansible) [Lab-Ansible](https://gitedu.hesge.ch/lsds/teaching/bachelor/cloud-and-deployment/lab-ansible)
Task #10, to (commit all related files in directory `Ansible/`): Task #10, to (commit all related files in directory `Ansible/`):
- expose the application portal's IP (i.e, the load-balancer's) to the - expose the following IPs to the Internet via `socat` or other
Internet via `socat` or other mechanism of your choice; mechanism of your choice;
+ the application portal's IP (i.e, the load-balancer's). Port
forwarding: 3000 (internal) -> 80 (external);
+ the backend node's IP. Port forwarding: 30080 (internal *NodePort*)
-> 8000 (external);
- rebuild and push the application images to your Docker Hub - rebuild and push the application images to your Docker Hub
repository. These shall be [`local_action` repository. These shall be [`local_action`
tasks.](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_delegation.html) tasks.](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_delegation.html)
...@@ -490,6 +494,11 @@ $ make -s docker-push ...@@ -490,6 +494,11 @@ $ make -s docker-push
You can manage your containers with the other make commands: dstop, dstart, You can manage your containers with the other make commands: dstop, dstart,
drm, etc. drm, etc.
:alert: To avoid [CORS-related
errors](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors) when
connecting to the application portal, make sure to point your browser to the URL
'http://localhost:3000'.
#### Kubernetes #### Kubernetes
...@@ -511,6 +520,27 @@ Of course, once all files are ready, any related operations shall be handled ...@@ -511,6 +520,27 @@ Of course, once all files are ready, any related operations shall be handled
by Ansible. Specifically, a change in the ConfigMap shall trigger a back-end by Ansible. Specifically, a change in the ConfigMap shall trigger a back-end
service redeployment. service redeployment.
:bulb: You may notice that the backend deployment service is of type
'NodePort': that allows the KinD worker node (IP like '172.x.x.x') to expose a
port (by default '30080') that shall be forwarded (e.g., via `socat`) to the
port expected by the frontend ('8000'). :alert: **Without the port forwarding,
the frontend will not be able to connect to the backend!**
Example. After deploying the backend, get the worker node's IP:
``` shell
VM$ kubectl get node/kind-worker -o jsonpath='{.status.addresses[0].address}'
172.18.0.3
```
Then get the backend service's NodePort:
``` shell
VM$ kubectl get service/backend-service -o jsonpath='{.spec.ports[0].nodePort}'
30080
```
Finally, expose the backend IP with port forwarding:
``` shell
VM$ nohup sudo socat -ly tcp-listen:8000,reuseaddr,fork tcp:172.18.0.3:30080 &
```
:bulb: For better security, you may use a K8s Secret manifest instead of the :bulb: For better security, you may use a K8s Secret manifest instead of the
ConfigMap. This would be a **bonus**. ConfigMap. This would be a **bonus**.
...@@ -552,4 +582,3 @@ The following tests shall be passed by your implementation: ...@@ -552,4 +582,3 @@ The following tests shall be passed by your implementation:
9. Logout an enrolled user: 9. Logout an enrolled user:
* with an active session: shall succeed * with an active session: shall succeed
* without an active session: shall fail * without an active session: shall fail
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment