Skip to content

drsanches/photobooth

Repository files navigation

PhotoBooth - a social network for sharing photos

Flow diagrams

Auth

sequenceDiagram
    actor user
    participant FilterChain
    participant auth as auth package
    
    note right of user: Login
    user -->> FilterChain: POST /api/v1/auth/token
    FilterChain ->> auth: 
    auth ->> auth: creates token
    auth -->> user: token

    note right of user: Refresh token
    user -->> FilterChain: GET /api/v1/auth/token/refresh
    FilterChain ->> auth: 
    auth ->> auth: creates new token
    auth -->> user: token

    note right of user: Logout
    user -->> FilterChain: DELETE /api/v1/auth/token
    FilterChain ->> auth: 
    auth ->> auth: removes token
    auth -->> user: 200
Loading

Account operations without 2FA

sequenceDiagram
    actor user
    participant FilterChain
    participant auth as auth package
    participant app as app package
    participant notifier as notifier package
    
    note right of user: Registration
    user -->> FilterChain: POST /api/v1/auth/account/create
    FilterChain ->> auth: 
    auth ->> auth: creates UserAuth
    auth ->> notifier: notify(email)
    activate notifier
    notifier ->> auth: 
    auth -->> user: token
    notifier ->> notifier: notifies about event
    deactivate notifier

    note right of user: Get info
    user -->> FilterChain: GET /api/v1/auth/account
    FilterChain ->> auth: 
    auth ->> auth: gets info
    auth -->> user: info

    note right of user: Update username
    user -->> FilterChain: POST /api/v1/auth/account/username
    FilterChain ->> auth: 
    auth ->> app: 
    app ->> app: updates username
    app ->> auth: 
    auth ->> auth: updates username
    auth ->> auth: removes all tokens
    auth ->> notifier: notify(email)
    activate notifier
    notifier ->> auth: 
    auth -->> user: 200
    notifier ->> notifier: notify about event
    deactivate notifier

    note right of user: update password
    user -->> FilterChain: POST /api/v1/auth/account/password
    FilterChain ->> auth: 
    auth ->> auth: changes password
    auth ->> auth: removes all tokens
    auth ->> notifier: notify(email)
    activate notifier
    notifier ->> auth: 
    auth -->> user: 200
    notifier ->> notifier: notify about event
    deactivate notifier

    note right of user: Updates email
    user -->> FilterChain: POST /api/v1/auth/account/email
    FilterChain ->> auth: 
    auth ->> auth: updates email
    auth ->> auth: removes all tokens
    auth ->> notifier: notify(email)
    activate notifier
    notifier ->> auth: 
    auth -->> user: 200
    notifier ->> notifier: notify about event
    deactivate notifier

    note right of user: Disable user
    user -->> FilterChain: DELETE /api/v1/auth/account
    FilterChain ->> auth: 
    auth ->> app: 
    app ->> app: disables UserProfile
    app ->> auth: 
    auth ->> auth: disables UserAuth
    auth ->> notifier: 
    notifier ->> notifier: removes email
    notifier ->> auth: 
    auth ->> auth: removes all tokens
    auth ->> notifier: notify(email)
    activate notifier
    notifier ->> auth: 
    auth -->> user: 200
    notifier ->> notifier: notify about event
    deactivate notifier
Loading

2FA logic

sequenceDiagram
    actor user
    participant FilterChain
    participant auth as auth package
    
    note right of user: Some operation
    user -->> FilterChain: /api/v1/auth/...
    FilterChain ->> auth: 
    auth ->> auth: saves request data
    auth ->> auth: send confirmation code to email
    auth -->> user: 200

    note right of user: Confirmation
    user -->> FilterChain: /api/v1/auth/confirm/{code}
    FilterChain ->> auth: 
    auth ->> auth: gets operation request data
    auth ->> auth: do operation
    auth -->> user: 200
Loading

FilterChain

sequenceDiagram
    actor user
    participant TokenAuthenticationFilter
    participant spring as Spring Security filter chain 
    participant LogFilter
    participant UserProfileSyncFilter
    participant auth as auth package
    participant app as app package
    
    note right of user: Public auth account operation
    user -->> TokenAuthenticationFilter: 
    TokenAuthenticationFilter ->> spring: 
    spring ->> spring: authenticate as anonymous
    spring ->> LogFilter:  
    LogFilter ->> LogFilter: writes log
    LogFilter ->> auth: 
    auth ->> auth: do something
    auth -->> user: 200
    
    note right of user: Private operation without (with invalid) token
    user -->> TokenAuthenticationFilter: 
    TokenAuthenticationFilter ->> TokenAuthenticationFilter: can't authenticate, do nothing
    TokenAuthenticationFilter ->> spring: 
    spring ->> spring: authenticate as anonymous
    spring ->> spring: reject request for anonymous
    spring -->> user: 401

    note right of user: Private auth account operation with valid user token
    user -->> TokenAuthenticationFilter: 
    TokenAuthenticationFilter ->> TokenAuthenticationFilter: authenticate as user
    TokenAuthenticationFilter ->>  spring: 
    spring ->>  spring: checks permissions
    spring ->>  LogFilter: 
    LogFilter ->> LogFilter: writes log
    LogFilter ->> auth: 
    auth ->> auth: do something
    auth -->> user: 200

    note right of user: Private user profile operation with valid user token first time
    user -->> TokenAuthenticationFilter: 
    TokenAuthenticationFilter ->> TokenAuthenticationFilter: authenticate as user
    TokenAuthenticationFilter ->> spring: 
    spring ->> spring: checks permissions
    spring ->> LogFilter: 
    LogFilter ->> LogFilter: writes log
    LogFilter ->> UserProfileSyncFilter: 
    UserProfileSyncFilter ->> UserProfileSyncFilter: create user profile
    UserProfileSyncFilter ->> app: 
    app ->> app: do something
    app -->> user: 200

    note right of user: Private user profile operation with valid user token and actual profile
    user -->> TokenAuthenticationFilter: 
    TokenAuthenticationFilter ->> TokenAuthenticationFilter: authenticate as user
    TokenAuthenticationFilter ->> spring: 
    spring ->> spring: checks permissions
    spring ->> LogFilter: 
    LogFilter ->> LogFilter: writes log
    LogFilter ->> UserProfileSyncFilter: 
    UserProfileSyncFilter ->> app: 
    app ->> app: do something
    app -->> user: 200

    note right of user: Private user profile operation with valid user token after username update
    user -->> TokenAuthenticationFilter: 
    TokenAuthenticationFilter ->> TokenAuthenticationFilter: authenticate as user
    TokenAuthenticationFilter ->> spring: 
    spring ->> spring: checks permissions
    spring ->> LogFilter: 
    LogFilter ->> LogFilter: writes log
    LogFilter ->> UserProfileSyncFilter: 
    UserProfileSyncFilter ->> UserProfileSyncFilter: update username in profile
    UserProfileSyncFilter ->> app: 
    app ->> app: do something
    app -->> user: 200

    note right of user: Admin operation with user token
    user -->> TokenAuthenticationFilter: 
    TokenAuthenticationFilter ->> TokenAuthenticationFilter: authenticate as user
    TokenAuthenticationFilter ->> spring: 
    spring ->> spring: reject request for user
    spring -->> user: 403
Loading

Auth with multiple methods

Add login by Google automatically (for the same email)

  1. Create account with Google email
  2. Get token by google token with the same email - google auth will be added
  3. Now token can be got by username/password or google token
sequenceDiagram
    actor user
    participant auth
    participant google

    note right of user: No auth
    user ->> auth: registration (username, password, email)
    auth ->> user: token
  
    note right of user: Google auth
    user ->> google: get token (email)
    google ->> user: googleToken
  
    note right of user: No auth
    user ->> auth: get token (googleToken)
    auth ->> google: get user info (googleToken)
    google ->> auth: email
    auth ->> auth: adds email for google auth
    auth ->> user: token
Loading

Add login by Google manually (for different emails)

  1. Create account
  2. Link account with Google (using google token) - google auth will be added
  3. Now token can be got by username/password or google token
sequenceDiagram
    actor user
    participant auth
    participant google
  
    note right of user: No auth
    user ->> auth: registration (username, password, email1)
    auth ->> user: token

    note right of user: Google auth
    user ->> google: get token (email2)
    google ->> user: googleToken

    note right of user: Auth by token
    user ->> auth: link (googleToken)
    auth ->> google: get user info (googleToken)
    google ->> auth: email1
    auth ->> auth: adds email1 for google auth
    auth ->> user: 200
Loading

Add login by username/password (for accounts created with Google)

  1. Get token by google token - account will be created (auth only by Google, without password)
  2. Set password - username/password auth will be added
  3. Now token can be got by username/password or google token
sequenceDiagram
    actor user
    participant auth
    participant app
    participant google

    note right of user: Google auth
    user ->> google: get token
    google ->> user: googleToken

    note right of user: No auth
    user ->> auth: get token (googleToken)
    auth ->> google: get user info (googleToken)
    google ->> auth: email, picture
    auth ->> auth: creates account (random username, email, picture)
    auth ->> app: create profile
    app ->> app: creates profile
    app ->> auth: 
    auth ->> user: token, code

    note right of user: Auth by token
    user ->> auth: update username (code)
    auth ->> app: 
    app ->> app: updates username
    app ->> auth: 
    auth ->> auth: updates username
    auth ->> user: 

    note right of user: Auth by token
    user ->> auth: change password
    auth ->> auth: adds password for auth
    auth ->> user: 200
Loading

Database structure

Database structure


Application

Requirements

There are two ways to run the application:

  • Using JVM
    • Java 17 - to build and run
    • PostgresSQL - (optional) to store the data in prod env
  • Using Docker - no additional dependencies are required

Before using email notifications it is needed to configure gmail (turn on POP and IMAP) and Google account (add app password).

Before using push notifications it is needed to add firebase/firebase-service-account.json.

Profiles

Application has 2 profiles:

  • dev - for local run, debug and tests:
    • Using by default
    • Using in-memory database h2
    • 2FA is disabled
    • Email notifications is disabled
    • ELK is disabled
    • Schedulers are disabled
  • prod - can bе configured by env variables (described in .env.app.dev):
    • Can be set with env variable SPRING_PROFILES_ACTIVE = prod
    • Using PostgreSQL as database (before run it is needed to create a database)
    • 2FA can be configured
    • Email notifications can be configured
    • ELK can be configured
    • Schedulers are enabled

How to run

App can be run locally by JVM, but for production it is recommended to use docker.

JVM

Removes all previous builds and builds executable jar:

gradlew clean bootJar

Run application with dev profile:

java -jar app/build/libs/photobooth-1.0.jar

Run application with prod profile and custom environment variables (the variables are described in .env.app.dev):

java -jar \
    -DSPRING_PROFILES_ACTIVE=prod \
    -DAPP_PORT=8080 \
    -DAPPLICATION_ADDRESS=http://localhost:8080 \
    -DADMIN_PASSWORD=pswd \
    -DJDBC_DATABASE_URL=jdbc:postgresql://localhost:5432/photobooth \
    -DJDBC_DATABASE_USERNAME=photobooth_app \
    -DJDBC_DATABASE_PASSWORD=pswd \
    app/build/libs/photobooth-1.0.jar

Docker

Files description:

  • app/Dockerfile for automatic photobooth application image building
  • docker-compose-app.yml services definitions for photobooth application
  • .env.app.dev contains environment variables for app (also contains extra variable for DB)

Use special .env.app.prod on prod.

Build services:

docker compose -f docker-compose-app.yml --env-file .env.app.dev build

Create and start containers:

docker compose -f docker-compose-app.yml --env-file .env.app.dev up

How to test

  • Runs unit-tests and integration tests (using dev profile by default):
    gradlew app:test
    
  • Runs end-to-end tests (the application must be started)
    gradlew end2end:test
    
  • Cleans test results (can be used before tests rerun)
    gradlew cleanTest
    

Nginx

Nginx reverse proxy is used to encrypt HTTP traffic.

How to create certs

The easiest way to create self-signed certificates is to run create_certs.sh from nginx directory with domain as argument.

Example:

create_certs.sh localhost

Result:

  • rootCA.crt - root cert for client (curl --cacert rootCA.crt https://example.com)
  • domain.crt - ssl certificate for nginx
  • domain.key - ssl certificate key for nginx

How to run

Nginx can be run in docker.

Files description:

  • docker-compose-nginx.yml services definitions
  • .env.nginx.dev contains environment variables

Use special .env.nginx.prod on prod.

Run by command:

docker compose -f docker-compose-nginx.yml --env-file .env.nginx.dev up

ELK-stack

ELK-stack (Elasticsearch, Logstash and Kibana) is used for monitoring. This stack is heavy, so for lightweight log monitoring Dozzle can be used.

How to run

ELK can be run in docker.

Files description:

  • docker-compose-elk.yml services definitions
  • .env.elk.dev contains environment variables

Use special .env.elk.prod on prod.

Run by command:

docker compose -f docker-compose-elk.yml --env-file .env.elk.dev up

ELK dashboards

The dashboard with all necessary indexes is located in the /elk folder (/elk/PhotoBooth_dashbaord.ndjson). It can be imported through Kibana web interface.

How to export

  • Open Stack Management
  • Open Saved Objects
  • Mark your dashboard
  • Press Export button
  • With Include related objects
  • Press Export button
  • Save

How to import

  • Open Stack Management
  • Open Saved Objects
  • Press Import button
  • Select a file to import
  • Press Import button
  • Done

Dozzle

Dozzle is a lightweight Docker log viewer that provides real-time monitoring.

How to run

Dozzle can be run in docker.

Files description:

  • docker-compose-dozzle.yml services definitions
  • .env.dozzle.dev contains environment variables

Use special .env.dozzle.prod on prod.

Run by command:

docker compose -f docker-compose-dozzle.yml --env-file .env.dozzle.dev up

Docker commands

  • docker ps -a - list of containers
  • docker stop container_name - stop container
  • docker rm container_name - remove container
  • docker logs container_name - show container's logs
  • docker compose -f docker-compose-file.yml --env-file .env-file build - build container
  • docker compose -f docker-compose-file.yml --env-file .env-file create - create container
  • docker compose -f docker-compose-file.yml --env-file .env-file up -d - create and start container (-d for background running)

Backlog

Back

  • Add cache?
  • Use annotations for authentication?
  • Use JpaRepository instead of CrudRepository?
  • Separate app, auth and notifier to different modules?
  • Add stub for Google auth?
  • Use login for auth and username for profile?
  • Use Spring Events for integrations before modules?
  • Log userId even for public urls?
  • Use docker swarm?
  • Own database for each module?
  • Add notificationEmail to confirmation model?
  • Limit container resources
  • Test indexes
  • Check docker container user permissions
  • Fix error "host not found in upstream" if nginx started without app and elk
  • Fix certs
  • Move ImageConsts to app
  • Check swagger errors for all urls. Maybe group all swagger annotations in one using array?
  • Add ObjectMapper bean
  • Refactor auth creation with google?
  • Rename domain to dao?
  • Refactor paging and soring. Use Page for responses?
  • Describe all features of the applications
  • Review log levels
  • Use unauthorized instead of wrong.token error?

UI

Tests

  • Use another framework?
  • Rewrite e2e with spring context and mocks?
  • Test paging
  • Test sorting
  • Test transactions
  • Remove redundant checks in e2e (such as status in friend tests)

About

Spring REST API for social network

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors