diff --git a/src/main/java/com/github/microcatalog/config/CSPProperties.java b/src/main/java/com/github/microcatalog/config/CSPProperties.java new file mode 100644 index 0000000..1a93e54 --- /dev/null +++ b/src/main/java/com/github/microcatalog/config/CSPProperties.java @@ -0,0 +1,19 @@ +package com.github.microcatalog.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "csp", ignoreUnknownFields = false) +public class CSPProperties { + private String imageSrc = "'self' data:"; + + public CSPProperties() { + } + + public String getImageSrc() { + return imageSrc; + } + + public void setImageSrc(String imageSrc) { + this.imageSrc = imageSrc; + } +} diff --git a/src/main/java/com/github/microcatalog/config/SecurityConfiguration.java b/src/main/java/com/github/microcatalog/config/SecurityConfiguration.java index 9c4ce9e..1ebe78b 100644 --- a/src/main/java/com/github/microcatalog/config/SecurityConfiguration.java +++ b/src/main/java/com/github/microcatalog/config/SecurityConfiguration.java @@ -1,8 +1,9 @@ package com.github.microcatalog.config; -import com.github.microcatalog.security.*; -import com.github.microcatalog.security.jwt.*; - +import com.github.microcatalog.security.AuthoritiesConstants; +import com.github.microcatalog.security.jwt.JWTConfigurer; +import com.github.microcatalog.security.jwt.TokenProvider; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.http.HttpMethod; @@ -22,17 +23,18 @@ @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) @Import(SecurityProblemSupport.class) +@EnableConfigurationProperties(CSPProperties.class) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { - + private final CSPProperties cspProperties; private final TokenProvider tokenProvider; - private final CorsFilter corsFilter; private final SecurityProblemSupport problemSupport; - public SecurityConfiguration(TokenProvider tokenProvider, CorsFilter corsFilter, SecurityProblemSupport problemSupport) { + public SecurityConfiguration(TokenProvider tokenProvider, CorsFilter corsFilter, SecurityProblemSupport problemSupport, CSPProperties cspProperties) { this.tokenProvider = tokenProvider; this.corsFilter = corsFilter; this.problemSupport = problemSupport; + this.cspProperties = cspProperties; } @Bean @@ -60,22 +62,23 @@ public void configure(HttpSecurity http) throws Exception { .disable() .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class) .exceptionHandling() - .authenticationEntryPoint(problemSupport) - .accessDeniedHandler(problemSupport) - .and() + .authenticationEntryPoint(problemSupport) + .accessDeniedHandler(problemSupport) + .and() .headers() - .contentSecurityPolicy("default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src *; font-src 'self' data:") - .and() + .contentSecurityPolicy( + String.format("default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src %s; font-src 'self' data:", cspProperties.getImageSrc())) + .and() .referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN) - .and() + .and() .featurePolicy("geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; fullscreen 'self'; payment 'none'") - .and() + .and() .frameOptions() .deny() - .and() + .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and() + .and() .authorizeRequests() .antMatchers("/api/authenticate").permitAll() .antMatchers("/api/register").permitAll() @@ -87,9 +90,9 @@ public void configure(HttpSecurity http) throws Exception { .antMatchers("/management/info").permitAll() .antMatchers("/management/prometheus").permitAll() .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN) - .and() + .and() .httpBasic() - .and() + .and() .apply(securityConfigurerAdapter()); // @formatter:on } diff --git a/src/main/resources/config/application-debug.yml b/src/main/resources/config/application-debug.yml new file mode 100644 index 0000000..949cf9e --- /dev/null +++ b/src/main/resources/config/application-debug.yml @@ -0,0 +1,80 @@ +# =================================================================== +# Spring Boot configuration for the "heroku" profile. +# +# This configuration overrides the application.yml file. +# =================================================================== + +# =================================================================== +# Standard Spring Boot properties. +# Full reference is available at: +# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# =================================================================== + +spring: + liquibase: + enabled: false + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: org.postgresql.Driver + url: ${JDBC_DATABASE_URL} + username: ${JDBC_DATABASE_USERNAME} + password: ${JDBC_DATABASE_PASSWORD} + hikari: + poolName: Hikari + auto-commit: false + jpa: + database-platform: io.github.jhipster.domain.util.FixedPostgreSQL10Dialect + show-sql: false + h2: + console: + enabled: false + mail: + host: localhost + port: 25 + username: + password: + thymeleaf: + cache: true + +server: + port: 8080 + compression: + enabled: true + mime-types: text/html,text/xml,text/plain,text/css, application/javascript, application/json + min-response-size: 1024 + +jhipster: + http: + cache: # Used by the CachingHttpHeadersFilter + timeToLiveInDays: 1461 + cache: # Cache configuration + ehcache: # Ehcache configuration + time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache + max-entries: 1000 # Number of objects in each cache entry + security: + authentication: + jwt: + # This token must be encoded using Base64 and be at least 256 bits long (you can type `openssl rand -base64 64` on your command line to generate a 512 bits one) + # As this is the PRODUCTION configuration, you MUST change the default key, and store it securely: + # - In the JHipster Registry (which includes a Spring Cloud Config server) + # - In a separate `application-prod.yml` file, in the same folder as your executable JAR file + # - In the `JHIPSTER_SECURITY_AUTHENTICATION_JWT_BASE64_SECRET` environment variable + base64-secret: YzljOTRhYTYwMDkwM2RhYjIxOGU2MTNjMDRjMDIyMjJjNjUyMzlmNzM5MjU1MzY3ZDI4NWFkY2YxNTM1YmI3N2EyZjRiNGE3OGZlYzcxNjJkYmRiNDNlNGFjYzI2YWZjMWNiYzI3NjE1NmVlMWJhMWVjNzZhNzhhNDY4Yzg0MzA= + # Token is valid 24 hours + token-validity-in-seconds: 86400 + token-validity-in-seconds-for-remember-me: 2592000 + mail: # specific JHipster mail property, for standard properties see MailProperties + base-url: http://my-server-url-to-change # Modify according to your server's URL + metrics: + logs: # Reports metrics in the logs + enabled: false + report-frequency: 60 # in seconds + logging: + use-json-format: false # By default, logs are not in Json format + logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration + enabled: false + host: localhost + port: 5000 + queue-size: 512 + audit-events: + retention-period: 30 # Number of days before audit events are deleted. diff --git a/src/main/resources/config/application-heroku.yml b/src/main/resources/config/application-heroku.yml index a2568de..1d7e7b4 100644 --- a/src/main/resources/config/application-heroku.yml +++ b/src/main/resources/config/application-heroku.yml @@ -16,6 +16,9 @@ eureka: non-secure-port: 80 prefer-ip-address: false +csp: + image-src: '*' + spring: datasource: type: com.zaxxer.hikari.HikariDataSource diff --git a/src/main/webapp/app/dashboard/dependency-dashboard/dependency-dashboard.component.ts b/src/main/webapp/app/dashboard/dependency-dashboard/dependency-dashboard.component.ts index 8cf39d2..d49f1ad 100644 --- a/src/main/webapp/app/dashboard/dependency-dashboard/dependency-dashboard.component.ts +++ b/src/main/webapp/app/dashboard/dependency-dashboard/dependency-dashboard.component.ts @@ -1,16 +1,17 @@ -import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'; import { DataSet } from 'vis-data/peer'; import { Network } from 'vis-network/peer'; import { DependencyService } from '../../entities/dependency/dependency.service'; import { IDependency } from '../../shared/model/dependency.model'; import { map } from 'rxjs/operators'; +import { IMicroservice } from '../../shared/model/microservice.model'; @Component({ selector: 'jhi-dependency-dashboard', templateUrl: './dependency-dashboard.component.html', styleUrls: ['./dependency-dashboard.component.scss'], }) -export class DependencyDashboardComponent implements OnInit, AfterViewInit { +export class DependencyDashboardComponent implements AfterViewInit { @ViewChild('visNetwork', { static: false }) visNetwork!: ElementRef; @@ -18,8 +19,6 @@ export class DependencyDashboardComponent implements OnInit, AfterViewInit { constructor(protected dependencyService: DependencyService) {} - ngOnInit(): void {} - ngAfterViewInit(): void { const container = this.visNetwork; @@ -55,23 +54,33 @@ export class DependencyDashboardComponent implements OnInit, AfterViewInit { } refreshGraph(dependencies: IDependency[]): void { - const nodes = new DataSet(); const edges = new DataSet(); + const microservicesMap = new Map(); + dependencies.forEach(d => { - nodes.add({ - id: d.id, - label: d.name, - }); + if (d.source != null && d.target != null) { + microservicesMap.set(d.source.id!, d.source); + microservicesMap.set(d.target.id!, d.target); - edges.add({ - from: d.source?.id, - to: d.target?.id, - }); + edges.add({ + from: d.source.id, + to: d.target.id, + }); + } }); + const nodes = new DataSet([...microservicesMap.values()].map(m => this.convertToGraphNode(m))); + const data = { nodes, edges }; this.networkInstance.setData(data); } + + convertToGraphNode(microservice: IMicroservice): any { + return { + id: microservice.id, + label: microservice.name, + }; + } }