Skip to content

Commit

Permalink
Bug/949 keycloak error (#982)
Browse files Browse the repository at this point in the history
* create run config for prod

* add new docker-compose file

* add check if user is already logged in

* don't merge queryparams per default

* start changing auth library

* improve Readme and add the possibility to use the postgres db with the prod env
  • Loading branch information
kcinay055679 authored Sep 9, 2024
1 parent 774df1c commit d121986
Show file tree
Hide file tree
Showing 33 changed files with 349 additions and 225 deletions.
19 changes: 19 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,31 @@ This is the Spring Boot backend for the OKR-Tool.
Build only the Backend
- Build backend without frontend: `mvn clean package`


Build Frontend + Backend together

NATIVE
- Build Frontend for production im `frontend` dir: `npm run build`
- Build Backend with frontend: `mvn clean package -P build-for-docker`
- Setup DB
- Start Backend `java -jar {path to .jar file}`

USING DOCKER
- cd into the root directory of the project
- cd `docker/local-prod`
- Run `docker compose up`
- You have to restart after every code change `docker compose down spring && docker compose up spring`
- Get the logs with `docker compose logs -f spring`
<br>

***!IMPORTANT!***
- If after the first start the backend is not reachable, restart the backend container with `docker compose restart spring`
- if any permission issues occur, delete all of the following folders
- .angular
- frontend/dist
- frontend/node_modules
- backend/target

Formatting:
- Check code formatting: `mvn formatter:validate`
- Format the code: `mvn formatter:format`
Expand Down
9 changes: 0 additions & 9 deletions backend/src/main/java/ch/puzzle/okr/ForwardFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,11 @@
public class ForwardFilter extends GenericFilterBean {

private static final Logger logger = LoggerFactory.getLogger(ForwardFilter.class);
private final String[] allowedRoutes = { "/keyresult", "/objective", "/?state" };

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String path = request.getRequestURI();

if (Arrays.stream(this.allowedRoutes).anyMatch(path::startsWith)) {
logger.info(String.format("Keycloak state parameter detected ====> make a forward from '%s' to '%s'",
request.getRequestURI(), "/"));
servletRequest.getRequestDispatcher("/").forward(servletRequest, servletResponse);
return;
}
logger.debug(String.format("====> pass through the filter '%s'", request.getRequestURI()));
filterChain.doFilter(servletRequest, servletResponse);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package ch.puzzle.okr.controller;

import ch.puzzle.okr.ForwardFilter;
import ch.puzzle.okr.dto.ClientConfigDto;
import ch.puzzle.okr.service.clientconfig.ClientConfigService;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/config")
@Controller
public class ClientConfigController {

private final ClientConfigService configService;
Expand All @@ -18,8 +20,21 @@ public ClientConfigController(ClientConfigService configService) {
this.configService = configService;
}

@GetMapping
@GetMapping("/config")
public ResponseEntity<ClientConfigDto> getConfig() {
return ResponseEntity.status(HttpStatus.OK).body(configService.getConfigBasedOnActiveEnv());
}

@RequestMapping(value = "/**/{[path:[^\\.]*}")
public String redirect(HttpServletRequest request) {
String path = request.getRequestURI();
// Serve static resources or paths containing a dot directly
if (path.startsWith("/assets/") || path.contains(".")) {
return "forward:" + path;
}

// Forward all other requests to index.html
return "forward:/";
}

}
1 change: 1 addition & 0 deletions backend/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#environment
spring.profiles.active=prod
spring.mvc.pathmatch.matching-strategy=ant_path_matcher

# database connection
spring.datasource.url=jdbc:postgresql://localhost:5432/okr
Expand Down
15 changes: 0 additions & 15 deletions backend/src/test/java/ch/puzzle/okr/ForwardFilterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,4 @@ void shouldNotFilterTheRootPath(String requestUri) throws ServletException, IOEx
verify(filterChain, times(1)).doFilter(Mockito.eq(request), Mockito.eq(response));
verify(request, never()).getRequestDispatcher(anyString());
}

@Test
void shouldFilterAuthPath() throws ServletException, IOException {
// given
when(request.getRequestURI()).thenReturn("/?state=''");
when(request.getRequestDispatcher(anyString())).thenReturn(requestDispatcher);
doNothing().when(requestDispatcher).forward(Mockito.eq(request), Mockito.eq(response));

// when
forwardFilter.doFilter(request, response, filterChain);

// then
verify(filterChain, never()).doFilter(Mockito.eq(request), Mockito.eq(response));
verify(request, times(1)).getRequestDispatcher(anyString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.RestController;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
Expand Down Expand Up @@ -91,7 +90,8 @@ void controllersAreAnnotatedWithRestController() {
JavaClasses importedClasses = getMainSourceClasses();

ArchRule rule = classes().that().areNotAnonymousClasses().and().resideInAPackage("ch.puzzle.okr.controller..")
.should().beAnnotatedWith(RestController.class).andShould().notBeInterfaces();
.should().beAnnotatedWith(RestController.class).orShould().beAnnotatedWith(Controller.class).andShould()
.notBeInterfaces();

rule.check(importedClasses);
}
Expand Down
4 changes: 3 additions & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ USER 1001

WORKDIR app-root/backend

COPY --chown=1001 backend/target .
RUN if [ -d "../backend/target" ]; then \
cp -r --chown=1001 ../backend/target .; \
fi

ENTRYPOINT ["/bin/sh", "-c", "export BACKEND_VERSION=$(find . -type f -name 'backend-*.jar' -print -quit | sed -n 's/.*backend-\\(.*\\)\\.jar/\\1/p'); java -jar backend-${BACKEND_VERSION}.jar"]
2 changes: 0 additions & 2 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3.5'

services:
okr-db:
container_name: okr-dev-db
Expand Down
54 changes: 54 additions & 0 deletions docker/local-prod/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
include:
- ../docker-compose.yml
services:
spring:
container_name: spring
build:
context: ./..
dockerfile: Dockerfile
restart: always
ports:
- 8080:8080
environment:
SPRING_PROFILES_ACTIVE: staging
LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: debug
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER-URI: http://localhost:8544/realms/pitc
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK-SET-URI: http://keycloak:8080/realms/pitc/protocol/openid-connect/certs
# SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER-URI: https://sso.puzzle.ch/auth/realms/pitc
# SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK-SET-URI: https://sso.puzzle.ch/auth/realms/pitc/protocol/openid-connect/certs
SPRING_SECURITY_OAUTH2_RESOURCESERVER_OPAQUETOKEN_CLIENT-ID: pitc_okr_staging
## Postgres DB
SPRING_DATASOURCE_URL: jdbc:postgresql://okr-dev-db:5432/okr
SPRING_DATASOURCE_USERNAME: user
SPRING_DATASOURCE_PASSWORD: pwd
SPRING_FLYWAY_LOCATIONS: classpath:db/data-migration,classpath:db/migration,classpath:db/callback

## In memory DB
# SPRING_DATASOURCE_URL: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
# SPRING_DATASOURCE_USERNAME: user
# SPRING_DATASOURCE_PASSWORD: sa
# SPRING_FLYWAY_LOCATIONS: classpath:db/h2-db/database-h2-schema,classpath:db/h2-db/data-test-h2
volumes:
- ../../../okr/backend/target:/app-root/backend
depends_on:
maven:
condition: service_completed_successfully

maven:
container_name: maven
image: maven:3.8.3-openjdk-17
volumes:
- ../../../okr:/opt
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
command: [ "/bin/bash", "-c", "cd /opt && mvn -B clean package --file pom.xml -P build-for-docker && chown -R 1000:1000 ./backend/target" ]

angular:
container_name: angular
image: node:20
user: "${UID:-1000}:${GID:-1000}"
volumes:
- ../../../okr:/opt
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
command: [ "/bin/bash", "-c", "cd /opt/frontend && npm ci && npm run watch:prod" ]
2 changes: 1 addition & 1 deletion frontend/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ function loginWithCredentials(username: string, password: string) {
cy.url().then((url) => {
const currentUrl = new URL(url);
const baseURL = new URL(Cypress.config().baseUrl!);
expect(currentUrl.pathname).equal(baseURL.pathname);
expect(currentUrl.pathname).equal(baseURL.pathname + 'callback');
});
}

Expand Down
25 changes: 17 additions & 8 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"build": "ng build",
"build:staging": "ng build --configuration staging",
"watch": "ng build --watch --configuration development",
"watch:prod": "ng build --watch",
"test": "jest --silent",
"cypress:open": "cypress open",
"cypress:run": "cypress run --browser chrome --headed",
Expand Down Expand Up @@ -34,7 +35,7 @@
"@angular/router": "^17.0.6",
"@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0",
"angular-oauth2-oidc": "^17.0.0",
"angular-auth-oidc-client": "^18.0.1",
"bootstrap": "^5.3.2",
"moment": "^2.30.1",
"ngx-toastr": "^18.0.0",
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { ActivatedRouteSnapshot, ResolveFn, RouterModule, Routes } from '@angula
import { OverviewComponent } from './overview/overview.component';
import { EMPTY, of } from 'rxjs';
import { SidepanelComponent } from './shared/custom/sidepanel/sidepanel.component';
import { authGuard } from './shared/guards/auth.guard';
import { AutoLoginPartialRoutesGuard } from 'angular-auth-oidc-client';
import { CallbackComponent } from './callback/callback.component';

/**
* Resolver for get the id from url like `/objective/42` or `/keyresult/42`.
Expand Down Expand Up @@ -36,8 +37,9 @@ const routes: Routes = [
data: { type: 'KeyResult' },
},
],
canActivate: [authGuard],
canActivate: [AutoLoginPartialRoutesGuard],
},
{ path: 'callback', component: CallbackComponent },
{ path: '**', redirectTo: '', pathMatch: 'full' },
];

Expand Down
37 changes: 12 additions & 25 deletions frontend/src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { TranslateTestingModule } from 'ngx-translate-testing';
import { AuthConfig, OAuthModule, OAuthService } from 'angular-oauth2-oidc';
import { HttpClientTestingModule } from '@angular/common/http/testing';
// @ts-ignore
import * as de from '../assets/i18n/de.json';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatSidenavModule } from '@angular/material/sidenav';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { NavigationEnd, Routes } from '@angular/router';
import { RouterModule, Routes } from '@angular/router';
import { of } from 'rxjs';
import { OverviewComponent } from './overview/overview.component';
import { ObjectiveDetailComponent } from './objective-detail/objective-detail.component';
import { CommonModule } from '@angular/common';

const oauthServiceMock = {
configure(environment: AuthConfig): void {},
initCodeFlow(): void {},
setupAutomaticSilentRefresh(): void {},
hasValidAccessToken(): boolean {
return true;
},
loadDiscoveryDocumentAndTryLogin(): Promise<any> {
this.initCodeFlow();
return Promise.resolve();
},
};

const routerMock = {
root: jest.fn(),
// Router
events: of(new NavigationEnd(0, 'http://localhost:4200/objective/2', 'http://localhost:4200/objective/2')),
};
import { StsConfigLoader } from 'angular-auth-oidc-client';

const routes: Routes = [
{
Expand All @@ -52,17 +32,24 @@ describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes(routes),
RouterModule.forRoot(routes),
HttpClientTestingModule,
TranslateTestingModule.withTranslations({
de: de,
}),
OAuthModule.forRoot(),
MatSidenavModule,
NoopAnimationsModule,
CommonModule,
],
providers: [{ provide: OAuthService, useValue: oauthServiceMock }],
providers: [
{
provide: StsConfigLoader,
useValue: {
loadConfig: () => of({}),
loadConfigs: () => of({}),
},
},
],
declarations: [AppComponent, OverviewComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
Expand Down
Loading

0 comments on commit d121986

Please sign in to comment.