diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..daa30a3f752706bce3bc36b34a7aab39ee5484d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +README.html diff --git a/README.md b/README.md index 614be9eb1ee99b11d258445b57f39bf29a966c6c..057e6944755161b5fa9b9be36374ee567e82f178 100644 --- a/README.md +++ b/README.md @@ -52,9 +52,9 @@ Buttons: 4, named as the routes above. ### Back-end -:construction: Session management subsystem written in Python/FlaskGunicorn/??? +:construction: Session management subsystem written in Python/Flask -:hammer_and_wrench: This part requires some development by the students. +:tools: This part requires some development by the students. ### Storage @@ -62,22 +62,110 @@ Buttons: 4, named as the routes above. :construction: S3-like object storage composed of 1 buckets with two directories: one for *enrollment* data, one for *session* data. +Objects shall be written as JSON data based on the following proposed schema. + + +#### Enrollment objects + +An enrollment object: + * is named after the user e-mail + * is unique in the system + * is created when a new user subscribes to the system via the `enroll` function + * is never updated (assume that users' passwords cannot be changed) + * is deleted when a subscribed (== existing) user unsubscribes from the + system via the `unenroll` function + +Minimum schema (you're free to extend it): + +``` json +{ + "title": "Enrollment", + "type": "object", + "properties": { + "password": { + "type": "string", + "description": "The user's password." + }, + "timestamp": { + "description": "The UNIX epoch time of object's creation", + "type": "integer", + "minimum": 0 + } + } +} +``` + +Example data for object named `foo@bar.com`: +``` json +{ + "password": "SECRET", + "timestamp": 1733330967 +} +``` + + +#### Session objects + +A session object: + * is named after the user e-mail + * is unique in the system -- multiple sessions are not permitted + * is created at the first user's connection to the system via the `login` + function + * is never updated + * is deleted when a logged-in user disconnects from the system via the + `logout` function + +Minimum schema (you're free to extend it): + +``` json +{ + "title": "Session", + "type": "object", + "properties": { + "client": { + "description": "The client host's IPv4 address", + "type": "string", + "format": "ipv4" + }, + "timestamp": { + "description": "The UNIX epoch time of object's creation", + "type": "integer", + "minimum": 0 + } + } +} +``` + +Example data for object named `foo@bar.com`: +``` json +{ + "timestamp": 1733330967 +} +``` + ### Workflows -The following workflows correspond to the front-end routes discussed above. +The following workflows correspond to the application "routes" discussed +above. Additional information is provided in the boilerplate files. All +involved data are in JSON format. Conventions: * KEYWORDS are capitalized. * **Actors** are in boldface. * 'CONSTANTS' are capitalized and single-quoted. * *data* items are lower-case and italicized. + * all times are represented as [UNIX epoch + timestamps](https://en.wikipedia.org/wiki/Unix_time) -:bulb: This is a fake SSO ;-) For the sake of simplicity: +:bulb: For the sake of simplicity: * Sessions never expire. * No cookies are stored at the client. * The e-mail address is used as authentication token. +All operations are *stateless*: apart from enrollment and session data, +nothing else is recorded by the back-end. + #### Enroll @@ -90,7 +178,7 @@ A new user subscribes to the system via the `enroll` function: - IF user exists THEN returns 'KO:ALREADY_ENROLLED' - ELSE 1. Writes enrollment data to storage - 2. Returns 'OK' to the front-end + 2. Returns 'OK:ENROLLED' to the front-end 4. **Front-end** receives response from the back-end and shows it to the user. @@ -102,10 +190,10 @@ An enrolled unsubscribes from the system with the `unenroll` function: front-end. 2. **Front-end** sends enrollment data to the back-end. 3. **Back-end** verifies enrollment data: - - IF user does not exists THEN returns 'KO:NO_SUCH_USER' + - IF user does not exists THEN returns 'KO:NO\_SUCH\_USER' - ELSE 1. Removes enrollment data and any active sessions from the storage - 2. Returns 'OK' to the front-end + 2. Returns 'OK:UNENROLLED' to the front-end 4. **Front-end** receives response from the back-end and shows it to the user. @@ -116,22 +204,22 @@ An enrolled user authenticates to the system with the `login` function: 1. **User** provides *e-mail* via the the front-end. 2. **Front-end** sends *e-mail* to the back-end. 3. **Back-end** verifies the *e-mail*: - - IF user does not exists THEN returns 'KO:NO_SUCH_USER' + - IF user does not exists THEN returns 'KO:NO\_SUCH\_USER' - ELSE verifies sessions: - 1. IF an active session exists THEN returns 'OK' - 2. ELSE returns 'NEED_PASSWORD'. + 1. IF an active session exists THEN returns 'OK:SESSION_EXISTS' + 2. ELSE returns 'OK:NEED_PASSWORD'. 4. **Front-end** receives first response from the back-end: - - IF response == 'NEED_PASSWORD' THEN + - IF response == 'OK:NEED_PASSWORD' THEN 1. Prompts the user for their *password*. 2. **User** provides *password*. 3. **Front-end** sends *e-mail* and *password* to the back-end. 4. **Back-end** verifies the *password*: - IF *password* matches THEN - 1. Writes session to storage. - 2. Returns 'OK' to the front-end. + 1. Writes a new session to storage. + 2. Returns 'OK:LOGGED_IN' to the front-end. - ELSE returns 'KO:WRONG_PASSWORD' to the front-end. - - ELSE shows 'OK' response to the user and terminates. - 5. **Front-end** receives second response 'OK/KO' from the back-end and + - ELSE shows other response to the user and terminates. + 5. **Front-end** receives second response from the back-end and shows it to the user. @@ -142,12 +230,12 @@ An enrolled user deauthenticates to the system with the `logout` function: 1. **User** provides *e-mail* via the the front-end. 2. **Front-end** sends *e-mail* to the back-end. 3. **Back-end** verifies the *e-mail*: - - IF user does not exists THEN returns 'KO' + - IF user does not exists THEN returns 'KO:NO\_SUCH\_USER' - ELSE verifies sessions: - IF an active session exists THEN 1. Removes session from storage. - 2. Returns 'OK'. - - ELSE returns 'KO'. + 2. Returns 'OK:LOGGED_OUT'. + - ELSE returns 'KO:NO\_ACTIVE\_SESSION'. 4. **Front-end** receives response from the back-end and shows it to the user. @@ -186,18 +274,53 @@ whenever its image is updated. ## Tasks +:construction: **To be finalized** + You shall: +0. Fork this repository. 1. Complete the Python back-end file(s) in folder `Application/back-end.py`. -2. Rebuild the application Docker image, and store it :question: somewhere -- - **(TO-DO: We should provide instructions. Build on the student's - workstation?)**. -3. Complete the `Terraform/main.tf` recipe to handle the provisioning of the S3 storage - bucket. -4. Complete the `Ansible/deploy.yml` playbook to handle: - - exposure of the application portal IP (e.g., load-balancer IP) to the +2. Rebuild the application Docker image, and store it (somewhere) -- + **(:question: TO-DO - We should provide instructions + Dockerfile: + - Build image on the student's workstation + - What's better: push to Dockerhub vs to scp to VM + import? + )**. This task shall be automated via Ansible -- see below. +3. Complete your Terraform files from the version you developed in + [Lab-Terraform](https://gitedu.hesge.ch/lsds/teaching/bachelor/cloud-and-deployment/lab-terraform/-/blob/main/SwitchEngines/README.md) + up to Task #8. Your recipe shall handle only the provisioning of the VM + plus an S3 storage bucket -- no KinD/Kubectl package installation. Commit + your recipe files (included Cloud-init) and in directory `Terraform/`. +4. Complete your Ansible playbook, starting from the version you developed in + [Lab-Ansible](https://gitedu.hesge.ch/lsds/teaching/bachelor/cloud-and-deployment/lab-ansible) + Task #10, to: + - expose the application portal IP (e.g., load-balancer IP) to the Internet via `socat` or other mechanism of your choice; - - :question: **(TO-DO: What's better? Local or registry [Helm/Docker])?** - transfer/download of the application image to your VM instance. - -Tests: :construction: **TO-DO** + - :question: **(TO-DO: What's better? Local or registry [Docker])?** + rebuild and transfer/download the application image to your VM instance. + Commit these files in directory `Ansible/`. + + +### Tests + +The following tests shall be passed by your implementation: + + * Starting with a non-provisioned infrastructure: + 1. Terraform apply shall provision a bare-bone VM and an S3 bucket + 2. Ansible-playbook shall install KinD/Kubectl and deploy your + load-balanced application + 3. Your application shall be reachable on port 80 (or another of your + choice) from any host outside the Cloud network. + * Once your application is installed, you shall exercise all the branch + conditions described by the above workflows: + 1. Enroll a new user: shall succeed + 2. Enroll an existing: user shall fail + 3. Unenroll a new user: shall fail + 4. Unenroll an existing user: shall succeed + 5. Login an enrolled user: shall succeed and ask for the password + * with a valid password: shall succeed + * with an invalid password: shall fail + 6. Login a non-enrolled user: shall fail + 7. Logout a non-enrolled user: shall fail + 8. Logout an enrolled user + * with an active session: shall succeed + * without an active session: shall fail