Compare commits

...

6 Commits

Author SHA1 Message Date
Paulo Gustavo Veiga
885de4e1c1 Fix login bug. 2024-02-07 00:04:06 -08:00
Paulo Gustavo Veiga
56c322fd3f Add redirect on expiration. 2024-02-06 23:43:52 -08:00
Paulo Gustavo Veiga
e3998ef3d4 Migrate to springboot emai. 2024-02-06 23:04:25 -08:00
Paulo Gustavo Veiga
88d5f0df43 Renove http basic. 2024-02-06 22:26:44 -08:00
Paulo Gustavo Veiga
37d7a9bb6d Add missing expoint test. 2024-02-06 21:12:15 -08:00
Paulo Gustavo Veiga
d798358fec Minor expoint fixes. 2024-02-05 21:21:34 -08:00
18 changed files with 227 additions and 272 deletions

View File

@ -12,6 +12,5 @@ import org.springframework.context.annotation.ImportResource;
@ComponentScan(basePackageClasses = {AuthenticationProvider.class, MindmapServiceImpl.class, LabelManagerImpl.class, VelocityEngineUtils.class}) @ComponentScan(basePackageClasses = {AuthenticationProvider.class, MindmapServiceImpl.class, LabelManagerImpl.class, VelocityEngineUtils.class})
@Import({JPAConfig.class, SecurityConfig.class}) @Import({JPAConfig.class, SecurityConfig.class})
@EnableAutoConfiguration @EnableAutoConfiguration
@ImportResource(value = {"classpath:spring/wisemapping-mail.xml"})
public class CommonConfig { public class CommonConfig {
} }

View File

@ -54,7 +54,7 @@ public class RestAppConfig {
})) }))
.csrf(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.httpBasic(withDefaults()) // .httpBasic(withDefaults())
.build(); .build();
} }
} }

View File

@ -1,29 +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 org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ServletConfig implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
public void customize(ConfigurableServletWebServerFactory factory){
factory.setPort(8081);
}
}

View File

@ -124,17 +124,16 @@ public class MindmapManagerImpl
@Override @Override
public Mindmap getMindmapByTitle(final String title, final User user) { public Mindmap getMindmapByTitle(final String title, final User user) {
final Mindmap result;
final TypedQuery<Mindmap> query = entityManager.createQuery("from com.wisemapping.model.Mindmap wisemapping where title=:title and creator=:creator", Mindmap.class); final TypedQuery<Mindmap> query = entityManager.createQuery("from com.wisemapping.model.Mindmap wisemapping where title=:title and creator=:creator", Mindmap.class);
query.setParameter("title", title); query.setParameter("title", title);
query.setParameter("creator", user); query.setParameter("creator", user);
List<Mindmap> mindMaps = query.getResultList(); List<Mindmap> mindMaps = query.getResultList();
Mindmap result = null;
if (mindMaps != null && !mindMaps.isEmpty()) { if (mindMaps != null && !mindMaps.isEmpty()) {
result = mindMaps.get(0); result = mindMaps.get(0);
} else {
result = null;
} }
return result; return result;
} }

View File

@ -11,8 +11,10 @@ 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.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.web.authentication.WebAuthenticationDetailsSource; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
@ -34,22 +36,22 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override @Override
protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) protected void doFilterInternal(@NotNull 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);
if (token.isPresent() && SecurityContextHolder.getContext().getAuthentication() == null) { if (token.isPresent() && SecurityContextHolder.getContext().getAuthentication() == null) {
// Extract email from token ... // Extract email from token ...
final Optional<String> email = extractEmailFromToken(token.get()); final Optional<String> email = extractEmailFromToken(token.get());
if (email.isPresent() && jwtTokenUtil.validateJwtToken(token.get())) { if (email.isPresent() && jwtTokenUtil.validateJwtToken(token.get())) {
// Is it an existing user ? // Is it an existing user ?
final UserDetails userDetails = userDetailsService.loadUserByUsername(email.get()); try {
if (userDetails != null) { final UserDetails userDetails = userDetailsService.loadUserByUsername(email.get());
final UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( final UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities()); userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken); SecurityContextHolder.getContext().setAuthentication(authenticationToken);
} else { } catch (UsernameNotFoundException e) {
logger.trace("User " + email.get() + " could not be found"); logger.trace("User " + email.get() + " could not be found");
} }
} }
@ -65,6 +67,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
// 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());
} }
logger.trace("JWT token email:" + result);
return result; return result;
} }
@ -74,7 +77,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
final String authorizationHeader = request.getHeader("Authorization"); final String authorizationHeader = request.getHeader("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.");
final String token = authorizationHeader.substring(BEARER_TOKEN_PREFIX.length()); final String token = authorizationHeader.substring(BEARER_TOKEN_PREFIX.length());
result = Optional.of(token); result = Optional.of(token);
} }

