1 Commits

Author SHA1 Message Date
e3bf3c1a25 Matrix app service test 2021-03-29 01:07:36 +02:00
53 changed files with 7879 additions and 2885 deletions

View File

@@ -1,12 +1,5 @@
# Okupa mi coche - Backend
Travel management in the valley.
THIS PROJECT IS IN EARLY DEVELOPMENT - WORK IN PROGRESS (including this README)
## Prerequisites
Install and run keycloak and synapse.
`docker/README` explains how to get a working local development environment.
Gestión de viajes en el valle.
## How to run
1. `git clone git@git.fosil.eu:eneko/okupamicoche.git`

View File

@@ -1,12 +1,12 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.6.1"
id("org.springframework.boot") version "2.5.0-M3"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
war
kotlin("jvm") version "1.6.10"
kotlin("plugin.spring") version "1.6.10"
kotlin("plugin.jpa") version "1.6.10"
kotlin("jvm") version "1.4.31"
kotlin("plugin.spring") version "1.4.31"
kotlin("plugin.jpa") version "1.4.31"
}
group = "eu.fosil"
@@ -15,6 +15,7 @@ java.sourceCompatibility = JavaVersion.VERSION_11
repositories {
mavenCentral()
maven { url = uri("https://repo.spring.io/milestone") }
}
dependencies {
@@ -26,12 +27,19 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
implementation("io.github.microutils:kotlin-logging-jvm:2.1.21")
implementation ("net.folivo:matrix-spring-boot-bot:0.5.2")
implementation("io.github.microutils:kotlin-logging-jvm:2.0.6")
implementation("com.nimbusds:nimbus-jose-jwt:8.20.2")
implementation("com.nimbusds:oauth2-oidc-sdk:8.36.1")
implementation("org.springframework.security:spring-security-oauth2-jose:5.4.5")
// https://mvnrepository.com/artifact/net.folivo/matrix-spring-boot-bot
// implementation ("net.folivo:matrix-spring-boot-bot:0.4.6")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("com.h2database:h2")
runtimeOnly("io.r2dbc:r2dbc-h2")
providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
// runtimeOnly("io.r2dbc:r2dbc-h2")
// providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("io.projectreactor:reactor-test")
}

View File

@@ -1,69 +1,22 @@
# Okupa mi coche - Docker containers for the backend
Guide for seting up a local development environment for the backend.
# Okupa mi coche - Backend
Gestión de viajes en el valle.
## Setup
1. Install Docker in local machine
2. Add following line to /etc/hosts
```
127.0.0.1 okupamicoche-keycloak okupamicoche-synapse
```
3. Create docker network okupamicoche
```
docker network create okupamicoche
```
3. Run dockerized Keycloak
```
cd docker/keycloak
docker run --name okupamicoche-keycloak -p 8443:8443 -v $(pwd)/https:/etc/x509/https \
-e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin --network=okupamicoche \
-e KEYCLOAK_IMPORT=/tmp/realm.json -v $(pwd)/okupamicoche-realm-export.json:/tmp/realm.json quay.io/keycloak/keycloak:16.1.0
```
4. Build Synapse container
```
cd docker/synape
docker build -t okupamicoche-synapse .
```
5. Generate data folder for Synapse
```
docker run -it --rm \
--mount type=volume,src=synapse-data,dst=/data \
-e SYNAPSE_SERVER_NAME=okupamicoche-synapse \
-e SYNAPSE_REPORT_STATS=no \
okupamicoche-synapse generate
```
6. Run dockerized Synapse
```
docker run --name okupamicoche-synapse -p 8008:8008 --mount type=volume,src=synapse-data,dst=/data \
-e SYNAPSE_CONFIG_PATH=/homeserver.yaml \
-v $(pwd)/homeserver.yaml:/homeserver.yaml -v $(pwd)/okupamicoche-appservice.yaml:/okupamicoche-appservice.yaml \
--network=okupamicoche okupamicoche-synapse
```
7. (Optional) Add keycloak certificate to local machine
Some clients (Quaternion, Nheko) fail with self-signed certificates. You can install the root certificate
(docker/synape/keycloak-root.crt) in you local machine. For example, in Linux:
```
sudo cp docker/synapse/keycloak-root.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
```
### Keycloak
`docker run --name keycloak -p 8080:8080 -p 8443:8443 --mount type=volume,src=https,dst=/etc/x509/https -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin --network=okupamicoche quay.io/keycloak/keycloak:12.0.4`
### Matrix
https://github.com/matrix-org/synapse/tree/master/docker
Add synapse in /etc/hosts as localhost alias.
`docker run -it --rm --mount type=volume,src=synapse-data,dst=/data -e SYNAPSE_SERVER_NAME=synapse -e SYNAPSE_REPORT_STATS=no matrixdotorg/synapse:latest generate`
`docker run --name synapse --mount type=volume,src=synapse-data,dst=/data -p 8008:8008 --network=okupamicoche matrixdotorg/synapse:latest`
## Run
`docker start okupamicoche-keycloak`
`docker start okupamicoche-synapse`
`docker restart keycloak`
`docker restart synapse`
## Inspect containter
`docker exec -t -i okupamicoche-synapse /bin/bash`
## Manage Keycloak
Go to https://localhost:8443/auth/admin and login with user=admin pass=admin
# Renew/create SSL certificates for development
1. Install mkcert from https://github.com/FiloSottile/mkcert
2. Create and install CA root certificate
`mkcert -install`
3. Create certificate for Keycloak
`mkcert okupamicoche-keycloak localhost 127.0.0.1 ::1`
4. Copy okupamicoche-keycloak+3.pem to docker/keycloak/https/tls.crt
5. Copy okupamicoche-keycloak+3-key.pem to docker/keycloak/https/tls.key
6. Copy /usr/local/share/ca-certificates/mkcert_development_CA_*.crt to docker/synapse/keycloak-root.crt
`docker exec -t -i synapse /bin/bash`

View File

@@ -0,0 +1,32 @@
version: '3'
services:
keycloak:
image: quay.io/keycloak/keycloak:12.0.4
container_name: keycloak
environment:
KEYCLOAK_USER: admin
KEYCLOAK_PASSWORD: admin
ports:
- "8080:8080"
networks:
- okupamicoche
synapse:
image: matrixdotorg/synapse:latest
container_name: synapse
ports:
- "8008:8008"
volumes:
- ./synapse-data:/data
networks:
- okupamicoche
depends_on:
- keycloak
volumes:
synapse-data:
driver: local
networks:
okupamicoche:
external: true

21
docker/full/https/tls.crt Normal file
View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIURQQZKTG7wENaPp3bnAVLUMhkBJEwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTAzMTQxNzE0MTZaFw0yMjAz
MTQxNzE0MTZaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQD4RoutMqZ9zzuowIZ02N5DneNdTUPp8KTP3ewCTp5B
pQB2ht06pEgb7AY0xjlxfwt+lEXc4aeN/B741frLDe6buts8IsedfL0Ub2KHfoqo
o3qAimn9+fgoHwZYsls3OJK+fKbPNefp+m65SkZHz4ufQhg2TSLsW0BWATnxnbd8
OQIXrCxtV/UKE2iaXfrlmaVSCqFeL4z7Rr+PJ8LiwOFMDLleLMsPiIo8CtR7u/lg
65zWI34rhdjwMq9tYXmZtq5sSpS83L/3InQDOvyhNt8vdNS8qL+v7tNhpHldBYqt
WJaC/QPeRGXQfa89qYZssZ+k32/i7del2raF8RxkcyVtAgMBAAGjUzBRMB0GA1Ud
DgQWBBREbPhToZrilLuC26iiFNj8t+K0hDAfBgNVHSMEGDAWgBREbPhToZrilLuC
26iiFNj8t+K0hDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAF
NtdTjLsVLhfS0q5tVKQFZ6Ek1CcuyUVeAvTxWDinZVfXzuNFdF1DeDlMwP3gKufz
RIAI//k3ISFMwXN0TzgETC86ck4edxpB08E5RKpBZhOrm7PZtoQ5h4hPpOgSG1pp
gPvzIzCEtC8Uaf0zpr+2AAm/2+DLgTDzdnO/cxN3UloydW9BslFM1PTeZ7TphT8X
3PgDzDBa/IACdTwIhh6RH03l7BhzvKbp5uXnwRSWrf/q1R3mErrsjq9Awx6GECWu
Y6YLsHjm05ELHs8r7STQC5Wq+vtfut/iDUNgnNFHmpiaedC1Md3qdnIIMeYrjBjo
ru7ot2RLcptsr9w7qd7C
-----END CERTIFICATE-----

