├── .gitignore
├── APPENDIX-README.md
├── README-NEXUS.md
├── README.md
├── _configuration
├── nexus
│ ├── maven-settings-template.xml
│ └── setup_nexus3.sh
└── openshift
│ ├── 3scale
│ ├── README.md
│ ├── _applications_index.html.liquid
│ ├── _custom_field_plans.html.liquid
│ ├── _group_membership_plans.html.liquid
│ ├── _mulitple_app_signup_form.html.liquid
│ ├── _single_app_signup_form.html.liquid
│ ├── index.html.liquid
│ ├── l_main_layout.html.liquid
│ └── services_index.html.liquid
│ ├── README.md
│ ├── docker
│ ├── Dockerfile
│ └── self-signed
│ │ ├── cert1.crt
│ │ ├── cert2.crt
│ │ ├── self-signed-cert.pem
│ │ └── sso73.crt
│ ├── s2i-microservices-fuse74-spring-boot-camel-selfsigned.yaml
│ └── s2i-microservices-fuse74-spring-boot-camel.yaml
├── _images
├── 01.png
├── 02.png
├── 04.png
├── 05.png
├── 06.png
├── 07.png
├── 08.png
├── 09.png
├── 10.png
├── 11.png
├── 12.png
├── 13.png
├── 14.png
├── 15.png
├── 16.png
├── 17.png
├── 18.png
├── 19.png
├── 20.png
├── 21.png
├── 22.png
├── 23.png
├── 24.png
├── 25.png
├── 26.png
├── 27.png
├── 28.png
├── 29.png
├── 30.png
├── 31.png
├── 32.png
├── 33.png
├── 34.png
├── 35.png
├── 36.png
├── 37.png
├── icon01.png
├── icon02.png
├── meme01.jpg
├── meme03.jpg
└── meme03.png
├── _videos
└── screen01.gif
├── auth-integration
├── configuration
│ ├── configmap
│ │ └── auth-integration-api-env.yml
│ └── secret
│ │ └── auth-integration-api.yml
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── microservices
│ │ └── apigateway
│ │ └── security
│ │ ├── AuthFuseApplication.java
│ │ ├── Combination.java
│ │ ├── common
│ │ ├── camel
│ │ │ ├── AuthComponent.java
│ │ │ ├── AuthConsumer.java
│ │ │ ├── AuthEndpoint.java
│ │ │ └── AuthProducer.java
│ │ └── oauth2
│ │ │ ├── OAuth2ClientCredentials.java
│ │ │ └── Token.java
│ │ ├── configuration
│ │ ├── KeycloakServiceAccountConfiguration.java
│ │ ├── MetricsConfiguration.java
│ │ ├── MicrometerConfiguration.java
│ │ ├── ProductConfiguration.java
│ │ ├── StockConfiguration.java
│ │ ├── SupplierConfiguration.java
│ │ └── TracingConfiguration.java
│ │ ├── model
│ │ ├── ApiResponse.java
│ │ ├── BaseModel.java
│ │ ├── ErrorMessage.java
│ │ ├── Event.java
│ │ ├── Product.java
│ │ └── health
│ │ │ ├── springboot1
│ │ │ ├── Camel.java
│ │ │ ├── CamelHealthChecks.java
│ │ │ ├── DiskSpace.java
│ │ │ └── Health.java
│ │ │ └── springboot2
│ │ │ └── Health.java
│ │ ├── processor
│ │ └── ExceptionProcessor.java
│ │ └── route
│ │ ├── external
│ │ └── IntegrationRestRoute.java
│ │ └── internal
│ │ ├── MetricsInternalRoute.java
│ │ ├── ProductInternalRoute.java
│ │ ├── StockInternalRoute.java
│ │ └── SupplierInternalRoute.java
│ └── resources
│ ├── META-INF
│ └── services
│ │ └── org
│ │ └── apache
│ │ └── camel
│ │ └── component
│ │ └── auth
│ ├── application.yaml
│ └── logback.xml
├── auth-sso-common
├── META-INF
│ └── services
│ │ └── org
│ │ └── apache
│ │ └── camel
│ │ └── component
│ │ └── auth
├── README.md
├── build.metadata
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── microservices
│ │ │ │ └── security
│ │ │ │ └── common
│ │ │ │ ├── camel
│ │ │ │ ├── AuthComponent.java
│ │ │ │ ├── AuthConsumer.java
│ │ │ │ ├── AuthEndpoint.java
│ │ │ │ └── AuthProducer.java
│ │ │ │ └── oauth2
│ │ │ │ ├── OAuth2ClientCredentials.java
│ │ │ │ └── Token.java
│ │ └── resources
│ │ │ └── META-INF
│ │ │ └── services
│ │ │ └── org
│ │ │ └── apache
│ │ │ └── camel
│ │ │ └── component
│ │ │ └── auth
│ └── test
│ │ └── java
│ │ └── com
│ │ └── microservices
│ │ └── security
│ │ └── common
│ │ └── oauth2
│ │ └── OAuth2ClientCredentialsTest.java
└── templates
│ └── README.md
├── maven-settings.xml
├── pom.xml
├── product-insecure
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── microservices
│ │ └── apigateway
│ │ └── security
│ │ ├── ProductApplication.java
│ │ ├── configuration
│ │ ├── SwaggerConfiguration.java
│ │ ├── TracingConfiguration.java
│ │ └── WebConfiguration.java
│ │ ├── controller
│ │ ├── BaseController.java
│ │ ├── ProductController.java
│ │ ├── error
│ │ │ └── Error.java
│ │ └── validator
│ │ │ └── ProductValidator.java
│ │ ├── model
│ │ ├── BaseModel.java
│ │ └── Product.java
│ │ ├── repository
│ │ └── ProductRepository.java
│ │ └── service
│ │ └── ProductService.java
│ └── resources
│ ├── application.yaml
│ ├── data.sql
│ ├── logback.xml
│ └── messages.properties
├── product
├── configuration
│ ├── configmap
│ │ └── product-api-env.yml
│ └── secret
│ │ └── product-api.yml
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── microservices
│ │ └── apigateway
│ │ └── security
│ │ ├── ProductApplication.java
│ │ ├── configuration
│ │ ├── SwaggerConfiguration.java
│ │ ├── TracingConfiguration.java
│ │ ├── WebConfiguration.java
│ │ └── security
│ │ │ ├── JwtAccessTokenCustomizer.java
│ │ │ ├── OAuth2RestTemplateConfigurer.java
│ │ │ ├── SecurityConfiguration.java
│ │ │ ├── SecurityConfigurer.java
│ │ │ └── SecurityContextUtils.java
│ │ ├── controller
│ │ ├── BaseController.java
│ │ ├── ProductController.java
│ │ ├── error
│ │ │ └── Error.java
│ │ └── validator
│ │ │ └── ProductValidator.java
│ │ ├── model
│ │ ├── BaseModel.java
│ │ └── Product.java
│ │ ├── repository
│ │ └── ProductRepository.java
│ │ └── service
│ │ └── ProductService.java
│ └── resources
│ ├── application.yaml
│ ├── data.sql
│ ├── logback.xml
│ └── messages.properties
├── stock
├── configuration
│ ├── configmap
│ │ └── stock-api-env.yml
│ └── secret
│ │ └── stock-api.yml
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── microservices
│ │ └── apigateway
│ │ └── security
│ │ ├── StockApplication.java
│ │ ├── configuration
│ │ ├── SwaggerConfiguration.java
│ │ └── TracingConfiguration.java
│ │ ├── controller
│ │ ├── BaseController.java
│ │ ├── StockController.java
│ │ ├── error
│ │ │ └── Error.java
│ │ └── validator
│ │ │ └── EventValidator.java
│ │ └── model
│ │ ├── BaseModel.java
│ │ └── Event.java
│ └── resources
│ ├── application.yaml
│ ├── logback.xml
│ └── messages.properties
├── supplier
├── configuration
│ ├── configmap
│ │ └── supplier-api-env.yml
│ └── secret
│ │ └── supplier-api.yml
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── microservices
│ │ └── apigateway
│ │ └── security
│ │ ├── SupplierApplication.java
│ │ ├── configuration
│ │ ├── SwaggerConfiguration.java
│ │ ├── TracingConfiguration.java
│ │ ├── WebConfiguration.java
│ │ └── security
│ │ │ ├── JwtAccessTokenCustomizer.java
│ │ │ ├── OAuth2RestTemplateConfigurer.java
│ │ │ ├── SecurityConfiguration.java
│ │ │ ├── SecurityConfigurer.java
│ │ │ └── SecurityContextUtils.java
│ │ ├── controller
│ │ ├── BaseController.java
│ │ ├── SupplierController.java
│ │ ├── error
│ │ │ └── Error.java
│ │ └── validator
│ │ │ └── EventValidator.java
│ │ └── model
│ │ ├── BaseModel.java
│ │ └── Event.java
│ └── resources
│ ├── application.yaml
│ ├── logback.xml
│ └── messages.properties
└── webapp
├── README.md
├── angular.json
├── build-local.sh
├── envsub.js
├── gen-cert.sh
├── ocp-hotdeploy.sh
├── package-lock.json
├── package.json
├── run-local.sh
├── s2i-build.sh
├── s2i-run.sh
├── server.cert
├── server.js
├── server.key
├── src
├── app
│ ├── app-animations.ts
│ ├── app-init.service.ts
│ ├── app.component.html
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── app.routes.ts
│ ├── auth.ts
│ ├── breadcrumb
│ │ ├── breadcrumb.component.html
│ │ ├── breadcrumb.component.ts
│ │ └── breadcrumb.ts
│ ├── header
│ │ ├── header.component.css
│ │ ├── header.component.html
│ │ └── header.component.ts
│ ├── home
│ │ ├── home.component.html
│ │ └── home.component.ts
│ ├── maintenance
│ │ ├── maintenance.component.html
│ │ ├── maintenance.component.ts
│ │ └── maintenance.service.ts
│ ├── message
│ │ ├── message-history.service.ts
│ │ ├── message-item.ts
│ │ └── message.service.ts
│ ├── product
│ │ ├── deserializable.model.ts
│ │ ├── json-date.pipe.ts
│ │ ├── product.component.css
│ │ ├── product.component.html
│ │ ├── product.component.ts
│ │ ├── product.model.ts
│ │ ├── product.pageable.model.ts
│ │ └── product.service.ts
│ ├── sidebar
│ │ ├── sidebar.component.html
│ │ ├── sidebar.component.ts
│ │ └── sidebar.service.ts
│ └── status
│ │ ├── status.component.html
│ │ ├── status.component.ts
│ │ └── status.service.ts
├── assets
│ ├── .gitkeep
│ ├── img
│ │ ├── 02.png
│ │ ├── Logo-RedHat-A-Color-RGB.svg
│ │ ├── angular.svg
│ │ ├── bootstrap.svg
│ │ ├── nodejs.svg
│ │ └── openshift.svg
│ └── js
│ │ └── env.template.js
├── browserslist
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── styles.scss
├── tsconfig.app.json
└── tslint.json
├── tsconfig.json
└── tslint.json
/.gitignore:
--------------------------------------------------------------------------------
1 | auth-integration/target
2 | product/target
3 | stock/target
4 | supplier/target
5 | webapp/dist
6 |
7 | target/
8 | _camtasia
9 |
10 | # Dependency directories
11 | node_modules/
12 | jspm_packages/
13 |
14 | .DS_Store
15 |
16 | # IntelliJ
17 | *.iml
18 | .idea/*.iml
19 | .idea/*
20 | !/maven-settings.xml
21 |
--------------------------------------------------------------------------------
/APPENDIX-README.md:
--------------------------------------------------------------------------------
1 | ### Using a custom FUSE image:
2 |
3 | You could use the Dockerfile inside the _configuration folder.
4 |
5 | This Dockerfile contains an example on how to add a custom CA certificate.
6 |
7 | In my environment I needed to add the RHSSO self-signed certificate in order to avoid http 401 for keycloak-adapter validation during realm public key retrieval.
8 |
--------------------------------------------------------------------------------
/README-NEXUS.md:
--------------------------------------------------------------------------------
1 | ```bash
2 | NEXUS_NAMESPACE=cicd-devtools
3 |
4 | oc new-app --docker-image docker.io/sonatype/nexus3:latest
5 |
6 | oc rollout pause dc/nexus3
7 |
8 | oc patch dc nexus3 -p '{"spec":{"strategy":{"type":"Recreate"}}}'
9 |
10 | oc expose svc nexus3
11 |
12 | oc set resources dc nexus3 --limits=memory=4Gi,cpu=2 --requests=memory=2Gi,cpu=500m
13 |
14 | oc set volume dc/nexus3 --add --overwrite --name=nexus3-volume-1 --mount-path=/nexus-data/ --type persistentVolumeClaim --claim-name=nexus-pvc --claim-size=10Gi
15 |
16 | oc set probe dc/nexus3 --liveness --failure-threshold 3 --initial-delay-seconds 60 -- echo ok
17 |
18 | oc set probe dc/nexus3 --readiness --failure-threshold 3 --initial-delay-seconds 60 --get-url=http://:8081/
19 |
20 | oc rollout resume dc nexus3
21 |
22 | oc get pods | grep Running
23 | #[root@ocp-bastion lab04]# oc get pods | grep Running
24 | #nexus3-xyz 1/1 Running 0 2m36s
25 |
26 | export NEXUS_POD=$(oc get pods --selector app=nexus3 -n ${NEXUS_NAMESPACE} | { read line1 ; read line2 ; echo "$line2" ; } | awk '{print $1;}')
27 | oc exec ${NEXUS_POD} cat /nexus-data/admin.password
28 | # 33456b65-7e85-4dfc-a063-78b413cf4a47
29 |
30 | oc get routes | grep nexus3
31 |
32 | # log into the nexus and change password to admin123
33 |
34 | curl -o setup_nexus3.sh -s https://raw.githubusercontent.com/aelkz/microservices-observability/master/_configuration/nexus/setup_nexus3.sh
35 |
36 | chmod +x setup_nexus3.sh
37 |
38 | ./setup_nexus3.sh admin admin123 http://$(oc get route nexus3 --template='{{ .spec.host }}')
39 |
40 | oc expose dc nexus3 --port=5000 --name=nexus-registry
41 |
42 | oc create route edge nexus-registry --com.microservices.apigateway.security.service=nexus-registry --port=5000
43 | ```
44 |
--------------------------------------------------------------------------------
/_configuration/nexus/maven-settings-template.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
59 |
60 |
14 |
23 |
24 |
25 | {% for com.microservices.apigateway.security.service in current_account.subscribed_services %}
26 | {% for application in com.microservices.apigateway.security.service.applications %}
27 | Name
15 | {% if provider.multiple_services_allowed? %}
16 | Service
17 | {% endif %}
18 | Credentials
19 | State
20 |
21 |
22 |
28 |
44 | {% endfor %}
45 | {% endfor %}
46 |
47 |
48 |
29 | {{ application.name | link_to: application.url }}
30 |
31 | {% if provider.multiple_services_allowed? %}
32 | {{ com.microservices.apigateway.security.service.name }}
33 | {% endif %}
34 | {{ application.key }}
35 | {{ application.state }}
36 |
37 | {% if application.can.be_updated? %}
38 |
39 |
40 |
41 | {% endif %}
42 |
43 |
49 |
57 |
58 |
50 |
51 |
52 | {% if current_user.can.create_application? %}
53 | Create new application
54 | {% endif %}
55 |
56 |
17 | * This configurer is run only when following properties are set in application.properties. 18 | *
19 | * 20 | *rest.security.enabled=true
21 | * security.oauth2.client.grant-type=client_credentials
22 | *
23 | */
24 | @Configuration
25 | @Conditional(value = {ServiceAccountEnabled.class})
26 | public class OAuth2RestTemplateConfigurer {
27 |
28 | private static final Logger LOG = LoggerFactory.getLogger(OAuth2RestTemplateConfigurer.class);
29 |
30 | @Bean
31 | public OAuth2RestTemplate oauth2RestTemplate(OAuth2ProtectedResourceDetails details) {
32 | OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(details);
33 |
34 | LOG.debug("Begin OAuth2RestTemplate: getAccessToken");
35 | /* To validate if required configurations are in place during startup */
36 | oAuth2RestTemplate.getAccessToken();
37 | LOG.debug("End OAuth2RestTemplate: getAccessToken");
38 | return oAuth2RestTemplate;
39 | }
40 |
41 | /**
42 | * Condition class to configure OAuth2RestTemplate when both security is enabled and
43 | * client credentials property is set for secured micro-com.microservices.apigateway.security.service
44 | * to micro-com.microservices.apigateway.security.service call.
45 | */
46 | static class ServiceAccountEnabled extends AllNestedConditions {
47 |
48 | ServiceAccountEnabled() {
49 | super(ConfigurationPhase.PARSE_CONFIGURATION);
50 | }
51 |
52 | @ConditionalOnProperty(prefix = "rest.security", value = "enabled", havingValue = "true")
53 | static class SecurityEnabled {}
54 |
55 | @ConditionalOnProperty(prefix = "security.oauth2.client", value = "grant-type", havingValue = "client_credentials")
56 | static class ClientCredentialConfigurationExists {}
57 |
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/product/src/main/java/com/microservices/apigateway/security/configuration/security/SecurityContextUtils.java:
--------------------------------------------------------------------------------
1 | package com.microservices.apigateway.security.configuration.security;
2 |
3 | import java.util.HashSet;
4 | import java.util.Set;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.security.core.Authentication;
8 | import org.springframework.security.core.context.SecurityContext;
9 | import org.springframework.security.core.context.SecurityContextHolder;
10 | import org.springframework.security.core.userdetails.UserDetails;
11 | import org.springframework.stereotype.Component;
12 |
13 | /**
14 | * SecurityContextUtils is used to get username and roles to set created by, last updated by fields.
15 | */
16 | @Component
17 | public class SecurityContextUtils {
18 |
19 | private static final Logger LOGGER = LoggerFactory.getLogger(SecurityContextUtils.class);
20 |
21 | private static final String ANONYMOUS = "anonymous";
22 |
23 | private SecurityContextUtils() { }
24 |
25 | public static String getUserName() {
26 | SecurityContext securityContext = SecurityContextHolder.getContext();
27 | Authentication authentication = securityContext.getAuthentication();
28 | String username = ANONYMOUS;
29 |
30 | if (null != authentication) {
31 | if (authentication.getPrincipal() instanceof UserDetails) {
32 | UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
33 | username = springSecurityUser.getUsername();
34 |
35 | } else if (authentication.getPrincipal() instanceof String) {
36 | username = (String) authentication.getPrincipal();
37 |
38 | } else {
39 | LOGGER.debug("User details not found in Security Context");
40 | }
41 | } else {
42 | LOGGER.debug("Request not authenticated, hence no user name available");
43 | }
44 |
45 | return username;
46 | }
47 |
48 | public static Set17 | * This configurer is run only when following properties are set in application.properties. 18 | *
19 | * 20 | *rest.security.enabled=true
21 | * security.oauth2.client.grant-type=client_credentials
22 | *
23 | */
24 | @Configuration
25 | @Conditional(value = {ServiceAccountEnabled.class})
26 | public class OAuth2RestTemplateConfigurer {
27 |
28 | private static final Logger LOG = LoggerFactory.getLogger(OAuth2RestTemplateConfigurer.class);
29 |
30 | @Bean
31 | public OAuth2RestTemplate oauth2RestTemplate(OAuth2ProtectedResourceDetails details) {
32 | OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(details);
33 |
34 | LOG.debug("Begin OAuth2RestTemplate: getAccessToken");
35 | /* To validate if required configurations are in place during startup */
36 | oAuth2RestTemplate.getAccessToken();
37 | LOG.debug("End OAuth2RestTemplate: getAccessToken");
38 | return oAuth2RestTemplate;
39 | }
40 |
41 | /**
42 | * Condition class to configure OAuth2RestTemplate when both security is enabled and
43 | * client credentials property is set for secured micro-com.microservices.apigateway.security.service
44 | * to micro-com.microservices.apigateway.security.service call.
45 | */
46 | static class ServiceAccountEnabled extends AllNestedConditions {
47 |
48 | ServiceAccountEnabled() {
49 | super(ConfigurationPhase.PARSE_CONFIGURATION);
50 | }
51 |
52 | @ConditionalOnProperty(prefix = "rest.security", value = "enabled", havingValue = "true")
53 | static class SecurityEnabled {}
54 |
55 | @ConditionalOnProperty(prefix = "security.oauth2.client", value = "grant-type", havingValue = "client_credentials")
56 | static class ClientCredentialConfigurationExists {}
57 |
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/supplier/src/main/java/com/microservices/apigateway/security/configuration/security/SecurityContextUtils.java:
--------------------------------------------------------------------------------
1 | package com.microservices.apigateway.security.configuration.security;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.security.core.Authentication;
6 | import org.springframework.security.core.context.SecurityContext;
7 | import org.springframework.security.core.context.SecurityContextHolder;
8 | import org.springframework.security.core.userdetails.UserDetails;
9 | import org.springframework.stereotype.Component;
10 |
11 | import java.util.HashSet;
12 | import java.util.Set;
13 |
14 | /**
15 | * SecurityContextUtils is used to get username and roles to set created by, last updated by fields.
16 | */
17 | @Component
18 | public class SecurityContextUtils {
19 |
20 | private static final Logger LOGGER = LoggerFactory.getLogger(SecurityContextUtils.class);
21 |
22 | private static final String ANONYMOUS = "anonymous";
23 |
24 | private SecurityContextUtils() { }
25 |
26 | public static String getUserName() {
27 | SecurityContext securityContext = SecurityContextHolder.getContext();
28 | Authentication authentication = securityContext.getAuthentication();
29 | String username = ANONYMOUS;
30 |
31 | if (null != authentication) {
32 | if (authentication.getPrincipal() instanceof UserDetails) {
33 | UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
34 | username = springSecurityUser.getUsername();
35 |
36 | } else if (authentication.getPrincipal() instanceof String) {
37 | username = (String) authentication.getPrincipal();
38 |
39 | } else {
40 | LOGGER.debug("User details not found in Security Context");
41 | }
42 | } else {
43 | LOGGER.debug("Request not authenticated, hence no user name available");
44 | }
45 |
46 | return username;
47 | }
48 |
49 | public static Set20 | This application runs natively on the 21 | Openshift Container Platform as a 22 | Node.js application 23 |
24 |
25 |
26 |
27 |
28 |
29 |
35 | 36 |
37 | 38 |Call stock-api maintenance
7 |ROLE: STOCK_MAINTAINER
8 |
9 | Secured by Auth Integration API? true
10 | Has it's own 3scale com.microservices.apigateway.security.service? (needs additional 3scale authentication) false
11 | Is managed by Keycloak? false
12 |
Call supplier-api maintenance
28 |ROLE: SUPPLIER_MAINTAINER
29 |
30 | Secured by Auth Integration API? true
31 | Has it's own 3scale com.microservices.apigateway.security.service? (needs additional 3scale authentication) true
32 | Is managed by Keycloak? true
33 |
{{ response | json }}57 |