├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── containers.sh
├── docker
├── ELK
│ ├── docker-compose.yml
│ └── logstash
│ │ └── logstash.conf
├── jaeger
│ └── docker-compose.yml
├── keycloak
│ ├── docker-compose.yml
│ └── realm-export.json
└── postgres
│ ├── docker-compose.yml
│ └── init.sql
├── k8s
├── java-deployment-svc.yml
└── postgres-deployment-svc.yml
├── pom.xml
├── ships
├── get.ship
├── get_paged.ship
├── keycloak_token.ship
└── post.ship
└── src
├── main
├── java
│ └── com
│ │ └── javi
│ │ ├── Application.java
│ │ ├── adapter
│ │ ├── in
│ │ │ └── DummyController.java
│ │ └── out
│ │ │ ├── DummyPersistenceAdapter.java
│ │ │ ├── entities
│ │ │ └── DummyEntity.java
│ │ │ ├── mappers
│ │ │ └── DummyMapper.java
│ │ │ └── repositories
│ │ │ └── DummyEntityRepository.java
│ │ ├── application
│ │ ├── in
│ │ │ ├── DummyUseCase.java
│ │ │ ├── request
│ │ │ │ └── DummyRequest.java
│ │ │ └── response
│ │ │ │ └── DummyResponse.java
│ │ └── out
│ │ │ └── DummyPersistence.java
│ │ ├── common
│ │ ├── configuration
│ │ │ └── BeanConfiguration.java
│ │ └── exception
│ │ │ └── NotFoundException.java
│ │ └── domain
│ │ ├── model
│ │ └── Dummy.java
│ │ └── service
│ │ └── DummyService.java
└── resources
│ └── application.yml
└── test
├── java
└── com
│ └── javi
│ ├── adapter
│ ├── in
│ │ └── DummyControllerTest.java
│ └── out
│ │ └── DummyPersistenceTest.java
│ └── domain
│ └── service
│ └── DummyServiceTest.java
└── resources
└── com
└── javi
└── adapter
└── out
└── db.sql
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | .gradle
3 | build/
4 | !gradle/wrapper/gradle-wrapper.jar
5 | !**/src/main/**/build/
6 | !**/src/test/**/build/
7 |
8 | ### STS ###
9 | .apt_generated
10 | .classpath
11 | .factorypath
12 | .project
13 | .settings
14 | .springBeans
15 | .sts4-cache
16 | bin/
17 | !**/src/main/**/bin/
18 | !**/src/test/**/bin/
19 |
20 | ### IntelliJ IDEA ###
21 | .idea
22 | *.iws
23 | *.iml
24 | *.ipr
25 | out/
26 | !**/src/main/**/out/
27 | !**/src/test/**/out/
28 |
29 | ### NetBeans ###
30 | /nbproject/private/
31 | /nbbuild/
32 | /dist/
33 | /nbdist/
34 | /.nb-gradle/
35 |
36 | ### VS Code ###
37 | .vscode/
38 |
39 | ### Schema SQL ###
40 | schema.sql
41 |
42 | ### Maven ###
43 | /target
44 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM eclipse-temurin:21-jre-alpine
2 |
3 | RUN addgroup -S spring && adduser -S spring -G spring
4 |
5 | COPY target/*.jar /opt/app.jar
6 |
7 | USER spring:spring
8 |
9 | WORKDIR /opt
10 |
11 | ENTRYPOINT ["java", "-jar", "app.jar"]
12 |
13 | EXPOSE 8080
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Javier Orfo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # java-spring3-microservice
2 | *Java archetype oriented to Microservices.*
3 |
4 | Clean architecture, Java 21, Spring Boot 3, distributed tracing, log centralization and Keycloak.
5 |
6 | ## Dependencies
7 | Java 21, Docker, Maven
8 |
9 | ## Features
10 | - Clean Architecture
11 | - Exception Controller
12 | - Custom Messages and Exceptions
13 | - Pagination and Ordering
14 | - Java 21
15 | - OpenJDK or GraalVM integration
16 | - Spring Boot 3
17 | - Spring Web
18 | - Spring Data JPA
19 | - Spring OAuth2 Resource Server
20 | - Spring Security
21 | - Spring Devtools
22 | - Spring Actuator
23 | - Keycloak as Auth Server
24 | - Distributed tracing
25 | - OpenTelemetry, Micrometer and Jaeger
26 | - Log Centralization
27 | - Logstash, ElasticSearch and Kibana
28 | - Swagger
29 | - OpenApi
30 | - Auditory
31 | - JPA auditing
32 | - Database
33 | - Postgres for the app
34 | - H2 for Test
35 | - Schema generation (schema.sql)
36 |
37 | ## Files
38 | - [Docker files](https://github.com/javiorfo/java-spring3-microservice/tree/master/docker)
39 | - [Kubernetes files](https://github.com/javiorfo/java-spring3-microservice/tree/master/k8s)
40 | - [Ship files](https://github.com/javiorfo/java-spring3-microservice/tree/master/ships)
41 | - For those using Neovim and [this plugin](https://github.com/javiorfo/nvim-ship)
42 |
43 | ## Usage
44 | - Create the containers executing `./containers.sh`
45 | - Download and compile [this library](https://github.com/javiorfo/java-spring3-microservice-lib)
46 | - Start the application with the command `mvn spring-boot:run -Pdev`
47 | - To delete all the containers: `./containers.sh d`
48 |
49 | ## MongoDB instead of Postgres
50 | - [MongoDB repo](https://github.com/javiorfo/java-spring3-microservice-mongo) contains version with MongoDB
51 | ---
52 |
53 | ### Donate
54 | - **Bitcoin** [(QR)](https://raw.githubusercontent.com/javiorfo/img/master/crypto/bitcoin.png) `1GqdJ63RDPE4eJKujHi166FAyigvHu5R7v`
55 | - [Paypal](https://www.paypal.com/donate/?hosted_button_id=FA7SGLSCT2H8G)
56 |
--------------------------------------------------------------------------------
/containers.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | if [ "$1" = "d" ]; then
4 | action="down"
5 | label="Deleting"
6 | else
7 | action="up"
8 | label="Creating"
9 | fi
10 |
11 | echo "$action"
12 |
13 | echo ""
14 | echo "========================================================="
15 | echo "= Docker Container Creation ="
16 | echo "========================================================="
17 | echo ""
18 |
19 | echo "$label Postgres container for java-spring3-microservice..."
20 | docker-compose -f docker/postgres/docker-compose.yml "$action" -d
21 |
22 | echo "$label Postgres and Keycloak..."
23 | docker-compose -f docker/keycloak/docker-compose.yml "$action" -d
24 |
25 | echo "$label Jaeger container..."
26 | docker-compose -f docker/jaeger/docker-compose.yml "$action" -d
27 |
28 | echo "$label Logstash, Elasticsearch and Kibana container..."
29 | docker-compose -f docker/ELK/docker-compose.yml "$action" -d
30 |
31 | echo "Done!"
32 |
33 |
34 |
--------------------------------------------------------------------------------
/docker/ELK/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | elasticsearch:
3 | image: elasticsearch:8.13.0
4 | container_name: elasticsearch
5 | environment:
6 | ES_JAVA_OPTS: "-Xmx256m -Xms256m"
7 | discovery.type: single-node
8 | xpack.security.enabled: false
9 | ports:
10 | - 9200:9200
11 | - 9300:9300
12 | volumes:
13 | - elastic_data:/usr/share/elasticsearch/data/
14 | networks:
15 | - elk
16 |
17 | kibana:
18 | image: kibana:8.13.0
19 | container_name: kibana
20 | ports:
21 | - 5601:5601
22 | environment:
23 | ELASTICSEARCH_URL: http://elasticsearch:9200
24 | ELASTICSEARCH_HOSTS: '["http://elasticsearch:9200"]'
25 | depends_on:
26 | - elasticsearch
27 | networks:
28 | - elk
29 |
30 | logstash:
31 | image: logstash:8.13.0
32 | container_name: logstash
33 | volumes:
34 | - ./logstash/:/logstash_dir
35 | command: logstash -f /logstash_dir/logstash.conf
36 | depends_on:
37 | - elasticsearch
38 | ports:
39 | - 5000:5000
40 | - 9600:9600
41 | environment:
42 | LS_JAVA_OPTS: "-Xmx256m -Xms256m"
43 | networks:
44 | - elk
45 |
46 | volumes:
47 | elastic_data: {}
48 |
49 | networks:
50 | elk:
51 | driver: bridge
52 |
--------------------------------------------------------------------------------
/docker/ELK/logstash/logstash.conf:
--------------------------------------------------------------------------------
1 | input {
2 | # Listen for logs on port 5000 using the TCP protocol
3 | tcp {
4 | port => 5000
5 | codec => json
6 | }
7 | }
8 |
9 | filter {
10 | # Parse the timestamp field as a date
11 | date {
12 | match => [ "timestamp", "ISO8601" ]
13 | }
14 | }
15 |
16 | output {
17 | # Send the logs to Elasticsearch
18 | elasticsearch {
19 | hosts => ["elasticsearch:9200"]
20 | index => "app-%{+YYYY.MM.dd}"
21 | }
22 | # Print the logs to the standard output (optional)
23 | stdout {
24 | codec => rubydebug
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/docker/jaeger/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | jaeger:
3 | container_name: jaeger
4 | image: jaegertracing/all-in-one:latest
5 | restart: always
6 | ports:
7 | - 4318:4318
8 | - 16686:16686
9 | environment:
10 | - COLLECTOR_OTLP_ENABLED=true
11 |
--------------------------------------------------------------------------------
/docker/keycloak/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | postgres:
3 | container_name: postgres_keycloak
4 | image: postgres
5 | restart: always
6 | environment:
7 | POSTGRES_PASSWORD: admin
8 | POSTGRES_USER: admin
9 | POSTGRES_DB: keycloakdb
10 | ports:
11 | - 5433:5432
12 | # networks:
13 | # - backend
14 | keycloak:
15 | container_name: keycloak
16 | image: quay.io/keycloak/keycloak:21.1.1
17 | restart: always
18 | volumes:
19 | ./realm-export.json:/opt/jboss/keycloak/imports/realm-export.json
20 | environment:
21 | KEYCLOAK_ADMIN: admin
22 | KEYCLOAK_ADMIN_PASSWORD: admin
23 | KC_DB: postgres
24 | KC_DB_URL: jdbc:postgresql://postgres/keycloakdb
25 | KC_DB_USERNAME: admin
26 | KC_DB_PASSWORD: admin
27 | KEYCLOAK_IMPORT: /opt/jboss/keycloak/imports/realm-export.json
28 | depends_on:
29 | - postgres
30 | ports:
31 | - 8081:8080
32 | command:
33 | - start-dev
34 | # networks:
35 | # - backend
36 |
37 | # networks:
38 | # backend:
39 | # name: backend
40 | # driver: bridge
41 |
--------------------------------------------------------------------------------
/docker/keycloak/realm-export.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "5b1be921-15e4-4abb-9770-ff34129a5991",
3 | "realm": "javi",
4 | "notBefore": 0,
5 | "defaultSignatureAlgorithm": "RS256",
6 | "revokeRefreshToken": false,
7 | "refreshTokenMaxReuse": 0,
8 | "accessTokenLifespan": 300,
9 | "accessTokenLifespanForImplicitFlow": 900,
10 | "ssoSessionIdleTimeout": 1800,
11 | "ssoSessionMaxLifespan": 36000,
12 | "ssoSessionIdleTimeoutRememberMe": 0,
13 | "ssoSessionMaxLifespanRememberMe": 0,
14 | "offlineSessionIdleTimeout": 2592000,
15 | "offlineSessionMaxLifespanEnabled": false,
16 | "offlineSessionMaxLifespan": 5184000,
17 | "clientSessionIdleTimeout": 0,
18 | "clientSessionMaxLifespan": 0,
19 | "clientOfflineSessionIdleTimeout": 0,
20 | "clientOfflineSessionMaxLifespan": 0,
21 | "accessCodeLifespan": 60,
22 | "accessCodeLifespanUserAction": 300,
23 | "accessCodeLifespanLogin": 1800,
24 | "actionTokenGeneratedByAdminLifespan": 43200,
25 | "actionTokenGeneratedByUserLifespan": 300,
26 | "oauth2DeviceCodeLifespan": 600,
27 | "oauth2DevicePollingInterval": 5,
28 | "enabled": true,
29 | "sslRequired": "external",
30 | "registrationAllowed": false,
31 | "registrationEmailAsUsername": false,
32 | "rememberMe": false,
33 | "verifyEmail": false,
34 | "loginWithEmailAllowed": true,
35 | "duplicateEmailsAllowed": false,
36 | "resetPasswordAllowed": false,
37 | "editUsernameAllowed": false,
38 | "bruteForceProtected": false,
39 | "permanentLockout": false,
40 | "maxFailureWaitSeconds": 900,
41 | "minimumQuickLoginWaitSeconds": 60,
42 | "waitIncrementSeconds": 60,
43 | "quickLoginCheckMilliSeconds": 1000,
44 | "maxDeltaTimeSeconds": 43200,
45 | "failureFactor": 30,
46 | "roles": {
47 | "realm": [
48 | {
49 | "id": "7a9038eb-969b-42be-ba85-e9c6aac9cc86",
50 | "name": "offline_access",
51 | "description": "${role_offline-access}",
52 | "composite": false,
53 | "clientRole": false,
54 | "containerId": "5b1be921-15e4-4abb-9770-ff34129a5991",
55 | "attributes": {}
56 | },
57 | {
58 | "id": "16346e15-3937-4fa8-8809-a8b8cf4eaafe",
59 | "name": "uma_authorization",
60 | "description": "${role_uma_authorization}",
61 | "composite": false,
62 | "clientRole": false,
63 | "containerId": "5b1be921-15e4-4abb-9770-ff34129a5991",
64 | "attributes": {}
65 | },
66 | {
67 | "id": "ae1ce714-097b-4ae4-954f-9406fa982093",
68 | "name": "ADMIN",
69 | "description": "",
70 | "composite": true,
71 | "composites": {
72 | "client": {
73 | "srv-client": [
74 | "CLIENT_ADMIN"
75 | ]
76 | }
77 | },
78 | "clientRole": false,
79 | "containerId": "5b1be921-15e4-4abb-9770-ff34129a5991",
80 | "attributes": {}
81 | },
82 | {
83 | "id": "c838f9d5-a8c3-4b6f-a24c-96e259db9121",
84 | "name": "default-roles-javi",
85 | "description": "${role_default-roles}",
86 | "composite": true,
87 | "composites": {
88 | "realm": [
89 | "offline_access",
90 | "uma_authorization"
91 | ],
92 | "client": {
93 | "account": [
94 | "view-profile",
95 | "manage-account"
96 | ]
97 | }
98 | },
99 | "clientRole": false,
100 | "containerId": "5b1be921-15e4-4abb-9770-ff34129a5991",
101 | "attributes": {}
102 | }
103 | ],
104 | "client": {
105 | "realm-management": [
106 | {
107 | "id": "ca8adc5b-68f4-4dfe-af7d-393a206f589b",
108 | "name": "query-users",
109 | "description": "${role_query-users}",
110 | "composite": false,
111 | "clientRole": true,
112 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
113 | "attributes": {}
114 | },
115 | {
116 | "id": "368d21e0-fbc1-42ce-96bf-a37f500ad543",
117 | "name": "manage-identity-providers",
118 | "description": "${role_manage-identity-providers}",
119 | "composite": false,
120 | "clientRole": true,
121 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
122 | "attributes": {}
123 | },
124 | {
125 | "id": "f32977ec-bd66-42e1-bf82-e9768f18c57b",
126 | "name": "view-realm",
127 | "description": "${role_view-realm}",
128 | "composite": false,
129 | "clientRole": true,
130 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
131 | "attributes": {}
132 | },
133 | {
134 | "id": "d8abefcf-681a-4552-a3c2-1b9211e72384",
135 | "name": "impersonation",
136 | "description": "${role_impersonation}",
137 | "composite": false,
138 | "clientRole": true,
139 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
140 | "attributes": {}
141 | },
142 | {
143 | "id": "1f5f7bb5-7cff-4037-a092-467bbf8e56e4",
144 | "name": "manage-clients",
145 | "description": "${role_manage-clients}",
146 | "composite": false,
147 | "clientRole": true,
148 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
149 | "attributes": {}
150 | },
151 | {
152 | "id": "495b5906-e87d-4085-beeb-77aa580aa289",
153 | "name": "query-groups",
154 | "description": "${role_query-groups}",
155 | "composite": false,
156 | "clientRole": true,
157 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
158 | "attributes": {}
159 | },
160 | {
161 | "id": "9f013f62-6401-480d-8eac-5022ae9da574",
162 | "name": "manage-authorization",
163 | "description": "${role_manage-authorization}",
164 | "composite": false,
165 | "clientRole": true,
166 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
167 | "attributes": {}
168 | },
169 | {
170 | "id": "57b8253d-5879-468b-8a05-db9771fcb0d6",
171 | "name": "view-clients",
172 | "description": "${role_view-clients}",
173 | "composite": true,
174 | "composites": {
175 | "client": {
176 | "realm-management": [
177 | "query-clients"
178 | ]
179 | }
180 | },
181 | "clientRole": true,
182 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
183 | "attributes": {}
184 | },
185 | {
186 | "id": "3c743148-928c-492d-9d37-306eb3bc4e3c",
187 | "name": "view-authorization",
188 | "description": "${role_view-authorization}",
189 | "composite": false,
190 | "clientRole": true,
191 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
192 | "attributes": {}
193 | },
194 | {
195 | "id": "b165e9c6-829e-4230-91bd-99b64380a7e8",
196 | "name": "realm-admin",
197 | "description": "${role_realm-admin}",
198 | "composite": true,
199 | "composites": {
200 | "client": {
201 | "realm-management": [
202 | "query-users",
203 | "view-realm",
204 | "manage-identity-providers",
205 | "manage-clients",
206 | "impersonation",
207 | "query-groups",
208 | "manage-authorization",
209 | "view-clients",
210 | "view-authorization",
211 | "view-identity-providers",
212 | "view-events",
213 | "manage-realm",
214 | "create-client",
215 | "manage-events",
216 | "view-users",
217 | "query-clients",
218 | "manage-users",
219 | "query-realms"
220 | ]
221 | }
222 | },
223 | "clientRole": true,
224 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
225 | "attributes": {}
226 | },
227 | {
228 | "id": "d867f965-e9c7-493d-836a-4fdc4b4cd3eb",
229 | "name": "view-identity-providers",
230 | "description": "${role_view-identity-providers}",
231 | "composite": false,
232 | "clientRole": true,
233 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
234 | "attributes": {}
235 | },
236 | {
237 | "id": "967c0e73-1ec8-4900-a2f6-5e5cca91cc41",
238 | "name": "manage-realm",
239 | "description": "${role_manage-realm}",
240 | "composite": false,
241 | "clientRole": true,
242 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
243 | "attributes": {}
244 | },
245 | {
246 | "id": "0a5add39-fd33-4001-9400-f52edf58df48",
247 | "name": "view-events",
248 | "description": "${role_view-events}",
249 | "composite": false,
250 | "clientRole": true,
251 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
252 | "attributes": {}
253 | },
254 | {
255 | "id": "794ec82a-a6ed-4298-89a1-75fd094b89f8",
256 | "name": "create-client",
257 | "description": "${role_create-client}",
258 | "composite": false,
259 | "clientRole": true,
260 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
261 | "attributes": {}
262 | },
263 | {
264 | "id": "20123a90-71ff-48d2-866a-ffc5f3567eda",
265 | "name": "manage-events",
266 | "description": "${role_manage-events}",
267 | "composite": false,
268 | "clientRole": true,
269 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
270 | "attributes": {}
271 | },
272 | {
273 | "id": "b8e99c58-2498-4fcd-9247-f7275f837db3",
274 | "name": "query-clients",
275 | "description": "${role_query-clients}",
276 | "composite": false,
277 | "clientRole": true,
278 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
279 | "attributes": {}
280 | },
281 | {
282 | "id": "8e47c8be-783a-4090-9c05-371caf5907a2",
283 | "name": "view-users",
284 | "description": "${role_view-users}",
285 | "composite": true,
286 | "composites": {
287 | "client": {
288 | "realm-management": [
289 | "query-users",
290 | "query-groups"
291 | ]
292 | }
293 | },
294 | "clientRole": true,
295 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
296 | "attributes": {}
297 | },
298 | {
299 | "id": "a9cbf23e-d58f-4015-88d3-48b4d955d7fa",
300 | "name": "manage-users",
301 | "description": "${role_manage-users}",
302 | "composite": false,
303 | "clientRole": true,
304 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
305 | "attributes": {}
306 | },
307 | {
308 | "id": "96b729a7-489a-43fe-a3c8-92f9364b9815",
309 | "name": "query-realms",
310 | "description": "${role_query-realms}",
311 | "composite": false,
312 | "clientRole": true,
313 | "containerId": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
314 | "attributes": {}
315 | }
316 | ],
317 | "security-admin-console": [],
318 | "admin-cli": [],
319 | "account-console": [],
320 | "broker": [
321 | {
322 | "id": "5a6059d3-d375-411f-a27b-4e8338c0abb5",
323 | "name": "read-token",
324 | "description": "${role_read-token}",
325 | "composite": false,
326 | "clientRole": true,
327 | "containerId": "57d3ba1e-fc5c-412e-92cc-2a5fcb06e097",
328 | "attributes": {}
329 | }
330 | ],
331 | "srv-client": [
332 | {
333 | "id": "1979fe66-0b49-4962-8755-937adfa051df",
334 | "name": "CLIENT_ADMIN",
335 | "description": "",
336 | "composite": false,
337 | "clientRole": true,
338 | "containerId": "ff218c79-85f1-40b0-811f-f75b0984f147",
339 | "attributes": {}
340 | },
341 | {
342 | "id": "037278cb-9459-474e-be20-638036ae5a82",
343 | "name": "uma_protection",
344 | "composite": false,
345 | "clientRole": true,
346 | "containerId": "ff218c79-85f1-40b0-811f-f75b0984f147",
347 | "attributes": {}
348 | }
349 | ],
350 | "account": [
351 | {
352 | "id": "ee7d74af-6765-4937-bd1e-6fd88cc491de",
353 | "name": "manage-account-links",
354 | "description": "${role_manage-account-links}",
355 | "composite": false,
356 | "clientRole": true,
357 | "containerId": "1a774c5f-2bf3-4149-9d34-78c5d0279425",
358 | "attributes": {}
359 | },
360 | {
361 | "id": "6c8f0eb8-992d-4add-8545-4e5c9d025f1b",
362 | "name": "view-consent",
363 | "description": "${role_view-consent}",
364 | "composite": false,
365 | "clientRole": true,
366 | "containerId": "1a774c5f-2bf3-4149-9d34-78c5d0279425",
367 | "attributes": {}
368 | },
369 | {
370 | "id": "7a36def2-8775-4b04-a06b-843c4d3ebf67",
371 | "name": "delete-account",
372 | "description": "${role_delete-account}",
373 | "composite": false,
374 | "clientRole": true,
375 | "containerId": "1a774c5f-2bf3-4149-9d34-78c5d0279425",
376 | "attributes": {}
377 | },
378 | {
379 | "id": "0b447522-baef-4c94-85a1-23c1426fdccb",
380 | "name": "manage-consent",
381 | "description": "${role_manage-consent}",
382 | "composite": true,
383 | "composites": {
384 | "client": {
385 | "account": [
386 | "view-consent"
387 | ]
388 | }
389 | },
390 | "clientRole": true,
391 | "containerId": "1a774c5f-2bf3-4149-9d34-78c5d0279425",
392 | "attributes": {}
393 | },
394 | {
395 | "id": "d72b9525-3e59-4b18-9b79-c5946f5eea69",
396 | "name": "view-groups",
397 | "description": "${role_view-groups}",
398 | "composite": false,
399 | "clientRole": true,
400 | "containerId": "1a774c5f-2bf3-4149-9d34-78c5d0279425",
401 | "attributes": {}
402 | },
403 | {
404 | "id": "baae6415-efcf-4037-bfde-7b8701957052",
405 | "name": "view-profile",
406 | "description": "${role_view-profile}",
407 | "composite": false,
408 | "clientRole": true,
409 | "containerId": "1a774c5f-2bf3-4149-9d34-78c5d0279425",
410 | "attributes": {}
411 | },
412 | {
413 | "id": "3abf2f2a-246f-43db-85a0-dafc5c81c514",
414 | "name": "view-applications",
415 | "description": "${role_view-applications}",
416 | "composite": false,
417 | "clientRole": true,
418 | "containerId": "1a774c5f-2bf3-4149-9d34-78c5d0279425",
419 | "attributes": {}
420 | },
421 | {
422 | "id": "18edbcea-033d-45dd-b0bc-550efd7949b3",
423 | "name": "manage-account",
424 | "description": "${role_manage-account}",
425 | "composite": true,
426 | "composites": {
427 | "client": {
428 | "account": [
429 | "manage-account-links"
430 | ]
431 | }
432 | },
433 | "clientRole": true,
434 | "containerId": "1a774c5f-2bf3-4149-9d34-78c5d0279425",
435 | "attributes": {}
436 | }
437 | ]
438 | }
439 | },
440 | "groups": [],
441 | "defaultRole": {
442 | "id": "c838f9d5-a8c3-4b6f-a24c-96e259db9121",
443 | "name": "default-roles-javi",
444 | "description": "${role_default-roles}",
445 | "composite": true,
446 | "clientRole": false,
447 | "containerId": "5b1be921-15e4-4abb-9770-ff34129a5991"
448 | },
449 | "requiredCredentials": [
450 | "password"
451 | ],
452 | "otpPolicyType": "totp",
453 | "otpPolicyAlgorithm": "HmacSHA1",
454 | "otpPolicyInitialCounter": 0,
455 | "otpPolicyDigits": 6,
456 | "otpPolicyLookAheadWindow": 1,
457 | "otpPolicyPeriod": 30,
458 | "otpPolicyCodeReusable": false,
459 | "otpSupportedApplications": [
460 | "totpAppGoogleName",
461 | "totpAppMicrosoftAuthenticatorName",
462 | "totpAppFreeOTPName"
463 | ],
464 | "webAuthnPolicyRpEntityName": "keycloak",
465 | "webAuthnPolicySignatureAlgorithms": [
466 | "ES256"
467 | ],
468 | "webAuthnPolicyRpId": "",
469 | "webAuthnPolicyAttestationConveyancePreference": "not specified",
470 | "webAuthnPolicyAuthenticatorAttachment": "not specified",
471 | "webAuthnPolicyRequireResidentKey": "not specified",
472 | "webAuthnPolicyUserVerificationRequirement": "not specified",
473 | "webAuthnPolicyCreateTimeout": 0,
474 | "webAuthnPolicyAvoidSameAuthenticatorRegister": false,
475 | "webAuthnPolicyAcceptableAaguids": [],
476 | "webAuthnPolicyPasswordlessRpEntityName": "keycloak",
477 | "webAuthnPolicyPasswordlessSignatureAlgorithms": [
478 | "ES256"
479 | ],
480 | "webAuthnPolicyPasswordlessRpId": "",
481 | "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified",
482 | "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified",
483 | "webAuthnPolicyPasswordlessRequireResidentKey": "not specified",
484 | "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified",
485 | "webAuthnPolicyPasswordlessCreateTimeout": 0,
486 | "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false,
487 | "webAuthnPolicyPasswordlessAcceptableAaguids": [],
488 | "users": [
489 | {
490 | "id": "54c7ef1a-d698-4d4d-9cb0-f0db0551a9cb",
491 | "createdTimestamp": 1712166900903,
492 | "username": "service-account-srv-client",
493 | "enabled": true,
494 | "totp": false,
495 | "emailVerified": false,
496 | "serviceAccountClientId": "srv-client",
497 | "disableableCredentialTypes": [],
498 | "requiredActions": [],
499 | "realmRoles": [
500 | "default-roles-javi"
501 | ],
502 | "clientRoles": {
503 | "srv-client": [
504 | "uma_protection"
505 | ]
506 | },
507 | "notBefore": 0,
508 | "groups": []
509 | }
510 | ],
511 | "scopeMappings": [
512 | {
513 | "clientScope": "offline_access",
514 | "roles": [
515 | "offline_access"
516 | ]
517 | }
518 | ],
519 | "clientScopeMappings": {
520 | "account": [
521 | {
522 | "client": "account-console",
523 | "roles": [
524 | "manage-account",
525 | "view-groups"
526 | ]
527 | }
528 | ]
529 | },
530 | "clients": [
531 | {
532 | "id": "1a774c5f-2bf3-4149-9d34-78c5d0279425",
533 | "clientId": "account",
534 | "name": "${client_account}",
535 | "rootUrl": "${authBaseUrl}",
536 | "baseUrl": "/realms/javi/account/",
537 | "surrogateAuthRequired": false,
538 | "enabled": true,
539 | "alwaysDisplayInConsole": false,
540 | "clientAuthenticatorType": "client-secret",
541 | "redirectUris": [
542 | "/realms/javi/account/*"
543 | ],
544 | "webOrigins": [],
545 | "notBefore": 0,
546 | "bearerOnly": false,
547 | "consentRequired": false,
548 | "standardFlowEnabled": true,
549 | "implicitFlowEnabled": false,
550 | "directAccessGrantsEnabled": false,
551 | "serviceAccountsEnabled": false,
552 | "publicClient": true,
553 | "frontchannelLogout": false,
554 | "protocol": "openid-connect",
555 | "attributes": {
556 | "post.logout.redirect.uris": "+"
557 | },
558 | "authenticationFlowBindingOverrides": {},
559 | "fullScopeAllowed": false,
560 | "nodeReRegistrationTimeout": 0,
561 | "defaultClientScopes": [
562 | "web-origins",
563 | "acr",
564 | "profile",
565 | "roles",
566 | "email"
567 | ],
568 | "optionalClientScopes": [
569 | "address",
570 | "phone",
571 | "offline_access",
572 | "microprofile-jwt"
573 | ]
574 | },
575 | {
576 | "id": "1e49b0dd-3cd0-464d-b2f7-2c70d834f3ec",
577 | "clientId": "account-console",
578 | "name": "${client_account-console}",
579 | "rootUrl": "${authBaseUrl}",
580 | "baseUrl": "/realms/javi/account/",
581 | "surrogateAuthRequired": false,
582 | "enabled": true,
583 | "alwaysDisplayInConsole": false,
584 | "clientAuthenticatorType": "client-secret",
585 | "redirectUris": [
586 | "/realms/javi/account/*"
587 | ],
588 | "webOrigins": [],
589 | "notBefore": 0,
590 | "bearerOnly": false,
591 | "consentRequired": false,
592 | "standardFlowEnabled": true,
593 | "implicitFlowEnabled": false,
594 | "directAccessGrantsEnabled": false,
595 | "serviceAccountsEnabled": false,
596 | "publicClient": true,
597 | "frontchannelLogout": false,
598 | "protocol": "openid-connect",
599 | "attributes": {
600 | "post.logout.redirect.uris": "+",
601 | "pkce.code.challenge.method": "S256"
602 | },
603 | "authenticationFlowBindingOverrides": {},
604 | "fullScopeAllowed": false,
605 | "nodeReRegistrationTimeout": 0,
606 | "protocolMappers": [
607 | {
608 | "id": "33be9718-439f-45cb-8f84-f0ed6eb77fe2",
609 | "name": "audience resolve",
610 | "protocol": "openid-connect",
611 | "protocolMapper": "oidc-audience-resolve-mapper",
612 | "consentRequired": false,
613 | "config": {}
614 | }
615 | ],
616 | "defaultClientScopes": [
617 | "web-origins",
618 | "acr",
619 | "profile",
620 | "roles",
621 | "email"
622 | ],
623 | "optionalClientScopes": [
624 | "address",
625 | "phone",
626 | "offline_access",
627 | "microprofile-jwt"
628 | ]
629 | },
630 | {
631 | "id": "00850315-fa66-4503-b018-977c82997f82",
632 | "clientId": "admin-cli",
633 | "name": "${client_admin-cli}",
634 | "surrogateAuthRequired": false,
635 | "enabled": true,
636 | "alwaysDisplayInConsole": false,
637 | "clientAuthenticatorType": "client-secret",
638 | "redirectUris": [],
639 | "webOrigins": [],
640 | "notBefore": 0,
641 | "bearerOnly": false,
642 | "consentRequired": false,
643 | "standardFlowEnabled": false,
644 | "implicitFlowEnabled": false,
645 | "directAccessGrantsEnabled": true,
646 | "serviceAccountsEnabled": false,
647 | "publicClient": true,
648 | "frontchannelLogout": false,
649 | "protocol": "openid-connect",
650 | "attributes": {},
651 | "authenticationFlowBindingOverrides": {},
652 | "fullScopeAllowed": false,
653 | "nodeReRegistrationTimeout": 0,
654 | "defaultClientScopes": [
655 | "web-origins",
656 | "acr",
657 | "profile",
658 | "roles",
659 | "email"
660 | ],
661 | "optionalClientScopes": [
662 | "address",
663 | "phone",
664 | "offline_access",
665 | "microprofile-jwt"
666 | ]
667 | },
668 | {
669 | "id": "57d3ba1e-fc5c-412e-92cc-2a5fcb06e097",
670 | "clientId": "broker",
671 | "name": "${client_broker}",
672 | "surrogateAuthRequired": false,
673 | "enabled": true,
674 | "alwaysDisplayInConsole": false,
675 | "clientAuthenticatorType": "client-secret",
676 | "redirectUris": [],
677 | "webOrigins": [],
678 | "notBefore": 0,
679 | "bearerOnly": true,
680 | "consentRequired": false,
681 | "standardFlowEnabled": true,
682 | "implicitFlowEnabled": false,
683 | "directAccessGrantsEnabled": false,
684 | "serviceAccountsEnabled": false,
685 | "publicClient": false,
686 | "frontchannelLogout": false,
687 | "protocol": "openid-connect",
688 | "attributes": {},
689 | "authenticationFlowBindingOverrides": {},
690 | "fullScopeAllowed": false,
691 | "nodeReRegistrationTimeout": 0,
692 | "defaultClientScopes": [
693 | "web-origins",
694 | "acr",
695 | "profile",
696 | "roles",
697 | "email"
698 | ],
699 | "optionalClientScopes": [
700 | "address",
701 | "phone",
702 | "offline_access",
703 | "microprofile-jwt"
704 | ]
705 | },
706 | {
707 | "id": "ff218c79-85f1-40b0-811f-f75b0984f147",
708 | "clientId": "srv-client",
709 | "name": "",
710 | "description": "",
711 | "rootUrl": "http://localhost:8080/app",
712 | "adminUrl": "http://localhost:8080/app",
713 | "baseUrl": "http://localhost:8080/app",
714 | "surrogateAuthRequired": false,
715 | "enabled": true,
716 | "alwaysDisplayInConsole": false,
717 | "clientAuthenticatorType": "client-secret",
718 | "secret": "**********",
719 | "redirectUris": [
720 | "http://localhost:8080/*"
721 | ],
722 | "webOrigins": [
723 | "*"
724 | ],
725 | "notBefore": 0,
726 | "bearerOnly": false,
727 | "consentRequired": false,
728 | "standardFlowEnabled": true,
729 | "implicitFlowEnabled": false,
730 | "directAccessGrantsEnabled": true,
731 | "serviceAccountsEnabled": true,
732 | "authorizationServicesEnabled": true,
733 | "publicClient": false,
734 | "frontchannelLogout": true,
735 | "protocol": "openid-connect",
736 | "attributes": {
737 | "oidc.ciba.grant.enabled": "false",
738 | "client.secret.creation.time": "1712166900",
739 | "backchannel.logout.session.required": "true",
740 | "post.logout.redirect.uris": "http://localhost:8080/*",
741 | "oauth2.device.authorization.grant.enabled": "false",
742 | "backchannel.logout.revoke.offline.tokens": "false"
743 | },
744 | "authenticationFlowBindingOverrides": {},
745 | "fullScopeAllowed": true,
746 | "nodeReRegistrationTimeout": -1,
747 | "protocolMappers": [
748 | {
749 | "id": "9aabd7b1-1fd0-4c9d-ab3b-67e9f0ed0485",
750 | "name": "Client ID",
751 | "protocol": "openid-connect",
752 | "protocolMapper": "oidc-usersessionmodel-note-mapper",
753 | "consentRequired": false,
754 | "config": {
755 | "user.session.note": "client_id",
756 | "id.token.claim": "true",
757 | "access.token.claim": "true",
758 | "claim.name": "client_id",
759 | "jsonType.label": "String"
760 | }
761 | },
762 | {
763 | "id": "91ad6bbf-b861-4d1a-a847-9576ceebba9e",
764 | "name": "Client IP Address",
765 | "protocol": "openid-connect",
766 | "protocolMapper": "oidc-usersessionmodel-note-mapper",
767 | "consentRequired": false,
768 | "config": {
769 | "user.session.note": "clientAddress",
770 | "id.token.claim": "true",
771 | "access.token.claim": "true",
772 | "claim.name": "clientAddress",
773 | "jsonType.label": "String"
774 | }
775 | },
776 | {
777 | "id": "a65aca89-fd07-4b0c-913b-a63067fefa2f",
778 | "name": "Client Host",
779 | "protocol": "openid-connect",
780 | "protocolMapper": "oidc-usersessionmodel-note-mapper",
781 | "consentRequired": false,
782 | "config": {
783 | "user.session.note": "clientHost",
784 | "id.token.claim": "true",
785 | "access.token.claim": "true",
786 | "claim.name": "clientHost",
787 | "jsonType.label": "String"
788 | }
789 | }
790 | ],
791 | "defaultClientScopes": [
792 | "web-origins",
793 | "acr",
794 | "profile",
795 | "roles",
796 | "email"
797 | ],
798 | "optionalClientScopes": [
799 | "address",
800 | "phone",
801 | "offline_access",
802 | "microprofile-jwt"
803 | ],
804 | "authorizationSettings": {
805 | "allowRemoteResourceManagement": true,
806 | "policyEnforcementMode": "ENFORCING",
807 | "resources": [
808 | {
809 | "name": "Default Resource",
810 | "type": "urn:srv-client:resources:default",
811 | "ownerManagedAccess": false,
812 | "attributes": {},
813 | "_id": "b3045091-de12-4667-a998-0a7459593233",
814 | "uris": [
815 | "/*"
816 | ]
817 | }
818 | ],
819 | "policies": [
820 | {
821 | "id": "412a4fc3-a2e1-4664-ae18-f2ee128cb9ab",
822 | "name": "Default Policy",
823 | "description": "A policy that grants access only for users within this realm",
824 | "type": "js",
825 | "logic": "POSITIVE",
826 | "decisionStrategy": "AFFIRMATIVE",
827 | "config": {
828 | "code": "// by default, grants any permission associated with this policy\n$evaluation.grant();\n"
829 | }
830 | },
831 | {
832 | "id": "7676c773-fa31-436b-859d-6238a7d53245",
833 | "name": "Default Permission",
834 | "description": "A permission that applies to the default resource type",
835 | "type": "resource",
836 | "logic": "POSITIVE",
837 | "decisionStrategy": "UNANIMOUS",
838 | "config": {
839 | "defaultResourceType": "urn:srv-client:resources:default",
840 | "applyPolicies": "[\"Default Policy\"]"
841 | }
842 | }
843 | ],
844 | "scopes": [],
845 | "decisionStrategy": "UNANIMOUS"
846 | }
847 | },
848 | {
849 | "id": "a113625e-fb17-4f4c-b3b5-fbb035cf840a",
850 | "clientId": "realm-management",
851 | "name": "${client_realm-management}",
852 | "surrogateAuthRequired": false,
853 | "enabled": true,
854 | "alwaysDisplayInConsole": false,
855 | "clientAuthenticatorType": "client-secret",
856 | "redirectUris": [],
857 | "webOrigins": [],
858 | "notBefore": 0,
859 | "bearerOnly": true,
860 | "consentRequired": false,
861 | "standardFlowEnabled": true,
862 | "implicitFlowEnabled": false,
863 | "directAccessGrantsEnabled": false,
864 | "serviceAccountsEnabled": false,
865 | "publicClient": false,
866 | "frontchannelLogout": false,
867 | "protocol": "openid-connect",
868 | "attributes": {},
869 | "authenticationFlowBindingOverrides": {},
870 | "fullScopeAllowed": false,
871 | "nodeReRegistrationTimeout": 0,
872 | "defaultClientScopes": [
873 | "web-origins",
874 | "acr",
875 | "profile",
876 | "roles",
877 | "email"
878 | ],
879 | "optionalClientScopes": [
880 | "address",
881 | "phone",
882 | "offline_access",
883 | "microprofile-jwt"
884 | ]
885 | },
886 | {
887 | "id": "0f6a1373-9dba-44cf-b4ab-f300b39f9671",
888 | "clientId": "security-admin-console",
889 | "name": "${client_security-admin-console}",
890 | "rootUrl": "${authAdminUrl}",
891 | "baseUrl": "/admin/javi/console/",
892 | "surrogateAuthRequired": false,
893 | "enabled": true,
894 | "alwaysDisplayInConsole": false,
895 | "clientAuthenticatorType": "client-secret",
896 | "redirectUris": [
897 | "/admin/javi/console/*"
898 | ],
899 | "webOrigins": [
900 | "+"
901 | ],
902 | "notBefore": 0,
903 | "bearerOnly": false,
904 | "consentRequired": false,
905 | "standardFlowEnabled": true,
906 | "implicitFlowEnabled": false,
907 | "directAccessGrantsEnabled": false,
908 | "serviceAccountsEnabled": false,
909 | "publicClient": true,
910 | "frontchannelLogout": false,
911 | "protocol": "openid-connect",
912 | "attributes": {
913 | "post.logout.redirect.uris": "+",
914 | "pkce.code.challenge.method": "S256"
915 | },
916 | "authenticationFlowBindingOverrides": {},
917 | "fullScopeAllowed": false,
918 | "nodeReRegistrationTimeout": 0,
919 | "protocolMappers": [
920 | {
921 | "id": "c64ccd34-78da-459c-bc55-0710b043223d",
922 | "name": "locale",
923 | "protocol": "openid-connect",
924 | "protocolMapper": "oidc-usermodel-attribute-mapper",
925 | "consentRequired": false,
926 | "config": {
927 | "userinfo.token.claim": "true",
928 | "user.attribute": "locale",
929 | "id.token.claim": "true",
930 | "access.token.claim": "true",
931 | "claim.name": "locale",
932 | "jsonType.label": "String"
933 | }
934 | }
935 | ],
936 | "defaultClientScopes": [
937 | "web-origins",
938 | "acr",
939 | "profile",
940 | "roles",
941 | "email"
942 | ],
943 | "optionalClientScopes": [
944 | "address",
945 | "phone",
946 | "offline_access",
947 | "microprofile-jwt"
948 | ]
949 | }
950 | ],
951 | "clientScopes": [
952 | {
953 | "id": "48c28ce8-961b-4210-9a48-d40736718cf5",
954 | "name": "microprofile-jwt",
955 | "description": "Microprofile - JWT built-in scope",
956 | "protocol": "openid-connect",
957 | "attributes": {
958 | "include.in.token.scope": "true",
959 | "display.on.consent.screen": "false"
960 | },
961 | "protocolMappers": [
962 | {
963 | "id": "b6d580a8-5b1d-411d-b411-72f65351c502",
964 | "name": "groups",
965 | "protocol": "openid-connect",
966 | "protocolMapper": "oidc-usermodel-realm-role-mapper",
967 | "consentRequired": false,
968 | "config": {
969 | "multivalued": "true",
970 | "user.attribute": "foo",
971 | "id.token.claim": "true",
972 | "access.token.claim": "true",
973 | "claim.name": "groups",
974 | "jsonType.label": "String"
975 | }
976 | },
977 | {
978 | "id": "de2cf414-be68-4306-97bd-eadbb0e59c8a",
979 | "name": "upn",
980 | "protocol": "openid-connect",
981 | "protocolMapper": "oidc-usermodel-property-mapper",
982 | "consentRequired": false,
983 | "config": {
984 | "userinfo.token.claim": "true",
985 | "user.attribute": "username",
986 | "id.token.claim": "true",
987 | "access.token.claim": "true",
988 | "claim.name": "upn",
989 | "jsonType.label": "String"
990 | }
991 | }
992 | ]
993 | },
994 | {
995 | "id": "00d98e33-cd70-4e44-a67b-b91a4401a909",
996 | "name": "acr",
997 | "description": "OpenID Connect scope for add acr (authentication context class reference) to the token",
998 | "protocol": "openid-connect",
999 | "attributes": {
1000 | "include.in.token.scope": "false",
1001 | "display.on.consent.screen": "false"
1002 | },
1003 | "protocolMappers": [
1004 | {
1005 | "id": "3f788998-7d11-4ba0-a28d-277378d7616a",
1006 | "name": "acr loa level",
1007 | "protocol": "openid-connect",
1008 | "protocolMapper": "oidc-acr-mapper",
1009 | "consentRequired": false,
1010 | "config": {
1011 | "id.token.claim": "true",
1012 | "access.token.claim": "true"
1013 | }
1014 | }
1015 | ]
1016 | },
1017 | {
1018 | "id": "538e3c5f-6fb4-4acf-8cc8-08848c5f249d",
1019 | "name": "web-origins",
1020 | "description": "OpenID Connect scope for add allowed web origins to the access token",
1021 | "protocol": "openid-connect",
1022 | "attributes": {
1023 | "include.in.token.scope": "false",
1024 | "display.on.consent.screen": "false",
1025 | "consent.screen.text": ""
1026 | },
1027 | "protocolMappers": [
1028 | {
1029 | "id": "6983c933-993e-436b-bf0e-11aadd789dfd",
1030 | "name": "allowed web origins",
1031 | "protocol": "openid-connect",
1032 | "protocolMapper": "oidc-allowed-origins-mapper",
1033 | "consentRequired": false,
1034 | "config": {}
1035 | }
1036 | ]
1037 | },
1038 | {
1039 | "id": "ce192ab5-b21f-4214-9d16-8915b88a1e75",
1040 | "name": "profile",
1041 | "description": "OpenID Connect built-in scope: profile",
1042 | "protocol": "openid-connect",
1043 | "attributes": {
1044 | "include.in.token.scope": "true",
1045 | "display.on.consent.screen": "true",
1046 | "consent.screen.text": "${profileScopeConsentText}"
1047 | },
1048 | "protocolMappers": [
1049 | {
1050 | "id": "a6b199e7-66a9-48ca-b689-6ae1a3ecdce8",
1051 | "name": "locale",
1052 | "protocol": "openid-connect",
1053 | "protocolMapper": "oidc-usermodel-attribute-mapper",
1054 | "consentRequired": false,
1055 | "config": {
1056 | "userinfo.token.claim": "true",
1057 | "user.attribute": "locale",
1058 | "id.token.claim": "true",
1059 | "access.token.claim": "true",
1060 | "claim.name": "locale",
1061 | "jsonType.label": "String"
1062 | }
1063 | },
1064 | {
1065 | "id": "1b4ed381-33ff-41cb-8a5c-c079ad72cf18",
1066 | "name": "profile",
1067 | "protocol": "openid-connect",
1068 | "protocolMapper": "oidc-usermodel-attribute-mapper",
1069 | "consentRequired": false,
1070 | "config": {
1071 | "userinfo.token.claim": "true",
1072 | "user.attribute": "profile",
1073 | "id.token.claim": "true",
1074 | "access.token.claim": "true",
1075 | "claim.name": "profile",
1076 | "jsonType.label": "String"
1077 | }
1078 | },
1079 | {
1080 | "id": "226022a7-12ee-4dfb-a01c-ba8dd5681534",
1081 | "name": "middle name",
1082 | "protocol": "openid-connect",
1083 | "protocolMapper": "oidc-usermodel-attribute-mapper",
1084 | "consentRequired": false,
1085 | "config": {
1086 | "userinfo.token.claim": "true",
1087 | "user.attribute": "middleName",
1088 | "id.token.claim": "true",
1089 | "access.token.claim": "true",
1090 | "claim.name": "middle_name",
1091 | "jsonType.label": "String"
1092 | }
1093 | },
1094 | {
1095 | "id": "d431227d-6ab3-482c-994e-176629203f6a",
1096 | "name": "updated at",
1097 | "protocol": "openid-connect",
1098 | "protocolMapper": "oidc-usermodel-attribute-mapper",
1099 | "consentRequired": false,
1100 | "config": {
1101 | "userinfo.token.claim": "true",
1102 | "user.attribute": "updatedAt",
1103 | "id.token.claim": "true",
1104 | "access.token.claim": "true",
1105 | "claim.name": "updated_at",
1106 | "jsonType.label": "long"
1107 | }
1108 | },
1109 | {
1110 | "id": "33b5e400-0acb-4e6a-ad90-aaeee91fbaae",
1111 | "name": "nickname",
1112 | "protocol": "openid-connect",
1113 | "protocolMapper": "oidc-usermodel-attribute-mapper",
1114 | "consentRequired": false,
1115 | "config": {
1116 | "userinfo.token.claim": "true",
1117 | "user.attribute": "nickname",
1118 | "id.token.claim": "true",
1119 | "access.token.claim": "true",
1120 | "claim.name": "nickname",
1121 | "jsonType.label": "String"
1122 | }
1123 | },
1124 | {
1125 | "id": "1dfddfcd-00bb-4605-99fa-9c13d5482978",
1126 | "name": "picture",
1127 | "protocol": "openid-connect",
1128 | "protocolMapper": "oidc-usermodel-attribute-mapper",
1129 | "consentRequired": false,
1130 | "config": {
1131 | "userinfo.token.claim": "true",
1132 | "user.attribute": "picture",
1133 | "id.token.claim": "true",
1134 | "access.token.claim": "true",
1135 | "claim.name": "picture",
1136 | "jsonType.label": "String"
1137 | }
1138 | },
1139 | {
1140 | "id": "5aac5a7e-22bb-47c9-a819-c584f8c37a0e",
1141 | "name": "birthdate",
1142 | "protocol": "openid-connect",
1143 | "protocolMapper": "oidc-usermodel-attribute-mapper",
1144 | "consentRequired": false,
1145 | "config": {
1146 | "userinfo.token.claim": "true",
1147 | "user.attribute": "birthdate",
1148 | "id.token.claim": "true",
1149 | "access.token.claim": "true",
1150 | "claim.name": "birthdate",
1151 | "jsonType.label": "String"
1152 | }
1153 | },
1154 | {
1155 | "id": "5eb920da-da7f-4b62-817a-c4a268a0cb9e",
1156 | "name": "family name",
1157 | "protocol": "openid-connect",
1158 | "protocolMapper": "oidc-usermodel-property-mapper",
1159 | "consentRequired": false,
1160 | "config": {
1161 | "userinfo.token.claim": "true",
1162 | "user.attribute": "lastName",
1163 | "id.token.claim": "true",
1164 | "access.token.claim": "true",
1165 | "claim.name": "family_name",
1166 | "jsonType.label": "String"
1167 | }
1168 | },
1169 | {
1170 | "id": "7a91ee27-dcda-42fb-82f6-ae9543dcc6f2",
1171 | "name": "given name",
1172 | "protocol": "openid-connect",
1173 | "protocolMapper": "oidc-usermodel-property-mapper",
1174 | "consentRequired": false,
1175 | "config": {
1176 | "userinfo.token.claim": "true",
1177 | "user.attribute": "firstName",
1178 | "id.token.claim": "true",
1179 | "access.token.claim": "true",
1180 | "claim.name": "given_name",
1181 | "jsonType.label": "String"
1182 | }
1183 | },
1184 | {
1185 | "id": "8a0405fc-7eb7-42e6-941d-d4f87582fc44",
1186 | "name": "username",
1187 | "protocol": "openid-connect",
1188 | "protocolMapper": "oidc-usermodel-property-mapper",
1189 | "consentRequired": false,
1190 | "config": {
1191 | "userinfo.token.claim": "true",
1192 | "user.attribute": "username",
1193 | "id.token.claim": "true",
1194 | "access.token.claim": "true",
1195 | "claim.name": "preferred_username",
1196 | "jsonType.label": "String"
1197 | }
1198 | },
1199 | {
1200 | "id": "368b184c-953e-444e-9de2-22a4a66cddfb",
1201 | "name": "zoneinfo",
1202 | "protocol": "openid-connect",
1203 | "protocolMapper": "oidc-usermodel-attribute-mapper",
1204 | "consentRequired": false,
1205 | "config": {
1206 | "userinfo.token.claim": "true",
1207 | "user.attribute": "zoneinfo",
1208 | "id.token.claim": "true",
1209 | "access.token.claim": "true",
1210 | "claim.name": "zoneinfo",
1211 | "jsonType.label": "String"
1212 | }
1213 | },
1214 | {
1215 | "id": "27cd63a0-2da7-49a1-87ff-ac5ad9773432",
1216 | "name": "website",
1217 | "protocol": "openid-connect",
1218 | "protocolMapper": "oidc-usermodel-attribute-mapper",
1219 | "consentRequired": false,
1220 | "config": {
1221 | "userinfo.token.claim": "true",
1222 | "user.attribute": "website",
1223 | "id.token.claim": "true",
1224 | "access.token.claim": "true",
1225 | "claim.name": "website",
1226 | "jsonType.label": "String"
1227 | }
1228 | },
1229 | {
1230 | "id": "df94f437-70c4-481c-a825-0ad78ef38100",
1231 | "name": "full name",
1232 | "protocol": "openid-connect",
1233 | "protocolMapper": "oidc-full-name-mapper",
1234 | "consentRequired": false,
1235 | "config": {
1236 | "id.token.claim": "true",
1237 | "access.token.claim": "true",
1238 | "userinfo.token.claim": "true"
1239 | }
1240 | },
1241 | {
1242 | "id": "41744cb0-c9bc-418f-a3f3-8b18bbae456f",
1243 | "name": "gender",
1244 | "protocol": "openid-connect",
1245 | "protocolMapper": "oidc-usermodel-attribute-mapper",
1246 | "consentRequired": false,
1247 | "config": {
1248 | "userinfo.token.claim": "true",
1249 | "user.attribute": "gender",
1250 | "id.token.claim": "true",
1251 | "access.token.claim": "true",
1252 | "claim.name": "gender",
1253 | "jsonType.label": "String"
1254 | }
1255 | }
1256 | ]
1257 | },
1258 | {
1259 | "id": "6619024c-88d9-40cc-b823-f7b32c06b679",
1260 | "name": "address",
1261 | "description": "OpenID Connect built-in scope: address",
1262 | "protocol": "openid-connect",
1263 | "attributes": {
1264 | "include.in.token.scope": "true",
1265 | "display.on.consent.screen": "true",
1266 | "consent.screen.text": "${addressScopeConsentText}"
1267 | },
1268 | "protocolMappers": [
1269 | {
1270 | "id": "432d5a4d-aef5-4671-b065-17ecdd7f631c",
1271 | "name": "address",
1272 | "protocol": "openid-connect",
1273 | "protocolMapper": "oidc-address-mapper",
1274 | "consentRequired": false,
1275 | "config": {
1276 | "user.attribute.formatted": "formatted",
1277 | "user.attribute.country": "country",
1278 | "user.attribute.postal_code": "postal_code",
1279 | "userinfo.token.claim": "true",
1280 | "user.attribute.street": "street",
1281 | "id.token.claim": "true",
1282 | "user.attribute.region": "region",
1283 | "access.token.claim": "true",
1284 | "user.attribute.locality": "locality"
1285 | }
1286 | }
1287 | ]
1288 | },
1289 | {
1290 | "id": "e4d2ab19-b43b-424b-917c-bc0f4042ea42",
1291 | "name": "role_list",
1292 | "description": "SAML role list",
1293 | "protocol": "saml",
1294 | "attributes": {
1295 | "consent.screen.text": "${samlRoleListScopeConsentText}",
1296 | "display.on.consent.screen": "true"
1297 | },
1298 | "protocolMappers": [
1299 | {
1300 | "id": "10852512-97f1-4f21-914f-395f5ff3bcce",
1301 | "name": "role list",
1302 | "protocol": "saml",
1303 | "protocolMapper": "saml-role-list-mapper",
1304 | "consentRequired": false,
1305 | "config": {
1306 | "single": "false",
1307 | "attribute.nameformat": "Basic",
1308 | "attribute.name": "Role"
1309 | }
1310 | }
1311 | ]
1312 | },
1313 | {
1314 | "id": "bf4b2970-ad3a-4ec3-9cd7-8826547d7f6d",
1315 | "name": "email",
1316 | "description": "OpenID Connect built-in scope: email",
1317 | "protocol": "openid-connect",
1318 | "attributes": {
1319 | "include.in.token.scope": "true",
1320 | "display.on.consent.screen": "true",
1321 | "consent.screen.text": "${emailScopeConsentText}"
1322 | },
1323 | "protocolMappers": [
1324 | {
1325 | "id": "88087ccb-faa0-484e-8857-f88a013b7967",
1326 | "name": "email",
1327 | "protocol": "openid-connect",
1328 | "protocolMapper": "oidc-usermodel-property-mapper",
1329 | "consentRequired": false,
1330 | "config": {
1331 | "userinfo.token.claim": "true",
1332 | "user.attribute": "email",
1333 | "id.token.claim": "true",
1334 | "access.token.claim": "true",
1335 | "claim.name": "email",
1336 | "jsonType.label": "String"
1337 | }
1338 | },
1339 | {
1340 | "id": "d5e7f8dd-e4c8-4781-b5dd-66c0163b4500",
1341 | "name": "email verified",
1342 | "protocol": "openid-connect",
1343 | "protocolMapper": "oidc-usermodel-property-mapper",
1344 | "consentRequired": false,
1345 | "config": {
1346 | "userinfo.token.claim": "true",
1347 | "user.attribute": "emailVerified",
1348 | "id.token.claim": "true",
1349 | "access.token.claim": "true",
1350 | "claim.name": "email_verified",
1351 | "jsonType.label": "boolean"
1352 | }
1353 | }
1354 | ]
1355 | },
1356 | {
1357 | "id": "cc897694-1d33-464b-b4fc-6ca42d790b71",
1358 | "name": "phone",
1359 | "description": "OpenID Connect built-in scope: phone",
1360 | "protocol": "openid-connect",
1361 | "attributes": {
1362 | "include.in.token.scope": "true",
1363 | "display.on.consent.screen": "true",
1364 | "consent.screen.text": "${phoneScopeConsentText}"
1365 | },
1366 | "protocolMappers": [
1367 | {
1368 | "id": "4c705c45-7ab8-42d7-8ee2-c737c729a83b",
1369 | "name": "phone number",
1370 | "protocol": "openid-connect",
1371 | "protocolMapper": "oidc-usermodel-attribute-mapper",
1372 | "consentRequired": false,
1373 | "config": {
1374 | "userinfo.token.claim": "true",
1375 | "user.attribute": "phoneNumber",
1376 | "id.token.claim": "true",
1377 | "access.token.claim": "true",
1378 | "claim.name": "phone_number",
1379 | "jsonType.label": "String"
1380 | }
1381 | },
1382 | {
1383 | "id": "4a16a7ac-c15a-414b-b415-99c6612ea513",
1384 | "name": "phone number verified",
1385 | "protocol": "openid-connect",
1386 | "protocolMapper": "oidc-usermodel-attribute-mapper",
1387 | "consentRequired": false,
1388 | "config": {
1389 | "userinfo.token.claim": "true",
1390 | "user.attribute": "phoneNumberVerified",
1391 | "id.token.claim": "true",
1392 | "access.token.claim": "true",
1393 | "claim.name": "phone_number_verified",
1394 | "jsonType.label": "boolean"
1395 | }
1396 | }
1397 | ]
1398 | },
1399 | {
1400 | "id": "2506cd1f-5376-4a53-9956-43215518f70e",
1401 | "name": "offline_access",
1402 | "description": "OpenID Connect built-in scope: offline_access",
1403 | "protocol": "openid-connect",
1404 | "attributes": {
1405 | "consent.screen.text": "${offlineAccessScopeConsentText}",
1406 | "display.on.consent.screen": "true"
1407 | }
1408 | },
1409 | {
1410 | "id": "a2a1eafa-357a-4f7c-9fee-eb886b989592",
1411 | "name": "roles",
1412 | "description": "OpenID Connect scope for add user roles to the access token",
1413 | "protocol": "openid-connect",
1414 | "attributes": {
1415 | "include.in.token.scope": "false",
1416 | "display.on.consent.screen": "true",
1417 | "consent.screen.text": "${rolesScopeConsentText}"
1418 | },
1419 | "protocolMappers": [
1420 | {
1421 | "id": "83fb5bf8-ecb3-4e19-8de2-2a252fd24c42",
1422 | "name": "audience resolve",
1423 | "protocol": "openid-connect",
1424 | "protocolMapper": "oidc-audience-resolve-mapper",
1425 | "consentRequired": false,
1426 | "config": {}
1427 | },
1428 | {
1429 | "id": "9ca3e197-4228-478f-a387-40da3d074f50",
1430 | "name": "realm roles",
1431 | "protocol": "openid-connect",
1432 | "protocolMapper": "oidc-usermodel-realm-role-mapper",
1433 | "consentRequired": false,
1434 | "config": {
1435 | "user.attribute": "foo",
1436 | "access.token.claim": "true",
1437 | "claim.name": "realm_access.roles",
1438 | "jsonType.label": "String",
1439 | "multivalued": "true"
1440 | }
1441 | },
1442 | {
1443 | "id": "1d5dd0d3-fb1f-4a31-ab6f-a42838900704",
1444 | "name": "client roles",
1445 | "protocol": "openid-connect",
1446 | "protocolMapper": "oidc-usermodel-client-role-mapper",
1447 | "consentRequired": false,
1448 | "config": {
1449 | "user.attribute": "foo",
1450 | "access.token.claim": "true",
1451 | "claim.name": "resource_access.${client_id}.roles",
1452 | "jsonType.label": "String",
1453 | "multivalued": "true"
1454 | }
1455 | }
1456 | ]
1457 | }
1458 | ],
1459 | "defaultDefaultClientScopes": [
1460 | "role_list",
1461 | "profile",
1462 | "email",
1463 | "roles",
1464 | "web-origins",
1465 | "acr"
1466 | ],
1467 | "defaultOptionalClientScopes": [
1468 | "offline_access",
1469 | "address",
1470 | "phone",
1471 | "microprofile-jwt"
1472 | ],
1473 | "browserSecurityHeaders": {
1474 | "contentSecurityPolicyReportOnly": "",
1475 | "xContentTypeOptions": "nosniff",
1476 | "xRobotsTag": "none",
1477 | "xFrameOptions": "SAMEORIGIN",
1478 | "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';",
1479 | "xXSSProtection": "1; mode=block",
1480 | "strictTransportSecurity": "max-age=31536000; includeSubDomains"
1481 | },
1482 | "smtpServer": {},
1483 | "eventsEnabled": false,
1484 | "eventsListeners": [
1485 | "jboss-logging"
1486 | ],
1487 | "enabledEventTypes": [],
1488 | "adminEventsEnabled": false,
1489 | "adminEventsDetailsEnabled": false,
1490 | "identityProviders": [],
1491 | "identityProviderMappers": [],
1492 | "components": {
1493 | "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [
1494 | {
1495 | "id": "fd24473a-a8bf-491d-84be-44653e8b662b",
1496 | "name": "Allowed Client Scopes",
1497 | "providerId": "allowed-client-templates",
1498 | "subType": "anonymous",
1499 | "subComponents": {},
1500 | "config": {
1501 | "allow-default-scopes": [
1502 | "true"
1503 | ]
1504 | }
1505 | },
1506 | {
1507 | "id": "a9ebe93a-c285-4fd2-92a4-d71d5ad7802b",
1508 | "name": "Max Clients Limit",
1509 | "providerId": "max-clients",
1510 | "subType": "anonymous",
1511 | "subComponents": {},
1512 | "config": {
1513 | "max-clients": [
1514 | "200"
1515 | ]
1516 | }
1517 | },
1518 | {
1519 | "id": "7557c775-512d-44ee-942c-b7d2d80f4c46",
1520 | "name": "Consent Required",
1521 | "providerId": "consent-required",
1522 | "subType": "anonymous",
1523 | "subComponents": {},
1524 | "config": {}
1525 | },
1526 | {
1527 | "id": "2c74c2d3-2981-4906-8da0-ecffe8d7bae8",
1528 | "name": "Allowed Protocol Mapper Types",
1529 | "providerId": "allowed-protocol-mappers",
1530 | "subType": "anonymous",
1531 | "subComponents": {},
1532 | "config": {
1533 | "allowed-protocol-mapper-types": [
1534 | "oidc-address-mapper",
1535 | "oidc-usermodel-attribute-mapper",
1536 | "oidc-full-name-mapper",
1537 | "saml-user-property-mapper",
1538 | "oidc-usermodel-property-mapper",
1539 | "saml-role-list-mapper",
1540 | "saml-user-attribute-mapper",
1541 | "oidc-sha256-pairwise-sub-mapper"
1542 | ]
1543 | }
1544 | },
1545 | {
1546 | "id": "46a36b5e-3978-4bb6-8dfc-d260e7437ad8",
1547 | "name": "Full Scope Disabled",
1548 | "providerId": "scope",
1549 | "subType": "anonymous",
1550 | "subComponents": {},
1551 | "config": {}
1552 | },
1553 | {
1554 | "id": "5327a1b3-cfcd-467c-ba64-fd2f656a431d",
1555 | "name": "Allowed Protocol Mapper Types",
1556 | "providerId": "allowed-protocol-mappers",
1557 | "subType": "authenticated",
1558 | "subComponents": {},
1559 | "config": {
1560 | "allowed-protocol-mapper-types": [
1561 | "oidc-usermodel-attribute-mapper",
1562 | "saml-user-property-mapper",
1563 | "oidc-sha256-pairwise-sub-mapper",
1564 | "oidc-full-name-mapper",
1565 | "oidc-address-mapper",
1566 | "oidc-usermodel-property-mapper",
1567 | "saml-user-attribute-mapper",
1568 | "saml-role-list-mapper"
1569 | ]
1570 | }
1571 | },
1572 | {
1573 | "id": "33b5aa2d-48c4-4bfa-8230-1f2612d64260",
1574 | "name": "Trusted Hosts",
1575 | "providerId": "trusted-hosts",
1576 | "subType": "anonymous",
1577 | "subComponents": {},
1578 | "config": {
1579 | "host-sending-registration-request-must-match": [
1580 | "true"
1581 | ],
1582 | "client-uris-must-match": [
1583 | "true"
1584 | ]
1585 | }
1586 | },
1587 | {
1588 | "id": "89adcfef-c031-4fdb-9dc4-420a1a3c095e",
1589 | "name": "Allowed Client Scopes",
1590 | "providerId": "allowed-client-templates",
1591 | "subType": "authenticated",
1592 | "subComponents": {},
1593 | "config": {
1594 | "allow-default-scopes": [
1595 | "true"
1596 | ]
1597 | }
1598 | }
1599 | ],
1600 | "org.keycloak.keys.KeyProvider": [
1601 | {
1602 | "id": "79f5abc5-da7b-46c5-a933-cf8873363cc0",
1603 | "name": "rsa-enc-generated",
1604 | "providerId": "rsa-enc-generated",
1605 | "subComponents": {},
1606 | "config": {
1607 | "priority": [
1608 | "100"
1609 | ],
1610 | "algorithm": [
1611 | "RSA-OAEP"
1612 | ]
1613 | }
1614 | },
1615 | {
1616 | "id": "2798d0f4-980f-49ea-a194-415082a77c59",
1617 | "name": "aes-generated",
1618 | "providerId": "aes-generated",
1619 | "subComponents": {},
1620 | "config": {
1621 | "priority": [
1622 | "100"
1623 | ]
1624 | }
1625 | },
1626 | {
1627 | "id": "0f23de01-fbfa-40c4-850a-ebbf60ba7aa9",
1628 | "name": "rsa-generated",
1629 | "providerId": "rsa-generated",
1630 | "subComponents": {},
1631 | "config": {
1632 | "priority": [
1633 | "100"
1634 | ]
1635 | }
1636 | },
1637 | {
1638 | "id": "6e5061b3-51cb-4317-9697-f221e27ca24a",
1639 | "name": "hmac-generated",
1640 | "providerId": "hmac-generated",
1641 | "subComponents": {},
1642 | "config": {
1643 | "priority": [
1644 | "100"
1645 | ],
1646 | "algorithm": [
1647 | "HS256"
1648 | ]
1649 | }
1650 | }
1651 | ]
1652 | },
1653 | "internationalizationEnabled": false,
1654 | "supportedLocales": [],
1655 | "authenticationFlows": [
1656 | {
1657 | "id": "1f68e228-75ab-4d43-b708-f79dcafd85f4",
1658 | "alias": "Account verification options",
1659 | "description": "Method with which to verity the existing account",
1660 | "providerId": "basic-flow",
1661 | "topLevel": false,
1662 | "builtIn": true,
1663 | "authenticationExecutions": [
1664 | {
1665 | "authenticator": "idp-email-verification",
1666 | "authenticatorFlow": false,
1667 | "requirement": "ALTERNATIVE",
1668 | "priority": 10,
1669 | "autheticatorFlow": false,
1670 | "userSetupAllowed": false
1671 | },
1672 | {
1673 | "authenticatorFlow": true,
1674 | "requirement": "ALTERNATIVE",
1675 | "priority": 20,
1676 | "autheticatorFlow": true,
1677 | "flowAlias": "Verify Existing Account by Re-authentication",
1678 | "userSetupAllowed": false
1679 | }
1680 | ]
1681 | },
1682 | {
1683 | "id": "345f34ce-08fd-4d3a-a547-d077be5a79f3",
1684 | "alias": "Authentication Options",
1685 | "description": "Authentication options.",
1686 | "providerId": "basic-flow",
1687 | "topLevel": false,
1688 | "builtIn": true,
1689 | "authenticationExecutions": [
1690 | {
1691 | "authenticator": "basic-auth",
1692 | "authenticatorFlow": false,
1693 | "requirement": "REQUIRED",
1694 | "priority": 10,
1695 | "autheticatorFlow": false,
1696 | "userSetupAllowed": false
1697 | },
1698 | {
1699 | "authenticator": "basic-auth-otp",
1700 | "authenticatorFlow": false,
1701 | "requirement": "DISABLED",
1702 | "priority": 20,
1703 | "autheticatorFlow": false,
1704 | "userSetupAllowed": false
1705 | },
1706 | {
1707 | "authenticator": "auth-spnego",
1708 | "authenticatorFlow": false,
1709 | "requirement": "DISABLED",
1710 | "priority": 30,
1711 | "autheticatorFlow": false,
1712 | "userSetupAllowed": false
1713 | }
1714 | ]
1715 | },
1716 | {
1717 | "id": "b0b19532-7a70-41ed-a661-415c6a5d2d57",
1718 | "alias": "Browser - Conditional OTP",
1719 | "description": "Flow to determine if the OTP is required for the authentication",
1720 | "providerId": "basic-flow",
1721 | "topLevel": false,
1722 | "builtIn": true,
1723 | "authenticationExecutions": [
1724 | {
1725 | "authenticator": "conditional-user-configured",
1726 | "authenticatorFlow": false,
1727 | "requirement": "REQUIRED",
1728 | "priority": 10,
1729 | "autheticatorFlow": false,
1730 | "userSetupAllowed": false
1731 | },
1732 | {
1733 | "authenticator": "auth-otp-form",
1734 | "authenticatorFlow": false,
1735 | "requirement": "REQUIRED",
1736 | "priority": 20,
1737 | "autheticatorFlow": false,
1738 | "userSetupAllowed": false
1739 | }
1740 | ]
1741 | },
1742 | {
1743 | "id": "3372899b-1523-4f7d-9ab3-e75fb794bca1",
1744 | "alias": "Direct Grant - Conditional OTP",
1745 | "description": "Flow to determine if the OTP is required for the authentication",
1746 | "providerId": "basic-flow",
1747 | "topLevel": false,
1748 | "builtIn": true,
1749 | "authenticationExecutions": [
1750 | {
1751 | "authenticator": "conditional-user-configured",
1752 | "authenticatorFlow": false,
1753 | "requirement": "REQUIRED",
1754 | "priority": 10,
1755 | "autheticatorFlow": false,
1756 | "userSetupAllowed": false
1757 | },
1758 | {
1759 | "authenticator": "direct-grant-validate-otp",
1760 | "authenticatorFlow": false,
1761 | "requirement": "REQUIRED",
1762 | "priority": 20,
1763 | "autheticatorFlow": false,
1764 | "userSetupAllowed": false
1765 | }
1766 | ]
1767 | },
1768 | {
1769 | "id": "7c094924-4ca8-4c0b-ad51-00ff602c50b5",
1770 | "alias": "First broker login - Conditional OTP",
1771 | "description": "Flow to determine if the OTP is required for the authentication",
1772 | "providerId": "basic-flow",
1773 | "topLevel": false,
1774 | "builtIn": true,
1775 | "authenticationExecutions": [
1776 | {
1777 | "authenticator": "conditional-user-configured",
1778 | "authenticatorFlow": false,
1779 | "requirement": "REQUIRED",
1780 | "priority": 10,
1781 | "autheticatorFlow": false,
1782 | "userSetupAllowed": false
1783 | },
1784 | {
1785 | "authenticator": "auth-otp-form",
1786 | "authenticatorFlow": false,
1787 | "requirement": "REQUIRED",
1788 | "priority": 20,
1789 | "autheticatorFlow": false,
1790 | "userSetupAllowed": false
1791 | }
1792 | ]
1793 | },
1794 | {
1795 | "id": "50cf3e25-4049-4c02-a24a-cc958f93812b",
1796 | "alias": "Handle Existing Account",
1797 | "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider",
1798 | "providerId": "basic-flow",
1799 | "topLevel": false,
1800 | "builtIn": true,
1801 | "authenticationExecutions": [
1802 | {
1803 | "authenticator": "idp-confirm-link",
1804 | "authenticatorFlow": false,
1805 | "requirement": "REQUIRED",
1806 | "priority": 10,
1807 | "autheticatorFlow": false,
1808 | "userSetupAllowed": false
1809 | },
1810 | {
1811 | "authenticatorFlow": true,
1812 | "requirement": "REQUIRED",
1813 | "priority": 20,
1814 | "autheticatorFlow": true,
1815 | "flowAlias": "Account verification options",
1816 | "userSetupAllowed": false
1817 | }
1818 | ]
1819 | },
1820 | {
1821 | "id": "061a239d-db17-41b0-9c71-19cbc5255047",
1822 | "alias": "Reset - Conditional OTP",
1823 | "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.",
1824 | "providerId": "basic-flow",
1825 | "topLevel": false,
1826 | "builtIn": true,
1827 | "authenticationExecutions": [
1828 | {
1829 | "authenticator": "conditional-user-configured",
1830 | "authenticatorFlow": false,
1831 | "requirement": "REQUIRED",
1832 | "priority": 10,
1833 | "autheticatorFlow": false,
1834 | "userSetupAllowed": false
1835 | },
1836 | {
1837 | "authenticator": "reset-otp",
1838 | "authenticatorFlow": false,
1839 | "requirement": "REQUIRED",
1840 | "priority": 20,
1841 | "autheticatorFlow": false,
1842 | "userSetupAllowed": false
1843 | }
1844 | ]
1845 | },
1846 | {
1847 | "id": "46f07489-bdca-46cc-b624-218b706db60e",
1848 | "alias": "User creation or linking",
1849 | "description": "Flow for the existing/non-existing user alternatives",
1850 | "providerId": "basic-flow",
1851 | "topLevel": false,
1852 | "builtIn": true,
1853 | "authenticationExecutions": [
1854 | {
1855 | "authenticatorConfig": "create unique user config",
1856 | "authenticator": "idp-create-user-if-unique",
1857 | "authenticatorFlow": false,
1858 | "requirement": "ALTERNATIVE",
1859 | "priority": 10,
1860 | "autheticatorFlow": false,
1861 | "userSetupAllowed": false
1862 | },
1863 | {
1864 | "authenticatorFlow": true,
1865 | "requirement": "ALTERNATIVE",
1866 | "priority": 20,
1867 | "autheticatorFlow": true,
1868 | "flowAlias": "Handle Existing Account",
1869 | "userSetupAllowed": false
1870 | }
1871 | ]
1872 | },
1873 | {
1874 | "id": "2eda9796-f4eb-4963-9f65-b02f3a1743ec",
1875 | "alias": "Verify Existing Account by Re-authentication",
1876 | "description": "Reauthentication of existing account",
1877 | "providerId": "basic-flow",
1878 | "topLevel": false,
1879 | "builtIn": true,
1880 | "authenticationExecutions": [
1881 | {
1882 | "authenticator": "idp-username-password-form",
1883 | "authenticatorFlow": false,
1884 | "requirement": "REQUIRED",
1885 | "priority": 10,
1886 | "autheticatorFlow": false,
1887 | "userSetupAllowed": false
1888 | },
1889 | {
1890 | "authenticatorFlow": true,
1891 | "requirement": "CONDITIONAL",
1892 | "priority": 20,
1893 | "autheticatorFlow": true,
1894 | "flowAlias": "First broker login - Conditional OTP",
1895 | "userSetupAllowed": false
1896 | }
1897 | ]
1898 | },
1899 | {
1900 | "id": "e3e47cdb-e6e7-4a0d-b3f4-54b1a192679c",
1901 | "alias": "browser",
1902 | "description": "browser based authentication",
1903 | "providerId": "basic-flow",
1904 | "topLevel": true,
1905 | "builtIn": true,
1906 | "authenticationExecutions": [
1907 | {
1908 | "authenticator": "auth-cookie",
1909 | "authenticatorFlow": false,
1910 | "requirement": "ALTERNATIVE",
1911 | "priority": 10,
1912 | "autheticatorFlow": false,
1913 | "userSetupAllowed": false
1914 | },
1915 | {
1916 | "authenticator": "auth-spnego",
1917 | "authenticatorFlow": false,
1918 | "requirement": "DISABLED",
1919 | "priority": 20,
1920 | "autheticatorFlow": false,
1921 | "userSetupAllowed": false
1922 | },
1923 | {
1924 | "authenticator": "identity-provider-redirector",
1925 | "authenticatorFlow": false,
1926 | "requirement": "ALTERNATIVE",
1927 | "priority": 25,
1928 | "autheticatorFlow": false,
1929 | "userSetupAllowed": false
1930 | },
1931 | {
1932 | "authenticatorFlow": true,
1933 | "requirement": "ALTERNATIVE",
1934 | "priority": 30,
1935 | "autheticatorFlow": true,
1936 | "flowAlias": "forms",
1937 | "userSetupAllowed": false
1938 | }
1939 | ]
1940 | },
1941 | {
1942 | "id": "a06e4990-f3e2-4e6c-a0d6-81ddf5d9a40e",
1943 | "alias": "clients",
1944 | "description": "Base authentication for clients",
1945 | "providerId": "client-flow",
1946 | "topLevel": true,
1947 | "builtIn": true,
1948 | "authenticationExecutions": [
1949 | {
1950 | "authenticator": "client-secret",
1951 | "authenticatorFlow": false,
1952 | "requirement": "ALTERNATIVE",
1953 | "priority": 10,
1954 | "autheticatorFlow": false,
1955 | "userSetupAllowed": false
1956 | },
1957 | {
1958 | "authenticator": "client-jwt",
1959 | "authenticatorFlow": false,
1960 | "requirement": "ALTERNATIVE",
1961 | "priority": 20,
1962 | "autheticatorFlow": false,
1963 | "userSetupAllowed": false
1964 | },
1965 | {
1966 | "authenticator": "client-secret-jwt",
1967 | "authenticatorFlow": false,
1968 | "requirement": "ALTERNATIVE",
1969 | "priority": 30,
1970 | "autheticatorFlow": false,
1971 | "userSetupAllowed": false
1972 | },
1973 | {
1974 | "authenticator": "client-x509",
1975 | "authenticatorFlow": false,
1976 | "requirement": "ALTERNATIVE",
1977 | "priority": 40,
1978 | "autheticatorFlow": false,
1979 | "userSetupAllowed": false
1980 | }
1981 | ]
1982 | },
1983 | {
1984 | "id": "083641b6-72a2-495c-8eb8-efd88b41a4de",
1985 | "alias": "direct grant",
1986 | "description": "OpenID Connect Resource Owner Grant",
1987 | "providerId": "basic-flow",
1988 | "topLevel": true,
1989 | "builtIn": true,
1990 | "authenticationExecutions": [
1991 | {
1992 | "authenticator": "direct-grant-validate-username",
1993 | "authenticatorFlow": false,
1994 | "requirement": "REQUIRED",
1995 | "priority": 10,
1996 | "autheticatorFlow": false,
1997 | "userSetupAllowed": false
1998 | },
1999 | {
2000 | "authenticator": "direct-grant-validate-password",
2001 | "authenticatorFlow": false,
2002 | "requirement": "REQUIRED",
2003 | "priority": 20,
2004 | "autheticatorFlow": false,
2005 | "userSetupAllowed": false
2006 | },
2007 | {
2008 | "authenticatorFlow": true,
2009 | "requirement": "CONDITIONAL",
2010 | "priority": 30,
2011 | "autheticatorFlow": true,
2012 | "flowAlias": "Direct Grant - Conditional OTP",
2013 | "userSetupAllowed": false
2014 | }
2015 | ]
2016 | },
2017 | {
2018 | "id": "e16fbff2-02b3-4975-9e19-4739b990f052",
2019 | "alias": "docker auth",
2020 | "description": "Used by Docker clients to authenticate against the IDP",
2021 | "providerId": "basic-flow",
2022 | "topLevel": true,
2023 | "builtIn": true,
2024 | "authenticationExecutions": [
2025 | {
2026 | "authenticator": "docker-http-basic-authenticator",
2027 | "authenticatorFlow": false,
2028 | "requirement": "REQUIRED",
2029 | "priority": 10,
2030 | "autheticatorFlow": false,
2031 | "userSetupAllowed": false
2032 | }
2033 | ]
2034 | },
2035 | {
2036 | "id": "cf89ccfd-19df-4f17-b487-7747bc49d95d",
2037 | "alias": "first broker login",
2038 | "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
2039 | "providerId": "basic-flow",
2040 | "topLevel": true,
2041 | "builtIn": true,
2042 | "authenticationExecutions": [
2043 | {
2044 | "authenticatorConfig": "review profile config",
2045 | "authenticator": "idp-review-profile",
2046 | "authenticatorFlow": false,
2047 | "requirement": "REQUIRED",
2048 | "priority": 10,
2049 | "autheticatorFlow": false,
2050 | "userSetupAllowed": false
2051 | },
2052 | {
2053 | "authenticatorFlow": true,
2054 | "requirement": "REQUIRED",
2055 | "priority": 20,
2056 | "autheticatorFlow": true,
2057 | "flowAlias": "User creation or linking",
2058 | "userSetupAllowed": false
2059 | }
2060 | ]
2061 | },
2062 | {
2063 | "id": "780296a3-679e-4ffe-982b-f173e9bf528e",
2064 | "alias": "forms",
2065 | "description": "Username, password, otp and other auth forms.",
2066 | "providerId": "basic-flow",
2067 | "topLevel": false,
2068 | "builtIn": true,
2069 | "authenticationExecutions": [
2070 | {
2071 | "authenticator": "auth-username-password-form",
2072 | "authenticatorFlow": false,
2073 | "requirement": "REQUIRED",
2074 | "priority": 10,
2075 | "autheticatorFlow": false,
2076 | "userSetupAllowed": false
2077 | },
2078 | {
2079 | "authenticatorFlow": true,
2080 | "requirement": "CONDITIONAL",
2081 | "priority": 20,
2082 | "autheticatorFlow": true,
2083 | "flowAlias": "Browser - Conditional OTP",
2084 | "userSetupAllowed": false
2085 | }
2086 | ]
2087 | },
2088 | {
2089 | "id": "8e771671-6a15-41bf-90c9-8749bfcd7a0a",
2090 | "alias": "http challenge",
2091 | "description": "An authentication flow based on challenge-response HTTP Authentication Schemes",
2092 | "providerId": "basic-flow",
2093 | "topLevel": true,
2094 | "builtIn": true,
2095 | "authenticationExecutions": [
2096 | {
2097 | "authenticator": "no-cookie-redirect",
2098 | "authenticatorFlow": false,
2099 | "requirement": "REQUIRED",
2100 | "priority": 10,
2101 | "autheticatorFlow": false,
2102 | "userSetupAllowed": false
2103 | },
2104 | {
2105 | "authenticatorFlow": true,
2106 | "requirement": "REQUIRED",
2107 | "priority": 20,
2108 | "autheticatorFlow": true,
2109 | "flowAlias": "Authentication Options",
2110 | "userSetupAllowed": false
2111 | }
2112 | ]
2113 | },
2114 | {
2115 | "id": "c9ee220d-8472-48b8-aed7-178d93f5ad11",
2116 | "alias": "registration",
2117 | "description": "registration flow",
2118 | "providerId": "basic-flow",
2119 | "topLevel": true,
2120 | "builtIn": true,
2121 | "authenticationExecutions": [
2122 | {
2123 | "authenticator": "registration-page-form",
2124 | "authenticatorFlow": true,
2125 | "requirement": "REQUIRED",
2126 | "priority": 10,
2127 | "autheticatorFlow": true,
2128 | "flowAlias": "registration form",
2129 | "userSetupAllowed": false
2130 | }
2131 | ]
2132 | },
2133 | {
2134 | "id": "73567936-4962-4c13-bb89-4aa5cb506ea9",
2135 | "alias": "registration form",
2136 | "description": "registration form",
2137 | "providerId": "form-flow",
2138 | "topLevel": false,
2139 | "builtIn": true,
2140 | "authenticationExecutions": [
2141 | {
2142 | "authenticator": "registration-user-creation",
2143 | "authenticatorFlow": false,
2144 | "requirement": "REQUIRED",
2145 | "priority": 20,
2146 | "autheticatorFlow": false,
2147 | "userSetupAllowed": false
2148 | },
2149 | {
2150 | "authenticator": "registration-profile-action",
2151 | "authenticatorFlow": false,
2152 | "requirement": "REQUIRED",
2153 | "priority": 40,
2154 | "autheticatorFlow": false,
2155 | "userSetupAllowed": false
2156 | },
2157 | {
2158 | "authenticator": "registration-password-action",
2159 | "authenticatorFlow": false,
2160 | "requirement": "REQUIRED",
2161 | "priority": 50,
2162 | "autheticatorFlow": false,
2163 | "userSetupAllowed": false
2164 | },
2165 | {
2166 | "authenticator": "registration-recaptcha-action",
2167 | "authenticatorFlow": false,
2168 | "requirement": "DISABLED",
2169 | "priority": 60,
2170 | "autheticatorFlow": false,
2171 | "userSetupAllowed": false
2172 | }
2173 | ]
2174 | },
2175 | {
2176 | "id": "c861408c-8071-4e70-b9c0-19c9c12e5f9e",
2177 | "alias": "reset credentials",
2178 | "description": "Reset credentials for a user if they forgot their password or something",
2179 | "providerId": "basic-flow",
2180 | "topLevel": true,
2181 | "builtIn": true,
2182 | "authenticationExecutions": [
2183 | {
2184 | "authenticator": "reset-credentials-choose-user",
2185 | "authenticatorFlow": false,
2186 | "requirement": "REQUIRED",
2187 | "priority": 10,
2188 | "autheticatorFlow": false,
2189 | "userSetupAllowed": false
2190 | },
2191 | {
2192 | "authenticator": "reset-credential-email",
2193 | "authenticatorFlow": false,
2194 | "requirement": "REQUIRED",
2195 | "priority": 20,
2196 | "autheticatorFlow": false,
2197 | "userSetupAllowed": false
2198 | },
2199 | {
2200 | "authenticator": "reset-password",
2201 | "authenticatorFlow": false,
2202 | "requirement": "REQUIRED",
2203 | "priority": 30,
2204 | "autheticatorFlow": false,
2205 | "userSetupAllowed": false
2206 | },
2207 | {
2208 | "authenticatorFlow": true,
2209 | "requirement": "CONDITIONAL",
2210 | "priority": 40,
2211 | "autheticatorFlow": true,
2212 | "flowAlias": "Reset - Conditional OTP",
2213 | "userSetupAllowed": false
2214 | }
2215 | ]
2216 | },
2217 | {
2218 | "id": "67e24d75-bb36-4643-90e7-8feae070ae94",
2219 | "alias": "saml ecp",
2220 | "description": "SAML ECP Profile Authentication Flow",
2221 | "providerId": "basic-flow",
2222 | "topLevel": true,
2223 | "builtIn": true,
2224 | "authenticationExecutions": [
2225 | {
2226 | "authenticator": "http-basic-authenticator",
2227 | "authenticatorFlow": false,
2228 | "requirement": "REQUIRED",
2229 | "priority": 10,
2230 | "autheticatorFlow": false,
2231 | "userSetupAllowed": false
2232 | }
2233 | ]
2234 | }
2235 | ],
2236 | "authenticatorConfig": [
2237 | {
2238 | "id": "9adcfad7-6c41-40d4-8950-ee30f76b92ce",
2239 | "alias": "create unique user config",
2240 | "config": {
2241 | "require.password.update.after.registration": "false"
2242 | }
2243 | },
2244 | {
2245 | "id": "983ba940-2695-4b52-81fc-13826eeddad1",
2246 | "alias": "review profile config",
2247 | "config": {
2248 | "update.profile.on.first.login": "missing"
2249 | }
2250 | }
2251 | ],
2252 | "requiredActions": [
2253 | {
2254 | "alias": "CONFIGURE_TOTP",
2255 | "name": "Configure OTP",
2256 | "providerId": "CONFIGURE_TOTP",
2257 | "enabled": true,
2258 | "defaultAction": false,
2259 | "priority": 10,
2260 | "config": {}
2261 | },
2262 | {
2263 | "alias": "TERMS_AND_CONDITIONS",
2264 | "name": "Terms and Conditions",
2265 | "providerId": "TERMS_AND_CONDITIONS",
2266 | "enabled": false,
2267 | "defaultAction": false,
2268 | "priority": 20,
2269 | "config": {}
2270 | },
2271 | {
2272 | "alias": "UPDATE_PASSWORD",
2273 | "name": "Update Password",
2274 | "providerId": "UPDATE_PASSWORD",
2275 | "enabled": true,
2276 | "defaultAction": false,
2277 | "priority": 30,
2278 | "config": {}
2279 | },
2280 | {
2281 | "alias": "UPDATE_PROFILE",
2282 | "name": "Update Profile",
2283 | "providerId": "UPDATE_PROFILE",
2284 | "enabled": true,
2285 | "defaultAction": false,
2286 | "priority": 40,
2287 | "config": {}
2288 | },
2289 | {
2290 | "alias": "VERIFY_EMAIL",
2291 | "name": "Verify Email",
2292 | "providerId": "VERIFY_EMAIL",
2293 | "enabled": true,
2294 | "defaultAction": false,
2295 | "priority": 50,
2296 | "config": {}
2297 | },
2298 | {
2299 | "alias": "delete_account",
2300 | "name": "Delete Account",
2301 | "providerId": "delete_account",
2302 | "enabled": false,
2303 | "defaultAction": false,
2304 | "priority": 60,
2305 | "config": {}
2306 | },
2307 | {
2308 | "alias": "webauthn-register",
2309 | "name": "Webauthn Register",
2310 | "providerId": "webauthn-register",
2311 | "enabled": true,
2312 | "defaultAction": false,
2313 | "priority": 70,
2314 | "config": {}
2315 | },
2316 | {
2317 | "alias": "webauthn-register-passwordless",
2318 | "name": "Webauthn Register Passwordless",
2319 | "providerId": "webauthn-register-passwordless",
2320 | "enabled": true,
2321 | "defaultAction": false,
2322 | "priority": 80,
2323 | "config": {}
2324 | },
2325 | {
2326 | "alias": "update_user_locale",
2327 | "name": "Update User Locale",
2328 | "providerId": "update_user_locale",
2329 | "enabled": true,
2330 | "defaultAction": false,
2331 | "priority": 1000,
2332 | "config": {}
2333 | }
2334 | ],
2335 | "browserFlow": "browser",
2336 | "registrationFlow": "registration",
2337 | "directGrantFlow": "direct grant",
2338 | "resetCredentialsFlow": "reset credentials",
2339 | "clientAuthenticationFlow": "clients",
2340 | "dockerAuthenticationFlow": "docker auth",
2341 | "attributes": {
2342 | "cibaBackchannelTokenDeliveryMode": "poll",
2343 | "cibaExpiresIn": "120",
2344 | "cibaAuthRequestedUserHint": "login_hint",
2345 | "oauth2DeviceCodeLifespan": "600",
2346 | "oauth2DevicePollingInterval": "5",
2347 | "parRequestUriLifespan": "60",
2348 | "cibaInterval": "5",
2349 | "realmReusableOtpCode": "false"
2350 | },
2351 | "keycloakVersion": "21.1.1",
2352 | "userManagedAccessAllowed": false,
2353 | "clientProfiles": {
2354 | "profiles": []
2355 | },
2356 | "clientPolicies": {
2357 | "policies": []
2358 | }
2359 | }
2360 |
--------------------------------------------------------------------------------
/docker/postgres/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | db:
3 | image: postgres
4 | container_name: db_postgres
5 | restart: always
6 | environment:
7 | POSTGRES_USER: admin
8 | POSTGRES_PASSWORD: admin
9 | POSTGRES_DB: db_dummy
10 | ports:
11 | - "5432:5432"
12 | # volumes:
13 | # - ./init.sql:/docker-entrypoint-initdb.d/init.sql
14 |
15 |
--------------------------------------------------------------------------------
/docker/postgres/init.sql:
--------------------------------------------------------------------------------
1 | create table dummies (
2 | id integer primary key,
3 | info varchar(255)
4 | );
5 |
--------------------------------------------------------------------------------
/k8s/java-deployment-svc.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: java-spring3-microservice
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: java-spring3-microservice
10 | template:
11 | metadata:
12 | labels:
13 | app: java-spring3-microservice
14 | spec:
15 | containers:
16 | - name: java-spring3-microservice
17 | image: java-spring3-microservice:0.1.0
18 | imagePullPolicy: IfNotPresent
19 | ports:
20 | - containerPort: 8080
21 | env:
22 | - name: DB_HOST
23 | value: postgresql
24 | - name: DB_NAME
25 | value: db_dummy
26 | - name: DB_USER
27 | value: admin
28 | - name: DB_PASSWORD
29 | value: admin
30 | ---
31 | apiVersion: v1
32 | kind: Service
33 | metadata:
34 | name: java-spring3-microservice-svc
35 | spec:
36 | selector:
37 | app: java-spring3-microservice
38 | ports:
39 | - protocol: TCP
40 | port: 8080
41 | targetPort: 8080
42 | type: NodePort
43 |
44 |
--------------------------------------------------------------------------------
/k8s/postgres-deployment-svc.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: StatefulSet
3 | metadata:
4 | name: postgresql
5 | spec:
6 | serviceName: postgresql
7 | replicas: 1
8 | selector:
9 | matchLabels:
10 | app: postgresql
11 | template:
12 | metadata:
13 | labels:
14 | app: postgresql
15 | spec:
16 | containers:
17 | - name: postgresql
18 | image: postgres:latest
19 | env:
20 | - name: POSTGRES_DB
21 | value: db_dummy
22 | - name: POSTGRES_USER
23 | value: admin
24 | - name: POSTGRES_PASSWORD
25 | value: admin
26 | ports:
27 | - containerPort: 5432
28 | name: postgresql
29 |
30 | ---
31 | apiVersion: v1
32 | kind: Service
33 | metadata:
34 | name: postgresql
35 | spec:
36 | selector:
37 | app: postgresql
38 | ports:
39 | - protocol: TCP
40 | port: 5432
41 | targetPort: 5432
42 |
43 | ---
44 | apiVersion: v1
45 | kind: PersistentVolumeClaim
46 | metadata:
47 | name: postgresql-pvc
48 | spec:
49 | accessModes:
50 | - ReadWriteOnce
51 | resources:
52 | requests:
53 | storage: 1Gi
54 |
55 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 3.2.8
9 |
10 |
11 |
12 | com.javi
13 | microservice
14 | 0.0.1
15 | jar
16 | java-spring3-microservice
17 | Java archetype oriented to Microservices
18 |
19 |
20 | 21
21 | 0.9.28
22 | 0.0.1
23 |
24 |
25 |
26 |
27 |
28 | com.javi
29 | microservice-lib
30 | ${microservice-lib.version}
31 |
32 |
33 |
34 |
35 | org.projectlombok
36 | lombok
37 |
38 |
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-web
43 |
44 |
45 | org.springframework.boot
46 | spring-boot-starter-data-jpa
47 |
48 |
49 | org.springframework.boot
50 | spring-boot-starter-security
51 |
52 |
53 | org.springframework.boot
54 | spring-boot-starter-actuator
55 |
56 |
57 | org.springframework.boot
58 | spring-boot-devtools
59 | runtime
60 | true
61 |
62 |
63 |
64 |
65 | org.postgresql
66 | postgresql
67 | runtime
68 |
69 |
70 |
71 |
72 | org.springframework.boot
73 | spring-boot-starter-test
74 | test
75 |
76 |
77 | org.junit.jupiter
78 | junit-jupiter-engine
79 | test
80 |
81 |
82 | org.mockito
83 | mockito-junit-jupiter
84 | test
85 |
86 |
87 | com.h2database
88 | h2
89 | test
90 |
91 |
92 |
93 |
94 |
95 |
96 | dev
97 |
98 | dev
99 |
100 |
101 |
102 |
103 |
104 | ${project.artifactId}
105 |
106 |
107 | org.springframework.boot
108 | spring-boot-maven-plugin
109 |
110 |
111 | org.graalvm.buildtools
112 | graalvm-native-maven-plugin
113 | ${graalvm.version}
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/ships/get.ship:
--------------------------------------------------------------------------------
1 |
2 | ~[BASE]~
3 | url http://localhost:8080/app/dummy
4 | method GET
5 |
6 | ~[HEADERS]~
7 | accept application/json
8 | authorization Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJJVktibk93SjNPUlYyVDloWGlqVl9oVXJfX3Y4ZEVIdkxyalgzXzhiZ29ZIn0.eyJleHAiOjE3MjUwNTIyMTUsImlhdCI6MTcyNTA1MTkxNSwianRpIjoiZTg3YjYwMDAtZTk2OC00NmVkLThmMGYtN2I3OGFjMzc2NTc5IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgxL3JlYWxtcy9qYXZpIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjZkZmQyY2M1LWVmMWItNDhmZS05Y2I0LWZhMDNmZjRiNTg4NyIsInR5cCI6IkJlYXJlciIsImF6cCI6InNydi1jbGllbnQiLCJzZXNzaW9uX3N0YXRlIjoiNDdkYmU5YzMtZDY1OS00MzJjLWFiZTktNmExODZmZGRjNDc3IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIqIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsImRlZmF1bHQtcm9sZXMtb3Jmb3N5cyIsInVtYV9hdXRob3JpemF0aW9uIiwiQURNSU4iXX0sInJlc291cmNlX2FjY2VzcyI6eyJzcnYtY2xpZW50Ijp7InJvbGVzIjpbIkNMSUVOVF9BRE1JTiJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2lkIjoiNDdkYmU5YzMtZDY1OS00MzJjLWFiZTktNmExODZmZGRjNDc3IiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJqYXZpIiwiZ2l2ZW5fbmFtZSI6IiIsImZhbWlseV9uYW1lIjoiIn0.P2rOEwr-l0gi4jN7BDZqsP_jW5xCPNn2lx-afWdEJoljG81RK-YwVjiAaCiNDroXMZLwQG8Ko-9UrXavWQUr9RDhMXlCQtH97rqpkBEoxq8mxPHt5y3m8DIxxt4iVbRZmM0i8Iz3C0RMxrA_fP6_0K4_sZO-gMaNkfOKKW07hhBSPCG_cI5hBEinmmwjvCd8H8fDDUbfbedih7bj7l1h8cWrzy3OeJe5rTp4Qqri9ckmkD4sGHXJeBIKTTDuc5zyqq_plY9R_JW7HLarWzgWff-Ul5Er2cIUTvTK2Da0-3Z88ip5qndYCCvDbQ4Nklkr1ziJY8hDGR4UF4qEwzuQ7Q
9 |
--------------------------------------------------------------------------------
/ships/get_paged.ship:
--------------------------------------------------------------------------------
1 |
2 | ~[BASE]~
3 | url http://localhost:8080/app/dummy?size=4&sortBy=id&sortOrder=desc
4 | method GET
5 |
6 | ~[HEADERS]~
7 | accept application/json
8 | authorization Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJJVktibk93SjNPUlYyVDloWGlqVl9oVXJfX3Y4ZEVIdkxyalgzXzhiZ29ZIn0.eyJleHAiOjE3MTIyNzQzOTIsImlhdCI6MTcxMjI3NDA5MiwianRpIjoiNGUzOWQwODItMjJjZS00YjE2LTk1MWUtMjZkYjUxNmY4MzVjIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgxL3JlYWxtcy9vcmZvc3lzIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjZkZmQyY2M1LWVmMWItNDhmZS05Y2I0LWZhMDNmZjRiNTg4NyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImphdmEtc3ByaW5nMy1rZXljbG9hayIsInNlc3Npb25fc3RhdGUiOiIxNDgyYzQ4Ni00OGMwLTQyZTgtOWUxOC0zYWQ4ZDU2Yjk2ZDIiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbIioiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwiZGVmYXVsdC1yb2xlcy1vcmZvc3lzIiwidW1hX2F1dGhvcml6YXRpb24iLCJBRE1JTiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImphdmEtc3ByaW5nMy1rZXljbG9hayI6eyJyb2xlcyI6WyJDTElFTlRfQURNSU4iXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsInNpZCI6IjE0ODJjNDg2LTQ4YzAtNDJlOC05ZTE4LTNhZDhkNTZiOTZkMiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiamF2aSIsImdpdmVuX25hbWUiOiIiLCJmYW1pbHlfbmFtZSI6IiJ9.q5sg2pBZ9_CVeZ57frz2f4Dx1W5poKPEDGQ9qduHHDv8poTehdvd84CW1i0mXph36i7JriQYmASIUzcDg7jdv__-dPXASLOfw3vL_EDQqpxaWTFv1eBjBZpGWvI3J8RkN3ns4kQprO0cbNvKr-bIHRgL-EX78E5r6Prn3EcrihI27SRm2e2tur9qILg5Cm9QHqWbOMXOP4iy1ZQ_AZ855Dyg0TRKwMDTf6mOo4f8xjhvS3hGr1PoihPMXrzo_f4olx3AsQE9MSkIOZPwkk5zNPYyq5K4J6ZzYqyvxRFr6lQ-mJeabcZV16AALZIcs9BfqadiGDPG_Io3qeq4p2b1pQ
9 |
--------------------------------------------------------------------------------
/ships/keycloak_token.ship:
--------------------------------------------------------------------------------
1 |
2 | ~[BASE]~
3 | url http://localhost:8081/realms/javi/protocol/openid-connect/token
4 | method POST
5 |
6 | ~[HEADERS]~
7 | accept application/json
8 | content-type application/x-www-form-urlencoded
9 |
10 | ~[BODY]~
11 | grant_type password
12 | client_id srv-client
13 | client_secret RqaTlO0d2OnBbeRuImNnbLWm5yZL66Mo
14 | username javi
15 | password javi
16 |
--------------------------------------------------------------------------------
/ships/post.ship:
--------------------------------------------------------------------------------
1 |
2 | ~[BASE]~
3 | url http://localhost:8080/app/dummy
4 | method POST
5 |
6 | ~[HEADERS]~
7 | accept application/json
8 | content-type application/json
9 | authorization Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJJVktibk93SjNPUlYyVDloWGlqVl9oVXJfX3Y4ZEVIdkxyalgzXzhiZ29ZIn0.eyJleHAiOjE3MTU3MTI4MDYsImlhdCI6MTcxNTcxMjUwNiwianRpIjoiODhjYjdjZDMtNjM5MC00ODc5LTg1YWItYzU2OTc3NjBmNTJiIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgxL3JlYWxtcy9vcmZvc3lzIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjZkZmQyY2M1LWVmMWItNDhmZS05Y2I0LWZhMDNmZjRiNTg4NyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImphdmEtc3ByaW5nMy1taWNyb3NlcnZpY2UiLCJzZXNzaW9uX3N0YXRlIjoiYzM1NGE1ZGMtNDI5Mi00MDg3LTk2YzAtOTc4OTQ2Mzk3M2E5IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIqIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsImRlZmF1bHQtcm9sZXMtb3Jmb3N5cyIsInVtYV9hdXRob3JpemF0aW9uIiwiQURNSU4iXX0sInJlc291cmNlX2FjY2VzcyI6eyJqYXZhLXNwcmluZzMtbWljcm9zZXJ2aWNlIjp7InJvbGVzIjpbIkNMSUVOVF9BRE1JTiJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2lkIjoiYzM1NGE1ZGMtNDI5Mi00MDg3LTk2YzAtOTc4OTQ2Mzk3M2E5IiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJqYXZpIiwiZ2l2ZW5fbmFtZSI6IiIsImZhbWlseV9uYW1lIjoiIn0.rzco5VSkouB1yvBp_kxA1C_KC7aMc_u9aV6ic1Hyocoimt93K28yDpzuI_LQxtWzgkm_eIel2N1iPf1cPPC_1fQnhqxS6pOTE2GT13a8T1Qmo6PkBYrOVTb02C2S1CnoR9nHj6UtKmbPShyiCZxNspQMzbcu_pQEJcxI7JmtccWpwRaDbiInzOZOvZrt9aEHeIBB-JI0Bs9hHyeSKU5KfjWfb0W41uFs6LfuimnVoJbkILAW0sa5EEB-i-lDSRxvanbdaIv0AJTleD7CeGfsvrmqZxZiZxHxc6F5MExPotysiHQI7Fp_cK8t-hZGr6E-auW4gh9OvzIic4ZOptKzcw
10 |
11 | ~[BODY]~
12 | {
13 | "info": "golang"
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/Application.java:
--------------------------------------------------------------------------------
1 | package com.javi;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class Application {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(Application.class, args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/adapter/in/DummyController.java:
--------------------------------------------------------------------------------
1 | package com.javi.adapter.in;
2 |
3 | import com.javi.annotation.SwaggerSecurity;
4 | import com.javi.annotation.WebAdapter;
5 | import com.javi.application.in.DummyUseCase;
6 | import com.javi.application.in.request.DummyRequest;
7 | import com.javi.application.in.response.DummyResponse;
8 | import com.javi.domain.model.Dummy;
9 | import com.javi.pagination.Page;
10 | import com.javi.response.RestResponsePagination;
11 |
12 | import jakarta.validation.Valid;
13 | import lombok.AllArgsConstructor;
14 |
15 | import org.springframework.web.bind.annotation.GetMapping;
16 | import org.springframework.web.bind.annotation.PathVariable;
17 | import org.springframework.web.bind.annotation.PostMapping;
18 | import org.springframework.web.bind.annotation.RequestBody;
19 | import org.springframework.web.bind.annotation.RequestMapping;
20 | import org.springframework.web.bind.annotation.RequestParam;
21 | import org.springframework.web.bind.annotation.RestController;
22 | import org.springframework.http.ResponseEntity;
23 | import org.springframework.security.access.prepost.PreAuthorize;
24 |
25 | @WebAdapter
26 | @RestController
27 | @AllArgsConstructor
28 | @RequestMapping(value = "/dummy")
29 | @SwaggerSecurity
30 | public class DummyController {
31 |
32 | private final DummyUseCase dummyUseCase;
33 |
34 | @GetMapping("/{id}")
35 | @PreAuthorize("hasRole('CLIENT_ADMIN')")
36 | public ResponseEntity find(@PathVariable("id") int id) {
37 | return ResponseEntity.ok(new DummyResponse(dummyUseCase.findById(id)));
38 | }
39 |
40 | @GetMapping
41 | @PreAuthorize("hasRole('CLIENT_ADMIN')")
42 | public ResponseEntity> findAll(
43 | @RequestParam(defaultValue = "0") int page,
44 | @RequestParam(defaultValue = "10") int size,
45 | @RequestParam(defaultValue = "id") String sortBy,
46 | @RequestParam(defaultValue = "asc") String sortOrder) {
47 | var pageObj = new Page(page, size, sortBy, sortOrder);
48 | var pageElements = dummyUseCase.findAll(pageObj);
49 | var response = new RestResponsePagination<>(pageElements.pagination(), pageElements.results());
50 | return ResponseEntity.ok(response);
51 | }
52 |
53 | @PostMapping
54 | @PreAuthorize("hasRole('CLIENT_ADMIN')")
55 | public ResponseEntity save(@Valid @RequestBody DummyRequest request) {
56 | var dummy = dummyUseCase.save(new Dummy(request.info()));
57 | return ResponseEntity.ok(new DummyResponse(dummy));
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/adapter/out/DummyPersistenceAdapter.java:
--------------------------------------------------------------------------------
1 | package com.javi.adapter.out;
2 |
3 | import java.util.Optional;
4 |
5 | import com.javi.application.out.DummyPersistence;
6 | import com.javi.domain.model.Dummy;
7 | import com.javi.pagination.Page;
8 | import com.javi.pagination.Paginator;
9 | import com.javi.adapter.out.repositories.DummyEntityRepository;
10 | import com.javi.annotation.PersistenceAdapter;
11 | import com.javi.adapter.out.mappers.DummyMapper;
12 |
13 | import lombok.AllArgsConstructor;
14 |
15 | @PersistenceAdapter
16 | @AllArgsConstructor
17 | public class DummyPersistenceAdapter implements DummyPersistence {
18 |
19 | private final DummyEntityRepository dummyEntityRepository;
20 |
21 | @Override
22 | public Optional findById(int id) {
23 | return dummyEntityRepository
24 | .findById(id)
25 | .map(DummyMapper::entityToDomain);
26 | }
27 |
28 | @Override
29 | public Dummy save(Dummy dummy) {
30 | var dummyEntity = DummyMapper.domainToEntity(dummy);
31 | dummy = DummyMapper.entityToDomain(dummyEntityRepository.save(dummyEntity));
32 | return dummy;
33 | }
34 |
35 | @Override
36 | public Paginator.Pair findAll(Page page) {
37 | return Paginator.create(dummyEntityRepository.findAll(page.getPageable()), DummyMapper::entityToDomain);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/adapter/out/entities/DummyEntity.java:
--------------------------------------------------------------------------------
1 | package com.javi.adapter.out.entities;
2 |
3 |
4 | import com.javi.auditory.model.Auditable;
5 |
6 | import jakarta.persistence.Column;
7 | import jakarta.persistence.Entity;
8 | import jakarta.persistence.GeneratedValue;
9 | import jakarta.persistence.GenerationType;
10 | import jakarta.persistence.Id;
11 | import lombok.AllArgsConstructor;
12 | import lombok.Getter;
13 | import lombok.NoArgsConstructor;
14 |
15 | @AllArgsConstructor
16 | @NoArgsConstructor
17 | @Getter
18 | @Entity(name = "dummies")
19 | public class DummyEntity extends Auditable {
20 |
21 | @Id
22 | @GeneratedValue(strategy = GenerationType.IDENTITY)
23 | private int id;
24 |
25 | @Column(nullable = false)
26 | private String info;
27 |
28 | public DummyEntity(String info) {
29 | this.info = info;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/adapter/out/mappers/DummyMapper.java:
--------------------------------------------------------------------------------
1 | package com.javi.adapter.out.mappers;
2 |
3 | import com.javi.adapter.out.entities.DummyEntity;
4 | import com.javi.domain.model.Dummy;
5 |
6 | public class DummyMapper {
7 |
8 | public static Dummy entityToDomain(DummyEntity dummyEntity) {
9 | if (dummyEntity != null) {
10 | return new Dummy(dummyEntity.getInfo());
11 | }
12 | throw new IllegalStateException("DummyEntity is null");
13 | }
14 |
15 | public static DummyEntity domainToEntity(Dummy dummy) {
16 | return dummy != null ? new DummyEntity(dummy.info()) : null;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/adapter/out/repositories/DummyEntityRepository.java:
--------------------------------------------------------------------------------
1 | package com.javi.adapter.out.repositories;
2 |
3 | import org.springframework.data.jpa.repository.JpaRepository;
4 |
5 | import com.javi.adapter.out.entities.DummyEntity;
6 |
7 | public interface DummyEntityRepository extends JpaRepository {
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/application/in/DummyUseCase.java:
--------------------------------------------------------------------------------
1 | package com.javi.application.in;
2 |
3 | import com.javi.domain.model.Dummy;
4 |
5 | public interface DummyUseCase extends FindByIdUseCase, FindAllUseCase, SaveUseCase {
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/application/in/request/DummyRequest.java:
--------------------------------------------------------------------------------
1 | package com.javi.application.in.request;
2 |
3 | import jakarta.validation.constraints.NotBlank;
4 |
5 | public record DummyRequest(@NotBlank(message = "Info is mandatory") String info) {}
6 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/application/in/response/DummyResponse.java:
--------------------------------------------------------------------------------
1 | package com.javi.application.in.response;
2 |
3 | import com.javi.domain.model.Dummy;
4 |
5 | public record DummyResponse(Dummy dummy) {
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/application/out/DummyPersistence.java:
--------------------------------------------------------------------------------
1 | package com.javi.application.out;
2 |
3 | import com.javi.domain.model.Dummy;
4 |
5 | public interface DummyPersistence extends FindByIdPersistence, FindAllPersistence, SavePersistence {
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/common/configuration/BeanConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.javi.common.configuration;
2 |
3 | import org.springframework.context.annotation.ComponentScan;
4 | import org.springframework.context.annotation.Configuration;
5 |
6 | @Configuration
7 | @ComponentScan("com.javi")
8 | public class BeanConfiguration {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/common/exception/NotFoundException.java:
--------------------------------------------------------------------------------
1 | package com.javi.common.exception;
2 |
3 | import org.springframework.http.HttpStatus;
4 |
5 | import com.javi.exception.BackEndException;
6 |
7 | public class NotFoundException extends BackEndException {
8 |
9 | public NotFoundException() {
10 | super(HttpStatus.NOT_FOUND, "NOT_FOUND", "Entity not found!");
11 | }
12 |
13 | public NotFoundException(HttpStatus httpStatus, String code, String description) {
14 | super(httpStatus, code, description);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/domain/model/Dummy.java:
--------------------------------------------------------------------------------
1 | package com.javi.domain.model;
2 |
3 | import java.util.Objects;
4 |
5 | public record Dummy(String info) {
6 | public Dummy {
7 | info = Objects.requireNonNull(info);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/javi/domain/service/DummyService.java:
--------------------------------------------------------------------------------
1 | package com.javi.domain.service;
2 |
3 | import com.javi.common.exception.NotFoundException;
4 | import com.javi.domain.model.Dummy;
5 | import com.javi.pagination.Page;
6 | import com.javi.pagination.Paginator;
7 |
8 | import lombok.AllArgsConstructor;
9 | import lombok.extern.slf4j.Slf4j;
10 |
11 | import com.javi.annotation.UseCase;
12 | import com.javi.application.in.DummyUseCase;
13 | import com.javi.application.out.DummyPersistence;
14 |
15 | @UseCase
16 | @AllArgsConstructor
17 | @Slf4j
18 | public class DummyService implements DummyUseCase {
19 |
20 | private final DummyPersistence dummyPersistence;
21 |
22 | @Override
23 | public Dummy findById(int id) {
24 | log.info("Searching dummy by id: {}", id);
25 | return this.dummyPersistence.findById(id).orElseThrow(NotFoundException::new);
26 | }
27 |
28 | @Override
29 | public Paginator.Pair findAll(Page page) {
30 | log.info("Searching dummies");
31 | return dummyPersistence.findAll(page);
32 | }
33 |
34 | @Override
35 | public Dummy save(Dummy dummy) {
36 | log.info("Saving dummy: {}", dummy.toString());
37 | return this.dummyPersistence.save(dummy);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application.name: java-spring3-microservice
3 | threads.virtual.enabled: true
4 | datasource:
5 | url: jdbc:postgresql://${DB_HOST}/${DB_NAME}
6 | username: ${DB_USER}
7 | password: ${DB_PASSWORD}
8 | driver-class-name: org.postgresql.Driver
9 | jpa:
10 | show-sql: ${JPA_SHOW_SQL:true}
11 | database-platform: org.hibernate.dialect.PostgreSQLDialect
12 | hibernate.ddl-auto: none
13 | security:
14 | oauth2:
15 | resourceserver:
16 | jwt:
17 | issuer-uri: ${KEYCLOAK_HOST:http://localhost:8081}/realms/javi
18 | jwk-set-uri: ${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs
19 | devtools.restart.enabled: false
20 |
21 | springdoc:
22 | api-docs:
23 | enabled: ${SWAGGER_ENABLED:true}
24 | swagger-ui:
25 | enabled: ${SWAGGER_ENABLED:true}
26 | oauth:
27 | client-id: ${spring.application.name}
28 | client-secret: ${KEYCLOAK_CLIENT_SECRET}
29 | # path: /swagger-ui/index.html
30 | default-consumes-media-type: application/json
31 | default-produces-media-type: application/json
32 |
33 | server.servlet.context-path: /app
34 |
35 | tracing.url: ${TRACING_HOST:http://localhost:4318}/v1/traces
36 |
37 | management:
38 | endpoints.web.exposure.include: health,env,metrics
39 | tracing.sampling.probability: 1.0
40 |
41 | logging:
42 | pattern:
43 | level: "%5p [${spring.application.name:}, traceID=%X{traceId:-}, spanID=%X{spanId:-}]"
44 |
45 | logstash.destination: ${LOGSTASH_HOST:http://localhost:5000}
46 |
47 | ---
48 |
49 | spring:
50 | config.activate.on-profile: dev
51 | datasource:
52 | url: jdbc:postgresql://localhost:5432/db_dummy
53 | username: admin
54 | password: admin
55 | jpa:
56 | hibernate:
57 | ddl-auto: create
58 | # ddl-auto: update
59 | properties:
60 | javax:
61 | persistence:
62 | schema-generation:
63 | create-source: metadata
64 | scripts:
65 | action: create
66 | create-target: schema.sql
67 | create-script-source: metadata
68 | devtools.restart.enabled: true
69 |
70 | springdoc:
71 | swagger-ui:
72 | oauth:
73 | client-secret: ${KEYCLOAK_CLIENT_SECRET:RqaTlO0d2OnBbeRuImNnbLWm5yZL66Mo}
74 |
--------------------------------------------------------------------------------
/src/test/java/com/javi/adapter/in/DummyControllerTest.java:
--------------------------------------------------------------------------------
1 | package com.javi.adapter.in;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.mockito.Mockito;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
7 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
8 | import org.springframework.boot.test.mock.mockito.MockBean;
9 | import org.springframework.test.context.aot.DisabledInAotMode;
10 | import org.springframework.test.web.servlet.MockMvc;
11 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
12 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
13 |
14 | import java.util.Collections;
15 |
16 | import com.javi.application.in.DummyUseCase;
17 | import com.javi.domain.model.Dummy;
18 | import com.javi.pagination.Page;
19 | import com.javi.pagination.Paginator;
20 | import com.javi.response.PaginationResponse;
21 |
22 | @WebMvcTest(controllers = DummyController.class)
23 | @AutoConfigureMockMvc(addFilters = false)
24 | @DisabledInAotMode
25 | public class DummyControllerTest {
26 |
27 | @Autowired
28 | private MockMvc mockMvc;
29 |
30 | @MockBean
31 | private DummyUseCase dummyUseCase;
32 |
33 | @Test
34 | void find() throws Exception {
35 | Mockito.when(dummyUseCase.findById(1)).thenReturn(new Dummy("dummy"));
36 |
37 | mockMvc.perform(get("/dummy/{id}", 1)
38 | .header("Content-Type", "application/json"))
39 | .andExpect(status().isOk());
40 | }
41 |
42 | @Test
43 | void findAll() throws Exception {
44 | var page = new Page(0, 10, "id", "asc");
45 | var pair = new Paginator.Pair(new PaginationResponse(0, 10, 0), Collections.emptyList());
46 |
47 | Mockito.when(dummyUseCase.findAll(page)).thenReturn(pair);
48 |
49 | mockMvc.perform(get("/dummy")
50 | .header("Content-Type", "application/json"))
51 | .andExpect(status().isOk());
52 | }
53 |
54 | @Test
55 | void save() throws Exception {
56 | var dummy = new Dummy("dummy");
57 | Mockito.when(dummyUseCase.save(dummy)).thenReturn(dummy);
58 |
59 | var json = """
60 | {
61 | "info": "papa"
62 | }
63 | """;
64 |
65 | mockMvc.perform(post("/dummy")
66 | .header("Content-Type", "application/json")
67 | .content(json))
68 | .andExpect(status().isOk());
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/test/java/com/javi/adapter/out/DummyPersistenceTest.java:
--------------------------------------------------------------------------------
1 | package com.javi.adapter.out;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
6 | import org.springframework.boot.test.context.TestConfiguration;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Import;
9 | import org.springframework.data.domain.AuditorAware;
10 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
11 | import org.springframework.test.context.jdbc.Sql;
12 |
13 | import static org.assertj.core.api.Assertions.assertThat;
14 |
15 | import java.util.Optional;
16 |
17 | import com.javi.adapter.out.mappers.DummyMapper;
18 | import com.javi.domain.model.Dummy;
19 | import com.javi.pagination.Page;
20 |
21 | @DataJpaTest(properties = {
22 | "spring.datasource.url=jdbc:h2:mem:testdb",
23 | "spring.jpa.hibernate.ddl-auto=create-drop"
24 | })
25 | @Import({ DummyPersistenceAdapter.class, DummyMapper.class })
26 | public class DummyPersistenceTest {
27 |
28 | @Autowired
29 | private DummyPersistenceAdapter dummyPersistenceAdapter;
30 |
31 | @TestConfiguration
32 | @EnableJpaAuditing(auditorAwareRef = "testAuditorAware")
33 | public static class TestConfig {
34 | @Bean
35 | public AuditorAware testAuditorAware() {
36 | return () -> Optional.ofNullable("TestUser");
37 | }
38 | }
39 |
40 | @Test
41 | @Sql("db.sql")
42 | public void findById() {
43 | var dummy = dummyPersistenceAdapter.findById(1);
44 | assertThat(dummy.get().info()).isEqualTo("dummy 1");
45 | }
46 |
47 | @Test
48 | @Sql("db.sql")
49 | public void findAll() {
50 | var page = new Page(0, 10, "id", "asc");
51 |
52 | var result = dummyPersistenceAdapter.findAll(page);
53 |
54 | assertThat(result.results().size()).isEqualTo(2);
55 | }
56 |
57 | @Test
58 | public void save() {
59 | var dummy = new Dummy("saved");
60 | dummy = dummyPersistenceAdapter.save(dummy);
61 |
62 | assertThat(dummy.info()).isEqualTo("saved");
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/test/java/com/javi/domain/service/DummyServiceTest.java:
--------------------------------------------------------------------------------
1 | package com.javi.domain.service;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.mockito.Mockito;
5 |
6 | import com.javi.application.out.DummyPersistence;
7 | import com.javi.domain.model.Dummy;
8 | import com.javi.pagination.Page;
9 | import com.javi.pagination.Paginator;
10 | import com.javi.response.PaginationResponse;
11 |
12 | import static org.assertj.core.api.Assertions.assertThat;
13 | import static org.mockito.BDDMockito.*;
14 |
15 | import java.util.Collections;
16 | import java.util.Optional;
17 |
18 | public class DummyServiceTest {
19 |
20 | private final DummyPersistence dummyPersistence = Mockito.mock(DummyPersistence.class);
21 | private final DummyService dummyService = new DummyService(dummyPersistence);
22 |
23 | @Test
24 | public void find() {
25 | Dummy dummy = Mockito.mock(Dummy.class);
26 | given(dummy.info()).willReturn("test");
27 |
28 | given(dummyPersistence.findById(eq(1))).willReturn(Optional.of(dummy));
29 |
30 | dummy = dummyService.findById(1);
31 |
32 | assertThat(dummy).isNotNull();
33 | }
34 |
35 | @Test
36 | public void findAll() {
37 | var page = new Page(0, 10, "id", "asc");
38 | var pair = new Paginator.Pair(new PaginationResponse(0, 10, 0), Collections.emptyList());
39 |
40 | given(dummyPersistence.findAll(page)).willReturn(pair);
41 |
42 | var res = dummyService.findAll(page);
43 |
44 | assertThat(res).isNotNull();
45 | }
46 | @Test
47 | public void save() {
48 | Dummy dummy = Mockito.mock(Dummy.class);
49 | given(dummy.info()).willReturn("save");
50 |
51 | given(dummyPersistence.save(eq(dummy))).willReturn(dummy);
52 |
53 | dummy = dummyService.save(dummy);
54 |
55 | assertThat(dummy.info()).isEqualTo("save");
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/test/resources/com/javi/adapter/out/db.sql:
--------------------------------------------------------------------------------
1 | insert into dummies (id, info, create_date, created_by) values (1, 'dummy 1', current_timestamp, 'javi');
2 | insert into dummies (id, info, create_date, created_by) values (2, 'dummy 2', current_timestamp, 'javi');
3 |
--------------------------------------------------------------------------------