Clean up.

main
Paulo Gustavo Veiga 2024-02-11 12:21:38 -08:00
parent f8b8aea901
commit eaf03ea28d
14 changed files with 119 additions and 368 deletions

View File

@ -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);
}
}

View File

@ -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()

View File

@ -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() {
}
}

View File

@ -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.");

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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);
}
}
} }

View File

@ -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);
}
} }

View File

@ -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;
}
} }

View File

@ -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;

View File

@ -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");

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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: