Skip to content

Commit

Permalink
Merge branch 'multitenancy_main' into feature/790_members_einladen
Browse files Browse the repository at this point in the history
  • Loading branch information
janikEndtner committed May 17, 2024
2 parents 088f785 + f198972 commit 7f86c70
Show file tree
Hide file tree
Showing 113 changed files with 3,500 additions and 1,501 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/addIssueToProject.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
name: Add issue to project
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@v0.5.0
- uses: actions/add-to-project@v1.0.1
with:
project-url: https://github.com/orgs/puzzle/projects/3
github-token: ${{ secrets.ADD_ISSUE_TO_PROJECT_CLASSIC }}
32 changes: 7 additions & 25 deletions .github/workflows/deploy-action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,42 +97,24 @@ jobs:
- name: Push
run: docker push ${{ needs.extract-version.outputs.okr-docker-image}}

- name: Trigger Deployment Workflow with latest Version
uses: actions/checkout@v4
with:
repository: ${{ vars.TARGET_REPOSITORY }}
ref: ${{ vars.TARGET_REFERENCE }}
path: ccy-repo
token: ${{secrets.VERSION_TOKEN}}

- name: Change Yaml
- name: Install yq
shell: bash
env:
FILEPATH: ${{ vars.FILEPATH }}
YAMLPATH: ${{ vars.YAML_PATH }}
NEWVALUE: ${{ needs.extract-version.outputs.okr-docker-image}}
VERSION: v4.25.2
BINARY: yq_linux_amd64
run: |
wget -q https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY}.tar.gz -O - |\
tar xz && mv ${BINARY} /usr/local/bin/yq
yq -i "${YAMLPATH} = \"${NEWVALUE}\"" ccy-repo/${FILEPATH}
- name: Commit and Push Changes
working-directory: ccy-repo
- name: Update YAML file
shell: bash
env:
COMMITPREFIX: '[CTS]'
run: |
git config --global user.email "[email protected]"
git config --global user.name "GitHub Actions"
git add ${{ vars.FILEPATH }} || {
echo "No files were changed, so we did not commit anything"
exit 1
} && \
git commit -m "$COMMITPREFIX Automated changes to ${{ vars.FILEPATH }}" && \
git push origin ${{ vars.TARGET_REFERENCE }}
- run: rm -rf ccy-repo
shell: bash
curl -s --header "PRIVATE-TOKEN: ${{secrets.GITLAB_ACCESS_TOKEN}}" "${{vars.TARGET_GITLAB_REPOSITORY}}/files/${{vars.GITLAB_FILEPATH}}?ref=${{vars.TARGET_GITLAB_REFERENCE}}" -H "Accept: application/json" -H "Content-Type: application/json" | jq -r '.content' | base64 --decode > response.yaml
yq -i "${{vars.YAML_PATH}} = \"${{needs.extract-version.outputs.okr-docker-image}}\"" response.yaml
UPDATED_CONTENT=$(cat response.yaml)
curl --request PUT --header 'PRIVATE-TOKEN: ${{secrets.GITLAB_ACCESS_TOKEN}}' -F "branch=${{vars.TARGET_GITLAB_REFERENCE}}" -F "[email protected]" -F "author_name=GitLab Actions" -F "content=${UPDATED_CONTENT}" -F "commit_message=$COMMITPREFIX Automated changes to ${{vars.FILEPATH_COMMIT}}" "${{vars.TARGET_GITLAB_REPOSITORY}}/files/${{vars.GITLAB_FILEPATH}}"
generate-and-push-sbom:
runs-on: ubuntu-latest
Expand Down
12 changes: 9 additions & 3 deletions .github/workflows/frontend-test-action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ jobs:
--name my_keycloak \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=keycloak \
-v ./docker/config/realm-export.json:/opt/keycloak/data/import/realm.json \
-v ./docker/config/realm-export-pitc.json:/opt/keycloak/data/import/realm-pitc.json \
-p 8544:8080 \
quay.io/keycloak/keycloak:23.0.1 \
quay.io/keycloak/keycloak:24.0.2 \
start-dev --import-realm
- name: start backend
Expand All @@ -58,10 +58,16 @@ jobs:
with:
working-directory: frontend
start: npm start
wait-on: 'http://localhost:8080/config, http://localhost:4200, http://localhost:8544'
wait-on: 'http://pitc.okr.localhost:8080/config, http://pitc.okr.localhost:4200, http://localhost:8544'
wait-on-timeout: 120
browser: chrome
headed: true

- uses: actions/upload-artifact@v4
if: always()
with:
name: cypress-screenshots
path: frontend/cypress/screenshots

- name: remove docker containers
run: docker ps -aq | xargs -r docker rm -f
32 changes: 6 additions & 26 deletions .github/workflows/staging-deploy-action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,44 +179,24 @@ jobs:
- name: Push
run: docker push ${{ needs.update-version.outputs.okr-docker-image}}

