TSK-1651: Add XSRF Support (#1602)
* TSK-1651: Add XSRF Support * TSK-1651: now sending XRSF Header for requests with absolute path * TSK-1651: created configuration to enable XSRF for spring-boot example Co-authored-by: Mustapha Zorgati <15628173+mustaphazorgati@users.noreply.github.com>
This commit is contained in:
parent
dd5bccc62c
commit
9b98b56ce4
|
|
@ -8,6 +8,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||||
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
|
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
|
||||||
|
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
||||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
|
||||||
|
|
@ -26,15 +27,18 @@ public class BootWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
|
||||||
private final String ldapUserDnPatterns;
|
private final String ldapUserDnPatterns;
|
||||||
|
|
||||||
private final boolean devMode;
|
private final boolean devMode;
|
||||||
|
private final boolean enableCsrf;
|
||||||
|
|
||||||
public BootWebSecurityConfigurer(
|
public BootWebSecurityConfigurer(
|
||||||
@Value("${taskana.ldap.serverUrl:ldap://localhost:10389}") String ldapServerUrl,
|
@Value("${taskana.ldap.serverUrl:ldap://localhost:10389}") String ldapServerUrl,
|
||||||
@Value("${taskana.ldap.baseDn:OU=Test,O=TASKANA}") String ldapBaseDn,
|
@Value("${taskana.ldap.baseDn:OU=Test,O=TASKANA}") String ldapBaseDn,
|
||||||
@Value("${taskana.ldap.groupSearchBase:cn=groups}") String ldapGroupSearchBase,
|
@Value("${taskana.ldap.groupSearchBase:cn=groups}") String ldapGroupSearchBase,
|
||||||
@Value("${taskana.ldap.userDnPatterns:uid={0},cn=users}") String ldapUserDnPatterns,
|
@Value("${taskana.ldap.userDnPatterns:uid={0},cn=users}") String ldapUserDnPatterns,
|
||||||
|
@Value("${enableCsrf:false}") boolean enableCsrf,
|
||||||
LdapAuthoritiesPopulator ldapAuthoritiesPopulator,
|
LdapAuthoritiesPopulator ldapAuthoritiesPopulator,
|
||||||
GrantedAuthoritiesMapper grantedAuthoritiesMapper,
|
GrantedAuthoritiesMapper grantedAuthoritiesMapper,
|
||||||
@Value("${devMode:false}") boolean devMode) {
|
@Value("${devMode:false}") boolean devMode) {
|
||||||
|
this.enableCsrf = enableCsrf;
|
||||||
this.ldapAuthoritiesPopulator = ldapAuthoritiesPopulator;
|
this.ldapAuthoritiesPopulator = ldapAuthoritiesPopulator;
|
||||||
this.grantedAuthoritiesMapper = grantedAuthoritiesMapper;
|
this.grantedAuthoritiesMapper = grantedAuthoritiesMapper;
|
||||||
this.ldapServerUrl = ldapServerUrl;
|
this.ldapServerUrl = ldapServerUrl;
|
||||||
|
|
@ -60,15 +64,11 @@ public class BootWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
HttpSecurity httpSecurity =
|
||||||
http.authorizeRequests()
|
http.authorizeRequests()
|
||||||
.antMatchers("/css/**", "/img/**")
|
.antMatchers("/css/**", "/img/**")
|
||||||
.permitAll()
|
.permitAll()
|
||||||
.and()
|
.and()
|
||||||
.csrf()
|
|
||||||
.disable()
|
|
||||||
.httpBasic()
|
|
||||||
.and()
|
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
.antMatchers(HttpMethod.GET, "/docs/**")
|
.antMatchers(HttpMethod.GET, "/docs/**")
|
||||||
.permitAll()
|
.permitAll()
|
||||||
|
|
@ -76,6 +76,14 @@ public class BootWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
|
||||||
.addFilter(jaasApiIntegrationFilter())
|
.addFilter(jaasApiIntegrationFilter())
|
||||||
.addFilterAfter(new SpringSecurityToJaasFilter(), JaasApiIntegrationFilter.class);
|
.addFilterAfter(new SpringSecurityToJaasFilter(), JaasApiIntegrationFilter.class);
|
||||||
|
|
||||||
|
if (enableCsrf) {
|
||||||
|
CookieCsrfTokenRepository csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
|
||||||
|
csrfTokenRepository.setCookiePath("/");
|
||||||
|
httpSecurity.csrf().csrfTokenRepository(csrfTokenRepository);
|
||||||
|
} else {
|
||||||
|
httpSecurity.csrf().disable().httpBasic();
|
||||||
|
}
|
||||||
|
|
||||||
if (devMode) {
|
if (devMode) {
|
||||||
http.headers()
|
http.headers()
|
||||||
.frameOptions()
|
.frameOptions()
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ taskana.schemaName=TASKANA
|
||||||
####### property that control rest api security deploy use true for no security.
|
####### property that control rest api security deploy use true for no security.
|
||||||
devMode=false
|
devMode=false
|
||||||
|
|
||||||
|
# This property enables the support of XSRF tokens. This will not work together with devMode.
|
||||||
|
enableCsrf=true
|
||||||
|
|
||||||
####### property that control if the database is cleaned and sample data is generated
|
####### property that control if the database is cleaned and sample data is generated
|
||||||
generateSampleData=true
|
generateSampleData=true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
import { HttpClientModule, HttpClientXsrfModule, HttpXsrfTokenExtractor } from '@angular/common/http';
|
||||||
import { NgxsModule } from '@ngxs/store';
|
import { NgxsModule } from '@ngxs/store';
|
||||||
import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin';
|
import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin';
|
||||||
import { AlertModule } from 'ngx-bootstrap';
|
import { AlertModule } from 'ngx-bootstrap';
|
||||||
|
|
@ -82,7 +82,8 @@ const MODULES = [
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatSelectModule,
|
MatSelectModule,
|
||||||
NgxsModule.forRoot(STATES, { developmentMode: !environment.production }),
|
NgxsModule.forRoot(STATES, { developmentMode: !environment.production }),
|
||||||
NgxsReduxDevtoolsPluginModule.forRoot({ disabled: environment.production, maxAge: 25 })
|
NgxsReduxDevtoolsPluginModule.forRoot({ disabled: environment.production, maxAge: 25 }),
|
||||||
|
HttpClientXsrfModule
|
||||||
];
|
];
|
||||||
|
|
||||||
const DECLARATIONS = [AppComponent, NavBarComponent, UserInformationComponent, NoAccessComponent, SidenavListComponent];
|
const DECLARATIONS = [AppComponent, NavBarComponent, UserInformationComponent, NoAccessComponent, SidenavListComponent];
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
|
import {
|
||||||
|
HttpErrorResponse,
|
||||||
|
HttpEvent,
|
||||||
|
HttpHandler,
|
||||||
|
HttpInterceptor,
|
||||||
|
HttpRequest,
|
||||||
|
HttpXsrfTokenExtractor
|
||||||
|
} from '@angular/common/http';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service';
|
import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service';
|
||||||
import { environment } from 'environments/environment';
|
import { environment } from 'environments/environment';
|
||||||
|
|
@ -9,10 +16,18 @@ import { NOTIFICATION_TYPES } from '../../models/notifications';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class HttpClientInterceptor implements HttpInterceptor {
|
export class HttpClientInterceptor implements HttpInterceptor {
|
||||||
constructor(private requestInProgressService: RequestInProgressService, private errorsService: NotificationService) {}
|
constructor(
|
||||||
|
private requestInProgressService: RequestInProgressService,
|
||||||
|
private errorsService: NotificationService,
|
||||||
|
private tokenExtractor: HttpXsrfTokenExtractor
|
||||||
|
) {}
|
||||||
|
|
||||||
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
let req = request.clone({ headers: request.headers.set('Content-Type', 'application/hal+json') });
|
let req = request.clone({ setHeaders: { 'Content-Type': 'application/hal+json' } });
|
||||||
|
let token = this.tokenExtractor.getToken() as string;
|
||||||
|
if (token !== null) {
|
||||||
|
req = req.clone({ setHeaders: { 'X-XSRF-TOKEN': token } });
|
||||||
|
}
|
||||||
if (!environment.production) {
|
if (!environment.production) {
|
||||||
req = req.clone({ headers: req.headers.set('Authorization', 'Basic YWRtaW46YWRtaW4=') });
|
req = req.clone({ headers: req.headers.set('Authorization', 'Basic YWRtaW46YWRtaW4=') });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue