Skip to content
Snippets Groups Projects
Verified Commit 321368d3 authored by Martin Weise's avatar Martin Weise
Browse files

Updated the security permissions

parent c10d8f18
No related branches found
No related tags found
No related merge requests found
Showing
with 267 additions and 254 deletions
......@@ -6,6 +6,7 @@ BROKER_PASSWORD=fda
WEBSITE=http://example.com
MAIL_FROM=Database Repository <noreply@example.com>
MAIL_REPLY_TO=Admin <somebody@example.com>
MAIL_VERIFY=true
JWT_ISSUER=dbrepo
JWT_SECRET=secret
JWT_EXPIRATION=86400000
......
This diff is collapsed.
......@@ -148,6 +148,7 @@ services:
SMTP_PORT: ${SMTP_PORT}
SMTP_USERNAME: ${SMTP_USERNAME}
SMTP_PASSWORD: ${SMTP_PASSWORD}
MAIL_VERIFY: ${MAIL_VERIFY}
depends_on:
discovery-service:
condition: service_healthy
......
......@@ -148,6 +148,7 @@ services:
SMTP_PORT: ${SMTP_PORT}
SMTP_USERNAME: ${SMTP_USERNAME}
SMTP_PASSWORD: ${SMTP_PASSWORD}
MAIL_VERIFY: ${MAIL_VERIFY}
depends_on:
discovery-service:
condition: service_healthy
......
......@@ -138,10 +138,6 @@ services:
JWT_SECRET: fda-secret
JWT_EXPIRATION: 86400000
SPRING_PROFILES_ACTIVE: docker
SMTP_HOST: ""
SMTP_PORT: ""
SMTP_USERNAME: ""
SMTP_PASSWORD: ""
ports:
- "9097:9097"
depends_on:
......
......@@ -28,6 +28,7 @@ ENV WEBSITE=http://example.com
ENV GATEWAY_ENDPOINT=http://gateway-service:9095
ENV MAIL_FROM="Database Repository <noreply@example.com>"
ENV MAIL_REPLY_TO="Somebody <somebody@example.com>"
ENV MAIL_VERIFY=false
ENV JWT_ISSUER=dbrepo
ENV JWT_SECRET=secret
ENV JWT_EXPIRATION=86400000
......
......@@ -77,7 +77,9 @@ public class UserEndpoint {
@Operation(summary = "Create user")
public ResponseEntity<UserDto> register(@NotNull @Valid @RequestBody SignupRequestDto data)
throws UserEmailExistsException,
UserNameExistsException, RoleNotFoundException, UserEmailFailedException, BrokerUserCreationException, OrcidMalformedException {
UserNameExistsException, RoleNotFoundException, UserEmailFailedException, BrokerUserCreationException,
OrcidMalformedException, AuthenticationInvalidException, UserNotFoundException,
UserEmailNotVerifiedException {
final User user = userService.create(data);
queueService.createUser(data);
final Token token = tokenService.create(user);
......@@ -109,7 +111,8 @@ public class UserEndpoint {
@Operation(summary = "Reset user information")
public void reset(@NotNull @Valid @RequestBody UserResetDto data,
@NotNull HttpServletResponse httpServletResponse)
throws UserEmailFailedException, TokenInvalidException, UserNotFoundException, BrokerUserCreationException {
throws UserEmailFailedException, TokenInvalidException, UserNotFoundException, BrokerUserCreationException,
UserEmailNotVerifiedException {
final User user = tokenService.invalidate(data.getToken());
final UserPasswordDto userPasswordDto = userMapper.userResetDtoToUserPasswordDto(data);
userService.updatePassword(user.getId(), userPasswordDto);
......@@ -125,7 +128,8 @@ public class UserEndpoint {
@Transactional(readOnly = true)
@PreAuthorize("hasRole('ROLE_DEVELOPER') or hasPermission(#id, 'READ_USER')")
@Operation(summary = "Find some user", security = @SecurityRequirement(name = "bearerAuth"))
public ResponseEntity<UserDto> find(@NotNull @PathVariable("id") Long id) throws UserNotFoundException, OrcidMalformedException {
public ResponseEntity<UserDto> find(@NotNull @PathVariable("id") Long id) throws UserNotFoundException,
OrcidMalformedException {
final User entity = userService.find(id);
return ResponseEntity.status(HttpStatus.OK)
.body(userMapper.userToUserDto(entity));
......@@ -172,7 +176,8 @@ public class UserEndpoint {
@Operation(summary = "Update user password", security = @SecurityRequirement(name = "bearerAuth"))
public ResponseEntity<UserDto> updatePassword(@NotNull @PathVariable("id") Long id,
@NotNull @Valid @RequestBody UserPasswordDto data)
throws UserNotFoundException, BrokerUserCreationException, OrcidMalformedException {
throws UserNotFoundException, BrokerUserCreationException, OrcidMalformedException,
UserEmailNotVerifiedException {
final User entity = userService.updatePassword(id, data);
queueService.modifyUserPassword(entity, data);
return ResponseEntity.status(HttpStatus.ACCEPTED)
......
......@@ -2,7 +2,7 @@ app.version: '@project.version@'
spring:
main.banner-mode: off
datasource:
url: jdbc:postgresql://fda-metadata-db:5432/fda
url: jdbc:postgresql://metadata-db:5432/fda
driver-class-name: org.postgresql.Driver
username: postgres
password: postgres
......@@ -17,7 +17,7 @@ spring:
jdbc:
time_zone: UTC
application:
name: fda-authentication-service
name: authentication-service
cloud:
loadbalancer.ribbon.enabled: false
mail:
......@@ -40,8 +40,8 @@ logging:
org.springframework.mail.: debug
org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver: debug
eureka:
instance.hostname: fda-authentication-service
client.serviceUrl.defaultZone: http://fda-discovery-service:9090/eureka/
instance.hostname: authentication-service
client.serviceUrl.defaultZone: http://discovery-service:9090/eureka/
fda:
ready.path: /ready
website: "${WEBSITE}"
......@@ -50,6 +50,7 @@ fda:
prefix: /
from: "${MAIL_FROM}"
replyto: "${MAIL_REPLY_TO}"
verify: false
jwt:
issuer: "${JWT_ISSUER}"
secret: "${JWT_SECRET}"
......
app.version: '@project.version@'
spring:
main.banner-mode: off
datasource:
url: jdbc:postgresql://localhost:5432/fda
driver-class-name: org.postgresql.Driver
username: postgres
password: postgres
jpa:
show-sql: false
database-platform: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: validate
open-in-view: false
properties:
hibernate:
jdbc:
time_zone: UTC
application:
name: authentication-service
cloud:
loadbalancer.ribbon.enabled: false
mail:
default-encoding: UTF-8
host: ""
port: 0
username: ""
password: ""
properties.mail.smtp:
timeout: 5000
auth: true
starttls.enable: true
server:
port: 9097
logging:
pattern.console: "%d %highlight(%-5level) %msg%n"
level:
root: warn
at.tuwien.: debug
org.springframework.mail.: debug
org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver: debug
eureka:
instance.hostname: authentication-service
client.serviceUrl.defaultZone: http://localhost:9090/eureka/
fda:
ready.path: ./ready
website: localhost:3000
gateway.endpoint: http://localhost:9095
mail:
prefix: /
from: Nobody
replyto: Nobody
verify: false
jwt:
issuer: dbrepo
secret: dbrepo
expiration.ms: 100000000
\ No newline at end of file
......@@ -4,8 +4,8 @@ spring:
datasource:
url: "jdbc:postgresql://metadata-db:5432/${METADATA_DB}"
driver-class-name: org.postgresql.Driver
username: postgres
password: postgres
username: "${METADATA_USERNAME}"
password: "${METADATA_PASSWORD}"
jpa:
show-sql: false
database-platform: org.hibernate.dialect.PostgreSQLDialect
......@@ -50,6 +50,7 @@ fda:
prefix: /
from: "${MAIL_FROM}"
replyto: "${MAIL_REPLY_TO}"
verify: "${MAIL_VERIFY}"
jwt:
issuer: "${JWT_ISSUER}"
secret: "${JWT_SECRET}"
......
......@@ -28,6 +28,9 @@ public class MailConfig {
@Value("${spring.mail.username}")
private String mailUsername;
@Value("${fda.mail.verify}")
private Boolean mailVerify;
@Bean
public SpringTemplateEngine springTemplateEngine() {
final SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
......
......@@ -3,7 +3,10 @@ package at.tuwien.gateway;
import at.tuwien.api.amqp.CreateUserDto;
import at.tuwien.api.user.UserModifyPasswordDto;
import at.tuwien.exception.AuthenticationInvalidException;
import at.tuwien.exception.BrokerUserCreationException;
import at.tuwien.exception.UserEmailNotVerifiedException;
import at.tuwien.exception.UserNotFoundException;
public interface BrokerServiceGateway {
......@@ -19,9 +22,9 @@ public interface BrokerServiceGateway {
/**
* Modify a user password for a user at the Queue Service
*
* @param username The username.
* @param data The user modification data.
* @throws BrokerUserCreationException The broker did not modify a user.
*/
void modifyUserPassword(String username, UserModifyPasswordDto data) throws BrokerUserCreationException;
void modifyUserPassword(UserModifyPasswordDto data)
throws BrokerUserCreationException, UserNotFoundException, UserEmailNotVerifiedException;
}
......@@ -2,16 +2,19 @@ package at.tuwien.gateway.impl;
import at.tuwien.api.amqp.CreateUserDto;
import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto;
import at.tuwien.api.auth.JwtResponseDto;
import at.tuwien.api.auth.LoginRequestDto;
import at.tuwien.api.user.UserModifyPasswordDto;
import at.tuwien.exception.AuthenticationInvalidException;
import at.tuwien.exception.BrokerUserCreationException;
import at.tuwien.exception.UserEmailNotVerifiedException;
import at.tuwien.exception.UserNotFoundException;
import at.tuwien.gateway.BrokerServiceGateway;
import at.tuwien.mapper.UserMapper;
import at.tuwien.service.AuthenticationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
......@@ -19,18 +22,21 @@ import org.springframework.web.client.RestTemplate;
@Service
public class BrokerServiceGatewayImpl implements BrokerServiceGateway {
private final RestTemplate restTemplate;
private final UserMapper userMapper;
private final RestTemplate restTemplate;
private final AuthenticationService authenticationService;
@Autowired
public BrokerServiceGatewayImpl(RestTemplate restTemplate, UserMapper userMapper) {
this.restTemplate = restTemplate;
public BrokerServiceGatewayImpl(UserMapper userMapper, RestTemplate restTemplate,
AuthenticationService authenticationService) {
this.userMapper = userMapper;
this.restTemplate = restTemplate;
this.authenticationService = authenticationService;
}
@Override
public void createUser(CreateUserDto data) throws BrokerUserCreationException {
final GrantVirtualHostPermissionsDto grantDto = userMapper.signupRequestDtoToGrantComponentDto();
/* create user */
final ResponseEntity<Void> createResponse = restTemplate.exchange("/api/broker/user", HttpMethod.POST,
new HttpEntity<>(data), Void.class);
if (!createResponse.getStatusCode().equals(HttpStatus.CREATED)) {
......@@ -38,9 +44,10 @@ public class BrokerServiceGatewayImpl implements BrokerServiceGateway {
throw new BrokerUserCreationException("Failed to create user at queue service");
}
log.info("Created user at queue service with username {}", data.getUsername());
final ResponseEntity<Void> grantResponse = restTemplate.exchange(
"/api/broker/user/" + data.getUsername() + "/permission", HttpMethod.PUT, new HttpEntity<>(grantDto),
Void.class);
final GrantVirtualHostPermissionsDto grantDto = userMapper.signupRequestDtoToGrantComponentDto();
final String url = "/api/broker/user/" + data.getUsername() + "/permission";
final ResponseEntity<Void> grantResponse = restTemplate.exchange(url, HttpMethod.PUT,
new HttpEntity<>(grantDto), Void.class);
if (!grantResponse.getStatusCode().equals(HttpStatus.ACCEPTED)) {
log.error("Failed to grant permissions at queue service: {}", createResponse.getStatusCode());
throw new BrokerUserCreationException("Failed to grant permissions at queue service");
......@@ -50,15 +57,22 @@ public class BrokerServiceGatewayImpl implements BrokerServiceGateway {
}
@Override
public void modifyUserPassword(String username, UserModifyPasswordDto data) throws BrokerUserCreationException {
public void modifyUserPassword(UserModifyPasswordDto data) throws BrokerUserCreationException,
UserNotFoundException, UserEmailNotVerifiedException {
/* obtain token */
final JwtResponseDto obtainResponse = authenticationService.authenticate(data);
/* modify at broker service */
final HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + obtainResponse.getToken());
log.debug("modify user at broker service {}", data);
final ResponseEntity<Void> response = restTemplate.exchange("/api/broker/user/" + username + "/password",
HttpMethod.PUT, new HttpEntity<>(data), Void.class);
final String url = "/api/broker/user/" + data.getUsername() + "/password";
final ResponseEntity<Void> response = restTemplate.exchange(url, HttpMethod.PUT,
new HttpEntity<>(data, headers), Void.class);
if (!response.getStatusCode().equals(HttpStatus.ACCEPTED)) {
log.error("Failed to update user password at queue service: {}", response.getStatusCode());
throw new BrokerUserCreationException("Failed to update user password at queue service");
}
log.info("Updated user password at queue service for username {}", username);
log.info("Updated user password at queue service for username {}", data.getUsername());
log.debug("updated user password at queue service {}", data);
}
......
......@@ -3,6 +3,7 @@ package at.tuwien.mapper;
import at.tuwien.api.amqp.CreateUserDto;
import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto;
import at.tuwien.api.auth.JwtResponseDto;
import at.tuwien.api.auth.LoginRequestDto;
import at.tuwien.api.auth.SignupRequestDto;
import at.tuwien.api.user.*;
import at.tuwien.entities.user.RoleType;
......@@ -30,6 +31,8 @@ public interface UserMapper {
CreateUserDto signupRequestDtoToCreateUserDto(SignupRequestDto data);
LoginRequestDto createUserDtoToLoginRequestDto(CreateUserDto data);
UserPasswordDto userResetDtoToUserPasswordDto(UserResetDto data);
@Transactional(readOnly = true)
......
......@@ -2,8 +2,10 @@ package at.tuwien.service;
import at.tuwien.api.auth.JwtResponseDto;
import at.tuwien.api.auth.LoginRequestDto;
import at.tuwien.api.user.UserModifyPasswordDto;
import at.tuwien.exception.UserEmailNotVerifiedException;
import at.tuwien.exception.UserNotFoundException;
import org.springframework.transaction.annotation.Transactional;
import java.security.Principal;
......@@ -14,9 +16,22 @@ public interface AuthenticationService {
*
* @param data The credentials.
* @return The token, if successful
* @throws UserEmailNotVerifiedException The user email is not verified.
* @throws UserNotFoundException The user was not found by username.
*/
JwtResponseDto authenticate(LoginRequestDto data) throws UserEmailNotVerifiedException, UserNotFoundException;
/**
* Authenticate a user with username and a reset token as credentials.
*
* @param data The credentials.
* @return The token if successful.
* @throws UserEmailNotVerifiedException The user email is not verified.
* @throws UserNotFoundException The user was not found by username.
*/
JwtResponseDto authenticate(UserModifyPasswordDto data) throws UserEmailNotVerifiedException,
UserNotFoundException;
/**
* Renews a token for a given principal
* TODO limit rate of renewal to 1/hour
......
......@@ -3,7 +3,10 @@ package at.tuwien.service;
import at.tuwien.api.auth.SignupRequestDto;
import at.tuwien.api.user.UserPasswordDto;
import at.tuwien.entities.user.User;
import at.tuwien.exception.AuthenticationInvalidException;
import at.tuwien.exception.BrokerUserCreationException;
import at.tuwien.exception.UserEmailNotVerifiedException;
import at.tuwien.exception.UserNotFoundException;
import org.springframework.stereotype.Service;
......@@ -13,17 +16,16 @@ public interface QueueService {
/**
* Creates a user at the Broker Service
*
* @param data The user data.
* @throws BrokerUserCreationException The broker did not create the user.
* @param data The user data@throws BrokerUserCreationException The broker did not create the user.
*/
void createUser(SignupRequestDto data) throws BrokerUserCreationException;
void createUser(SignupRequestDto data) throws BrokerUserCreationException, AuthenticationInvalidException, UserNotFoundException, UserEmailNotVerifiedException;
/**
* Modify a user password at the Broker Service
*
* @param user The user data.
* @param data The user password.
* @param data The user password..
* @throws BrokerUserCreationException The broker did not modify the user.
*/
void modifyUserPassword(User user, UserPasswordDto data) throws BrokerUserCreationException;
void modifyUserPassword(User user, UserPasswordDto data) throws BrokerUserCreationException, UserNotFoundException, UserEmailNotVerifiedException;
}
......@@ -2,7 +2,9 @@ package at.tuwien.service.impl;
import at.tuwien.api.auth.JwtResponseDto;
import at.tuwien.api.auth.LoginRequestDto;
import at.tuwien.api.user.UserModifyPasswordDto;
import at.tuwien.auth.JwtUtils;
import at.tuwien.config.MailConfig;
import at.tuwien.entities.user.User;
import at.tuwien.exception.UserEmailNotVerifiedException;
import at.tuwien.exception.UserNotFoundException;
......@@ -11,7 +13,6 @@ import at.tuwien.service.AuthenticationService;
import at.tuwien.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
......@@ -20,7 +21,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.security.Principal;
import java.util.Arrays;
@Slf4j
......@@ -29,18 +29,17 @@ public class AuthenticationServiceImpl implements AuthenticationService {
private final JwtUtils jwtUtils;
private final UserMapper userMapper;
private final MailConfig mailConfig;
private final UserService userService;
private final Environment environment;
private final AuthenticationManager authenticationManager;
@Autowired
public AuthenticationServiceImpl(JwtUtils jwtUtils, UserMapper userMapper,
UserService userService, Environment environment,
AuthenticationManager authenticationManager) {
public AuthenticationServiceImpl(JwtUtils jwtUtils, UserMapper userMapper, MailConfig mailConfig,
UserService userService, AuthenticationManager authenticationManager) {
this.jwtUtils = jwtUtils;
this.userMapper = userMapper;
this.mailConfig = mailConfig;
this.userService = userService;
this.environment = environment;
this.authenticationManager = authenticationManager;
}
......@@ -49,7 +48,25 @@ public class AuthenticationServiceImpl implements AuthenticationService {
public JwtResponseDto authenticate(LoginRequestDto data) throws UserEmailNotVerifiedException,
UserNotFoundException {
final User user = userService.findByUsername(data.getUsername());
if (isProduction() && !user.getEmailVerified()) {
if (mailConfig.getMailVerify() && !user.getEmailVerified()) {
log.error("E-Mail not verified for username {}", data.getUsername());
throw new UserEmailNotVerifiedException("E-Mail not verified");
}
final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(data.getUsername(),
data.getPassword());
final Authentication authentication = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
final JwtResponseDto response = userMapper.principalToJwtResponseDto(authentication.getPrincipal());
response.setToken(jwtUtils.generateJwtToken(authentication.getPrincipal()));
return response;
}
@Override
@Transactional(readOnly = true)
public JwtResponseDto authenticate(UserModifyPasswordDto data) throws UserEmailNotVerifiedException,
UserNotFoundException {
final User user = userService.findByUsername(data.getUsername());
if (mailConfig.getMailVerify() && !user.getEmailVerified()) {
log.error("E-Mail not verified for username {}", data.getUsername());
throw new UserEmailNotVerifiedException("E-Mail not verified");
}
......@@ -70,8 +87,4 @@ public class AuthenticationServiceImpl implements AuthenticationService {
response.setToken(jwtUtils.generateJwtToken(token.getPrincipal()));
return response;
}
private Boolean isProduction() {
return Arrays.asList(this.environment.getActiveProfiles()).contains("prod");
}
}
......@@ -5,7 +5,10 @@ import at.tuwien.api.auth.SignupRequestDto;
import at.tuwien.api.user.UserModifyPasswordDto;
import at.tuwien.api.user.UserPasswordDto;
import at.tuwien.entities.user.User;
import at.tuwien.exception.AuthenticationInvalidException;
import at.tuwien.exception.BrokerUserCreationException;
import at.tuwien.exception.UserEmailNotVerifiedException;
import at.tuwien.exception.UserNotFoundException;
import at.tuwien.gateway.BrokerServiceGateway;
import at.tuwien.mapper.UserMapper;
import at.tuwien.service.QueueService;
......@@ -33,12 +36,13 @@ public class QueueServiceImpl implements QueueService {
}
@Override
public void modifyUserPassword(User user, UserPasswordDto data) throws BrokerUserCreationException {
public void modifyUserPassword(User user, UserPasswordDto data) throws BrokerUserCreationException,
UserNotFoundException, UserEmailNotVerifiedException {
final UserModifyPasswordDto passwordDto = UserModifyPasswordDto.builder()
.username(user.getUsername())
.password(data.getPassword())
.build();
brokerServiceGateway.modifyUserPassword(user.getUsername(), passwordDto);
brokerServiceGateway.modifyUserPassword(passwordDto);
}
}
......@@ -85,7 +85,7 @@ public class UserServiceImpl implements UserService {
@Override
@Transactional
public User create(SignupRequestDto data) throws UserEmailExistsException, UserNameExistsException {
/* check */
/* duplicate */
final Optional<User> email = userRepository.findByEmail(data.getEmail());
if (email.isPresent()) {
log.error("Email address is already present in the database");
......
......@@ -11,6 +11,7 @@ import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
......@@ -32,7 +33,7 @@ public class UserEndpoint {
}
@PostMapping
@Operation(summary = "Create user", security = @SecurityRequirement(name = "bearerAuth"))
@Operation(summary = "Create user")
public ResponseEntity<?> create(@NotNull @Valid @RequestBody CreateUserDto data) throws ProcessCompletionException {
queueService.createUser(data);
return ResponseEntity.status(HttpStatus.CREATED)
......@@ -40,6 +41,7 @@ public class UserEndpoint {
}
@PutMapping("/{username}/password")
@PreAuthorize("hasRole('ROLE_RESEARCHER')")
@Operation(summary = "Modifies user password", security = @SecurityRequirement(name = "bearerAuth"))
public ResponseEntity<?> modify(@NotNull @PathVariable("username") String username,
@NotNull @Valid @RequestBody UserModifyPasswordDto data,
......@@ -51,12 +53,11 @@ public class UserEndpoint {
}
@PutMapping("/{username}/permission")
@Operation(summary = "Grants user permission", security = @SecurityRequirement(name = "bearerAuth"))
@Operation(summary = "Grants user permission")
public ResponseEntity<?> grant(@NotNull @PathVariable("username") String username,
@NotNull @Valid @RequestBody GrantVirtualHostPermissionsDto data,
@NotNull Principal principal)
@NotNull @Valid @RequestBody GrantVirtualHostPermissionsDto data)
throws ProcessCompletionException {
queueService.grantVirtualHost(username, data, principal);
queueService.grantVirtualHost(username, data);
return ResponseEntity.accepted()
.build();
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment