diff --git a/README.md b/README.md index 5aaa319..fa7df03 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,30 @@ # Accounting application for the LgHS + +## Devenv, compile and run + +1. create an alias for docker-compose +```bash +alias dcacc='docker-compose -p lghs-accounting -f path/to/accounting/dev-env/docker-compose.yml' +``` + +2. start and init the dev-env +```bash +./dev-env/init.sh +``` + +3. launch app +```bash +dcacc up -d +``` + +4. add entry to host `127.0.0.1 keycloak` and connect to https://localhost or to http://localhost:80 (use user foobar/pwd) + + + + + + ## Dependencies You'll need a postgresql database and a compiled version of [coda-rs](https://github.com/bendem/coda-rs/tree/develop). diff --git a/dev-env/apache/Dockerfile b/dev-env/apache/Dockerfile new file mode 100644 index 0000000..2edcc9b --- /dev/null +++ b/dev-env/apache/Dockerfile @@ -0,0 +1,13 @@ +FROM httpd:2.4 + +RUN mkdir -p /usr/local/apache2/conf/sites/ /usr/local/apache2/htdocs/login +RUN echo 'IncludeOptional /conf/sites/*.conf' | tee -a /usr/local/apache2/conf/httpd.conf + +COPY resources/custom.conf /conf/sites/custom.conf +COPY resources/key /key +COPY resources/cert /cert + + + + + diff --git a/dev-env/apache/resources/cert b/dev-env/apache/resources/cert new file mode 100644 index 0000000..81a4afe --- /dev/null +++ b/dev-env/apache/resources/cert @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgIUaDm8Odr9Md37KJh+ddvLHtf+fn0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQkUxEzARBgNVBAgMClNvbWUtU3RhdGUxDTALBgNVBAoM +BGZyb2wxFTATBgNVBAMMDGNoYW1hbi5sb2NhbDAgFw0yMTExMjIyMTQ5MDBaGA8y +Mjk1MDkwNzIxNDkwMFowSDELMAkGA1UEBhMCQkUxEzARBgNVBAgMClNvbWUtU3Rh +dGUxDTALBgNVBAoMBGZyb2wxFTATBgNVBAMMDGNoYW1hbi5sb2NhbDCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMjT9okqxA20zrn78rEs+wmYlvmtZtQg +FGDQjRfLPfl8APYiKxkCR5BV6MAabX0b9JVIr4iyxUvn00Ln+8rgLIA1ZylwQ/aN +2qOAGgKSJrTysKDrPi+nBQBMRFrg+KmtKQadkBp960G+muZ4mlOn2KQiADsBUEXj +7C7g6zaFW+AucTxuB1/iGSIHN8JJuWeR+tWTnZKD9Cj9RW123iJm6MzF3mU8jEju ++3ymkr56guvtKI2DznARzKaoZA3SALFcL9HST766dlfnVJ9xBoXvFI9tbQr6jYI7 +pkdqWILVQ+6D4/nfYkzTdJBncV9sSkMC30KrD+T1YtePCZpJDGToaecCAwEAAaNT +MFEwHQYDVR0OBBYEFDQnyWPiMtOEjmS+VG8TBybCQQQiMB8GA1UdIwQYMBaAFDQn +yWPiMtOEjmS+VG8TBybCQQQiMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBADn7Zpk3hGzV+FeppkJDVZOv5mXjZMDGtJq05ZnDrw52sQgSkOAYw1ed +EAzl08Bh5D48hbUiTAIZfIg0UciZB4ONwfggjirX+3HGx4i+rkxfLn/CTG4bDs0B +p+1a7y0VAw9C6KcL8+J2s7QYN/LvQ7rRmwAvpkNzcY2uNTldGsjDSFiprS62JfLu +NDVzAzLAUEt8yo6Nz+IMOrDqO/BLAkWiOaNt5fA7l8ZhhAb0gXScXJUqvKphuXVo +cXUL5MKgBWwCfZVqhtM2oa43163IdfxJZvBfInWBMXi2sZn2KsBxBkX0yicNbcDw +psN5uaKYHML7D5qwFdiIQByco7js8DQ= +-----END CERTIFICATE----- diff --git a/dev-env/apache/resources/custom.conf b/dev-env/apache/resources/custom.conf new file mode 100644 index 0000000..f32e915 --- /dev/null +++ b/dev-env/apache/resources/custom.conf @@ -0,0 +1,30 @@ +Listen 443 + +ServerName accounting.local + + + + + LoadModule proxy_module modules/mod_proxy.so + LoadModule proxy_ajp_module modules/mod_proxy_ajp.so + LoadModule proxy_http_module modules/mod_proxy_http.so + LoadModule proxy_html_module modules/mod_proxy_html.so + LoadModule proxy_connect_module modules/mod_proxy_connect.so + LoadModule rewrite_module modules/mod_rewrite.so + LoadModule ssl_module modules/mod_ssl.so + + SSLEngine on + SSLCertificateFile "/cert" + SSLCertificateKeyFile "/key" + + RequestHeader set "X-Forwarded-SSL" expr=%{HTTPS} + RequestHeader set X-Forwarded-Proto https + RequestHeader set X-Forwarded-Port 443 + + ProxyPreserveHost On + ProxyPass / http://app:8080/ + ProxyPassReverse / http://app:8080/ + + + + diff --git a/dev-env/apache/resources/key b/dev-env/apache/resources/key new file mode 100644 index 0000000..cde913c --- /dev/null +++ b/dev-env/apache/resources/key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDI0/aJKsQNtM65 ++/KxLPsJmJb5rWbUIBRg0I0Xyz35fAD2IisZAkeQVejAGm19G/SVSK+IssVL59NC +5/vK4CyANWcpcEP2jdqjgBoCkia08rCg6z4vpwUATERa4PiprSkGnZAafetBvprm +eJpTp9ikIgA7AVBF4+wu4Os2hVvgLnE8bgdf4hkiBzfCSblnkfrVk52Sg/Qo/UVt +dt4iZujMxd5lPIxI7vt8ppK+eoLr7SiNg85wEcymqGQN0gCxXC/R0k++unZX51Sf +cQaF7xSPbW0K+o2CO6ZHaliC1UPug+P532JM03SQZ3FfbEpDAt9Cqw/k9WLXjwma +SQxk6GnnAgMBAAECggEAcHbrQEN3yWkwv8yNS5Ux/Fy8Z5Qo2rAgWVtRODr0OOut +qijWU8oIA5dooXPrTr3CCXvL+VSL0oc/st8xDZresOm3HOim1sL35soNRtVL4ddq +/KLgj1UMIu+S9AW6m2LCb9b65SsQ/R9dpaVyX1jGpJv53xFMP+d004fXkQ+7tN2v +NI/C9WAVwocNX/1nz/tiNgIYDqFpeS//cce89m/k1VtYQsPypIsIkVh9gBDMtJW2 +KTJoShd+hLUlYj5JYoyNHWQZKQ9YIN8p+GDhxL72Xxz3iq0XmsiQTOkqVuq+4Qw0 +GwMZLT1+HUCw5NmNYG61b3HDwPS+ABVvu6l1OOtwCQKBgQDpE124L/rJatLovBJH +BGYnTu0Y1tD9PsYW27HDVWXm9yKiDKss+5KnkZLecAKWUdvQejG8dwdsjCvz0O4v +L4B8esuuhvxoARmzHdKiPw3jwnrf8BokIz3j4smhGizb5M2n9zzSKPqOGaYbUy4Z +UNcfECyzLJbzgW3op1dysN3gGwKBgQDclKE8+w0vrkxW1+GcPk4xMcJUGLC5hzgQ +OLODyqW0rBIW82A8SjBssRynmcO7tZinBbxZaQQtVf6eGP9U3fQPgwR90SrTy0K2 +URmC75QuwZw6OiR0HSZ8ylTJ6B3dW1J3P/lpOPiCgLlpQs63sdZVE6IvlcZlF0k3 +ipBxQ9FyJQKBgHcIWu68IjpHghs2Iad/SaR9nHz5CJatPoFiBfEaZgaE5H0aNzmr +6YQGxpkvX0Lh338jSM9Vgsce9zt30ozql5X7KJajfbioBzYA3xfZWACfzB0eBFu8 +W9lMc2kBczOCML29eKRkJUH5O2ntGzf2n0bP/eQru5P03+rXu8NpLIdNAoGBAJ38 +5q+isQMhuBPLdBBQn/ikRyest4dqzkDwG8Oddao+RIajsp38FUsEYB27CHJ90uRu +O0Ve7lr6aPAbv2Wtw40IrkLfeelQgGvNEpSxvFTw31DlLA7ugg6HbMtgsWNGcX5w +F9fBslzmjeZSr3TqrzUoZtXmVL87OnnJiUNW68DxAoGAKSeg8xQRG26QBKVtrLja +TZpwkbEZIpOY8wMSeOYIVH6PiautbOcWKQo4UpIH2wIX7FM56kgPHitw7/5sZ7CE +pZZNbfS+dPQZp/SIvU3Sw3qxcu2dXcm0dMSS+Wz0HjBfhR0bF8lfF2/dQTLBhAko +fxOx5Y2q7R4hoVa4ysOmye8= +-----END PRIVATE KEY----- diff --git a/dev-env/dbMockData.sql b/dev-env/dbMockData.sql new file mode 100644 index 0000000..3d19475 --- /dev/null +++ b/dev-env/dbMockData.sql @@ -0,0 +1,10 @@ +insert into accounting.accounts (id,name, description, current_balance) values ('25e575be-1123-435a-aea4-d45ec68796f5','foo','bar',42); + +insert into accounting.codas (id,filename, content, account_id, sequence_number) VALUES +('4fc22296-4313-4845-b823-5f97268e26cc', 'test', 'test', '25e575be-1123-435a-aea4-d45ec68796f5', 1); + +insert into accounting.movement_categories (id, name, description, type) VALUES ('93b87203-896a-4951-8c69-3f739e5df96e', 'test', 'test', 'test'); + +insert into accounting.movements (id, amount, entry_date, account_id, coda_id, coda_sequence_number, counter_party_account_number, counter_party_name, communication, category_id) VALUES +('6e289df4-1e35-4206-9b12-ab1723c6f446', 1, now(), '25e575be-1123-435a-aea4-d45ec68796f5', '4fc22296-4313-4845-b823-5f97268e26cc', 1, 'test', 'test', 'test', '93b87203-896a-4951-8c69-3f739e5df96e'); + diff --git a/dev-env/docker-compose.yml b/dev-env/docker-compose.yml new file mode 100644 index 0000000..9d025a4 --- /dev/null +++ b/dev-env/docker-compose.yml @@ -0,0 +1,34 @@ +version: "2" + +services: + + apache: + build: apache + ports: + - 443:443 + + app: + image: registry.gitlab.com/lghs/accounting/accounting-build-docker + volumes: + - ..:/src + - $HOME/.gradle:/gradle/.gradle + ports: + - 80:8080 + user: $UID + working_dir: /src + command: ["gradle","--stacktrace","bootRun"] + + db: + image: postgres:13 + environment: + - POSTGRES_HOST_AUTH_METHOD=trust + + + keycloak: + image: quay.io/keycloak/keycloak:15.0.2 + ports: + - 8080:8080 + environment: + - KEYCLOAK_USER=admin + - KEYCLOAK_PASSWORD=pwd + - KEYCLOAK_FRONTEND_URL=http://keycloak:8080/auth/ diff --git a/dev-env/init.sh b/dev-env/init.sh new file mode 100755 index 0000000..ca6c500 --- /dev/null +++ b/dev-env/init.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +set -ue +set -o pipefail + +DIR=$(cd $(dirname $0) && pwd) + +INFO_COLOR='\033[0;33m' +ERROR_COLOR='\033[0;3om' +NC='\033[0m' # No Color + +info(){ + echo -e ${INFO_COLOR}$@${NC} +} + +error(){ + >&2 echo -e ${ERROR_COLOR}$@${NC} +} + + +function dcacc(){ + docker-compose -p lghs-accounting -f $DIR//docker-compose.yml $@ +} + +function waitDbReady(){ + (dcacc exec -T db psql -U postgres < <( echo 'select 1 ;') 2>&1 > /dev/null ) || ( + if [ $1 -lt 20 ] + then + echo '.' + sleep 2 + waitDbReady $(( $1 + 1 )) + else + error "time out waiting for db" + exit 1 + fi + ) +} + +function getKeycloakToken(){ + curl --data "username=admin&password=pwd&grant_type=password&client_id=admin-cli" http://localhost:8080/auth/realms/master/protocol/openid-connect/token \ + | sed 's/.*access_token":"//g' | sed 's/".*//g' \ + || ( + + if [ $1 -lt 20 ] + then + (>&2 wait for keycloak to be ready) + sleep 2 + getKeycloakToken $(( $1 + 1 )) + else + error "time out waiting for db" + exit 2 + fi + ) +} + +info start db and keycloak +dcacc up -d db keycloak + +info wait for db to start +waitDbReady 0 + +info init db +cat $DIR/../prepare_db.sql | dcacc exec -T db psql -Upostgres + + +info generate jook tables +dcacc run --rm app gradle --stacktrace jooq + +info fill db with mock data +cat $DIR/dbMockData.sql | dcacc exec -T db psql -U postgres lghs_accounting + + +info get keycloak token + +TOKEN=$(getKeycloakToken 1) +info token : $TOKEN + +curl -v http://localhost:8080/auth/admin/realms/master/clients -H "Content-Type: application/json" -H "Authorization: bearer $TOKEN" --data @$DIR/keycloak/accounting.json +curl -v http://localhost:8080/auth/admin/realms/master/users -H "Content-Type: application/json" -H "Authorization: bearer $TOKEN" --data @$DIR/keycloak/fooBarUser.json diff --git a/dev-env/keycloak/accounting.json b/dev-env/keycloak/accounting.json new file mode 100644 index 0000000..ac57fa4 --- /dev/null +++ b/dev-env/keycloak/accounting.json @@ -0,0 +1,66 @@ +{ + "clientId": "accounting", + "secret":"f539b20e-048a-4d81-90bf-238477fb3ad6", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "id.token.as.detached.signature": "false", + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "use.refresh.tokens": "true", + "exclude.session.state.from.auth.response": "false", + "oidc.ciba.grant.enabled": "false", + "saml.artifact.binding": "false", + "backchannel.logout.session.required": "true", + "client_credentials.use_refresh_token": "false", + "saml_force_name_id_format": "false", + "require.pushed.authorization.requests": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ], + "access": { + "view": true, + "configure": true, + "manage": true + } +} diff --git a/dev-env/keycloak/fooBarUser.json b/dev-env/keycloak/fooBarUser.json new file mode 100644 index 0000000..45488c8 --- /dev/null +++ b/dev-env/keycloak/fooBarUser.json @@ -0,0 +1,18 @@ +{"firstName":"foo", + "lastName":"bar", + "email":"foo@bar.com", + "enabled":"true", + "credentials": [ { + "type" : "password", + "hashedSaltedValue" : "26g/2ah/a+FR1NyRZw762fiu1q3vToa50El8AdXDKOZacF13v5FZ3S6dqgj9wTq9QUBA8CbCCPQdzRKozK1+hQ==", + "salt" : "rERt21H5qyALrIAghSi4Ow==", + "hashIterations" : 27500, + "counter" : 0, + "algorithm" : "pbkdf2-sha256", + "digits" : 0, + "period" : 0, + "createdDate" : 1645982432000, + "config" : { } + } ], + "username":"foobar" +} diff --git a/gradle.properties b/gradle.properties index 38e4cf9..a45a1d8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ -database_url=jdbc:postgresql:lghs_accounting +database_url=jdbc:postgresql://db/lghs_accounting database_schema=accounting database_driver=org.postgresql.Driver database_user=lghs_accounting_root -database_password= +database_password=lghs_accounting_root_password diff --git a/prepare_db.sql b/prepare_db.sql new file mode 100644 index 0000000..d65fc1f --- /dev/null +++ b/prepare_db.sql @@ -0,0 +1,15 @@ +create user lghs_accounting_root with password 'lghs_accounting_root_password'; -- change it really +create database lghs_accounting owner lghs_accounting_root; + +\c lghs_accounting +revoke all on schema public from public; + +create user lghs_accounting_app with password 'lghs_accounting_app_password'; -- change it really +grant connect on database lghs_accounting to lghs_accounting_app; + +grant usage on schema public to lghs_accounting_root; +grant usage on schema public to lghs_accounting_app; + +create extension "uuid-ossp"; +create extension pgcrypto; +create extension btree_gist; diff --git a/src/main/java/be/lghs/accounting/configuration/WebConfiguration.java b/src/main/java/be/lghs/accounting/configuration/WebConfiguration.java index c66c283..b0bdf1e 100644 --- a/src/main/java/be/lghs/accounting/configuration/WebConfiguration.java +++ b/src/main/java/be/lghs/accounting/configuration/WebConfiguration.java @@ -1,7 +1,10 @@ package be.lghs.accounting.configuration; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.CacheControl; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -28,4 +31,10 @@ public void addResourceHandlers(ResourceHandlerRegistry registry) { .addResourceLocations("classpath:/public/") .setCacheControl(staticCacheControl); } + + + @Bean + public JavaMailSender javaMailSender() { + return new JavaMailSenderImpl(); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 49b1d88..82c04db 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -7,21 +7,21 @@ logging.config: classpath:logback-spring.xml lghs.accounting: average-monthly-rent: 855 coda-rs: "../coda-rs" - deploy-url: "http://localhost:${server.port:8080}" + deploy-url: "https://accounting.local:${server.port:''}" spring: profiles: active: dev datasource: - url: jdbc:postgresql:lghs_accounting + url: jdbc:postgresql://db/lghs_accounting username: lghs_accounting_app - password: 'change-me' + password: 'lghs_accounting_app_password' flyway: url: ${spring.datasource.url} user: lghs_accounting_root - password: 'change-me' + password: 'lghs_accounting_root_password' schemas: accounting security: