Linux Workstation with Docker¶
For local development and fast debugging, without pushing changes to the version control and waiting for the build,
it is possible to install a local development environment in a laptop or a workstation.
The development workstation should be powerful enough to run PostgreSQL database, Docker containers, web server, the Java IDE
and the running application (advised is a 12-core CPU with min 32GB RAM.)
To be completely independent, it is possible also to run a local copy of Odoo 17 CRM/ERP.
In the further guide, we will assume that
- The development machine is named tqadm-dev.vanevski.net and the /etc/hosts file has a pointer to that name with either the real IP address of the machine, or 127.0.0.1
- The communication is HTTP (otherwise certificates and different NGINX setup are required).
- The database is installed and configured / imported
- Minimum of Java 17 SDK is available (tested on Amazon Corretto 17)
IDE installation¶
For the IDE, most convenient is to use JetBrains IntelliJ Idea; however, any IDE with Gradle support and ability to run/debug Java will suffice.
Database¶
PostgreSQL server 13 or later is recommended. The database tlinq and respective users should be created and imported.
User Authentication¶
For user authentication and authorization, we use Keycloak and Oauth2-Proxy. For local development, it is most convenient that they are installed as Docker containers. The container will be configured to expose the keycloak port on 8081 and oauth2-proxy port on standard port 4180.
Installation¶
To install and run the containers, it is enough to install Docker on the development machine; most convenient would be to use Docker Desktop (as it is multiplatform). You need to create the docker compose file as well as the oauth2-proxy configuration file:
-
Docker compose file (docker-compose.yaml)
version: '3.8' services: keycloak: image: quay.io/keycloak/keycloak container_name: keycloak command: [ 'start-dev', '--hostname=http://tqadm-dev.vanevski.net', '--hostname-strict=false' ] environment: KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin # expose the internal 8080 on host 8081 to avoid clashes ports: - "8081:8080" networks: - authnet oauth2-proxy: image: quay.io/oauth2-proxy/oauth2-proxy container_name: oauth2-proxy-adm depends_on: - keycloak volumes: - ./oauth2-proxy-adm.cfg:/etc/oauth2-proxy.cfg:ro ports: - "4180:4180" command: [ "--config", "/etc/oauth2-proxy.cfg" ] networks: - authnet networks: authnet: driver: bridge -
Configuration file for oauth-proxy (oauth2-proxy-cfg.adm) which is externally bound to the container and not injected in it:
# OIDC provider: Keycloak provider = "keycloak-oidc" # URL to the OIDC realm. If working under HTTPS, change this url to https:// oidc_issuer_url = "http://tqadm-dev.vanevski.net/realms/tqpro-adm" # Must match client created in Keycloak client_id = "tqweb-adm" # Copy from Keycloak → Credentials client_secret = "AXRZyCfifIwC5yd1KBi1zZ8QMT9ZQ2bU" # Where oauth2-proxy listens (all addresses) http_address = "0.0.0.0:4180" # Redirect URL after login (your frontend app) redirect_url = "http://tqadm-dev.vanevski.net/oauth2/callback" # Add whitelist domains for redirects whitelist_domains = ["tqadm-dev.vanevski.net"] # Skip authentication for sign_out endpoint (optional, if needed) skip_auth_routes = [ "^/oauth2/sign_out" ] # Session cookie settings cookie_secret = "U0ui7gdP2AyXRgsRWVCke5Lm7asK0xQZEjWCZKOk8wg=" # use: `openssl rand -base64 32` cookie_secure = false # must be true for HTTPS cookie_samesite = "lax" cookie_domains = ["tqadm-dev.vanevski.net"] set_authorization_header = true # Email domain filtering (allow all for now) email_domains = ["*"] # Session duration and refresh cookie_expire = "8h" cookie_refresh = "1h" # Upstream destination (local web app) # upstreams = ["http://127.0.0.1:8081/"] # optional if handled fully in NGINX # Disable automatic sign-in redirect (optional) skip_provider_button = true set_xauthrequest = true pass_user_headers = true pass_access_token = true # Logging (optional) standard_logging = true auth_logging = true request_logging = true
Once these two files are created, run Docker compose and start the containers.
Configuration¶
Once keycloak is installed, following configuration needs to be applied:
- Create realm tqpro-adm
- Configure the endpoints (URLs)
- Create client tqweb-adm
- Create two roles - agent and admin
- Create the user(s) and assign corresponding roles
After that, for the TQPro API server authentication and authorization to be functional,
you also need to configure in tlinq-api.properties file:
#####
# Locations for keycloak/oauth2-Proxy authentication
auth-server=http://tqadm-dev.vanevski.net
redir-server=http://tqadm-dev.vanevski.net
Nginx configuration¶
For Nginx to serve the pages properly with authentication, we need to place correct locations in the file. Following locations are important:
- Internal location
/oauth2/authwhere nginx will proxy all authentication requests:# Authentication endpoint location /oauth2/auth { internal; proxy_pass http://oauth2_proxy; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } - Public endpoints of
oauth2-proxy:location /oauth2/ { proxy_pass http://oauth2_proxy; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_buffers 16 64k; proxy_buffer_size 128k; } - Location where
oauth2-proxywill forward unauthorized users to sign in: - Website pages that need to be protected (in our case, it is the whole website):
# Protect / pages # For these pages, nginx will always send an authentication request to oauth2-proxy # if the proxy returns 401 (unauthorized), user will be redirected to the sign-in page location / { auth_request /oauth2/auth; error_page 401 = @oauth2_signin; root /var/www/nginx/html-adm; index index.html; try_files $uri $uri/ =404; } - Keycloak endpoints
- Sign-Out page (if needed; in TQPro we manage sign-out with an internal API)
location /oauth2/sign_out { proxy_pass http://oauth2_proxy; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Auth-Request-Redirect $scheme://$host/; } - Pages that do not need protection:
- The API server (note the header pass-through - these headers are required for authorization for TQPro APIs)
location /tlinq-api/ { auth_request /oauth2/auth; # capture upstream headers auth_request_set $user $upstream_http_x_auth_request_user; auth_request_set $roles $upstream_http_x_auth_request_groups; auth_request_set $email $upstream_http_x_auth_request_email; auth_request_set $token $upstream_http_x_auth_request_access_token; auth_request_set $uname $upstream_http_x_auth_request_preferred_username; auth_request_set $idtok $upstream_http_authorization; # forward to Jetty proxy_set_header X-User $user; proxy_set_header X-Roles $roles; proxy_set_header X-Email $email; proxy_set_header X-Name $uname; proxy_set_header Authorization $idtok; add_header Access-Control-Allow-Origin "*" always; add_header Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT" always; add_header Access-Control-Max-Age "1000" always; add_header Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept, client-security-token" always; proxy_pass http://tqapi_backend_adm; } - Finally, the upstream definitions of the servers: