├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ ├── resources │ │ ├── static │ │ │ ├── favicon.ico │ │ │ └── css │ │ │ │ └── style.css │ │ ├── templates │ │ │ ├── models │ │ │ │ ├── user-list-item.mustache │ │ │ │ └── user-profile.mustache │ │ │ ├── layout │ │ │ │ ├── footer.mustache │ │ │ │ ├── end.mustache │ │ │ │ ├── start.mustache │ │ │ │ └── nav.mustache │ │ │ ├── error │ │ │ │ └── 404.mustache │ │ │ ├── profiles │ │ │ │ └── view.mustache │ │ │ └── index.mustache │ │ └── application.yaml │ └── java │ │ └── daggerok │ │ ├── security │ │ ├── userdetails │ │ │ ├── ReactiveRepositoryUserDetailsService.java │ │ │ └── CustomUserDetails.java │ │ ├── SecurityAdvice.java │ │ └── SecurityConfig.java │ │ ├── user │ │ ├── User.java │ │ ├── UserRepository.java │ │ └── UserRoutes.java │ │ ├── web │ │ ├── ProfilePage.java │ │ └── IndexPage.java │ │ └── WebappApplication.java └── test │ └── java │ └── daggerok │ └── WebappApplicationTests.java ├── .travis.yml ├── .gitignore ├── gradlew.bat ├── README.adoc ├── pom.xml ├── mvnw.cmd ├── gradlew └── mvnw /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daggerok/csrf-spring-webflux-mustache/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daggerok/csrf-spring-webflux-mustache/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip -------------------------------------------------------------------------------- /src/main/resources/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daggerok/csrf-spring-webflux-mustache/HEAD/src/main/resources/static/favicon.ico -------------------------------------------------------------------------------- /src/main/resources/templates/models/user-list-item.mustache: -------------------------------------------------------------------------------- 1 | {{#id}} 2 | {{#username}} 3 | 4 | {{id}} - {{username}} 5 | 6 | {{/username}} 7 | {{/id}} 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip 6 | -------------------------------------------------------------------------------- /src/main/resources/templates/layout/footer.mustache: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/main/resources/templates/layout/end.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | output: 3 | ansi: 4 | enabled: always 5 | data: 6 | mongodb: 7 | database: csrf 8 | host: ${SPRING_DATA_MONGODB_HOST:127.0.0.1} 9 | mvc: 10 | favicon: 11 | enabled: false 12 | mustache: 13 | expose-request-attributes: true 14 | expose-spring-macro-helpers: true 15 | -------------------------------------------------------------------------------- /src/main/resources/templates/error/404.mustache: -------------------------------------------------------------------------------- 1 | {{>layout/start}} 2 | 3 | 4 | 5 | {{>layout/nav}} 6 | 7 |
8 |
9 |

\=

10 | 11 | home 12 | 13 |
14 |
15 | 16 | {{>layout/footer}} 17 | 18 | {{>layout/end}} 19 | -------------------------------------------------------------------------------- /src/main/resources/templates/profiles/view.mustache: -------------------------------------------------------------------------------- 1 | {{>layout/start}} 2 | 3 | 4 | 5 | {{>layout/nav}} 6 | 7 |
8 |
9 | 10 |
11 | hey, {{#currentUser}}{{currentUser.username}} ({{currentUser.id}}){{/currentUser}}! 12 |
13 | 14 | {{> models/user-profile}} 15 | 16 |
17 |
18 | 19 | {{>layout/footer}} 20 | 21 | {{>layout/end}} 22 | -------------------------------------------------------------------------------- /src/test/java/daggerok/WebappApplicationTests.java: -------------------------------------------------------------------------------- 1 | package daggerok; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 10 | public class WebappApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/templates/models/user-profile.mustache: -------------------------------------------------------------------------------- 1 | {{#user}} 2 | 7 | {{/user}} 8 | -------------------------------------------------------------------------------- /src/main/resources/templates/layout/start.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | User creator 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/resources/templates/layout/nav.mustache: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /src/main/resources/static/css/style.css: -------------------------------------------------------------------------------- 1 | /* CSS Grid: begin */ 2 | 3 | .grid-container { 4 | width: 100vw; 5 | height: 100vh; 6 | display: grid; 7 | grid-template-columns: 1fr; 8 | grid-template-rows: 1fr 10fr 1fr; 9 | grid-template-areas: 10 | "grid-header" 11 | "grid-main-content" 12 | "grid-footer"; 13 | } 14 | 15 | .grid-header { 16 | grid-area: grid-header; 17 | } 18 | 19 | .grid-main-content { 20 | grid-area: grid-main-content; 21 | } 22 | 23 | .grid-footer { 24 | grid-area: grid-footer; 25 | } 26 | 27 | /* CSS Grid: end */ 28 | 29 | .transparent-radius-border { 30 | border: none; 31 | border-radius: 50%; 32 | } 33 | 34 | .grid-footer { 35 | padding: 0; 36 | } 37 | 38 | .footer-copyright { 39 | height: 100%; 40 | text-align: center; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/daggerok/security/userdetails/ReactiveRepositoryUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package daggerok.security.userdetails; 2 | 3 | import daggerok.user.UserRepository; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.security.core.userdetails.ReactiveUserDetailsService; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.stereotype.Service; 8 | import reactor.core.publisher.Mono; 9 | 10 | @Service 11 | @RequiredArgsConstructor 12 | public class ReactiveRepositoryUserDetailsService implements ReactiveUserDetailsService { 13 | 14 | final UserRepository users; 15 | 16 | @Override public Mono findByUsername(final String username) { 17 | 18 | return users.findByUsername(username) 19 | .map(CustomUserDetails::new); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/daggerok/user/User.java: -------------------------------------------------------------------------------- 1 | package daggerok.user; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | import org.springframework.data.annotation.Id; 8 | import org.springframework.data.mongodb.core.mapping.Document; 9 | import org.springframework.format.annotation.DateTimeFormat; 10 | 11 | import java.io.Serializable; 12 | import java.time.LocalDateTime; 13 | 14 | @Data 15 | @Document 16 | @Accessors(chain = true) 17 | @JsonIgnoreProperties(ignoreUnknown = true) 18 | public class User implements Serializable { 19 | 20 | private static final long serialVersionUID = -221084014830948927L; 21 | 22 | @Id String id; 23 | 24 | String username; 25 | @JsonIgnore String password; 26 | 27 | @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) 28 | LocalDateTime lastModifiedAt; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/daggerok/user/UserRepository.java: -------------------------------------------------------------------------------- 1 | package daggerok.user; 2 | 3 | import org.springframework.data.mongodb.repository.ReactiveMongoRepository; 4 | import org.springframework.data.repository.query.Param; 5 | import org.springframework.stereotype.Repository; 6 | import reactor.core.publisher.Flux; 7 | import reactor.core.publisher.Mono; 8 | 9 | @Repository 10 | public interface UserRepository extends ReactiveMongoRepository { 11 | 12 | // use getCreatedBy(username) instead 13 | Flux findTop10ByUsernameContainingIgnoreCaseAndLastModifiedAtNotNullOrderByLastModifiedAtDesc( 14 | @Param("username") final String username 15 | ); 16 | 17 | default Flux getCreatedBy(final String username) { 18 | return findTop10ByUsernameContainingIgnoreCaseAndLastModifiedAtNotNullOrderByLastModifiedAtDesc(username); 19 | } 20 | 21 | // use getAny() instead 22 | Flux findTop10ByLastModifiedAtNotNullOrderByLastModifiedAtDesc(); 23 | 24 | default Flux getAny() { 25 | return findTop10ByLastModifiedAtNotNullOrderByLastModifiedAtDesc(); 26 | } 27 | 28 | Mono findByUsername(final String username); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/daggerok/web/ProfilePage.java: -------------------------------------------------------------------------------- 1 | package daggerok.web; 2 | 3 | import daggerok.user.User; 4 | import daggerok.user.UserRepository; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PathVariable; 11 | import org.springframework.web.reactive.result.view.Rendering; 12 | 13 | @Slf4j 14 | @Controller 15 | @RequiredArgsConstructor 16 | public class ProfilePage { 17 | 18 | final UserRepository users; 19 | 20 | @GetMapping({ "/profiles/{username}" }) 21 | public Rendering byUsername(@AuthenticationPrincipal final User owner, 22 | @PathVariable(required = false) final String username) { 23 | 24 | final String parsed = null == username ? owner.getUsername() : username; 25 | 26 | return Rendering.view("profiles/view") 27 | .modelAttribute("user", users.findByUsername(parsed)) 28 | .build(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/daggerok/security/userdetails/CustomUserDetails.java: -------------------------------------------------------------------------------- 1 | package daggerok.security.userdetails; 2 | 3 | import daggerok.user.User; 4 | import org.springframework.security.core.GrantedAuthority; 5 | import org.springframework.security.core.authority.AuthorityUtils; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | 8 | import java.util.Collection; 9 | 10 | class CustomUserDetails extends User implements UserDetails { 11 | 12 | private static final long serialVersionUID = -5306760386322252897L; 13 | 14 | public CustomUserDetails(final User user) { 15 | 16 | this.setId(user.getId()) 17 | .setUsername(user.getUsername()) 18 | .setPassword(user.getPassword()) 19 | .setLastModifiedAt(user.getLastModifiedAt()); 20 | } 21 | 22 | @Override public Collection getAuthorities() { 23 | return AuthorityUtils.createAuthorityList("ROLE_USER"); 24 | } 25 | 26 | @Override public boolean isAccountNonExpired() { 27 | return isEnabled(); 28 | } 29 | 30 | @Override public boolean isAccountNonLocked() { 31 | return isEnabled(); 32 | } 33 | 34 | @Override public boolean isCredentialsNonExpired() { 35 | return isEnabled(); 36 | } 37 | 38 | @Override public boolean isEnabled() { 39 | return true; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | env: 2 | global: 3 | TERM=dumb 4 | language: java 5 | jdk: oraclejdk8 6 | install: true 7 | before_script: 8 | - sudo apt update -y || true 9 | - sudo apt -y install python-pip 10 | - sudo pip install httpie 11 | - source <(curl -fsSL https://raw.github.com/daggerok/bash-functions/master/main.bash) 12 | script: 13 | # maven 14 | - ./mvnw 15 | - java -jar target/*.jar & 16 | - wait_for 8080 17 | - http -a user:user get :8080/ 18 | - http -a user:user get :8080/api/v1/users 19 | - http -a admin:admin get :8080/api/v1/users 20 | - http -a user:user get :8080/profiles/admin 21 | - http -a admin:admin get :8080/profiles/admin 22 | - http -a admin:admin get :8080/api/v1/users/ accept:application/stream+json 23 | - http -a admin:admin post :8080/api/v1/users username=ololo 24 | - stop_any 8080 25 | # gradle 26 | - ./gradlew 27 | - java -jar build/libs/*.jar & 28 | - wait_for 8080 29 | - http -a user:user get :8080/ 30 | - http -a user:user get :8080/api/v1/users 31 | - http -a admin:admin get :8080/api/v1/users 32 | - http -a user:user get :8080/profiles/admin 33 | - http -a admin:admin get :8080/profiles/admin 34 | - http -a admin:admin get :8080/api/v1/users/ accept:application/stream+json 35 | - http -a admin:admin post :8080/api/v1/users username=ololo 36 | - stop_any 8080 37 | cache: 38 | directories: 39 | - $HOME/.m2/ 40 | - $HOME/.gradle/ 41 | -------------------------------------------------------------------------------- /src/main/java/daggerok/security/SecurityAdvice.java: -------------------------------------------------------------------------------- 1 | package daggerok.security; 2 | 3 | import daggerok.user.User; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 6 | import org.springframework.security.web.server.csrf.CsrfToken; 7 | import org.springframework.web.bind.annotation.ControllerAdvice; 8 | import org.springframework.web.bind.annotation.ModelAttribute; 9 | import org.springframework.web.server.ServerWebExchange; 10 | import reactor.core.publisher.Mono; 11 | 12 | import static org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME; 13 | 14 | @Slf4j 15 | @ControllerAdvice 16 | public class SecurityAdvice { 17 | 18 | /** 19 | * @return CSRF:see src/main/resources/templates/index.mustache 20 | */ 21 | @ModelAttribute("_csrf") 22 | Mono csrfToken(final ServerWebExchange exchange) { 23 | 24 | final Mono csrfToken = exchange.getAttribute(CsrfToken.class.getName()); 25 | 26 | return csrfToken.doOnSuccess(token -> exchange.getAttributes() 27 | .put(DEFAULT_CSRF_ATTR_NAME, token)); 28 | } 29 | 30 | @ModelAttribute("currentUser") // 1: replace `Principle` with project `User` 31 | Mono currentUser(@AuthenticationPrincipal final Mono currentUser) { 32 | return currentUser; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | build/ 3 | out/ 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea/ 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ 25 | 26 | node_modules/ 27 | yarn.lock 28 | back/.gradle 29 | build/ 30 | .gradle/ 31 | !gradle/wrapper/gradle-wrapper.jar 32 | 33 | ### STS ### 34 | .apt_generated 35 | .classpath 36 | .factorypath 37 | .project 38 | .settings 39 | .springBeans 40 | 41 | ### IntelliJ IDEA ### 42 | .idea 43 | *.iws 44 | *.iml 45 | *.ipr 46 | 47 | ### NetBeans ### 48 | nbproject/private/ 49 | nbbuild/ 50 | dist/ 51 | nbdist/ 52 | .nb-gradle/ 53 | 54 | # See http://help.github.com/ignore-files/ for more about ignoring files. 55 | 56 | # compiled output 57 | /dist 58 | /tmp 59 | /out-tsc 60 | 61 | # dependencies 62 | /node_modules 63 | 64 | # IDEs and editors 65 | .c9/ 66 | *.launch 67 | .settings/ 68 | *.sublime-workspace 69 | 70 | # IDE - VSCode 71 | .vscode/* 72 | !.vscode/settings.json 73 | !.vscode/tasks.json 74 | !.vscode/launch.json 75 | !.vscode/extensions.json 76 | 77 | # misc 78 | /.sass-cache 79 | /connect.lock 80 | /coverage 81 | /libpeerconnection.log 82 | npm-debug.log 83 | testem.log 84 | /typings 85 | 86 | # e2e 87 | /e2e/*.js 88 | /e2e/*.map 89 | 90 | # System Files 91 | .DS_Store 92 | Thumbs.db 93 | 94 | **/ui/src/main/resources/static/ 95 | yarn.lock 96 | package-lock.json 97 | 98 | -------------------------------------------------------------------------------- /src/main/resources/templates/index.mustache: -------------------------------------------------------------------------------- 1 | {{>layout/start}} 2 | 3 | 4 | 5 | {{>layout/nav}} 6 | 7 |
8 |
9 | 10 |
11 | hey, {{#currentUser}}{{currentUser.username}} ({{currentUser.id}}){{/currentUser}}! 12 |
13 | 14 |
15 |
16 |
17 |
18 | account_circle 19 | 24 | 25 |
26 |
27 | 32 |
33 |
34 | 35 | 37 |
38 |
39 | 40 |
maybe mine users
41 |
    42 | {{#createdByMe}} 43 |
  • {{> models/user-list-item}}
  • 44 | {{/createdByMe}} 45 |
46 | 47 |
all users
48 |
    49 | {{#users}} 50 |
  • {{> models/user-list-item}}
  • 51 | {{/users}} 52 |
53 |
54 |
55 | 56 | {{>layout/footer}} 57 | 58 | {{>layout/end}} 59 | -------------------------------------------------------------------------------- /src/main/java/daggerok/user/UserRoutes.java: -------------------------------------------------------------------------------- 1 | package daggerok.user; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.security.access.prepost.PreAuthorize; 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.web.reactive.function.server.RouterFunction; 9 | import org.springframework.web.reactive.function.server.ServerRequest; 10 | import org.springframework.web.reactive.function.server.ServerResponse; 11 | import reactor.core.publisher.Mono; 12 | 13 | import static org.springframework.http.MediaType.APPLICATION_STREAM_JSON; 14 | import static org.springframework.web.reactive.function.server.RequestPredicates.GET; 15 | import static org.springframework.web.reactive.function.server.RequestPredicates.POST; 16 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 17 | 18 | @Service 19 | @RequiredArgsConstructor class UserHandlers { 20 | 21 | final UserRepository users; 22 | 23 | public Mono streamUsers(final ServerRequest inore) { 24 | 25 | return ServerResponse.ok() 26 | .contentType(APPLICATION_STREAM_JSON) 27 | .body(users.findAll(), User.class); 28 | } 29 | 30 | @PreAuthorize("hasRole('ROLE_ADMIN')") 31 | public Mono saveUser(final ServerRequest request) { 32 | 33 | return ServerResponse.ok() 34 | .contentType(APPLICATION_STREAM_JSON) 35 | .body(request.bodyToFlux(User.class) 36 | .flatMap(users::save), User.class); 37 | } 38 | } 39 | 40 | @Configuration 41 | public class UserRoutes { 42 | 43 | @Bean 44 | RouterFunction routes(final UserHandlers handlers) { 45 | 46 | return route(GET("/api/v1/users/**"), 47 | handlers::streamUsers) 48 | 49 | .andRoute(POST("/api/v1/users/**"), 50 | handlers::saveUser) 51 | ; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/daggerok/WebappApplication.java: -------------------------------------------------------------------------------- 1 | package daggerok; 2 | 3 | import daggerok.user.User; 4 | import daggerok.user.UserRepository; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.InitializingBean; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.core.Ordered; 11 | import org.springframework.core.annotation.Order; 12 | import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; 13 | import org.springframework.security.crypto.password.PasswordEncoder; 14 | import org.springframework.web.filter.reactive.HiddenHttpMethodFilter; 15 | import reactor.core.publisher.Flux; 16 | 17 | import static java.time.LocalDateTime.now; 18 | 19 | @Slf4j 20 | @SpringBootApplication 21 | @EnableReactiveMongoRepositories(considerNestedRepositories = true, basePackageClasses = User.class) 22 | public class WebappApplication { 23 | 24 | @Bean 25 | @Order(Ordered.HIGHEST_PRECEDENCE) 26 | public HiddenHttpMethodFilter hiddenHttpMethodFilter() { 27 | return new HiddenHttpMethodFilter(); 28 | } 29 | 30 | @Bean 31 | InitializingBean initializingBean(final UserRepository users, 32 | final PasswordEncoder encoder) { 33 | 34 | return () -> users.deleteAll() 35 | .thenMany(v -> Flux.just("user", "admin") 36 | .map(user -> new User().setUsername(user) 37 | .setPassword(encoder.encode(user)) 38 | .setLastModifiedAt(now())) 39 | .flatMap(users::save) 40 | .subscribe(u -> log.info("created user: {}", u.getUsername()))) 41 | .subscribe(); 42 | } 43 | 44 | public static void main(String[] args) { 45 | SpringApplication.run(WebappApplication.class, args); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/daggerok/web/IndexPage.java: -------------------------------------------------------------------------------- 1 | package daggerok.web; 2 | 3 | import daggerok.user.User; 4 | import daggerok.user.UserRepository; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 8 | import org.springframework.security.crypto.password.PasswordEncoder; 9 | import org.springframework.stereotype.Controller; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.reactive.result.view.Rendering; 13 | import reactor.core.publisher.Flux; 14 | import reactor.core.publisher.Mono; 15 | import reactor.core.scheduler.Schedulers; 16 | 17 | import static java.lang.String.format; 18 | import static java.time.LocalDateTime.now; 19 | 20 | @Slf4j 21 | @Controller 22 | @RequiredArgsConstructor 23 | public class IndexPage { 24 | 25 | final UserRepository users; 26 | final PasswordEncoder encoder; 27 | 28 | @GetMapping("/") 29 | public Rendering index(@AuthenticationPrincipal final User owner) { 30 | 31 | final Flux sharedUsers = users.getAny().share(); 32 | 33 | return Rendering.view("index") 34 | .modelAttribute("users", sharedUsers) 35 | .modelAttribute("createdByMe", 36 | sharedUsers.filter(u -> u.getUsername() 37 | .startsWith(format("%s-", owner.getUsername())))) 38 | .build(); 39 | } 40 | 41 | @PostMapping("/") 42 | public Mono post(final Mono user, 43 | @AuthenticationPrincipal final Mono owner) { 44 | 45 | return Flux.zip(owner, user) 46 | .map(p -> format("%s-%s", p.getT1().getUsername(), p.getT2().getUsername())) 47 | .map(r -> new User().setUsername(r)) 48 | .map(u -> u.setLastModifiedAt(now())) 49 | .subscribeOn(Schedulers.parallel()) 50 | .doOnNext(u -> u.setPassword(encoder.encode(u.getUsername()))) 51 | .flatMap(users::save) 52 | .then(Mono.just("redirect:/")) 53 | ; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/daggerok/security/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package daggerok.security; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.authorization.AuthorizationDecision; 6 | import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; 7 | import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; 8 | import org.springframework.security.config.web.server.ServerHttpSecurity; 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.crypto.factory.PasswordEncoderFactories; 11 | import org.springframework.security.crypto.password.PasswordEncoder; 12 | import org.springframework.security.web.server.SecurityWebFilterChain; 13 | import org.springframework.security.web.server.authorization.AuthorizationContext; 14 | import reactor.core.publisher.Mono; 15 | 16 | @Configuration 17 | @EnableWebFluxSecurity // if you need secure your functional routes 18 | @EnableReactiveMethodSecurity // if you need secure methods, like with @PreAuthorize 19 | public class SecurityConfig { 20 | 21 | @Bean SecurityWebFilterChain springSecurityFilterChain(final ServerHttpSecurity http) { 22 | 23 | http 24 | .authorizeExchange() 25 | .pathMatchers("/profiles/admin/**", "/api/**") 26 | .access(this::isAdmin) // custom decision to REST API security 27 | .pathMatchers("/favicon.ico", "/css/**", "/webjars/**") 28 | .permitAll() 29 | .anyExchange() 30 | .authenticated() 31 | .and() 32 | .httpBasic() 33 | .and() 34 | .formLogin() 35 | .and() 36 | .logout() 37 | ; 38 | 39 | return http.build(); 40 | } 41 | 42 | private Mono isAdmin(final Mono authentication, final AuthorizationContext authorizationContext) { 43 | 44 | return authentication.map(Authentication::getName) 45 | .map(login -> login.toLowerCase().contains("admin")) 46 | .map(AuthorizationDecision::new); 47 | } 48 | 49 | @Bean PasswordEncoder passwordEncoder() { 50 | return PasswordEncoderFactories.createDelegatingPasswordEncoder(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = reactive security image:https://travis-ci.org/daggerok/csrf-spring-webflux-mustache.svg?branch=master["Build Status", link="https://travis-ci.org/daggerok/csrf-spring-webflux-mustache"] 2 | 3 | This repository is contains example application using spring boot 2.0, webflux, spring security 5, reactive mongodb and mustache template engine 4 | 5 | .maven 6 | ---- 7 | ./mvnw clean spring-boot:run 8 | ---- 9 | 10 | .gradle 11 | ---- 12 | ./gradlew clean bootRun 13 | ---- 14 | 15 | .test 16 | ---- 17 | open http://127.0.0.1:8080 18 | # user / user 19 | # adin / admin 20 | 21 | http get :8080/api/v1/users 22 | # 401: Unauthorized 23 | 24 | http --auth user:user get :8080/api/v1/users 25 | # 403 Forbidden: Access Denied 26 | 27 | http -a admin:admin get :8080/api/v1/users accept:'text/html' 28 | # OK 29 | ---- 30 | 31 | NOTE: for details, see link:https://github.com/daggerok/csrf-spring-webflux-mustache/branches[repository branches] 32 | 33 | . csrf protection, no op password encoder (done link:../../blob/csrf/src/main/java/daggerok/web/SecurityConfig.java[in csrf branch]) 34 | . mongo userDetailService, standart password encoder (done link:../../tree/reactive-repository-user-details/[here]) 35 | . share mongodb publisher, use project user authentication principal (done link:../../tree/application-authentication-user/[in that branch]) 36 | . encode password (cpu costs) in separate thread (see link:https://github.com/daggerok/csrf-spring-webflux-mustache/blob/schedulers-parallel/src/main/java/daggerok/web/IndexPage.java[IndexPage.java]) 37 | . fix deprecated password encoder for support passwords migrations (done link:https://github.com/daggerok/csrf-spring-webflux-mustache/blob/delegate-password-encoder/src/main/java/daggerok/web/config/SecurityConfig.java[SecurityConfig.java]) 38 | . password-hash upgrade strategy on authentication (done link:https://github.com/daggerok/csrf-spring-webflux-mustache/blob/password-upgrade-strategy/src/main/java/daggerok/web/config/passwordmigration/PasswordUpgradeStrategyOnAuthenticationService.java[here]) 39 | . pathMatches by role (done link:https://github.com/daggerok/csrf-spring-webflux-mustache/blob/path-matchers-role/src/main/java/daggerok/web/config/SecurityConfig.java[SecurityConfig.java]) 40 | . pathMatches access authorization decision (done link:https://github.com/daggerok/csrf-spring-webflux-mustache/blob/path-matchers-authorization-decision/src/main/java/daggerok/web/config/SecurityConfig.java[SecurityConfig.java] 41 | and link:https://github.com/daggerok/csrf-spring-webflux-mustache/blob/path-matchers-authorization-decision/src/main/java/daggerok/web/security/SecurityConfig.java[SecurityConfig.java]) 42 | . method security (see link:https://github.com/daggerok/csrf-spring-webflux-mustache/blob/methods-webflux-routes-security/src/main/java/daggerok/user/UserRepository.java[UserRepository.java] 43 | and link:https://github.com/daggerok/csrf-spring-webflux-mustache/blob/methods-webflux-routes-security/src/main/java/daggerok/web/security/SecurityConfig.java[SecurityConfig.java]) 44 | . webflux functional routes API security (see link:https://github.com/daggerok/csrf-spring-webflux-mustache/blob/methods-webflux-routes-security/src/main/java/daggerok/user/UserRoutes.java[UserRoutes.java]) 45 | . oauth2, spring-cloud (todo) 46 | . authorization-server, resource-server, jwt... (todo) 47 | 48 | help resources: 49 | 50 | . link:http://mustache.github.io/mustache.5.html[mustache reference] 51 | . link:https://www.youtube.com/watch?v=EDO1zlyFq6I[Rob Winch talk] 52 | . link:https://projectreactor.io/docs/core/release/reference/[project reactor reference] 53 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | daggerok 6 | csrf-spring-webflux-mustache 7 | 0.0.1 8 | jar 9 | 10 | 11 | org.springframework.boot 12 | spring-boot-starter-parent 13 | 2.0.4.RELEASE 14 | 15 | 16 | 17 | 18 | UTF-8 19 | UTF-8 20 | 1.8 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-actuator 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-mustache 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-webflux 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-security 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-data-mongodb-reactive 43 | 44 | 45 | de.flapdoodle.embed 46 | de.flapdoodle.embed.mongo 47 | 48 | 49 | 50 | org.projectlombok 51 | lombok 52 | true 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-devtools 57 | true 58 | runtime 59 | 60 | 61 | 62 | org.webjars 63 | webjars-locator 64 | 0.32-1 65 | 66 | 67 | org.webjars 68 | materializecss 69 | 0.100.2 70 | 71 | 72 | org.webjars 73 | material-design-icons 74 | 3.0.1 75 | 76 | 77 | org.webjars 78 | jquery 79 | 3.2.1 80 | 81 | 82 | org.webjars 83 | hammerjs 84 | 2.0.8 85 | 86 | 87 | 88 | org.springframework.security 89 | spring-security-test 90 | test 91 | 92 | 93 | org.springframework.boot 94 | spring-boot-starter-test 95 | test 96 | 97 | 98 | io.projectreactor 99 | reactor-test 100 | test 101 | 102 | 103 | 104 | 105 | clean package 106 | 107 | 108 | 109 | org.springframework.boot 110 | spring-boot-maven-plugin 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | if [ "$MVNW_VERBOSE" = true ]; then 205 | echo $MAVEN_PROJECTBASEDIR 206 | fi 207 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 208 | 209 | # For Cygwin, switch paths to Windows format before running java 210 | if $cygwin; then 211 | [ -n "$M2_HOME" ] && 212 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 213 | [ -n "$JAVA_HOME" ] && 214 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 215 | [ -n "$CLASSPATH" ] && 216 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 217 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 218 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 219 | fi 220 | 221 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 222 | 223 | exec "$JAVACMD" \ 224 | $MAVEN_OPTS \ 225 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 226 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 227 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 228 | --------------------------------------------------------------------------------