Clean up.
parent
f8b8aea901
commit
eaf03ea28d
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright [2022] [wisemapping]
|
|
||||||
*
|
|
||||||
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
|
|
||||||
* It is basically the Apache License, Version 2.0 (the "License") plus the
|
|
||||||
* "powered by wisemapping" text requirement on every single page;
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the license at
|
|
||||||
*
|
|
||||||
* http://www.wisemapping.org/license
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.wisemapping.config.rest;
|
|
||||||
|
|
||||||
import com.wisemapping.filter.RequestPropertiesInterceptor;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@ComponentScan(basePackageClasses = RequestPropertiesInterceptor.class)
|
|
||||||
public class InterceptorsConfig implements WebMvcConfigurer {
|
|
||||||
@Autowired
|
|
||||||
private RequestPropertiesInterceptor requestPropertiesInterceptor;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addInterceptors(@NotNull final InterceptorRegistry registry) {
|
|
||||||
registry.addInterceptor(requestPropertiesInterceptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -8,7 +8,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
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.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
|
@ -21,8 +20,7 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
import static org.springframework.security.config.Customizer.withDefaults;
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
|
|
||||||
|
|
||||||
@SpringBootApplication(scanBasePackageClasses = MindmapController.class)
|
@SpringBootApplication(scanBasePackageClasses = {MindmapController.class, JwtAuthenticationFilter.class})
|
||||||
@Import({InterceptorsConfig.class})
|
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
public class RestAppConfig {
|
public class RestAppConfig {
|
||||||
|
|
||||||
|
@ -43,12 +41,13 @@ public class RestAppConfig {
|
||||||
.securityMatcher("/**")
|
.securityMatcher("/**")
|
||||||
.addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
|
.addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
|
.requestMatchers(mvc.pattern("/error")).permitAll()
|
||||||
.requestMatchers(mvc.pattern("/api/restful/authenticate")).permitAll()
|
.requestMatchers(mvc.pattern("/api/restful/authenticate")).permitAll()
|
||||||
.requestMatchers(mvc.pattern("/api/restful/users/")).permitAll()
|
.requestMatchers(mvc.pattern("/api/restful/users/")).permitAll()
|
||||||
.requestMatchers(mvc.pattern("/api/restful/maps/*/document/xml-pub")).permitAll()
|
.requestMatchers(mvc.pattern("/api/restful/maps/*/document/xml-pub")).permitAll()
|
||||||
.requestMatchers(mvc.pattern("/api/restful/users/resetPassword")).permitAll()
|
.requestMatchers(mvc.pattern("/api/restful/users/resetPassword")).permitAll()
|
||||||
.requestMatchers(mvc.pattern("/api/restful/oauth2/googlecallback")).permitAll()
|
.requestMatchers(mvc.pattern("/api/oauth2/googlecallback")).permitAll()
|
||||||
.requestMatchers(mvc.pattern("/api/restful/oauth2/confirmaccountsync")).permitAll()
|
.requestMatchers(mvc.pattern("/api/oauth2/confirmaccountsync")).permitAll()
|
||||||
.requestMatchers(mvc.pattern("/api/restful/admin/**")).hasAnyRole("ADMIN")
|
.requestMatchers(mvc.pattern("/api/restful/admin/**")).hasAnyRole("ADMIN")
|
||||||
.requestMatchers(mvc.pattern("/**")).hasAnyRole("USER", "ADMIN")
|
.requestMatchers(mvc.pattern("/**")).hasAnyRole("USER", "ADMIN")
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright [2022] [wisemapping]
|
|
||||||
*
|
|
||||||
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
|
|
||||||
* It is basically the Apache License, Version 2.0 (the "License") plus the
|
|
||||||
* "powered by wisemapping" text requirement on every single page;
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the license at
|
|
||||||
*
|
|
||||||
* http://www.wisemapping.org/license
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.wisemapping.filter;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import jakarta.servlet.Filter;
|
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.FilterConfig;
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.ServletRequest;
|
|
||||||
import jakarta.servlet.ServletResponse;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* If your wisemapping customization throws cross domain errores in browser, you can configure this filter in webdefault.xml
|
|
||||||
* By default it will accept all domains, but you can restrict to the domain you need
|
|
||||||
*
|
|
||||||
* <filter>
|
|
||||||
* <filter-name>cross-origin</filter-name>
|
|
||||||
* <filter-class>com.wisemapping.filter.CorsFilter</filter-class>
|
|
||||||
* <init-param>
|
|
||||||
* <param-name>allowedOrigins</param-name>
|
|
||||||
* <param-value>*</param-value>
|
|
||||||
* </init-param>
|
|
||||||
* <init-param>
|
|
||||||
* <param-name>allowedMethods</param-name>
|
|
||||||
* <param-value>GET,POST,HEAD</param-value>
|
|
||||||
* </init-param>
|
|
||||||
* <init-param>
|
|
||||||
* <param-name>allowedHeaders</param-name>
|
|
||||||
* <param-value>X-Requested-With,Content-Type,Accept,Origin</param-value>
|
|
||||||
* </init-param>
|
|
||||||
* </filter>
|
|
||||||
* <filter-mapping>
|
|
||||||
* <filter-name>cross-origin</filter-name>
|
|
||||||
* <url-pattern>/*</url-pattern>
|
|
||||||
* </filter-mapping>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class CorsFilter implements Filter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(FilterConfig filterConfig) throws ServletException {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
|
|
||||||
throws IOException, ServletException {
|
|
||||||
if (servletResponse != null) {
|
|
||||||
// Authorize (allow) all domains to consume the content
|
|
||||||
((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Origin", "*");
|
|
||||||
((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Methods","GET, OPTIONS, HEAD, PUT, POST");
|
|
||||||
}
|
|
||||||
|
|
||||||
chain.doFilter(servletRequest, servletResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroy() {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,7 @@ import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.dao.DataAccessException;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
@ -22,10 +22,10 @@ import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static com.wisemapping.security.JwtTokenUtil.BEARER_TOKEN_PREFIX;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
private static final String BEARER_TOKEN_PREFIX = "Bearer ";
|
|
||||||
private static final String AUTHORIZATION_HEADER = "Authorization";
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserDetailsService userDetailsService;
|
private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
final private static Logger logger = LogManager.getLogger();
|
final private static Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain)
|
protected void doFilterInternal(@NotNull final HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
final Optional<String> token = getJwtTokenFromRequest(request);
|
final Optional<String> token = getJwtTokenFromRequest(request);
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
private Optional<String> extractEmailFromToken(final @NotNull String token) {
|
private Optional<String> extractEmailFromToken(final @NotNull String token) {
|
||||||
Optional<String> result = Optional.empty();
|
Optional<String> result = Optional.empty();
|
||||||
try {
|
try {
|
||||||
result = Optional.of(jwtTokenUtil.extractFromJwtToken(token));
|
result = Optional.ofNullable(jwtTokenUtil.extractFromJwtToken(token));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Handle token extraction/validation errors
|
// Handle token extraction/validation errors
|
||||||
logger.debug("Error extracting email from token: " + e.getMessage());
|
logger.debug("Error extracting email from token: " + e.getMessage());
|
||||||
|
@ -74,7 +74,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
private static Optional<String> getJwtTokenFromRequest(@NotNull HttpServletRequest request) {
|
private static Optional<String> getJwtTokenFromRequest(@NotNull HttpServletRequest request) {
|
||||||
Optional<String> result = Optional.empty();
|
Optional<String> result = Optional.empty();
|
||||||
|
|
||||||
final String authorizationHeader = request.getHeader(AUTHORIZATION_HEADER);
|
final String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||||
if (authorizationHeader != null) {
|
if (authorizationHeader != null) {
|
||||||
if (authorizationHeader.startsWith(BEARER_TOKEN_PREFIX)) {
|
if (authorizationHeader.startsWith(BEARER_TOKEN_PREFIX)) {
|
||||||
logger.trace("JWT Bearer token found.");
|
logger.trace("JWT Bearer token found.");
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright [2022] [wisemapping]
|
|
||||||
*
|
|
||||||
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
|
|
||||||
* It is basically the Apache License, Version 2.0 (the "License") plus the
|
|
||||||
* "powered by wisemapping" text requirement on every single page;
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the license at
|
|
||||||
*
|
|
||||||
* http://www.wisemapping.org/license
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.wisemapping.filter;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class RequestPropertiesInterceptor implements HandlerInterceptor {
|
|
||||||
@Value("${google.analytics.enabled}")
|
|
||||||
private Boolean analyticsEnabled;
|
|
||||||
|
|
||||||
@Value("${google.analytics.account}")
|
|
||||||
private String analyticsAccount;
|
|
||||||
|
|
||||||
@Value("${google.recaptcha2.enabled}")
|
|
||||||
private Boolean recaptcha2Enabled;
|
|
||||||
|
|
||||||
@Value("${site.static.js.url}")
|
|
||||||
private String siteStaticUrl;
|
|
||||||
|
|
||||||
@Value("${google.recaptcha2.siteKey}")
|
|
||||||
private String recaptcha2SiteKey;
|
|
||||||
|
|
||||||
@Value("${google.ads.enabled}")
|
|
||||||
private Boolean adsEnabled;
|
|
||||||
|
|
||||||
@Value("${site.homepage}")
|
|
||||||
private String siteHomepage;
|
|
||||||
|
|
||||||
@Value("${site.baseurl:}")
|
|
||||||
private String siteUrl;
|
|
||||||
|
|
||||||
@Value("${security.oauth2.google.url}")
|
|
||||||
private String googleOauth2Url;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, Object object) throws Exception {
|
|
||||||
|
|
||||||
request.setAttribute("google.analytics.enabled", analyticsEnabled);
|
|
||||||
request.setAttribute("google.analytics.account", analyticsAccount);
|
|
||||||
request.setAttribute("google.ads.enabled", adsEnabled);
|
|
||||||
|
|
||||||
request.setAttribute("google.recaptcha2.enabled", recaptcha2Enabled);
|
|
||||||
request.setAttribute("google.recaptcha2.siteKey", recaptcha2SiteKey);
|
|
||||||
|
|
||||||
request.setAttribute("security.oauth2.google.url", googleOauth2Url);
|
|
||||||
|
|
||||||
request.setAttribute("site.homepage", siteHomepage);
|
|
||||||
request.setAttribute("site.static.js.url", siteStaticUrl);
|
|
||||||
|
|
||||||
request.setAttribute("security.type", "db");
|
|
||||||
|
|
||||||
// If the property could not be resolved, try to infer one from the request...
|
|
||||||
if (siteUrl.isBlank()) {
|
|
||||||
siteUrl = request.getRequestURL().toString().replace(request.getRequestURI(), request.getContextPath());
|
|
||||||
}
|
|
||||||
request.setAttribute("site.baseurl", siteUrl);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -164,7 +164,7 @@ public class User
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getGoogleSync() {
|
public Boolean getGoogleSync() {
|
||||||
return googleSync;
|
return googleSync!=null && googleSync;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGoogleSync(Boolean googleSync) {
|
public void setGoogleSync(Boolean googleSync) {
|
||||||
|
|
|
@ -18,64 +18,44 @@
|
||||||
|
|
||||||
package com.wisemapping.rest;
|
package com.wisemapping.rest;
|
||||||
|
|
||||||
|
import com.wisemapping.exceptions.WiseMappingException;
|
||||||
import com.wisemapping.rest.model.RestJwtUser;
|
import com.wisemapping.rest.model.RestJwtUser;
|
||||||
import com.wisemapping.security.JwtTokenUtil;
|
import com.wisemapping.security.JwtTokenUtil;
|
||||||
import com.wisemapping.security.UserDetailsService;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.authentication.DisabledException;
|
import org.springframework.security.authentication.DisabledException;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@RequestMapping("/api/restful")
|
@RequestMapping("/api/restful")
|
||||||
public class JwtAuthController {
|
public class JwtAuthController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AuthenticationManager authenticationManager;
|
private AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserDetailsService userDetailsService;
|
private JwtTokenUtil jwtTokenUtil;
|
||||||
|
|
||||||
@Autowired
|
@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
|
||||||
private JwtTokenUtil jwtTokenUtil;
|
public ResponseEntity<String> createAuthenticationToken(@RequestBody RestJwtUser user, @NotNull HttpServletResponse response) throws WiseMappingException {
|
||||||
|
// Is a valid user ?
|
||||||
|
authenticate(user.getEmail(), user.getPassword());
|
||||||
|
final String result = jwtTokenUtil.doLogin(response, user.getEmail());
|
||||||
|
|
||||||
@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
|
return ResponseEntity.ok(result);
|
||||||
public ResponseEntity<?> createAuthenticationToken(@RequestBody RestJwtUser user, @NotNull HttpServletResponse response) throws Exception {
|
}
|
||||||
|
|
||||||
// Is a valid user ?
|
private void authenticate(@NotNull String username, @NotNull String password) throws WiseMappingException {
|
||||||
authenticate(user.getEmail(), user.getPassword());
|
try {
|
||||||
|
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
|
||||||
// Create token ...
|
} catch (DisabledException | BadCredentialsException e) {
|
||||||
final UserDetails userDetails = userDetailsService
|
throw new WiseMappingException(e.getMessage(), e);
|
||||||
.loadUserByUsername(user.getEmail());
|
}
|
||||||
|
}
|
||||||
final String token = jwtTokenUtil.generateJwtToken(userDetails);
|
|
||||||
|
|
||||||
// Add token in the header ...
|
|
||||||
response.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);
|
|
||||||
|
|
||||||
return ResponseEntity.ok(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void authenticate(@NotNull String username, @NotNull String password) throws Exception {
|
|
||||||
try {
|
|
||||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
|
|
||||||
} catch (DisabledException e) {
|
|
||||||
throw new Exception("USER_DISABLED", e);
|
|
||||||
} catch (BadCredentialsException e) {
|
|
||||||
throw new Exception("INVALID_CREDENTIALS", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -21,68 +21,60 @@ package com.wisemapping.rest;
|
||||||
import com.wisemapping.exceptions.WiseMappingException;
|
import com.wisemapping.exceptions.WiseMappingException;
|
||||||
import com.wisemapping.model.User;
|
import com.wisemapping.model.User;
|
||||||
import com.wisemapping.rest.model.RestOath2CallbackResponse;
|
import com.wisemapping.rest.model.RestOath2CallbackResponse;
|
||||||
import com.wisemapping.service.*;
|
import com.wisemapping.security.JwtTokenUtil;
|
||||||
|
import com.wisemapping.service.UserService;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpSession;
|
|
||||||
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@RequestMapping("/api/oauth2/")
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
public class OAuth2Controller extends BaseController {
|
public class OAuth2Controller extends BaseController {
|
||||||
@Qualifier("userService")
|
@Qualifier("userService")
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
@Qualifier("authenticationManager")
|
@Qualifier("authenticationManager")
|
||||||
@Autowired
|
@Autowired
|
||||||
private AuthenticationManager authManager;
|
private AuthenticationManager authManager;
|
||||||
|
|
||||||
@Value("${google.recaptcha2.enabled}")
|
@Autowired
|
||||||
private Boolean recatchaEnabled;
|
private JwtTokenUtil jwtTokenUtil;
|
||||||
|
|
||||||
@Value("${accounts.exclusion.domain:''}")
|
|
||||||
private String domainBanExclusion;
|
|
||||||
|
|
||||||
private void doLogin(HttpServletRequest request, String email) {
|
@RequestMapping(method = RequestMethod.POST, value = "googlecallback", produces = {"application/json"})
|
||||||
PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(email,null);
|
@ResponseStatus(value = HttpStatus.OK)
|
||||||
Authentication auth = authManager.authenticate(token);
|
public RestOath2CallbackResponse processGoogleCallback(@NotNull @RequestParam String code, @NotNull HttpServletResponse response, @NotNull HttpServletRequest request) throws WiseMappingException {
|
||||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
User user = userService.createAndAuthUserFromGoogle(code);
|
||||||
// update spring mvc session
|
if (user.getGoogleSync()) {
|
||||||
HttpSession session = request.getSession(true);
|
jwtTokenUtil.doLogin(response, user.getEmail());
|
||||||
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.POST, value = "/oauth2/googlecallback", produces = { "application/json" })
|
// Response ...
|
||||||
@ResponseStatus(value = HttpStatus.OK)
|
final RestOath2CallbackResponse result = new RestOath2CallbackResponse();
|
||||||
public RestOath2CallbackResponse processGoogleCallback(@NotNull @RequestParam String code, @NotNull HttpServletRequest request) throws WiseMappingException {
|
result.setEmail(user.getEmail());
|
||||||
User user = userService.createUserFromGoogle(code);
|
result.setGoogleSync(user.getGoogleSync());
|
||||||
if (user.getGoogleSync() != null && user.getGoogleSync()) {
|
result.setSyncCode(user.getSyncCode());
|
||||||
doLogin(request, user.getEmail());
|
return result;
|
||||||
}
|
}
|
||||||
RestOath2CallbackResponse response = new RestOath2CallbackResponse();
|
|
||||||
response.setEmail(user.getEmail());
|
|
||||||
response.setGoogleSync(user.getGoogleSync());
|
|
||||||
response.setSyncCode(user.getSyncCode());
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.PUT, value = "/oauth2/confirmaccountsync", produces = { "application/json" })
|
@RequestMapping(method = RequestMethod.PUT, value = "confirmaccountsync", produces = {"application/json"})
|
||||||
@ResponseStatus(value = HttpStatus.OK)
|
@ResponseStatus(value = HttpStatus.OK)
|
||||||
public void confirmAccountSync(@NotNull @RequestParam String email, @NotNull @RequestParam String code, @NotNull HttpServletRequest request) throws WiseMappingException {
|
public void confirmAccountSync(@NotNull @RequestParam String email, @NotNull @RequestParam String code, @NotNull HttpServletResponse response) throws WiseMappingException {
|
||||||
userService.confirmAccountSync(email, code);
|
// Authenticate ...
|
||||||
doLogin(request, email);
|
userService.createAndAuthUserFromGoogle(code);
|
||||||
}
|
|
||||||
|
|
||||||
|
// Update login
|
||||||
|
userService.confirmAccountSync(email, code);
|
||||||
|
|
||||||
|
// Add header ...
|
||||||
|
jwtTokenUtil.doLogin(response, email);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,14 @@ package com.wisemapping.security;
|
||||||
import io.jsonwebtoken.*;
|
import io.jsonwebtoken.*;
|
||||||
import io.jsonwebtoken.io.Decoders;
|
import io.jsonwebtoken.io.Decoders;
|
||||||
import io.jsonwebtoken.security.Keys;
|
import io.jsonwebtoken.security.Keys;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@ -18,6 +21,8 @@ import java.util.Date;
|
||||||
@Component
|
@Component
|
||||||
public class JwtTokenUtil implements Serializable {
|
public class JwtTokenUtil implements Serializable {
|
||||||
final private Logger logger = LogManager.getLogger();
|
final private Logger logger = LogManager.getLogger();
|
||||||
|
public final static String BEARER_TOKEN_PREFIX = "Bearer ";
|
||||||
|
|
||||||
|
|
||||||
@Value("${app.jwt.secret}")
|
@Value("${app.jwt.secret}")
|
||||||
private String jwtSecret;
|
private String jwtSecret;
|
||||||
|
@ -25,6 +30,10 @@ public class JwtTokenUtil implements Serializable {
|
||||||
@Value("${app.jwt.expirationMin}")
|
@Value("${app.jwt.expirationMin}")
|
||||||
private int jwtExpirationMin;
|
private int jwtExpirationMin;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
|
||||||
public String generateJwtToken(@NotNull final UserDetails user) {
|
public String generateJwtToken(@NotNull final UserDetails user) {
|
||||||
return Jwts.builder()
|
return Jwts.builder()
|
||||||
.setSubject((user.getUsername()))
|
.setSubject((user.getUsername()))
|
||||||
|
@ -63,4 +72,15 @@ public class JwtTokenUtil implements Serializable {
|
||||||
logger.trace("Is JWT token valid:" + result);
|
logger.trace("Is JWT token valid:" + result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public String doLogin(@NotNull HttpServletResponse response, @NotNull String email) {
|
||||||
|
final UserDetails userDetails = userDetailsService.loadUserByUsername(email);
|
||||||
|
|
||||||
|
// Add JWT in the HTTP header ...
|
||||||
|
final String token = generateJwtToken(userDetails);
|
||||||
|
response.addHeader(HttpHeaders.AUTHORIZATION, BEARER_TOKEN_PREFIX + token);
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -30,7 +30,7 @@ public interface UserService {
|
||||||
|
|
||||||
User createUser(@NotNull User user, boolean emailConfirmEnabled, boolean welcomeEmail) throws WiseMappingException;
|
User createUser(@NotNull User user, boolean emailConfirmEnabled, boolean welcomeEmail) throws WiseMappingException;
|
||||||
|
|
||||||
User createUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException;
|
User createAndAuthUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException;
|
||||||
|
|
||||||
User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException;
|
User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException;
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,6 @@ public class UserServiceImpl
|
||||||
final Mindmap mindMap = buildTutorialMindmap(user.getFirstname());
|
final Mindmap mindMap = buildTutorialMindmap(user.getFirstname());
|
||||||
mindmapService.addMindmap(mindMap, user);
|
mindmapService.addMindmap(mindMap, user);
|
||||||
|
|
||||||
|
|
||||||
// Send registration email.
|
// Send registration email.
|
||||||
if (emailConfirmEnabled) {
|
if (emailConfirmEnabled) {
|
||||||
notificationService.sendRegistrationEmail(user);
|
notificationService.sendRegistrationEmail(user);
|
||||||
|
@ -174,7 +173,7 @@ public class UserServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public User createUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException {
|
public User createAndAuthUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException {
|
||||||
GoogleAccountBasicData data;
|
GoogleAccountBasicData data;
|
||||||
try {
|
try {
|
||||||
data = googleService.processCallback(callbackCode);
|
data = googleService.processCallback(callbackCode);
|
||||||
|
@ -185,7 +184,7 @@ public class UserServiceImpl
|
||||||
User existingUser = userManager.getUserBy(data.getEmail());
|
User existingUser = userManager.getUserBy(data.getEmail());
|
||||||
if (existingUser == null) {
|
if (existingUser == null) {
|
||||||
User newUser = new User();
|
User newUser = new User();
|
||||||
// new registrations from google starts synched
|
// new registrations from google starts sync
|
||||||
newUser.setGoogleSync(true);
|
newUser.setGoogleSync(true);
|
||||||
newUser.setEmail(data.getEmail());
|
newUser.setEmail(data.getEmail());
|
||||||
newUser.setFirstname(data.getName());
|
newUser.setFirstname(data.getName());
|
||||||
|
@ -208,7 +207,7 @@ public class UserServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
public User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException {
|
public User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException {
|
||||||
User existingUser = userManager.getUserBy(email);
|
final User existingUser = userManager.getUserBy(email);
|
||||||
// additional security check
|
// additional security check
|
||||||
if (existingUser == null || !existingUser.getSyncCode().equals(code)) {
|
if (existingUser == null || !existingUser.getSyncCode().equals(code)) {
|
||||||
throw new WiseMappingException("User not found / incorrect code");
|
throw new WiseMappingException("User not found / incorrect code");
|
||||||
|
|
|
@ -34,15 +34,15 @@ import com.wisemapping.service.google.http.HttpInvokerException;
|
||||||
public class GoogleService {
|
public class GoogleService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private HttpInvoker httpInvoker;
|
private HttpInvoker httpInvoker;
|
||||||
@Value("${security.oauth2.google.confirmUrl:}")
|
@Value("${app.security.oauth2.google.confirmUrl:}")
|
||||||
private String optinConfirmUrl;
|
private String optinConfirmUrl;
|
||||||
@Value("${security.oauth2.google.userinfoUrl:}")
|
@Value("${app.security.oauth2.google.userinfoUrl:}")
|
||||||
private String accountBasicDataUrl;
|
private String accountBasicDataUrl;
|
||||||
@Value("${security.oauth2.google.clientId:}")
|
@Value("${app.security.oauth2.google.clientId:}")
|
||||||
private String clientId;
|
private String clientId;
|
||||||
@Value("${security.oauth2.google.clientSecret:}")
|
@Value("${app.security.oauth2.google.clientSecret:}")
|
||||||
private String clientSecret;
|
private String clientSecret;
|
||||||
@Value("${security.oauth2.google.callbackUrl:}")
|
@Value("${app.security.oauth2.google.callbackUrl:}")
|
||||||
private String callbackUrl;
|
private String callbackUrl;
|
||||||
|
|
||||||
public void setHttpInvoker(HttpInvoker httpInvoker) {
|
public void setHttpInvoker(HttpInvoker httpInvoker) {
|
||||||
|
@ -108,8 +108,9 @@ public class GoogleService {
|
||||||
|
|
||||||
public GoogleAccountBasicData processCallback(final String code)
|
public GoogleAccountBasicData processCallback(final String code)
|
||||||
throws HttpInvokerException {
|
throws HttpInvokerException {
|
||||||
Map<String, String> body = this.getOptinConfirmBody(code);
|
|
||||||
JsonNode optionConfirmResponse = httpInvoker.invoke(
|
final Map<String, String> body = this.getOptinConfirmBody(code);
|
||||||
|
final JsonNode optionConfirmResponse = httpInvoker.invoke(
|
||||||
optinConfirmUrl,
|
optinConfirmUrl,
|
||||||
HttpInvokerContentType.FORM_ENCODED,
|
HttpInvokerContentType.FORM_ENCODED,
|
||||||
HttpMethod.POST,
|
HttpMethod.POST,
|
||||||
|
@ -120,7 +121,7 @@ public class GoogleService {
|
||||||
final String accessToken = getNodeAsString(optionConfirmResponse, "access_token");
|
final String accessToken = getNodeAsString(optionConfirmResponse, "access_token");
|
||||||
final String refreshToken = getNodeAsString(optionConfirmResponse, "refresh_token");
|
final String refreshToken = getNodeAsString(optionConfirmResponse, "refresh_token");
|
||||||
|
|
||||||
GoogleAccountBasicData data = this.getAccountBasicData(accessToken);
|
final GoogleAccountBasicData data = this.getAccountBasicData(accessToken);
|
||||||
data.setAccessToken(accessToken);
|
data.setAccessToken(accessToken);
|
||||||
data.setRefreshToken(refreshToken);
|
data.setRefreshToken(refreshToken);
|
||||||
return data;
|
return data;
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright [2022] [wisemapping]
|
|
||||||
*
|
|
||||||
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
|
|
||||||
* It is basically the Apache License, Version 2.0 (the "License") plus the
|
|
||||||
* "powered by wisemapping" text requirement on every single page;
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the license at
|
|
||||||
*
|
|
||||||
* http://www.wisemapping.org/license
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.wisemapping.webmvc;
|
|
||||||
|
|
||||||
import com.wisemapping.model.User;
|
|
||||||
import com.wisemapping.security.Utils;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
@PreAuthorize("permitAll()")
|
|
||||||
public class MvcLoginController {
|
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value = "c/login", method = RequestMethod.GET)
|
|
||||||
protected ModelAndView showLoginPage() {
|
|
||||||
final User user = Utils.getUser(false);
|
|
||||||
ModelAndView result;
|
|
||||||
if (user != null) {
|
|
||||||
result = new ModelAndView("forward:/c/maps/");
|
|
||||||
} else {
|
|
||||||
result = new ModelAndView("reactInclude");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -60,15 +60,28 @@ app:
|
||||||
mail:
|
mail:
|
||||||
serverSendEmail: root@localhost
|
serverSendEmail: root@localhost
|
||||||
supportEmail: root@localhost
|
supportEmail: root@localhost
|
||||||
|
|
||||||
|
#######################################################################################
|
||||||
|
# Google OAuth Authentication
|
||||||
|
#######################################################################################
|
||||||
|
# OAuth Client id
|
||||||
|
#security.oauth2.google.clientId=<config settings>
|
||||||
|
# OAuth Client secret
|
||||||
|
#security.oauth2.google.clientSecret=<oauth client>
|
||||||
|
# Redirect to this url, this url must be configured in the google app {baseurl}/c/registration-google
|
||||||
|
#security.oauth2.google.callbackUrl=<oauth callback url>
|
||||||
|
security:
|
||||||
|
oauth2:
|
||||||
|
google:
|
||||||
|
confirmUrl: https://oauth2.googleapis.com/token
|
||||||
|
userinfoUrl: https://www:googleapis.com/oauth2/v3/userinfo
|
||||||
|
url: https://accounts.google.com/o/oauth2/v2/auth?redirect_uri=${app.security.oauth2.google.callbackUrl}&prompt=consent&response_type=code&client_id=${app.security.oauth2.google.clientId}&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&access_type=offline&state=wisemapping&include_granted_scopes=true
|
||||||
|
|
||||||
# accounts:
|
# accounts:
|
||||||
# exclusion:
|
# exclusion:
|
||||||
# domain:
|
# domain:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
google:
|
google:
|
||||||
ads:
|
ads:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
@ -79,12 +92,6 @@ google:
|
||||||
enabled: true
|
enabled: true
|
||||||
secretKey: 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe
|
secretKey: 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe
|
||||||
siteKey: 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
|
siteKey: 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
|
||||||
security:
|
|
||||||
oauth2:
|
|
||||||
google:
|
|
||||||
confirmUrl: https://oauth2.googleapis.com/token
|
|
||||||
url: https//review
|
|
||||||
userinfoUrl: https://www.googleapis.com/oauth2/v3/userinfo
|
|
||||||
site:
|
site:
|
||||||
homepage: c/login
|
homepage: c/login
|
||||||
static:
|
static:
|
||||||
|
|
Loading…
Reference in New Issue