View File

@ -311,13 +311,10 @@ public class Mindmap implements Serializable {
} }
public static String getDefaultMindmapXml(@NotNull final String title) { public static String getDefaultMindmapXml(@NotNull final String title) {
return "<map version=\"tango\" theme=\"prism\">" +
final StringBuilder result = new StringBuilder(); "<topic central=\"true\" text=\"" +
result.append("<map version=\"tango\" theme=\"prism\">"); escapeXmlAttribute(title) +
result.append("<topic central=\"true\" text=\""); "\"/></map>";
result.append(escapeXmlAttribute(title));
result.append("\"/></map>");
return result.toString();
} }
static private String escapeXmlAttribute(String attValue) { static private String escapeXmlAttribute(String attValue) {

View File

@ -39,7 +39,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
@RestController @RestController
@RequestMapping("/api/restful/account/") @RequestMapping("/api/restful/account")
@PreAuthorize("isAuthenticated() and hasRole('ROLE_USER')") @PreAuthorize("isAuthenticated() and hasRole('ROLE_USER')")
public class AccountController extends BaseController { public class AccountController extends BaseController {
@Qualifier("userService") @Qualifier("userService")
@ -54,7 +54,7 @@ public class AccountController extends BaseController {
@Autowired @Autowired
private LabelService labelService; private LabelService labelService;
@RequestMapping(method = RequestMethod.PUT, value = "password", consumes = {"text/plain"}) @RequestMapping(method = RequestMethod.PUT, value = "/password", consumes = {"text/plain"})
@ResponseStatus(value = HttpStatus.NO_CONTENT) @ResponseStatus(value = HttpStatus.NO_CONTENT)
public void changePassword(@RequestBody String password) throws PasswordTooLongException { public void changePassword(@RequestBody String password) throws PasswordTooLongException {
if (password == null) { if (password == null) {
@ -76,7 +76,7 @@ public class AccountController extends BaseController {
return new RestUser(user); return new RestUser(user);
} }
@RequestMapping(method = RequestMethod.PUT, value = "firstname", consumes = {"text/plain"}) @RequestMapping(method = RequestMethod.PUT, value = "/firstname", consumes = {"text/plain"})
@ResponseStatus(value = HttpStatus.NO_CONTENT) @ResponseStatus(value = HttpStatus.NO_CONTENT)
public void changeFirstname(@RequestBody String firstname) { public void changeFirstname(@RequestBody String firstname) {
if (firstname == null) { if (firstname == null) {
@ -88,7 +88,7 @@ public class AccountController extends BaseController {
userService.updateUser(user); userService.updateUser(user);
} }
@RequestMapping(method = RequestMethod.PUT, value = "lastname", consumes = {"text/plain"}) @RequestMapping(method = RequestMethod.PUT, value = "/lastname", consumes = {"text/plain"})
@ResponseStatus(value = HttpStatus.NO_CONTENT) @ResponseStatus(value = HttpStatus.NO_CONTENT)
public void changeLastName(@RequestBody String lastname) { public void changeLastName(@RequestBody String lastname) {
if (lastname == null) { if (lastname == null) {
@ -100,7 +100,7 @@ public class AccountController extends BaseController {
userService.updateUser(user); userService.updateUser(user);
} }
@RequestMapping(method = RequestMethod.PUT, value = "locale", consumes = {"text/plain"}) @RequestMapping(method = RequestMethod.PUT, value = "/locale", consumes = {"text/plain"})
@ResponseStatus(value = HttpStatus.NO_CONTENT) @ResponseStatus(value = HttpStatus.NO_CONTENT)
public void changeLanguage(@RequestBody String language) { public void changeLanguage(@RequestBody String language) {
if (language == null) { if (language == null) {

View File

@ -69,7 +69,7 @@ public class MindmapController extends BaseController {
@PreAuthorize("isAuthenticated() and hasRole('ROLE_USER')") @PreAuthorize("isAuthenticated() and hasRole('ROLE_USER')")
@RequestMapping(method = RequestMethod.GET, value = "/{id}", produces = {"application/json"}) @RequestMapping(method = RequestMethod.GET, value = "/{id}", produces = {"application/json"})
@ResponseBody @ResponseBody
public RestMindmap retrieve(@PathVariable int id) throws WiseMappingException { public RestMindmap retrieve(@PathVariable int id) throws WiseMappingException {
final User user = Utils.getUser(); final User user = Utils.getUser();
@ -77,6 +77,16 @@ public class MindmapController extends BaseController {
return new RestMindmap(mindMap, user); return new RestMindmap(mindMap, user);
} }
@PreAuthorize("isAuthenticated() and hasRole('ROLE_USER')")
@RequestMapping(method = RequestMethod.GET, value = "/{id}/metadata", produces = {"application/json"})
@ResponseBody
public RestMindmap retrieveMetadata(@PathVariable int id) throws WiseMappingException {
final User user = Utils.getUser();
final Mindmap mindMap = findMindmapById(id);
return new RestMindmap(mindMap, user);
}
@PreAuthorize("isAuthenticated() and hasRole('ROLE_USER')") @PreAuthorize("isAuthenticated() and hasRole('ROLE_USER')")
@RequestMapping(method = RequestMethod.GET, value = "/", produces = {"application/json"}) @RequestMapping(method = RequestMethod.GET, value = "/", produces = {"application/json"})
public RestMindmapList retrieveList(@RequestParam(required = false) String q) { public RestMindmapList retrieveList(@RequestParam(required = false) String q) {

View File

@ -49,103 +49,104 @@ import java.util.List;
@RestController @RestController
@RequestMapping("/api/restful/users") @RequestMapping("/api/restful/users")
@CrossOrigin
public class UserController extends BaseController { public class UserController extends BaseController {
@Qualifier("userService") @Qualifier("userService")
@Autowired @Autowired
private UserService userService; private UserService userService;
@Autowired @Autowired
private RecaptchaService captchaService; private RecaptchaService captchaService;
@Qualifier("authenticationManager") @Qualifier("authenticationManager")
@Autowired @Autowired
private AuthenticationManager authManager; private AuthenticationManager authManager;
@Value("${google.recaptcha2.enabled:false}") @Value("${google.recaptcha2.enabled:false}")
private Boolean recatchaEnabled; private Boolean recatchaEnabled;
@Value("${accounts.exclusion.domain:''}") @Value("${app.accounts.exclusion.domain:''}")
private String domainBanExclusion; private String domainBanExclusion;
private static final Logger logger = LogManager.getLogger(); private static final Logger logger = LogManager.getLogger();
private static final String REAL_IP_ADDRESS_HEADER = "X-Real-IP"; private static final String REAL_IP_ADDRESS_HEADER = "X-Real-IP";
@RequestMapping(method = RequestMethod.POST, value = "/", produces = { "application/json" }) @RequestMapping(method = RequestMethod.POST, value = "/", produces = {"application/json"})
@ResponseStatus(value = HttpStatus.CREATED) @ResponseStatus(value = HttpStatus.CREATED)
public void registerUser(@RequestBody RestUserRegistration registration, @NotNull HttpServletRequest request, public void registerUser(@RequestBody RestUserRegistration registration, @NotNull HttpServletRequest request,
@NotNull HttpServletResponse response) throws WiseMappingException, BindException { @NotNull HttpServletResponse response) throws WiseMappingException, BindException {
logger.debug("Register new user:" + registration.getEmail()); logger.debug("Register new user:" + registration.getEmail());
if (registration.getPassword().length() > User.MAX_PASSWORD_LENGTH_SIZE) { if (registration.getPassword().length() > User.MAX_PASSWORD_LENGTH_SIZE) {
throw new PasswordTooLongException(); throw new PasswordTooLongException();
} }
// If tomcat is behind a reverse proxy, ip needs to be found in other header. // If tomcat is behind a reverse proxy, ip needs to be found in other header.
String remoteIp = request.getHeader(REAL_IP_ADDRESS_HEADER); String remoteIp = request.getHeader(REAL_IP_ADDRESS_HEADER);
if (remoteIp == null || remoteIp.isEmpty()) { if (remoteIp == null || remoteIp.isEmpty()) {
remoteIp = request.getRemoteAddr(); remoteIp = request.getRemoteAddr();
} }
logger.debug("Remote address" + remoteIp); logger.debug("Remote address" + remoteIp);
verify(registration, remoteIp); verify(registration, remoteIp);
final User user = new User(); final User user = new User();
user.setEmail(registration.getEmail().trim()); user.setEmail(registration.getEmail().trim());
user.setFirstname(registration.getFirstname()); user.setFirstname(registration.getFirstname());
user.setLastname(registration.getLastname()); user.setLastname(registration.getLastname());
user.setPassword(registration.getPassword()); user.setPassword(registration.getPassword());
user.setAuthenticationType(AuthenticationType.DATABASE); user.setAuthenticationType(AuthenticationType.DATABASE);
userService.createUser(user, false, true); userService.createUser(user, false, true);
response.setHeader("Location", "/api/restful/users/" + user.getId()); response.setHeader("Location", "/api/restful/users/" + user.getId());
} response.setHeader("ResourceId", Integer.toString(user.getId()));
@RequestMapping(method = RequestMethod.PUT, value = "/resetPassword", produces = { "application/json" }) }
@ResponseStatus(value = HttpStatus.OK)
public RestResetPasswordResponse resetPassword(@RequestParam String email) throws InvalidAuthSchemaException, EmailNotExistsException {
try {
return userService.resetPassword(email);
} catch (InvalidUserEmailException e) {
throw new EmailNotExistsException(e);
}
}
private void verify(@NotNull final RestUserRegistration registration, @NotNull String remoteAddress) @RequestMapping(method = RequestMethod.PUT, value = "/resetPassword", produces = {"application/json"})
throws BindException { @ResponseStatus(value = HttpStatus.OK)
public RestResetPasswordResponse resetPassword(@RequestParam String email) throws InvalidAuthSchemaException, EmailNotExistsException {
try {
return userService.resetPassword(email);
} catch (InvalidUserEmailException e) {
throw new EmailNotExistsException(e);
}
}
final BindException errors = new RegistrationException(registration, "registration"); private void verify(@NotNull final RestUserRegistration registration, @NotNull String remoteAddress)
final UserValidator validator = new UserValidator(); throws BindException {
validator.setUserService(userService);
validator.validate(registration, errors);
// If captcha is enabled, generate it ... final BindException errors = new RegistrationException(registration, "registration");
if (recatchaEnabled) { final UserValidator validator = new UserValidator();
final String recaptcha = registration.getRecaptcha(); validator.setUserService(userService);
if (recaptcha != null) { validator.validate(registration, errors);
final String reCaptchaResponse = captchaService.verifyRecaptcha(remoteAddress, recaptcha);
if (reCaptchaResponse != null && !reCaptchaResponse.isEmpty()) {
errors.rejectValue("recaptcha", reCaptchaResponse);
}
} else {
errors.rejectValue("recaptcha", Messages.CAPTCHA_LOADING_ERROR);
}
} else {
logger.warn("captchaEnabled is enabled.Recommend to enable it for production environments.");
}
if (errors.hasErrors()) { // If captcha is enabled, generate it ...
throw errors; if (recatchaEnabled) {
} final String recaptcha = registration.getRecaptcha();
if (recaptcha != null) {
final String reCaptchaResponse = captchaService.verifyRecaptcha(remoteAddress, recaptcha);
if (reCaptchaResponse != null && !reCaptchaResponse.isEmpty()) {
errors.rejectValue("recaptcha", reCaptchaResponse);
}
} else {
errors.rejectValue("recaptcha", Messages.CAPTCHA_LOADING_ERROR);
}
} else {
logger.warn("captchaEnabled is enabled.Recommend to enable it for production environments.");
}
// Is excluded ?. if (errors.hasErrors()) {
final List<String> excludedDomains = Arrays.asList(domainBanExclusion.split(",")); throw errors;
final String emailDomain = registration.getEmail().split("@")[1]; }
if (excludedDomains.contains(emailDomain)) {
throw new IllegalArgumentException( // Is excluded ?.
"Email is part of ban exclusion list due to abuse. Please, contact site admin if you think this is an error." final List<String> excludedDomains = Arrays.asList(domainBanExclusion.split(","));
+ emailDomain); final String emailDomain = registration.getEmail().split("@")[1];
} if (excludedDomains.contains(emailDomain)) {
} throw new IllegalArgumentException(
"Email is part of ban exclusion list due to abuse. Please, contact site admin if you think this is an error."
+ emailDomain);
}
}
} }

View File

@ -0,0 +1,47 @@
/*
* 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.rest.model;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.wisemapping.exceptions.InvalidMindmapException;
import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.model.*;
import com.wisemapping.util.TimeUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.Calendar;
@JsonAutoDetect(
fieldVisibility = JsonAutoDetect.Visibility.NONE,
setterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY,
isGetterVisibility = JsonAutoDetect.Visibility.NONE,
getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY
)
@JsonIgnoreProperties(ignoreUnknown = true)
public class RestMindmapMetadata {
public RestMindmapMetadata() throws WiseMappingException {
}
}

View File

@ -6,6 +6,7 @@ import io.jsonwebtoken.security.Keys;
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.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -37,15 +38,18 @@ public class JwtTokenUtil implements Serializable {
return Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtSecret)); return Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtSecret));
} }
@Nullable
public String extractFromJwtToken(String token) { public String extractFromJwtToken(String token) {
return Jwts.parserBuilder().setSigningKey(key()).build() return Jwts.parserBuilder().setSigningKey(key()).build()
.parseClaimsJws(token).getBody().getSubject(); .parseClaimsJws(token).getBody().getSubject();
} }
public boolean validateJwtToken(@NotNull String authToken) { public boolean validateJwtToken(@NotNull String authToken) {
boolean result = false;
try { try {
Jwts.parserBuilder().setSigningKey(key()).build().parse(authToken); Jwts.parserBuilder().setSigningKey(key()).build().parse(authToken);
return true; result = true;
} catch (MalformedJwtException e) { } catch (MalformedJwtException e) {
logger.error("Invalid JWT token: {}", e.getMessage()); logger.error("Invalid JWT token: {}", e.getMessage());
} catch (ExpiredJwtException e) { } catch (ExpiredJwtException e) {
@ -56,6 +60,7 @@ public class JwtTokenUtil implements Serializable {
logger.error("JWT claims string is empty: {}", e.getMessage()); logger.error("JWT claims string is empty: {}", e.getMessage());
} }
return false; logger.trace("Is JWT token valid:" + result);
return result;
} }
} }

View File

@ -38,21 +38,18 @@ public final class MailerService {
//~ Instance fields ...................................................................................... //~ Instance fields ......................................................................................
// @Autowired @Autowired
private JavaMailSender mailSender; private JavaMailSender mailSender;
@Autowired @Autowired
private VelocityEngineWrapper velocityEngineWrapper; private VelocityEngineWrapper velocityEngineWrapper;
@Value("${mail.serverSendEmail}") @Value("${app.mail.serverSendEmail}")
private String serverFromEmail; private String serverFromEmail;
@Value("${mail.supportEmail}") @Value("${app.mail.supportEmail}")
private String supportEmail; private String supportEmail;
@Value("${mail.errorReporterEmail:}")
private String errorReporterEmail;
//~ Methods .............................................................................................. //~ Methods ..............................................................................................
public String getServerSenderEmail() { public String getServerSenderEmail() {
@ -86,8 +83,4 @@ public final class MailerService {
public String getSupportEmail() { public String getSupportEmail() {
return supportEmail; return supportEmail;
} }
public String getErrorReporterEmail() {
return errorReporterEmail;
}
} }

View File

@ -1,54 +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.service;
import org.jetbrains.annotations.NotNull;
import org.springframework.util.DigestUtils;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
public class NotifierFilter {
public static final int MAX_CACHE_ENTRY = 500;
private final Map<String, String> emailByMd5 = Collections.synchronizedMap(new LinkedHashMap<String, String>() {
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_CACHE_ENTRY;
}
});
public boolean hasBeenSend(@NotNull final String email, @NotNull final Map<String, String> model) {
final StringBuilder buff = new StringBuilder();
for (String key : model.keySet()) {
if (!key.equals("mapXML")) {
buff.append(key);
buff.append("=");
buff.append(model.get(key));
}
}
final String digest = DigestUtils.md5DigestAsHex(buff.toString().getBytes());
boolean result = emailByMd5.containsKey(digest);
if (!result) {
emailByMd5.put(digest, email);
}
return result;
}
}

View File

@ -1,6 +1,17 @@
# SpringBoot Configuration ... # SpringBoot Configuration ...
spring: spring:
mail:
host: smtp.example.com
port: 25
username: setusername
password: setpassword
properties:
mail:
smtp:
connectiontimeout: 5000
timeout: 3000
writetimeout: 5000
output: output:
ansi: ansi:
enabled=always: enabled=always:
@ -38,13 +49,23 @@ logging:
root: TRACE root: TRACE
# Application Configuration. # Application Configuration.
app: app:
jwt: jwt:
secret: dlqxKAg685SaKhsQXIMeM=JWCw3bkl3Ei3Tb7LMlnd19oMd66burPNlJ0Po1qguyjgpakQTk2CN3 secret: dlqxKAg685SaKhsQXIMeM=JWCw3bkl3Ei3Tb7LMlnd19oMd66burPNlJ0Po1qguyjgpakQTk2CN3
expirationMin: 10080 # One week expirationMin: 10080 # One week
admin: admin:
user: admin@wisemapping.org user: admin@wisemapping.org
mail:
serverSendEmail: root@localhost
supportEmail: root@localhost
# accounts:
# exclusion:
# domain:
google: google:
ads: ads:
@ -56,18 +77,6 @@ google:
enabled: true enabled: true
secretKey: 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe secretKey: 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe
siteKey: 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI siteKey: 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
mail:
password: ''
serverSendEmail: root@localhost
smtp:
auth: false
host: localhost
port: 25
quitwait: false
starttls:
enable: false
supportEmail: root@localhost
username: root
security: security:
oauth2: oauth2:
google: google:

View File

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="${mail.smtp.host}"/>
<property name="port" value="${mail.smtp.port}"/>
<property name="protocol" value="smtp"/>
<property name="username" value="${mail.username}"/>
<property name="password" value="${mail.password}"/>
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">${mail.smtp.auth:false}</prop>
<prop key="mail.smtp.starttls.enable">${mail.smtp.starttls.enable:false}</prop>
<prop key="mail.smtp.quitwait">${mail.smtp.quitwait:true}</prop>
</props>
</property>
</bean>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="defaultEncoding" value="UTF-8"/>
<property name="basenames">
<list>
<value>messages</value>
</list>
</property>
</bean>
</beans>

View File

@ -18,27 +18,30 @@
package com.wisemapping.test.rest; package com.wisemapping.test.rest;
import com.wisemapping.config.common.CommonConfig; import com.wisemapping.config.common.CommonConfig;
import com.wisemapping.config.rest.RestAppConfig; import com.wisemapping.config.rest.RestAppConfig;
import com.wisemapping.rest.AdminController; import com.wisemapping.rest.AdminController;
import com.wisemapping.rest.MindmapController; import com.wisemapping.rest.MindmapController;
import com.wisemapping.rest.UserController; import com.wisemapping.rest.UserController;
import com.wisemapping.rest.model.RestUser; import com.wisemapping.rest.model.RestUser;
import com.wisemapping.security.UserDetailsService;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.*; import org.springframework.http.*;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.net.URI; import java.net.URI;
import static com.wisemapping.test.rest.RestHelper.*; import static com.wisemapping.test.rest.RestHelper.*;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest(classes = {RestAppConfig.class, CommonConfig.class, MindmapController.class, AdminController.class, UserController.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(classes = {RestAppConfig.class, CommonConfig.class, MindmapController.class, AdminController.class, UserController.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class RestAccountControllerTest { public class RestAccountControllerTest {
private static final String ADMIN_USER = "admin@wisemapping.org"; private static final String ADMIN_USER = "admin@wisemapping.org";
private static final String ADMIN_PASSWORD = "test"; private static final String ADMIN_PASSWORD = "test";
@ -46,6 +49,9 @@ public class RestAccountControllerTest {
@Autowired @Autowired
private TestRestTemplate restTemplate; private TestRestTemplate restTemplate;
@Autowired
private UserDetailsService service;
static public RestAccountControllerTest create(@NotNull TestRestTemplate restTemplate) { static public RestAccountControllerTest create(@NotNull TestRestTemplate restTemplate) {
final RestAccountControllerTest result = new RestAccountControllerTest(); final RestAccountControllerTest result = new RestAccountControllerTest();
result.restTemplate = restTemplate; result.restTemplate = restTemplate;
@ -53,27 +59,40 @@ public class RestAccountControllerTest {
} }
@Test @Test
public void deleteUser() { // Configure media types ... public void deleteAccount() {
final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON); final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON);
final TestRestTemplate adminRestTemplate = this.restTemplate.withBasicAuth(ADMIN_USER, ADMIN_PASSWORD); final TestRestTemplate adminRestTemplate = this.restTemplate.withBasicAuth(ADMIN_USER, ADMIN_PASSWORD);
final RestUser dummyUser = createDummyUser(); final RestUser newUser = createDummyUser();
createUser(requestHeaders, adminRestTemplate, dummyUser); createUser(requestHeaders, adminRestTemplate, newUser);
// Delete user ... // Delete user ...
final TestRestTemplate dummyTemplate = this.restTemplate.withBasicAuth(dummyUser.getEmail(), "fooPassword"); final TestRestTemplate newUserTemplate = this.restTemplate.withBasicAuth(newUser.getEmail(), newUser.getPassword());
dummyTemplate.delete(BASE_REST_URL + "/account"); final ResponseEntity<String> exchange = newUserTemplate.exchange(BASE_REST_URL + "/account", HttpMethod.DELETE, null, String.class);
assertTrue(exchange.getStatusCode().is2xxSuccessful(), exchange.toString());
// Is the user there ? // Check that the account has been deleted ...
// Check that the user has been created ... assertThrows(UsernameNotFoundException.class, () -> {
// try { service.loadUserByUsername(newUser.getEmail());
// findUser(requestHeaders, adminTemplate, location); });
// fail("User could not be deleted !");
// } catch (Exception e) {
// }
} }
@Test
public void accessAccount() {
final HttpHeaders requestHeaders = createHeaders(MediaType.APPLICATION_JSON);
final TestRestTemplate adminRestTemplate = this.restTemplate.withBasicAuth(ADMIN_USER, ADMIN_PASSWORD);
final RestUser newUser = createDummyUser();
createUser(requestHeaders, adminRestTemplate, newUser);
final TestRestTemplate newUserTemplate = this.restTemplate.withBasicAuth(newUser.getEmail(), newUser.getPassword());
final ResponseEntity<RestUser> exchange = newUserTemplate.exchange(BASE_REST_URL + "/account", HttpMethod.GET, null, RestUser.class);
assertTrue(exchange.getStatusCode().is2xxSuccessful(), exchange.toString());
assertEquals(exchange.getBody().getEmail(), newUser.getEmail());
}
@Test @Test
public RestUser createNewUser() { public RestUser createNewUser() {
// Configure media types ... // Configure media types ...

View File

@ -22,14 +22,10 @@ package com.wisemapping.test.rest;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.wisemapping.config.common.CommonConfig; import com.wisemapping.config.common.CommonConfig;
import com.wisemapping.config.rest.RestAppConfig; import com.wisemapping.config.rest.RestAppConfig;
import com.wisemapping.model.User;
import com.wisemapping.rest.JwtAuthController; import com.wisemapping.rest.JwtAuthController;
import com.wisemapping.rest.model.RestJwtUser; import com.wisemapping.rest.model.RestJwtUser;
import com.wisemapping.rest.model.RestUser;
import com.wisemapping.rest.model.RestUserRegistration;
import com.wisemapping.security.JwtTokenUtil; import com.wisemapping.security.JwtTokenUtil;
import com.wisemapping.service.UserService; import com.wisemapping.service.UserService;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
@ -38,15 +34,8 @@ import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.MvcResult;
import static com.wisemapping.test.rest.RestHelper.createDummyUser;
import static org.hamcrest.Matchers.containsString;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(classes = {RestAppConfig.class, CommonConfig.class, JwtAuthController.class}) @SpringBootTest(classes = {RestAppConfig.class, CommonConfig.class, JwtAuthController.class})

View File

@ -27,22 +27,20 @@ import com.wisemapping.rest.UserController;
import com.wisemapping.rest.model.RestUser; import com.wisemapping.rest.model.RestUser;
import com.wisemapping.rest.model.RestUserRegistration; import com.wisemapping.rest.model.RestUserRegistration;
import com.wisemapping.service.UserService; import com.wisemapping.service.UserService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import static com.wisemapping.test.rest.RestHelper.createDummyUser; import static com.wisemapping.test.rest.RestHelper.createDummyUser;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;