27
docker/full/https/tls.key Normal file
View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA+EaLrTKmfc87qMCGdNjeQ53jXU1D6fCkz93sAk6eQaUAdobd
OqRIG+wGNMY5cX8LfpRF3OGnjfwe+NX6yw3um7rbPCLHnXy9FG9ih36KqKN6gIpp
/fn4KB8GWLJbNziSvnymzzXn6fpuuUpGR8+Ln0IYNk0i7FtAVgE58Z23fDkCF6ws
bVf1ChNoml365ZmlUgqhXi+M+0a/jyfC4sDhTAy5XizLD4iKPArUe7v5YOuc1iN+
K4XY8DKvbWF5mbaubEqUvNy/9yJ0Azr8oTbfL3TUvKi/r+7TYaR5XQWKrViWgv0D
3kRl0H2vPamGbLGfpN9v4u3Xpdq2hfEcZHMlbQIDAQABAoIBACTQoSmflxyUvC37
znRJLDwuj2ZobKel7Wp9Z9+3tLPbOcRZnzhw39h0GT9+HUp9IkE0z18/fs8JEbao
VDYD7Nvey1+RcLQjqQ38rkmVNA5pn2KsI6drh6a7Yv+IAwqfMvNYHIwhXDBP2FdV
cjJ3ziZhcKGssn8F0PZv3B2921Vp++brFuVtDxvFdhRSSLtcwKI7L5SaOKsA8j2f
8Yspq1eigkgCTYTFn1+wdjn3FxyndCV7IFs2BvgdHDBTcB/o6DipXVvK+Px1B3Cp
g+ioAHiqn4EkxFkz/ceiscjpUZuITPS+e6aF5Qar7xO5VmeThhNlmsoCFW1nwaea
wjwH6CkCgYEA/DxBU1N0o0ZObOnt1eb/LvrSRmxiNH8RKIbb8QL9gQ9Hx1GbP+5N
JN5rayBNEg8UAtr0tvbil+ofoxpwlpSGFsFBG5NRcH1LIIHD1Sf0gkGc78ojdCT8
O4PcWnCWjLtLIgZCVBxTupa1vsWmUMBzqTxdxn92ECLcPFvo21SRIYsCgYEA+/sp
J2Do1lpUjkRDwWAIauHJ01ZHcA5epu2vXXZOnGw+OvPX8a493kwwJDjjrfOgDLTC
1FDDBMzbCQUHUa1w3ZfsSOyheHr/8xlVUJ3gz98q+aizaJLJ8lZraL8lvsC9uogf
x7P9iTp+SpIHQ1jXp+9WdFgeEgXVkK2GY1bzw+cCgYBiVOcuodFNuaHnSccDZZtD
6FpDRAuA5ax9vR1PNtg3EQrthD3ezXrbja4YxC3nhWNKvas7DMJHcOlGf4821M31
Xv+PzX2pOd8o3A3JMlta0FNrE8WAiM6gMQadZ1j5oiZnLEN9YNGvYwOVTJ5KyswM
RNFWCeiv37c1/Kqpnq05gwKBgGys2QXzxNfV44vsIzC+Y0L9mFb+ahcJC4eBEVYE
1UifYoN4cVT5qhM61rR4mLGIVinEuBZrsoBafck5EvwGCpx3jl+xNr7IhaTp8yKu
xKvCez1rpdzfGhvba72kWvoXFHzjgplVpm5N/PPaYSmJopD6J1ZMPsPVIlOgk0o6
0S1XAoGBALm8/9Gyer2jtfL/WZDILEeOV/rG13ELspTIx0pcbHkvZKFXrddu27E0
e89SqTCIXhn3nFLvk4pdWjJbE2QA4uS99vV5HXIpvvEBgwzid5hyqxE3b7xuQwl6
bAJld+V2lh5e1tQuaX/bF7B87k4ODlZFatCzhrOXBKMdRm4SkzSk
-----END RSA PRIVATE KEY-----

27
docker/full/https/trb.pem Normal file
View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA+EaLrTKmfc87qMCGdNjeQ53jXU1D6fCkz93sAk6eQaUAdobd
OqRIG+wGNMY5cX8LfpRF3OGnjfwe+NX6yw3um7rbPCLHnXy9FG9ih36KqKN6gIpp
/fn4KB8GWLJbNziSvnymzzXn6fpuuUpGR8+Ln0IYNk0i7FtAVgE58Z23fDkCF6ws
bVf1ChNoml365ZmlUgqhXi+M+0a/jyfC4sDhTAy5XizLD4iKPArUe7v5YOuc1iN+
K4XY8DKvbWF5mbaubEqUvNy/9yJ0Azr8oTbfL3TUvKi/r+7TYaR5XQWKrViWgv0D
3kRl0H2vPamGbLGfpN9v4u3Xpdq2hfEcZHMlbQIDAQABAoIBACTQoSmflxyUvC37
znRJLDwuj2ZobKel7Wp9Z9+3tLPbOcRZnzhw39h0GT9+HUp9IkE0z18/fs8JEbao
VDYD7Nvey1+RcLQjqQ38rkmVNA5pn2KsI6drh6a7Yv+IAwqfMvNYHIwhXDBP2FdV
cjJ3ziZhcKGssn8F0PZv3B2921Vp++brFuVtDxvFdhRSSLtcwKI7L5SaOKsA8j2f
8Yspq1eigkgCTYTFn1+wdjn3FxyndCV7IFs2BvgdHDBTcB/o6DipXVvK+Px1B3Cp
g+ioAHiqn4EkxFkz/ceiscjpUZuITPS+e6aF5Qar7xO5VmeThhNlmsoCFW1nwaea
wjwH6CkCgYEA/DxBU1N0o0ZObOnt1eb/LvrSRmxiNH8RKIbb8QL9gQ9Hx1GbP+5N
JN5rayBNEg8UAtr0tvbil+ofoxpwlpSGFsFBG5NRcH1LIIHD1Sf0gkGc78ojdCT8
O4PcWnCWjLtLIgZCVBxTupa1vsWmUMBzqTxdxn92ECLcPFvo21SRIYsCgYEA+/sp
J2Do1lpUjkRDwWAIauHJ01ZHcA5epu2vXXZOnGw+OvPX8a493kwwJDjjrfOgDLTC
1FDDBMzbCQUHUa1w3ZfsSOyheHr/8xlVUJ3gz98q+aizaJLJ8lZraL8lvsC9uogf
x7P9iTp+SpIHQ1jXp+9WdFgeEgXVkK2GY1bzw+cCgYBiVOcuodFNuaHnSccDZZtD
6FpDRAuA5ax9vR1PNtg3EQrthD3ezXrbja4YxC3nhWNKvas7DMJHcOlGf4821M31
Xv+PzX2pOd8o3A3JMlta0FNrE8WAiM6gMQadZ1j5oiZnLEN9YNGvYwOVTJ5KyswM
RNFWCeiv37c1/Kqpnq05gwKBgGys2QXzxNfV44vsIzC+Y0L9mFb+ahcJC4eBEVYE
1UifYoN4cVT5qhM61rR4mLGIVinEuBZrsoBafck5EvwGCpx3jl+xNr7IhaTp8yKu
xKvCez1rpdzfGhvba72kWvoXFHzjgplVpm5N/PPaYSmJopD6J1ZMPsPVIlOgk0o6
0S1XAoGBALm8/9Gyer2jtfL/WZDILEeOV/rG13ELspTIx0pcbHkvZKFXrddu27E0
e89SqTCIXhn3nFLvk4pdWjJbE2QA4uS99vV5HXIpvvEBgwzid5hyqxE3b7xuQwl6
bAJld+V2lh5e1tQuaX/bF7B87k4ODlZFatCzhrOXBKMdRm4SkzSk
-----END RSA PRIVATE KEY-----

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
version: 1
formatters:
precise:
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
handlers:
console:
class: logging.StreamHandler
formatter: precise
loggers:
synapse.storage.SQL:
# beware: increasing this to DEBUG will make synapse log sensitive
# information such as access tokens.
level: INFO
root:
level: INFO
handlers: [console]
disable_existing_loggers: false

View File

@@ -0,0 +1 @@
ed25519 a_IyNs PQ0dHIEEsxnfN6pnfBoXEkwNciqCvWAJJf4B2y88tvc

View File

@@ -1,25 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEQzCCAqugAwIBAgIQP8YUKLJIkSt+rdx8XntwITANBgkqhkiG9w0BAQsFADBj
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExHDAaBgNVBAsME2VuZWtv
QGVuZWtvIChFbmVrbykxIzAhBgNVBAMMGm1rY2VydCBlbmVrb0BlbmVrbyAoRW5l
a28pMB4XDTIxMTIyNjE0MDE1OVoXDTI0MDMyNjE0MDE1OVowRzEnMCUGA1UEChMe
bWtjZXJ0IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMRwwGgYDVQQLDBNlbmVrb0Bl
bmVrbyAoRW5la28pMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApRvc
rk9QWGx2SyqNZhWN7r/+9lKZTG0mQDwnPkiTr02PDAcVaVaVoYTFuBRdDCFvjIIN
07SfogkzNTKAL+YaGTDIauqcEins0/3jUdBhy80ujoyouW6+YBUNIExT03VFmyRL
Etmt+08pP7rlSqq5m2WyLcMhrCcq5U7Q1w0rDnkxM9JGKfyQIPRqnyp+6C/m9gMM
mCe7KzqlFXOsAAmDUfW1yAk2evOWbauwMbd5nwAq13yUdCtJKNfUeWv45+nP/B1w
mBinQqYmFYI2srm1R5vtcFgeEh0DiIloZputQhmYVLEXqKOtZsOcQB8l7BSO9s8J
9KcfSmroxJZgT+cTQwIDAQABo4GOMIGLMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE
DDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBSsH2h4QtygW++i5NrI4ZLgj1PG/DBD
BgNVHREEPDA6ghVva3VwYW1pY29jaGUta2V5Y2xvYWuCCWxvY2FsaG9zdIcEfwAA
AYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAYEAIVtjxGfI7q5B
pp7OL5dm0WKjqEyewrj8xd2DBXLMYncO8CcrwazRumNR/JWa9TUog0+RzQBoDcVp
oTipybeMFOu5MuiVVdwqk/0q+JSHJ6SbZO5Yrh6NukXfzPM+k/5JLc/4a9yv1JG9
IgQ9zwAQuyewr+AsG3fy48U1D9EZABXks0L3rMX7u4lBqqQz4whAyDTv7+mzoInR
M4y8f1Ztvlp9Ss389Pz+1/wmnjzOhJ3Z350rXLv6Ax/pnf9pmOELfx6IuYmIQi6j
vpCwEVcoBBU/gQqSsJLTJj3TFnNDFCxSyIg0jY+6UOBEGVI5QZK1MdB4RvygDJcn
Udaep4E+JKBS+O5kAo21QDWvNds2/CRPhK8RB3M32BFU1JB8VPN0t5zSAPBpcQoL
07oGVWF2Xd/A51SCtFqOxtZOTRgBSmTmsPinzlOMS4shqgQB4cjDqZ1RNHI7MbJ8
Tbq3LN8/8hTM0hy78rdxuzedirkWq6sf4daTnCZ3sMTYMVgXdHwz
-----END CERTIFICATE-----

View File

@@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQClG9yuT1BYbHZL
Ko1mFY3uv/72UplMbSZAPCc+SJOvTY8MBxVpVpWhhMW4FF0MIW+Mgg3TtJ+iCTM1
MoAv5hoZMMhq6pwSKezT/eNR0GHLzS6OjKi5br5gFQ0gTFPTdUWbJEsS2a37Tyk/
uuVKqrmbZbItwyGsJyrlTtDXDSsOeTEz0kYp/JAg9GqfKn7oL+b2AwyYJ7srOqUV
c6wACYNR9bXICTZ685Ztq7Axt3mfACrXfJR0K0ko19R5a/jn6c/8HXCYGKdCpiYV
gjayubVHm+1wWB4SHQOIiWhmm61CGZhUsReoo61mw5xAHyXsFI72zwn0px9KaujE
lmBP5xNDAgMBAAECggEBAJUxFvi/HHe6Ei4blkdfRLAu9yD3RCRmIIE8ma1TAXgP
WinhILzdizm28r5CUE3MzLInNn1NIk3ViFsSdfhSMNUAWqkZs8tJ7QEl0km0RRpW
HjKa8T8VzZqeiwzbS4ew2SO16MxwvCBwyHIY3GXa3PqJKzbkssGPZthrnpQkBvr8
ng+oueLSKbPploUs/0YjMB0SMtiYgRWwdCeRonQg93AAId5hg9GDUclXEJK0LGER
vWHjXjRB0KmSnHTdCxSIqml0689cie4RKlUzn9GdN1oH1Ck7Z6hyPLpmQZQlTgJr
gJToT/AU+v7eyuSFSod6klAmjTxwKl/0uL1Tp+I12tECgYEAyQnVqV6M472QJxfk
MtV7VikqsmfWlR5aPJRjiGfcYsxC4JZhWes8O20ADR43rGzSQdmqBmZNoy77vpMZ
bdOOoxSsaBN5UPyv48zWDoO9eUG+snj1o4ycq6aj7JDAZP0CH/30/1/2rTHGN+Wd
b4f2/44X/4rvZFw/nVRnRE45/fsCgYEA0j9oaCkZ4MyZA8QgWBpskgJtNE/iyRJd
SRtHyKZGoCcdVDMbR19VVOMbRd2yccPNIFKG9ZTbqzWcM3iKDwWONlT1FtWbV2LQ
FNKBVFzoXudTgDB0Ikp+Y3lJJi3Q3QTeL9QTDkQUsvckeO3rBNyta3cLzraXOy2r
FzVSOmAxpVkCgYBtqXywL9BApnXMrV6gUA1AOaJ9wOCvZOgqJXOJw8XW6r8MTVt7
NjMb42Bkx3ftCUtD+lyhswpkmktecFUtsHodATjuaLkDcWLsqx6Uuk4Pp8pDBnJP
rXMEuxiuAWPBcrXHB7ut1gX0AbOzaAASB6O3USZMEdh18VAOHS1beCRwIwKBgAC/
Wvar1ry23YBm7RqDFYm7WnoKIqtGVnCRGUM8XBnrqa4H9HvfeeUwXYN1E1JWON95
i6NJ4TINob2JGyyMf4Hv8WMgZUn9PvIkAXO0VNsphWF6Sp9olmRyJPFuzkRK5SNU
ZLDzSwOL32RSrDg7NJ6iMLeObKE4O1h8xwsQFn0ZAoGAPchxDGNsWs9P0SLjNmJI
92hkB4g54EYgFmPlsUYucjgYbGSsDd4bjrUSLT5/xX3nn2MNvRsEVdJWrvH97Isn
qbE+LKH+i1T/4EDjgFYMsdx1sh5Vec1Yxm1oDsvBoUQzUqOMxZoQyQlEm/MIvISS
4NjyOn2zOmWANHbQ/G8Ru5c=
-----END PRIVATE KEY-----

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +0,0 @@
FROM matrixdotorg/synapse:latest
COPY keycloak-root.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates

View File

@@ -0,0 +1,13 @@
version: '3.3'
services:
matrix-synapse:
image: matrixdotorg/synapse:latest
container_name: synapse
volumes:
- ./synapse_data:/data
environment:
- SYNAPSE_REPORT_STATS=false
- UID=1000
- GID=1000
ports:
- 8008:8008

View File

@@ -1,40 +0,0 @@
server_name: "okupamicoche-synapse"
pid_file: /data/homeserver.pid
public_baseurl: http://okupamicoche-synapse:8008/
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
resources:
- names: [ client, federation ]
compress: false
database:
name: sqlite3
args:
database: /data/homeserver.db
log_config: "/data/okupamicoche-synapse.log.config"
media_store_path: "/data/media_store"
registration_shared_secret: "Y_XNuno*Dh,T2IpHA;i,bWF^fg&x.*t=iEz*@:y5REBMhgCA63"
report_stats: false
macaroon_secret_key: "6VvBQj_TedGcDDB_z,-qXV1W3:.CXrRG6AWF&4p:~iGNguy&_h"
form_secret: "FM,2TSq++sZ@Tl0atcQP"
signing_key_path: "/data/okupamicoche-synapse.signing.key"
trusted_key_servers:
- server_name: "matrix.org"
oidc_providers:
- idp_id: keycloak
idp_name: Keycloak
issuer: "https://okupamicoche-keycloak:8443/auth/realms/okupamicoche"
client_id: "synapse"
# client_secret: "PUBLIC-CLIENT-WITH-NO-PASSWORD"
scopes: [ "openid", "profile" ]
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ user.name }}"
sso:
client_whitelist:
- http://localhost:4200/
app_service_config_files:
- /okupamicoche-appservice.yaml

View File

@@ -1,27 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIElTCCAv2gAwIBAgIQBi3ZVqJiORvrJIo554aoHzANBgkqhkiG9w0BAQsFADBj
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExHDAaBgNVBAsME2VuZWtv
QGVuZWtvIChFbmVrbykxIzAhBgNVBAMMGm1rY2VydCBlbmVrb0BlbmVrbyAoRW5l
a28pMB4XDTIxMTIyNjEzNTgzM1oXDTMxMTIyNjEzNTgzM1owYzEeMBwGA1UEChMV
bWtjZXJ0IGRldmVsb3BtZW50IENBMRwwGgYDVQQLDBNlbmVrb0BlbmVrbyAoRW5l
a28pMSMwIQYDVQQDDBpta2NlcnQgZW5la29AZW5la28gKEVuZWtvKTCCAaIwDQYJ
KoZIhvcNAQEBBQADggGPADCCAYoCggGBAKjoNBECRJZyqJe39ijNkHoVeqENGgwK
jmmzqgkVq3BR4p+hOtNXDWKQrakkIpyw4NLLoAbctFaHSctWb9G/UWJLOLTzkp8E
0rYkxayLVfqRhO5Hl5ZA2CAFafoMYgNmPQFUzobg3k8jTLdCMJ2sYSHdu4TYqWwY
5+e+vWgB0wU0z8mdiLlE1ampikjvGtVgWIOM5vTKfsUhYxKMJwLMmyAt/GKeq6Hc
8tYdh/ZeNfeITgIQY9zHVB7xsyxi/JiubaKKoTnglznWwmbogvHZ6sjyzfgWSXEu
N7GHGcxVN1yH9RadmrY2KesQ6yv6PI5tKZf0kt8F3XgLsRG9UFpBw7VNrlGwgT05
O8JogpnZ6tnOxL+8vuLgzJ9lD5dk/ZKxkUx6IYhblbuE4p3ZlrId0WlMgUwN1Ppx
U0K2mExQat5Zy+LLUZ/vjReObvdNyWYYDRWzM6iFmLDpnqMDNbx0kH8Ok/Pi1zvk
Ho5VcAhpiU11VSKs0RJDVCTNA02/AiDIMwIDAQABo0UwQzAOBgNVHQ8BAf8EBAMC
AgQwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUrB9oeELcoFvvouTayOGS
4I9TxvwwDQYJKoZIhvcNAQELBQADggGBADXTUSQ3u5vxFXUdgpRhvPf4H6z4qWuK
wH0rIzIf5QGMe/UUgElRnUcMnRcaGafjshDiQnPAlFT7FwkxqxdvEnGMhziF6xn1
yvKZAbKfMJEPlgZ39vdeMiWL2NVqzdcbD/I6hJZML9/OUqZGEq5oed46f6rY/Rnp
9/V66bXl0Z1iermnObcyUmgGwNzEI6GhVAALylaHdOBEI5PRsGE3lQ5dCrvXq7LV
NVzuj3bpRhINPO6Ry9r1ZJnRqwVdw319MUqTwIxokx1Q07B/1dXf+g6KkmL1nILT
zowiTF+Mr0Q13DXKn6u67WpAIXsmzTu0shNPxpWzXykqY7RhUZ9bFMeeTb8ib6g4
xUVhUZuq6dqrltMhNKlNZCSdOzAlxZrjJrdTb/+EUXIls6N58moMlHnKlI9eWhIu
nMifCG7fX2em7fI6lnuZQXN/X9T3LLHbjp00Garxm28y2RWOIezuam0ALFS7IV8X
lJlVtLS5yuOrbHb2QdmwqKM3+kVLtCGo/Q==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,5 @@
homeserver.db
media_store
!.gitignore
!homeserver.yaml

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIURQQZKTG7wENaPp3bnAVLUMhkBJEwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTAzMTQxNzE0MTZaFw0yMjAz
MTQxNzE0MTZaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQD4RoutMqZ9zzuowIZ02N5DneNdTUPp8KTP3ewCTp5B
pQB2ht06pEgb7AY0xjlxfwt+lEXc4aeN/B741frLDe6buts8IsedfL0Ub2KHfoqo
o3qAimn9+fgoHwZYsls3OJK+fKbPNefp+m65SkZHz4ufQhg2TSLsW0BWATnxnbd8
OQIXrCxtV/UKE2iaXfrlmaVSCqFeL4z7Rr+PJ8LiwOFMDLleLMsPiIo8CtR7u/lg
65zWI34rhdjwMq9tYXmZtq5sSpS83L/3InQDOvyhNt8vdNS8qL+v7tNhpHldBYqt
WJaC/QPeRGXQfa89qYZssZ+k32/i7del2raF8RxkcyVtAgMBAAGjUzBRMB0GA1Ud
DgQWBBREbPhToZrilLuC26iiFNj8t+K0hDAfBgNVHSMEGDAWgBREbPhToZrilLuC
26iiFNj8t+K0hDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAF
NtdTjLsVLhfS0q5tVKQFZ6Ek1CcuyUVeAvTxWDinZVfXzuNFdF1DeDlMwP3gKufz
RIAI//k3ISFMwXN0TzgETC86ck4edxpB08E5RKpBZhOrm7PZtoQ5h4hPpOgSG1pp
gPvzIzCEtC8Uaf0zpr+2AAm/2+DLgTDzdnO/cxN3UloydW9BslFM1PTeZ7TphT8X
3PgDzDBa/IACdTwIhh6RH03l7BhzvKbp5uXnwRSWrf/q1R3mErrsjq9Awx6GECWu
Y6YLsHjm05ELHs8r7STQC5Wq+vtfut/iDUNgnNFHmpiaedC1Md3qdnIIMeYrjBjo
ru7ot2RLcptsr9w7qd7C
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA+EaLrTKmfc87qMCGdNjeQ53jXU1D6fCkz93sAk6eQaUAdobd
OqRIG+wGNMY5cX8LfpRF3OGnjfwe+NX6yw3um7rbPCLHnXy9FG9ih36KqKN6gIpp
/fn4KB8GWLJbNziSvnymzzXn6fpuuUpGR8+Ln0IYNk0i7FtAVgE58Z23fDkCF6ws
bVf1ChNoml365ZmlUgqhXi+M+0a/jyfC4sDhTAy5XizLD4iKPArUe7v5YOuc1iN+
K4XY8DKvbWF5mbaubEqUvNy/9yJ0Azr8oTbfL3TUvKi/r+7TYaR5XQWKrViWgv0D
3kRl0H2vPamGbLGfpN9v4u3Xpdq2hfEcZHMlbQIDAQABAoIBACTQoSmflxyUvC37
znRJLDwuj2ZobKel7Wp9Z9+3tLPbOcRZnzhw39h0GT9+HUp9IkE0z18/fs8JEbao
VDYD7Nvey1+RcLQjqQ38rkmVNA5pn2KsI6drh6a7Yv+IAwqfMvNYHIwhXDBP2FdV
cjJ3ziZhcKGssn8F0PZv3B2921Vp++brFuVtDxvFdhRSSLtcwKI7L5SaOKsA8j2f
8Yspq1eigkgCTYTFn1+wdjn3FxyndCV7IFs2BvgdHDBTcB/o6DipXVvK+Px1B3Cp
g+ioAHiqn4EkxFkz/ceiscjpUZuITPS+e6aF5Qar7xO5VmeThhNlmsoCFW1nwaea
wjwH6CkCgYEA/DxBU1N0o0ZObOnt1eb/LvrSRmxiNH8RKIbb8QL9gQ9Hx1GbP+5N
JN5rayBNEg8UAtr0tvbil+ofoxpwlpSGFsFBG5NRcH1LIIHD1Sf0gkGc78ojdCT8
O4PcWnCWjLtLIgZCVBxTupa1vsWmUMBzqTxdxn92ECLcPFvo21SRIYsCgYEA+/sp
J2Do1lpUjkRDwWAIauHJ01ZHcA5epu2vXXZOnGw+OvPX8a493kwwJDjjrfOgDLTC
1FDDBMzbCQUHUa1w3ZfsSOyheHr/8xlVUJ3gz98q+aizaJLJ8lZraL8lvsC9uogf
x7P9iTp+SpIHQ1jXp+9WdFgeEgXVkK2GY1bzw+cCgYBiVOcuodFNuaHnSccDZZtD
6FpDRAuA5ax9vR1PNtg3EQrthD3ezXrbja4YxC3nhWNKvas7DMJHcOlGf4821M31
Xv+PzX2pOd8o3A3JMlta0FNrE8WAiM6gMQadZ1j5oiZnLEN9YNGvYwOVTJ5KyswM
RNFWCeiv37c1/Kqpnq05gwKBgGys2QXzxNfV44vsIzC+Y0L9mFb+ahcJC4eBEVYE
1UifYoN4cVT5qhM61rR4mLGIVinEuBZrsoBafck5EvwGCpx3jl+xNr7IhaTp8yKu
xKvCez1rpdzfGhvba72kWvoXFHzjgplVpm5N/PPaYSmJopD6J1ZMPsPVIlOgk0o6
0S1XAoGBALm8/9Gyer2jtfL/WZDILEeOV/rG13ELspTIx0pcbHkvZKFXrddu27E0
e89SqTCIXhn3nFLvk4pdWjJbE2QA4uS99vV5HXIpvvEBgwzid5hyqxE3b7xuQwl6
bAJld+V2lh5e1tQuaX/bF7B87k4ODlZFatCzhrOXBKMdRm4SkzSk
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,22 @@
version: 1
formatters:
precise:
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
handlers:
console:
class: logging.StreamHandler
formatter: precise
loggers:
synapse.storage.SQL:
# beware: increasing this to DEBUG will make synapse log sensitive
# information such as access tokens.
level: INFO
root:
level: INFO
handlers: [console]
disable_existing_loggers: false

View File

@@ -0,0 +1 @@
ed25519 a_cGhG bkRaBjufoVnCJ8Vk3S0h7cF4/7zDmQwM6Q+vnDj3baw

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -1 +1,7 @@
rootProject.name = "okupamicoche"
pluginManagement {
repositories {
maven { url = uri("https://repo.spring.io/milestone") }
gradlePluginPortal()
}
}
rootProject . name = "okupamicoche"

View File

@@ -1,15 +1,16 @@
package eu.fosil.okupamicoche.spring
package eu.fosil.okupamicoche
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.boot.runApplication
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.web.reactive.config.EnableWebFlux
@SpringBootApplication
@EnableJpaRepositories("eu.fosil.okupamicoche.*")
@EntityScan("eu.fosil.okupamicoche.*")
//@EnableJpaRepositories("eu.fosil.okupamicoche.*")
//@EntityScan("eu.fosil.okupamicoche.*")
class OkupaMiCocheApplication
fun main(args: Array<String>) {
runApplication<OkupaMiCocheApplication>(*args)
runApplication<OkupaMiCocheApplication>(*args)
}

View File

@@ -3,27 +3,27 @@ package eu.fosil.okupamicoche.dto
import eu.fosil.okupamicoche.entities.User
class CreateUserDto(
private val id: String,
private val username: String,
private val matrixId: String,
private val name: String,
private val email: String?
private val id: String,
private val username: String,
private val matrixId: String,
private val name: String,
private val email: String?
) {
constructor(user: User) : this(
user.id,
user.username,
user.matrixId,
user.name,
user.email
user.id,
user.username,
user.matrixId,
user.name,
user.email
)
fun toUser(): User {
return User(
id = id,
username = username,
matrixId = matrixId,
name = name,
email = email
id = id,
username = username,
matrixId = matrixId,
name = name,
email = email
)
}
}

View File

@@ -1,54 +1,51 @@
package eu.fosil.okupamicoche.dto
import com.fasterxml.jackson.annotation.JsonFormat
import eu.fosil.okupamicoche.entities.Travel
import eu.fosil.okupamicoche.entities.TravelId
import eu.fosil.okupamicoche.entities.UserIdNotFoundException
import eu.fosil.okupamicoche.entities.UserNotSpecifiedException
import eu.fosil.okupamicoche.repositories.UserRepository
import org.springframework.data.repository.findByIdOrNull
import java.time.LocalDateTime
class TravelDto(
// Los campos deben ser públicos para que aparezcan en el JSON
val id: TravelId? = null,
var driverInfo: UserInfoDto? = null,
val travelersInfo: List<UserInfoDto> = emptyList(),
@JsonFormat(shape = JsonFormat.Shape.STRING)
val departureDate: LocalDateTime = LocalDateTime.MIN,
val origin: String = "",
val destination: String = "",
val places: Int = 0,
var description: String? = null,
val matrixRoomId: String = ""
// Los campos deben ser públicos para que aparezcan en el JSON
val id: TravelId? = null,
var driverInfo: UserInfoDto? = null,
val travelersInfo: List<UserInfoDto> = emptyList(),
val departureDate: String = "",
val origin: String = "",
val destination: String = "",
val places: Int = 0,
var description: String? = null,
val matrixRoomId: String = ""
) {
constructor(travel: Travel) : this(
travel.id,
UserInfoDto(travel.driver),
travel.travelers.map { traveler -> UserInfoDto(traveler) },
travel.departureDate,
travel.origin,
travel.destination,
travel.places,
travel.description,
travel.matrixRoomId
travel.id,
UserInfoDto(travel.driver),
travel.travelers.map { traveler -> UserInfoDto(traveler) },
travel.departureDate,
travel.origin,
travel.destination,
travel.places,
travel.description,
travel.matrixRoomId
)
fun toTravel(userRepository: UserRepository): Travel {
driverInfo?.let { driverInfo ->
val driver = userRepository.findByIdOrNull(driverInfo.id) ?: throw UserIdNotFoundException()
val travelers = travelersInfo.mapNotNull { t -> userRepository.findByIdOrNull(t.id) }
.toMutableList()
.toMutableList()
return Travel(
id,
driver,
travelers,
departureDate,
origin,
destination,
places,
description,
matrixRoomId
id,
driver,
travelers,
departureDate,
origin,
destination,
places,
description,
matrixRoomId
)
}
throw UserNotSpecifiedException()

View File

@@ -5,34 +5,34 @@ import eu.fosil.okupamicoche.entities.UserId
import eu.fosil.okupamicoche.repositories.UserRepository
class UserDto(
// Los campos deben ser públicos para que aparezcan en el JSON
val id: UserId,
val username: String,
val matrixId: String,
val name: String,
val email: String?,
val travelsAsDriver: List<TravelDto> = emptyList(),
val travelsAsTraveler: List<TravelDto> = emptyList()
// Los campos deben ser públicos para que aparezcan en el JSON
val id: UserId,
val username: String,
val matrixId: String,
val name: String,
val email: String?,
val travelsAsDriver: List<TravelDto> = emptyList(),
val travelsAsTraveler: List<TravelDto> = emptyList()
) {
constructor(user: User) : this(
user.id,
user.username,
user.matrixId,
user.name,
user.email,
user.travelsAsDriver.map { t -> TravelDto(t) },
user.travelsAsTraveler.map { t -> TravelDto(t) }
user.id,
user.username,
user.matrixId,
user.name,
user.email,
user.travelsAsDriver.map { t -> TravelDto(t) },
user.travelsAsTraveler.map { t -> TravelDto(t) }
)
fun toUser(userRepository: UserRepository): User {
return User(
id,
username,
matrixId,
name,
email,
travelsAsDriver.map { t -> t.toTravel(userRepository) },
travelsAsTraveler.map { t -> t.toTravel(userRepository) }
id,
username,
matrixId,
name,
email,
travelsAsDriver.map { t -> t.toTravel(userRepository) },
travelsAsTraveler.map { t -> t.toTravel(userRepository) }
)
}
}

View File

@@ -4,25 +4,25 @@ import eu.fosil.okupamicoche.entities.User
import eu.fosil.okupamicoche.entities.UserId
class UserInfoDto(
// Los campos deben ser públicos para que aparezcan en el JSON
val id: UserId,
val username: String,
val matrixId: String,
val name: String
// Los campos deben ser públicos para que aparezcan en el JSON
val id: UserId,
val username: String,
val matrixId: String,
val name: String
) {
constructor(user: User) : this(
user.id,
user.username,
user.matrixId,
user.name
user.id,
user.username,
user.matrixId,
user.name
)
fun toUser(): User {
return User(
id,
username,
matrixId,
name
id,
username,
matrixId,
name
)
}
}

View File

@@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonInclude
@JsonInclude(JsonInclude.Include.NON_NULL)
open class ApiResponse<T>(val success: Boolean, val data: T?, val error: ApiError?) {
constructor(success: Boolean, data: T?) : this(success, data, null)
constructor(success: Boolean, data: T) : this(success, data, null)
constructor(success: Boolean, error: ApiError) : this(success, null, error)
}

View File

@@ -1,28 +1,22 @@
package eu.fosil.okupamicoche.entities
import org.hibernate.annotations.LazyCollection
import org.hibernate.annotations.LazyCollectionOption
import java.time.LocalDateTime
import javax.persistence.*
typealias TravelId = Long
@Entity
@Table
class Travel(
@Id @GeneratedValue var id: TravelId? = null,
@ManyToOne
@LazyCollection(LazyCollectionOption.FALSE)
var driver: User,
@ManyToMany
@LazyCollection(LazyCollectionOption.FALSE)
var travelers: MutableList<User>,
var departureDate: LocalDateTime,
var origin: String,
var destination: String,
var places: Int,
var description: String? = null,
var matrixRoomId: String
@Id @GeneratedValue var id: TravelId? = null,
@ManyToOne
var driver: User,
@ManyToMany(fetch = FetchType.EAGER) // https://stackoverflow.com/questions/22821695/how-to-fix-hibernate-lazyinitializationexception-failed-to-lazily-initialize-a
var travelers: MutableList<User>,
var departureDate: String,
var origin: String,
var destination: String,
var places: Int,
var description: String? = null,
var matrixRoomId: String
) {
fun users(): Set<User> {
val allUsers = mutableSetOf(driver)

View File

@@ -1,22 +1,18 @@
package eu.fosil.okupamicoche.entities
import org.hibernate.annotations.LazyCollection
import org.hibernate.annotations.LazyCollectionOption
import javax.persistence.*
typealias UserId = String
@Entity
@Table
class User(
@Id var id: UserId,
var username: String,
var matrixId: String,
var name: String,
var email: String? = null,
@OneToMany
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(mappedBy = "driver")
var travelsAsDriver: List<Travel> = emptyList(),
@ManyToMany @LazyCollection(LazyCollectionOption.FALSE)
@ManyToMany(mappedBy = "travelers")
var travelsAsTraveler: List<Travel> = emptyList()
)

View File

@@ -1,31 +1,31 @@
package eu.fosil.okupamicoche.entities
class UserKeycloak(
val id: UserId,
val username: String,
val admin: Boolean,
val name: String,
val email: String
val id: UserId,
val username: String,
val admin: Boolean,
val name: String,
val email: String
) {
constructor(claims: Map<String, Any>) : this(
claims["sub"].toString(),
claims["preferred_username"].toString(),
try {
claims["admin"] as Boolean
} catch (e: Exception) {
false
},
claims["name"].toString(),
claims["email"].toString()
claims["sub"].toString(),
claims["preferred_username"].toString(),
try {
claims["admin"] as Boolean
} catch (e: Exception) {
false
},
claims["name"].toString(),
claims["email"].toString()
)
fun toUser(): User {
return User(
id,
username,
"@$username:okupamicoche-synapse",
name,
email
id,
username,
"@$username:synapse",
name,
email
)
}
}

View File

@@ -10,60 +10,17 @@ import org.springframework.data.repository.PagingAndSortingRepository
import org.springframework.data.repository.query.Param
interface TravelRepository : PagingAndSortingRepository<Travel, TravelId> {
@Query("SELECT count(t) FROM Travel t " +
"LEFT JOIN t.travelers u " +
"WHERE (lower(t.origin) LIKE lower(concat('%', :filter,'%')) OR " +
"lower(t.destination) LIKE lower(concat('%', :filter,'%')))")
fun countTravels(
@Param("filter") filter: String
): Long
@Query("SELECT t FROM Travel t " +
"LEFT JOIN t.travelers u " +
"WHERE t.driver.id = :userId OR u.id = :userId")
fun findUserTravels(@Param("userId") userId: UserId): List<Travel>
@Query("SELECT count(t) FROM Travel t " +
"LEFT JOIN t.travelers u " +
"WHERE t.driver.id = :userId OR u.id = :userId")
fun countUserTravels(@Param("userId") userId: UserId): Long
@Query("SELECT t FROM Travel t " +
"LEFT JOIN t.travelers u " +
"WHERE (t.driver.id = :userId OR u.id = :userId) " +
"AND " +
"(lower(t.origin) LIKE lower(concat('%', :filter,'%')) OR " +
"lower(t.destination) LIKE lower(concat('%', :filter,'%')))")
fun findUserTravels(
@Param("userId") userId: UserId,
@Param("filter") filter: String,
pageable: Pageable
): Page<Travel>
@Query("SELECT count(t) FROM Travel t " +
"LEFT JOIN t.travelers u " +
"WHERE (t.driver.id = :userId OR u.id = :userId) " +
"AND " +
"(lower(t.origin) LIKE lower(concat('%', :filter,'%')) OR " +
"lower(t.destination) LIKE lower(concat('%', :filter,'%')))")
fun countUserTravels(
@Param("userId") userId: UserId,
@Param("filter") filter: String
): Long
@Query("SELECT t FROM Travel t WHERE t.driver.id = :userId")
fun findUserTravelsAsDriver(@Param("userId") userId: UserId): List<Travel>
@Query("SELECT count(t) FROM Travel t WHERE t.driver.id = :userId")
fun countUserTravelsAsDriver(@Param("userId") userId: UserId): Long
@Query("SELECT t FROM Travel t JOIN t.travelers u WHERE u.id = :userId")
fun findUserTravelsAsTraveler(@Param("userId") userId: UserId): List<Travel>
@Query("SELECT count(t) FROM Travel t JOIN t.travelers u WHERE u.id = :userId")
fun countUserTravelsAsTraveler(@Param("userId") userId: UserId): Long
fun findByOriginContainingOrDestinationContainingAllIgnoreCase(
filterOrigin: String, filterDestination: String, pageable: Pageable
filter: String, filter2: String, pageable: Pageable
): Page<Travel>
}

View File

@@ -0,0 +1,13 @@
package eu.fosil.okupamicoche.spring
import eu.fosil.okupamicoche.OkupaMiCocheApplication
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer
class ServletInitializer : SpringBootServletInitializer() {
override fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder {
return application.sources(OkupaMiCocheApplication::class.java)
}
}

View File

@@ -2,32 +2,36 @@ package eu.fosil.okupamicoche.spring.conf
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpMethod
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.reactive.CorsWebFilter
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource
import org.springframework.web.reactive.config.CorsRegistry
import org.springframework.web.reactive.config.EnableWebFlux
import org.springframework.web.reactive.config.WebFluxConfigurer
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
class SecurityConfig {
@Bean
fun configure(http: ServerHttpSecurity): SecurityWebFilterChain {//@formatter:off
return http.cors()
.and()
.csrf().disable()
.authorizeExchange()
.pathMatchers(HttpMethod.OPTIONS).permitAll()
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {//@formatter:off
http
.authorizeExchange { exchanges ->
exchanges
.pathMatchers("/_matrix/**").permitAll()
.pathMatchers("/api/public/**").permitAll()
.pathMatchers("/api/user/**").authenticated()
.pathMatchers("/api/travel/**").authenticated()
.anyExchange().denyAll()
.and()
.oauth2ResourceServer().jwt()
.and().and().build()
}
.oauth2ResourceServer { oauth2ResourceServer ->
oauth2ResourceServer.jwt { jwt -> jwt }
}
return http.build()
}//@formatter:on
}
@@ -36,7 +40,8 @@ class SecurityConfig {
class CorsGlobalConfiguration : WebFluxConfigurer {
override fun addCorsMappings(corsRegistry: CorsRegistry) {
corsRegistry.addMapping("/**")
.allowedOrigins("http://localhost:4200")
.allowedMethods("*")
.allowedOrigins("*")
.allowedMethods("PUT", "GET", "POST")
.maxAge(3600)
}
}
}

View File

@@ -8,146 +8,117 @@ import eu.fosil.okupamicoche.repositories.TravelRepository
import eu.fosil.okupamicoche.repositories.UserRepository
import eu.fosil.okupamicoche.spring.services.AuthService
import eu.fosil.okupamicoche.spring.services.UseCaseService
import eu.fosil.okupamicoche.usecases.travel.CancelTravel
import eu.fosil.okupamicoche.usecases.travel.DeleteTravel
import eu.fosil.okupamicoche.usecases.travel.EditTravel
import eu.fosil.okupamicoche.usecases.travel.ListUserTravels
import eu.fosil.okupamicoche.usecases.travel.*
import mu.KotlinLogging
import org.springframework.data.repository.findByIdOrNull
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import java.security.Principal
@RestController
@RequestMapping("/api/travel")
class PrivateTravelRestController(
private val authService: AuthService,
private val userRepository: UserRepository,
private val travelRepository: TravelRepository,
private val useCaseService: UseCaseService
private val authService: AuthService,
private val userRepository: UserRepository,
private val travelRepository: TravelRepository,
private val useCaseService: UseCaseService
) : ApiRestController {
// private val logger = KotlinLogging.logger {}
private val logger = KotlinLogging.logger {}
@RequestMapping("/create")
suspend fun createTravel(@RequestBody @Validated travel: TravelDto, principal: Principal): ApiResponse<Unit> {
suspend fun createTravel(@RequestBody @Validated travel: TravelDto): ApiResponse<Unit> {
return response {
val driver = userRepository.findByIdOrNull(authService.currentUser(principal).id)
?: throw UserIdNotFoundException("Current user not found.")
val driver = userRepository.findByIdOrNull(authService.currentUser().id)
?: throw UserIdNotFoundException("Current user not found.")
if (travel.id != null && (travelRepository.findByIdOrNull(travel.id) != null))
throw CannotDuplicateIdException("Travel id already exists.")
travel.driverInfo = UserInfoDto(driver)
val useCase = useCaseService.getCreateTravel()
useCase.createTravel(travel.toTravel(userRepository))
val createTravel = useCaseService.getCreateTravel()
createTravel.createTravel(travel.toTravel(userRepository))
}
}
@RequestMapping("/cancel")
suspend fun cancelTravel(@RequestParam @Validated travelId: TravelId, principal: Principal): ApiResponse<Unit> {
suspend fun cancelTravel(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
return response {
throwErrorIfCannotEditTravel(travelId, principal)
throwErrorIfCannotEditTravel(travelId)
CancelTravel(travelRepository).cancelTravel(travelId)
}
}
@RequestMapping("/delete")
suspend fun deleteTravel(@RequestParam @Validated travelId: TravelId, principal: Principal): ApiResponse<Unit> {
suspend fun deleteTravel(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
return response {
throwErrorIfCannotEditTravel(travelId, principal)
throwErrorIfCannotEditTravel(travelId)
DeleteTravel(travelRepository).deleteTravel(travelId)
}
}
@RequestMapping("/edit")
suspend fun editTravel(@RequestBody @Validated travel: TravelDto, principal: Principal): ApiResponse<Unit> {
suspend fun editTravel(@RequestBody @Validated travel: TravelDto): ApiResponse<Unit> {
return response {
throwErrorIfCannotEditTravel(travel.id, principal)
throwErrorIfCannotEditTravel(travel.id)
EditTravel(travelRepository).editTravel(travel.toTravel(userRepository))
}
}
@RequestMapping("/listusertravels")
suspend fun listUserTravels(
@RequestParam @Validated filter: String?,
@RequestParam @Validated sortColumn: String?,
@RequestParam @Validated sortAscending: Boolean?,
@RequestParam @Validated pageIndex: Int?,
@RequestParam @Validated pageSize: Int?,
principal: Principal
): ApiResponse<ListDto<TravelDto>> {
suspend fun listUserTravels(): ApiResponse<ListDto<TravelDto>> {
return response {
val userId = authService.currentUser(principal).id
val useCase = ListUserTravels(travelRepository)
val travels = useCase.listUserTravels(
userId, filter, sortColumn, sortAscending, pageIndex, pageSize
).map { t -> TravelDto(t) }
ListDto(useCase.countUserTravels(userId, filter), travels)
}
}
@RequestMapping("/listallusertravels")
suspend fun listAllUserTravels(principal: Principal): ApiResponse<ListDto<TravelDto>> {
return response {
val userId = authService.currentUser(principal).id
val userId = authService.currentUser().id
val useCase = ListUserTravels(travelRepository)
val travels = useCase.listUserTravels(userId).map { t -> TravelDto(t) }
logger.debug { "travels=$travels" }
ListDto(useCase.countUserTravels(userId), travels)
}
}
@RequestMapping("/join")
suspend fun join(@RequestParam @Validated travelId: TravelId, principal: Principal): ApiResponse<Unit> {
suspend fun join(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
return response {
val userId = authService.currentUser(principal).id
val useCase = useCaseService.getAddTraveler()
useCase.addTraveler(travelId, userId)
val userId = authService.currentUser().id
AddTraveler(userRepository, travelRepository).addTraveler(travelId, userId)
}
}
@RequestMapping("/leave")
suspend fun leave(@RequestParam @Validated travelId: TravelId, principal: Principal): ApiResponse<Unit> {
suspend fun leave(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
return response {
val userId = authService.currentUser(principal).id
val useCase = useCaseService.getRemoveTraveler()
useCase.removeTraveler(travelId, userId)
val userId = authService.currentUser().id
RemoveTraveler(userRepository, travelRepository).removeTraveler(travelId, userId)
}
}
@RequestMapping("/addtraveler")
suspend fun addTraveler(
@RequestParam @Validated travelId: TravelId,
@RequestParam @Validated userId: UserId,
principal: Principal
@RequestParam @Validated travelId: TravelId,
@RequestParam @Validated userId: UserId
): ApiResponse<Unit> {
throwErrorIfCannotEditTravel(travelId, principal)
return response {
throwErrorIfCannotEditTravel(travelId, principal)
val useCase = useCaseService.getAddTraveler()
useCase.addTraveler(travelId, userId)
throwErrorIfCannotEditTravel(travelId)
AddTraveler(userRepository, travelRepository).addTraveler(travelId, userId)
}
}
@RequestMapping("/removetraveler")
suspend fun removeTraveler(
@RequestParam @Validated travelId: TravelId,
@RequestParam @Validated userId: UserId,
principal: Principal
@RequestParam @Validated travelId: TravelId,
@RequestParam @Validated userId: UserId
): ApiResponse<Unit> {
throwErrorIfCannotEditTravel(travelId, principal)
return response {
val useCase = useCaseService.getRemoveTraveler()
useCase.removeTraveler(travelId, userId)
throwErrorIfCannotEditTravel(travelId)
RemoveTraveler(userRepository, travelRepository).removeTraveler(travelId, userId)
}
}
private fun throwErrorIfCannotEditTravel(
travelId: TravelId?,
principal: Principal,
message: String = "Only admins and travel driver can modify this travel."
travelId: TravelId?,
message: String = "Only admins and travel driver can modify this travel."
) {
if (!authService.canEditTravel(travelId, principal))
if (!authService.canEditTravel(travelId))
throw InsufficientPermissions(message)
}
}

View File

@@ -13,19 +13,18 @@ import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.security.Principal
@RestController
@RequestMapping("/api/user")
class PrivateUserRestController(
private val authService: AuthService,
private val userRepository: UserRepository
private val authService: AuthService,
private val userRepository: UserRepository
) : ApiRestController {
@RequestMapping("/user")
suspend fun getCurrentUserCreateIfNeeded(principal: Principal): ApiResponse<UserDto> {
suspend fun getCurrentUserCreateIfNeeded(): ApiResponse<UserDto> {
return response {
val userKeycloak = authService.currentUser(principal)
val userKeycloak = authService.currentUser()
var user = userRepository.findByIdOrNull(userKeycloak.id)
if (user == null) {
@@ -38,12 +37,9 @@ class PrivateUserRestController(
}
@RequestMapping("/create")
suspend fun createUser(
@RequestBody @Validated createUserDto: CreateUserDto,
principal: Principal
): ApiResponse<Unit> {
suspend fun createUser(@RequestBody @Validated createUserDto: CreateUserDto): ApiResponse<Unit> {
return response {
if (!authService.isAdmin(principal))
if (!authService.isAdmin())
throw InsufficientPermissions("Only admins can create users.")
CreateUser(userRepository).createUser(createUserDto.toUser())
}
@@ -57,17 +53,17 @@ class PrivateUserRestController(
}
@RequestMapping("/delete")
suspend fun deleteUser(@RequestBody @Validated userId: UserId, principal: Principal): ApiResponse<Unit> {
suspend fun deleteUser(@RequestBody @Validated userId: UserId): ApiResponse<Unit> {
return response {
throwErrorIfCannotEditUser(userId, principal)
throwErrorIfCannotEditUser(userId)
DeleteUser(userRepository).deleteUser(userId)
}
}
@RequestMapping("/edit")
suspend fun editUser(@RequestBody @Validated userDto: UserDto, principal: Principal): ApiResponse<Unit> {
suspend fun editUser(@RequestBody @Validated userDto: UserDto): ApiResponse<Unit> {
return response {
throwErrorIfCannotEditUser(userDto.id, principal)
throwErrorIfCannotEditUser(userDto.id)
EditUser(userRepository).editUser(userDto.toUser(userRepository))
}
}
@@ -80,11 +76,10 @@ class PrivateUserRestController(
}
private fun throwErrorIfCannotEditUser(
userId: UserId?,
principal: Principal,
message: String = "Only admins and travel driver can modify this user."
userId: UserId?,
message: String = "Only admins and travel driver can modify this user."
) {
if (!authService.canEditUser(userId, principal))
if (!authService.canEditUser(userId))
throw InsufficientPermissions(message)
}
}

View File

@@ -1,51 +1,47 @@
package eu.fosil.okupamicoche.spring.controller
import eu.fosil.okupamicoche.dto.ListDto
import eu.fosil.okupamicoche.dto.TravelDto
import eu.fosil.okupamicoche.dto.ListDto
import eu.fosil.okupamicoche.entities.ApiResponse
import eu.fosil.okupamicoche.entities.TravelId
import eu.fosil.okupamicoche.repositories.TravelRepository
import eu.fosil.okupamicoche.usecases.travel.ListTravels
import org.springframework.data.repository.findByIdOrNull
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.CrossOrigin
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Mono
@RestController
@RequestMapping("/api/public")
@CrossOrigin(value = [ "http://allowed-origin.com"],
maxAge = 900
)
class PublicRestController(private val travelRepository: TravelRepository) : ApiRestController {
@RequestMapping("/list")
suspend fun listTravels(
@RequestParam @Validated filter: String?,
@RequestParam @Validated sortColumn: String?,
@RequestParam @Validated sortAscending: Boolean?,
@RequestParam @Validated pageIndex: Int?,
@RequestParam @Validated pageSize: Int?,
): ApiResponse<ListDto<TravelDto>> {
return response {
val usecase = ListTravels(travelRepository)
val count = usecase.countTravels(filter)
val travels = usecase.listTravels(
@RequestParam @Validated filter: String?,
@RequestParam @Validated sortColumn: String?,
@RequestParam @Validated sortAscending: Boolean?,
@RequestParam @Validated pageIndex: Int?,
@RequestParam @Validated pageSize: Int?,
): Mono<ApiResponse<ListDto<TravelDto>>> {
return Mono.just(response {
val travels =
ListTravels(travelRepository).listTravels(
filter, sortColumn, sortAscending, pageIndex, pageSize
).map { t -> TravelDto(t) }
ListDto(count, travels)
}
}
@RequestMapping("/listall")
suspend fun listTravels(): ApiResponse<ListDto<TravelDto>> {
return response {
val travels = ListTravels(travelRepository).listTravels().map { t -> TravelDto(t) }
).map { t -> TravelDto(t) }
ListDto(travelRepository.count(), travels)
}
})
}
@RequestMapping("/travel")
suspend fun getTravel(@RequestParam @Validated travelId: TravelId): ApiResponse<TravelDto?> {
return response {
suspend fun getTravel(@RequestParam @Validated travelId: TravelId): Mono<ApiResponse<TravelDto?>> {
return Mono.just(response {
travelRepository.findByIdOrNull(travelId)?.let { TravelDto(it) }
}
})
}
}

View File

@@ -1,43 +1,41 @@
package eu.fosil.okupamicoche.spring.services
import eu.fosil.okupamicoche.entities.TravelId
import eu.fosil.okupamicoche.entities.UserId
import eu.fosil.okupamicoche.entities.UserIdNotFoundException
import eu.fosil.okupamicoche.entities.UserKeycloak
import eu.fosil.okupamicoche.entities.*
import eu.fosil.okupamicoche.repositories.TravelRepository
import mu.KotlinLogging
import eu.fosil.okupamicoche.repositories.UserRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.stereotype.Service
import java.security.Principal
@Service
class AuthService(
private val travelRepository: TravelRepository
private val userRepository: UserRepository,
private val travelRepository: TravelRepository
) {
private val logger = KotlinLogging.logger {}
/**
* Devuelve el id del usuario actual.
*/
fun currentUser(principal: Principal): UserKeycloak {
if (principal is JwtAuthenticationToken) {
return UserKeycloak(principal.token.claims)
fun currentUser(): UserKeycloak {
val authentication = SecurityContextHolder.getContext().authentication
if (authentication.principal is Jwt) {
val jwt = authentication.principal as Jwt
return UserKeycloak(jwt.claims)
}
throw UserIdNotFoundException()
}
fun isAdmin(principal: Principal): Boolean {
return currentUser(principal).admin
fun isAdmin(): Boolean {
return currentUser().admin
}
fun canEditTravel(travelId: TravelId?, principal: Principal): Boolean {
fun canEditTravel(travelId: TravelId?): Boolean {
val travel = travelRepository.findByIdOrNull(travelId) ?: return false
return currentUser(principal).admin || currentUser(principal).id == travel.driver.id
return currentUser().admin || currentUser().id == travel.driver.id
}
fun canEditUser(userId: UserId?, principal: Principal): Boolean {
fun canEditUser(userId: UserId?): Boolean {
if (userId == null) return false
return currentUser(principal).admin || currentUser(principal).id == userId
return currentUser().admin || currentUser().id == userId
}
}

View File

@@ -3,36 +3,33 @@ package eu.fosil.okupamicoche.spring.services
import eu.fosil.okupamicoche.entities.User
import eu.fosil.okupamicoche.usecases.matrix.MatrixApi
import mu.KotlinLogging
import net.folivo.trixnity.client.rest.MatrixClient
import net.folivo.trixnity.core.model.MatrixId
//import net.folivo.matrix.core.model.MatrixId
//import net.folivo.matrix.restclient.MatrixClient
import org.springframework.stereotype.Service
import java.util.stream.Collectors
@Service
class MatrixService(private val matrixClient: MatrixClient) : MatrixApi {
class MatrixService(
// private val matrixClient: MatrixClient
): MatrixApi {
private val logger = KotlinLogging.logger {}
override suspend fun createRoom(name: String, alias: String, usersToInvite: Set<User>, topic: String?): String {
logger.debug { "Creating room name=$name alias=$alias" }
val usersToInviteId = usersToInvite.stream().map { user -> MatrixId.UserId(user.matrixId) }
val roomId = matrixClient.room.createRoom(
name = name,
roomAliasId = MatrixId.RoomAliasId("#$alias:okupamicoche-synapse"),
invite = usersToInviteId.collect(Collectors.toSet()),
topic = topic
)
return roomId.full
// val usersToInviteId = usersToInvite.stream().map { user -> MatrixId.UserId(user.matrixId) }
// val roomId = matrixClient.roomsApi.createRoom(
// name = name,
// roomAliasId = MatrixId.RoomAliasId("#$alias:synapse"),
//// invite = usersToInviteId.collect(Collectors.toSet()),
// topic = topic
// )
// return roomId.full
return "0"
}
override suspend fun inviteUser(roomId: String, user: User) {
val matrixUserId = MatrixId.UserId(user.matrixId)
logger.debug { "Invite user $matrixUserId to room $roomId" }
matrixClient.room.inviteUser(MatrixId.RoomId(roomId), matrixUserId)
}
override suspend fun kickUser(roomId: String, user: User) {
val matrixUserId = MatrixId.UserId(user.matrixId)
logger.debug { "Kick user $matrixUserId from room $roomId" }
matrixClient.room.leaveRoom(MatrixId.RoomId(roomId), matrixUserId) // TODO should be kickUser
// val matrixUserId = MatrixId.UserId(user.matrixId)
// logger.debug { "Invite user $matrixUserId to room $roomId" }
// matrixClient.roomsApi.inviteUser(MatrixId.RoomId(roomId), matrixUserId)
}
}

View File

@@ -1,20 +1,16 @@
package eu.fosil.okupamicoche.spring.services
import eu.fosil.okupamicoche.repositories.TravelRepository
import eu.fosil.okupamicoche.repositories.UserRepository
import eu.fosil.okupamicoche.usecases.UseCaseFactory
import eu.fosil.okupamicoche.usecases.matrix.CreateRoomForTravel
import eu.fosil.okupamicoche.usecases.travel.AddTraveler
import eu.fosil.okupamicoche.usecases.travel.CreateTravel
import eu.fosil.okupamicoche.usecases.travel.RemoveTraveler
import org.springframework.stereotype.Service
@Service
class UseCaseService(
private val travelRepository: TravelRepository,
private val userRepository: UserRepository,
private val matrixService: MatrixService
) : UseCaseFactory {
private val travelRepository: TravelRepository,
private val matrixService: MatrixService
): UseCaseFactory {
override fun getCreateTravel(): CreateTravel {
return CreateTravel(travelRepository, getCreateRoomForTravel())
}
@@ -22,12 +18,4 @@ class UseCaseService(
override fun getCreateRoomForTravel(): CreateRoomForTravel {
return CreateRoomForTravel(matrixService)
}
override fun getAddTraveler(): AddTraveler {
return AddTraveler(userRepository, travelRepository, matrixService)
}
override fun getRemoveTraveler(): RemoveTraveler {
return RemoveTraveler(userRepository, travelRepository, matrixService)
}
}

View File

@@ -1,13 +1,10 @@
package eu.fosil.okupamicoche.usecases
import eu.fosil.okupamicoche.usecases.matrix.CreateRoomForTravel
import eu.fosil.okupamicoche.usecases.travel.AddTraveler
import eu.fosil.okupamicoche.usecases.travel.CreateTravel
import eu.fosil.okupamicoche.usecases.travel.RemoveTraveler
interface UseCaseFactory {
fun getCreateTravel(): CreateTravel
fun getCreateRoomForTravel(): CreateRoomForTravel
fun getAddTraveler(): AddTraveler
fun getRemoveTraveler(): RemoveTraveler
}

View File

@@ -1,7 +1,6 @@
package eu.fosil.okupamicoche.usecases.matrix
import eu.fosil.okupamicoche.entities.Travel
import java.time.format.DateTimeFormatter
const val ROOM_NAME_PREFIX = "Viaje"
const val ROOM_ALIAS_PREFIX = "viaje"
@@ -14,15 +13,11 @@ class CreateRoomForTravel(private val matrixApi: MatrixApi) {
}
private fun createRoomName(travel: Travel): String {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm")
val formattedDate = travel.departureDate.format(formatter)
return "$ROOM_NAME_PREFIX ${travel.origin} - ${travel.destination} $formattedDate"
return "$ROOM_NAME_PREFIX ${travel.origin} - ${travel.destination}"
}
private fun createRoomAlias(travel: Travel): String {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_hh'H'mm")
val formattedDate = travel.departureDate.format(formatter)
val alias = "${ROOM_ALIAS_PREFIX}_${travel.origin}-${travel.destination}_${formattedDate}"
val alias = "$ROOM_ALIAS_PREFIX-${travel.origin}-${travel.destination}"
return alias.decapitalize().replace(' ', '_')
}
}

View File

@@ -10,6 +10,4 @@ interface MatrixApi {
suspend fun createRoom(name: String, alias: String, usersToInvite: Set<User>, topic: String?): String
suspend fun inviteUser(roomId: String, user: User)
suspend fun kickUser(roomId: String, user: User)
}

View File

@@ -4,21 +4,16 @@ import eu.fosil.okupamicoche.entities.TravelId
import eu.fosil.okupamicoche.entities.UserId
import eu.fosil.okupamicoche.repositories.TravelRepository
import eu.fosil.okupamicoche.repositories.UserRepository
import eu.fosil.okupamicoche.usecases.matrix.MatrixApi
import org.springframework.data.repository.findByIdOrNull
class AddTraveler(
private val userRepository: UserRepository,
private val travelRepository: TravelRepository,
private val matrixApi: MatrixApi
private val travelRepository: TravelRepository
) {
suspend fun addTraveler(travelId: TravelId, userId: UserId) {
fun addTraveler(travelId: TravelId, userId: UserId) {
val user = userRepository.findByIdOrNull(userId)
val travel = travelRepository.findByIdOrNull(travelId)
val traveler = travel?.travelers?.find { it.id == userId }
if ((traveler == null) && (travel != null) && (user != null)) {
matrixApi.inviteUser(travel.matrixRoomId, user)
if ((travel?.travelers?.contains(user) == false) && (user != null)) {
travel.travelers.add(user)
travelRepository.save(travel)
}

View File

@@ -11,32 +11,28 @@ class ListTravels(private val travelRepository: TravelRepository) {
}
fun listTravels(
filter: String?,
sortColumn: String?,
sortAscending: Boolean?,
pageIndex: Int?,
pageSize: Int?
filter: String?,
sortColumn: String?,
sortAscending: Boolean?,
pageIndex: Int?,
pageSize: Int?
): List<Travel> {
val sort = if (sortColumn == null) {
Sort.unsorted()
} else {
Sort.by(
if (sortAscending != false) Sort.Direction.ASC else Sort.Direction.DESC,
sortColumn
if (sortAscending != false) Sort.Direction.ASC else Sort.Direction.DESC,
sortColumn
)
}
return travelRepository.findByOriginContainingOrDestinationContainingAllIgnoreCase(
filter ?: "",
filter ?: "",
PageRequest.of(
pageIndex ?: 0,
pageSize ?: 10,
sort
)
filter ?: "",
filter ?: "",
PageRequest.of(
pageIndex ?: 0,
pageSize ?: 10,
sort
)
).toList()
}
fun countTravels(filter: String?): Long {
return travelRepository.countTravels(filter ?: "")
}
}

View File

@@ -3,46 +3,17 @@ package eu.fosil.okupamicoche.usecases.travel
import eu.fosil.okupamicoche.entities.Travel
import eu.fosil.okupamicoche.entities.UserId
import eu.fosil.okupamicoche.repositories.TravelRepository
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
class ListUserTravels(private val travelRepository: TravelRepository) {
fun listUserTravels(idUser: UserId): List<Travel> {
return travelRepository.findUserTravels(idUser)
return listOf(
travelRepository.findUserTravelsAsDriver(idUser),
travelRepository.findUserTravelsAsTraveler(idUser)
).flatten()
}
fun countUserTravels(idUser: UserId): Long {
return travelRepository.countUserTravels(idUser)
}
fun listUserTravels(
userId: UserId,
filter: String?,
sortColumn: String?,
sortAscending: Boolean?,
pageIndex: Int?,
pageSize: Int?
): List<Travel> {
val sort = if (sortColumn == null) {
Sort.unsorted()
} else {
Sort.by(
if (sortAscending != false) Sort.Direction.ASC else Sort.Direction.DESC,
sortColumn
)
}
return travelRepository.findUserTravels(
userId,
filter ?: "",
PageRequest.of(
pageIndex ?: 0,
pageSize ?: 10,
sort
)
).toList()
}
fun countUserTravels(idUser: UserId, filter: String?): Long {
return travelRepository.countUserTravels(idUser, filter ?: "")
return travelRepository.countUserTravelsAsDriver(idUser) +
travelRepository.countUserTravelsAsTraveler(idUser)
}
}

View File

@@ -4,24 +4,17 @@ import eu.fosil.okupamicoche.entities.TravelId
import eu.fosil.okupamicoche.entities.UserId
import eu.fosil.okupamicoche.repositories.TravelRepository
import eu.fosil.okupamicoche.repositories.UserRepository
import eu.fosil.okupamicoche.usecases.matrix.MatrixApi
import org.springframework.data.repository.findByIdOrNull
class RemoveTraveler(
private val userRepository: UserRepository,
private val travelRepository: TravelRepository,
private val matrixApi: MatrixApi
private val travelRepository: TravelRepository
) {
suspend fun removeTraveler(travelId: TravelId, userId: UserId, kickFromChat: Boolean = false) {
fun removeTraveler(travelId: TravelId, userId: UserId) {
val user = userRepository.findByIdOrNull(userId)
val travel = travelRepository.findByIdOrNull(travelId)
println("userId=$userId user=$user travel=$travel contains=${travel?.travelers?.contains(user)}")
val traveler = travel?.travelers?.find { it.id == userId }
if ((traveler != null) && (user != null)) {
if (kickFromChat) matrixApi.kickUser(travel.matrixRoomId, user)
travel.travelers.remove(traveler)
if ((user != null) && (travel != null)) {
travel.travelers.remove(user)
travelRepository.save(travel)
}
}

View File

@@ -13,62 +13,58 @@ spring:
oauth2:
resourceserver:
jwt:
issuer-uri: https://okupamicoche-keycloak:8443/auth/realms/okupamicoche
jackson:
serialization:
write-dates-as-timestamps: false
issuer-uri: http://localhost:8080/auth/realms/okupamicoche
logging:
level:
org:
springframework: INFO
eu.fosil.okupamicoche: DEBUG
net.folivo.matrix: DEBUG
matrix:
bot:
# The domain-part of matrix-ids.E.g. example.org when your userIds look like @unicorn:example.org
serverName: okupamicoche-synapse
# The localpart (username) of the user associated with the application service
# or just the username of your bot.
username: okupamicoche
# (optional) Display name for the bot user.
displayname: Okupa mi coche
# (optional) The mode you want to use to create a bot. Default is CLIENT. The other is APPSERVICE.
mode: APPSERVICE
# (optional) Configure how users managed by your bot do automatically join rooms.
# ENABLED allows automatic joins to every invited room.
# DISABLED disables this feature.
# Default is RESTRICTED, which means, that only automatic joins to serverName are allowed.
autoJoin: DISABLED
# (optional) Configure if ALL membership changes should be tracked/saved with help of MatrixAppserviceRoomService
# or only membership changes of users, which are MANAGED by the bridge. Default is ALL (no tracking/saving).
trackMembership: ALL
# Connection setting to the database for migration purpose only (only jdbc drivers ar supported)
migration:
url: jdbc:h2:file:./matrix
username: sa
password:
# Connection settings to the database (only r2dbc drivers are supported)
database:
url: r2dbc:h2:file:///./matrix
username: sa
password:
client:
homeServer:
# The hostname of your Homeserver.
hostname: okupamicoche-synapse
# (optional) The port of your Homeserver. Default is 443.
port: 8008
# (optional) Use http or https. Default is true (so uses https).
secure: false
# The token to authenticate against the Homeserver.
token: "30c05ae90a248a4188e620216fa72e349803310ec83e2a77b34fe90be6081f46"
appservice:
# A unique token for Homeservers to use to authenticate requests to application services.
hsToken: "312df522183efd404ec1cd22d2ffa4bbc76a8c1ccf541dd692eef281356bb74e"
# A list of users, aliases and rooms namespaces that the application service controls.
namespaces:
users: [ ]
aliases:
- localpartRegex: "viaje_.*"
rooms: [ ]
#matrix:
# bot:
# # The domain-part of matrix-ids. E. g. example.org when your userIds look like @unicorn:example.org
# serverName: synapse
# # The localpart (username) of the user associated with the application service
# # or just the username of your bot.
# username: okupamicoche
# # (optional) Display name for the bot user.
# displayname: Okupa mi coche
# # (optional) The mode you want to use to create a bot. Default is CLIENT. The other is APPSERVICE.
# mode: APPSERVICE
# # (optional) Configure how users managed by your bot do automatically join rooms.
# # ENABLED allows automatic joins to every invited room.
# # DISABLED disables this feature.
# # Default is RESTRICTED, which means, that only automatic joins to serverName are allowed.
# autoJoin: DISABLED
# # (optional) Configure if ALL membership changes should be tracked/saved with help of MatrixAppserviceRoomService
# # or only membership changes of users, which are MANAGED by the bridge. Default is ALL (no tracking/saving).
# trackMembership: ALL
# # Connection setting to the database for migration purpose only (only jdbc drivers ar supported)
# migration:
# url: jdbc:h2:file:./matrix
# username: sa
# password:
# # Connection settings to the database (only r2dbc drivers are supported)
# database:
# url: r2dbc:h2:file:///./matrix
# username: sa
# password:
# client:
# homeServer:
# # The hostname of your Homeserver.
# hostname: synapse
# # (optional) The port of your Homeserver. Default is 443.
# port: 8008
# # (optional) Use http or https. Default is true (so uses https).
# secure: false
# # The token to authenticate against the Homeserver.
# token: "30c05ae90a248a4188e620216fa72e349803310ec83e2a77b34fe90be6081f46"
# appservice:
# # A unique token for Homeservers to use to authenticate requests to application services.
# hsToken: "312df522183efd404ec1cd22d2ffa4bbc76a8c1ccf541dd692eef281356bb74e"
# # A list of users, aliases and rooms namespaces that the application service controls.
# namespaces:
# users: [ ]
# aliases:
# - localpartRegex: "viaje_.*"
# rooms: [ ]