- name: Trigger Deployment Workflow with latest Version
uses: actions/checkout@v4
with:
repository: ${{ vars.TARGET_REPOSITORY }}
ref: ${{ vars.TARGET_REFERENCE }}
path: ccy-repo
token: ${{secrets.VERSION_TOKEN}}

- name: Change Yaml
- name: Install yq
shell: bash
env:
FILEPATH: ${{ vars.FILEPATH }}
YAMLPATH: ${{ vars.YAML_PATH }}
NEWVALUE: ${{ needs.update-version.outputs.okr-docker-image}}
VERSION: v4.25.2
BINARY: yq_linux_amd64
run: |
wget -q https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY}.tar.gz -O - |\
tar xz && mv ${BINARY} /usr/local/bin/yq
yq -i "${YAMLPATH} = \"${NEWVALUE}\"" ccy-repo/${FILEPATH}
- name: Commit and Push Changes
working-directory: ccy-repo
- name: Update YAML file
shell: bash
env:
COMMITPREFIX: '[CTS]'
run: |
git config --global user.email "[email protected]"
git config --global user.name "GitHub Actions"
git add ${{ vars.FILEPATH }} || {
echo "No files were changed, so we did not commit anything"
exit 1
} && \
git commit -m "$COMMITPREFIX Automated changes to ${{ vars.FILEPATH }}" && \
git push origin ${{ vars.TARGET_REFERENCE }}
- run: rm -rf ccy-repo
shell: bash
curl -s --header "PRIVATE-TOKEN: ${{secrets.GITLAB_ACCESS_TOKEN}}" "${{vars.TARGET_GITLAB_REPOSITORY}}/files/${{vars.GITLAB_FILEPATH}}?ref=${{vars.TARGET_GITLAB_REFERENCE}}" -H "Accept: application/json" -H "Content-Type: application/json" | jq -r '.content' | base64 --decode > response.yaml
yq -i "${{vars.YAML_PATH}} = \"${{needs.update-version.outputs.okr-docker-image}}\"" response.yaml
UPDATED_CONTENT=$(cat response.yaml)
curl --request PUT --header 'PRIVATE-TOKEN: ${{secrets.GITLAB_ACCESS_TOKEN}}' -F "branch=${{vars.TARGET_GITLAB_REFERENCE}}" -F "[email protected]" -F "author_name=GitLab Actions" -F "content=${UPDATED_CONTENT}" -F "commit_message=$COMMITPREFIX Automated changes to ${{vars.FILEPATH_COMMIT}}" "${{vars.TARGET_GITLAB_REPOSITORY}}/files/${{vars.GITLAB_FILEPATH}}"
generate-and-push-sbom:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .run/OkrApplication-E2E.run.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="OkrApplication-E2E" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot">
<option name="ACTIVE_PROFILES" value="integration-test" />
<option name="ALTERNATIVE_JRE_PATH" value="$USER_HOME$/.sdkman/candidates/java/current" />
<option name="ALTERNATIVE_JRE_PATH" value="$USER_HOME$/.jdks/corretto-17.0.9" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<module name="backend" />
<option name="SPRING_BOOT_MAIN_CLASS" value="ch.puzzle.okr.OkrApplication" />
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ This is an open source application for managing OKRs, developed by the team of a

This project contains two parts:

## Setup
Add subdomains to hosts file:
```shell
sudo sh -c 'echo "127.0.0.1 pitc.okr.localhost\n127.0.0.1 acme.okr.localhost" >> /etc/hosts'
```

## Frontend

[Frontend description](frontend/README.md)
Expand Down
16 changes: 8 additions & 8 deletions backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
<parent>
<groupId>ch.puzzle.okr</groupId>
<artifactId>parent</artifactId>
<version>2.0.39-SNAPSHOT</version>
<version>2.0.112-SNAPSHOT</version>
</parent>

<artifactId>backend</artifactId>
<version>2.0.39-SNAPSHOT</version>
<version>2.0.112-SNAPSHOT</version>
<name>backend</name>
<description>Puzzle OKR Tool</description>

Expand Down Expand Up @@ -54,17 +54,17 @@
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>10.6.0</version>
<version>10.11.1</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
<version>10.6.0</version>
<version>10.11.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
Expand All @@ -78,13 +78,13 @@
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<version>1.2.1</version>
<version>1.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.25.1</version>
<version>3.25.3</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand All @@ -102,7 +102,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
<version>0.8.12</version>
<configuration>
<excludes>
<exclude>ch/puzzle/okr/models/**</exclude>
Expand Down
22 changes: 22 additions & 0 deletions backend/src/main/java/ch/puzzle/okr/FlywayMultitenantConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ch.puzzle.okr;

import ch.puzzle.okr.multitenancy.FlywayMultitenantMigrationInitializer;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FlywayMultitenantConfig {

@Bean
public FlywayMigrationStrategy cleanMigrateStrategy(FlywayMultitenantMigrationInitializer flywayMigration) {
return flyway -> flywayMigration.migrateFlyway();
}

@Bean("customKeyGenerator")
public KeyGenerator keyGenerator() {
return new UserKeyGenerator();
}

}
3 changes: 3 additions & 0 deletions backend/src/main/java/ch/puzzle/okr/OkrApplication.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package ch.puzzle.okr;

import ch.puzzle.okr.service.clientconfig.ClientCustomizationProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableScheduling
@EnableConfigurationProperties(ClientCustomizationProperties.class)
public class OkrApplication {
public static void main(String[] args) {
SpringApplication.run(OkrApplication.class, args);
Expand Down
37 changes: 37 additions & 0 deletions backend/src/main/java/ch/puzzle/okr/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
package ch.puzzle.okr;

import com.nimbusds.jose.proc.SecurityContext;
import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
import com.nimbusds.jwt.proc.DefaultJWTProcessor;
import com.nimbusds.jwt.proc.JWTClaimsSetAwareJWSKeySelector;
import com.nimbusds.jwt.proc.JWTProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtValidators;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
Expand Down Expand Up @@ -41,6 +55,22 @@ public SecurityFilterChain securityHeadersFilter(HttpSecurity http) throws Excep
return setHeaders(http).build();
}

@Bean
JWTProcessor<SecurityContext> jwtProcessor(JWTClaimsSetAwareJWSKeySelector<SecurityContext> keySelector) {
ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
jwtProcessor.setJWTClaimsSetAwareJWSKeySelector(keySelector);
return jwtProcessor;
}

@Bean
JwtDecoder jwtDecoder(JWTProcessor<SecurityContext> jwtProcessor, OAuth2TokenValidator<Jwt> jwtValidator) {
NimbusJwtDecoder decoder = new NimbusJwtDecoder(jwtProcessor);
OAuth2TokenValidator<Jwt> validator = new DelegatingOAuth2TokenValidator<>(JwtValidators.createDefault(),
jwtValidator);
decoder.setJwtValidator(validator);
return decoder;
}

private HttpSecurity setHeaders(HttpSecurity http) throws Exception {
http.headers(h -> h
.contentSecurityPolicy(e -> e.policyDirectives("default-src 'self';"
Expand Down Expand Up @@ -69,4 +99,11 @@ private HttpSecurity setHeaders(HttpSecurity http) throws Exception {
+ " publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(self), "
+ "usb=(), web-share=(), xr-spatial-tracking=()")));
}

@Bean
public AuthenticationEventPublisher authenticationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
return new DefaultAuthenticationEventPublisher(applicationEventPublisher);
}

}
7 changes: 6 additions & 1 deletion backend/src/main/java/ch/puzzle/okr/SpringCachingConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package ch.puzzle.okr;

import ch.puzzle.okr.models.User;
import ch.puzzle.okr.multitenancy.TenantContext;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
Expand All @@ -11,10 +13,13 @@
public class SpringCachingConfig {

public static final String AUTHORIZATION_USER_CACHE = "authorizationUsers";
public static final String USER_CACHE = "users";

@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager(AUTHORIZATION_USER_CACHE);
}

public static String cacheKey(User user) {
return TenantContext.getCurrentTenant() + "_" + user.getEmail();
}
}
16 changes: 16 additions & 0 deletions backend/src/main/java/ch/puzzle/okr/UserKeyGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ch.puzzle.okr;

import ch.puzzle.okr.models.User;
import ch.puzzle.okr.multitenancy.TenantContext;
import org.springframework.cache.interceptor.KeyGenerator;

import java.lang.reflect.Method;
import java.text.MessageFormat;

public class UserKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
User user = (User) params[0];
return MessageFormat.format("{0}{1}{2}", TenantContext.getCurrentTenant(), user.getEmail(), user.getId());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ch.puzzle.okr.controller;

import ch.puzzle.okr.service.ClientConfigService;
import ch.puzzle.okr.dto.ClientConfigDto;
import ch.puzzle.okr.service.clientconfig.ClientConfigService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -20,7 +22,8 @@ public ClientConfigController(ClientConfigService configService) {
}

@GetMapping
public ResponseEntity<Map<String, String>> getConfig() {
return ResponseEntity.status(HttpStatus.OK).body(configService.getConfigBasedOnActiveEnv());
public ResponseEntity<ClientConfigDto> getConfig(HttpServletRequest request) {
return ResponseEntity.status(HttpStatus.OK)
.body(configService.getConfigBasedOnActiveEnv(request.getServerName()));
}
}
Loading

0 comments on commit 7f86c70

Please sign in to comment.