Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • simonrose/naming-backend
  • anderslindh1/naming-backend
  • andersharrisson/naming-backend
3 results
Show changes
Showing
with 2112 additions and 152 deletions
/*
* Copyright (C) 2023 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service;
import java.text.MessageFormat;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
import org.openepics.names.repository.IAuditStructureRepository;
import org.openepics.names.repository.ISubsystemRepository;
import org.openepics.names.repository.ISystemGroupRepository;
import org.openepics.names.repository.ISystemRepository;
import org.openepics.names.repository.AuditStructureRepository;
import org.openepics.names.repository.SystemRepository;
import org.openepics.names.repository.model.AuditStructure;
import org.openepics.names.repository.model.Subsystem;
import org.openepics.names.repository.model.System;
import org.openepics.names.repository.model.SystemGroup;
import org.openepics.names.rest.beans.Status;
import org.openepics.names.rest.beans.Type;
import org.openepics.names.rest.beans.element.NameElementCommand;
import org.openepics.names.rest.beans.element.StructureElementCommand;
import org.openepics.names.util.EssNamingConvention;
import org.openepics.names.util.HolderStructures;
import org.openepics.names.util.StructureChoice;
import org.openepics.names.util.StructureCommand;
import org.openepics.names.util.StructureElementUtil;
import org.openepics.names.util.StructureUtil;
import org.openepics.names.util.TextUtil;
import org.openepics.names.util.notification.NotificationUtil;
import org.openepics.names.util.notification.StructureElementNotification;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.google.common.collect.Lists;
/**
* This class provides structures services for system.
*
* @author Lars Johansson
*/
@Service
public class SystemService {
private static final Logger LOGGER = Logger.getLogger(SystemService.class.getName());
private final NamesService namesService;
private final SubsystemService subsystemService;
private final ISystemGroupRepository iSystemGroupRepository;
private final ISystemRepository iSystemRepository;
private final ISubsystemRepository iSubsystemRepository;
private final IAuditStructureRepository iAuditStructureRepository;
private final SystemRepository systemRepository;
private final AuditStructureRepository auditStructureRepository;
@Autowired
public SystemService(
NamesService namesService,
SubsystemService subsystemService,
ISystemGroupRepository iSystemGroupRepository,
ISystemRepository iSystemRepository,
ISubsystemRepository iSubsystemRepository,
IAuditStructureRepository iAuditStructureRepository,
SystemRepository systemRepository,
AuditStructureRepository auditStructureRepository) {
this.namesService = namesService;
this.subsystemService = subsystemService;
this.iSystemGroupRepository = iSystemGroupRepository;
this.iSystemRepository = iSystemRepository;
this.iSubsystemRepository = iSubsystemRepository;
this.iAuditStructureRepository = iAuditStructureRepository;
this.systemRepository = systemRepository;
this.auditStructureRepository = auditStructureRepository;
}
@Transactional(propagation = Propagation.MANDATORY)
public StructureElementNotification createStructure(StructureElementCommand structureElementCommand, Date when, String username,
EssNamingConvention namingConvention, HolderStructures holderStructures) {
// validation outside method
// transaction
// support a current transaction, throw an exception if none exists
// create structure
// create audit
// additional
// automatically create name when system structure is created
// conditions on name and structure entry
// system structure should exist - mnemonic
// name should not exist - system structure mnemonic path
// return
// structure element for created structure
// notification
UUID uuid = UUID.randomUUID();
String mnemonic = structureElementCommand.getMnemonic();
mnemonic = StringUtils.isEmpty(mnemonic) ? null : mnemonic;
String equivalenceClassRepresentative = namingConvention.equivalenceClassRepresentative(mnemonic);
// create structure
// create audit
SystemGroup systemGroup = iSystemGroupRepository.findByUuid(structureElementCommand.getParent().toString());
System system = new System(uuid, systemGroup.getId(),
mnemonic, equivalenceClassRepresentative, structureElementCommand.getOrdering(),
structureElementCommand.getDescription(), Status.APPROVED, Boolean.FALSE,
when, username, null);
systemRepository.createSystem(system);
auditStructureRepository.createAuditStructure(new AuditStructure(TextUtil.CREATE, system));
// additional
boolean hasMnemonic = !StringUtils.isEmpty(system.getMnemonic());
boolean existsName = hasMnemonic && namesService.existsName(StructureUtil.getMnemonicPath(system, holderStructures));
if (hasMnemonic && !existsName) {
NameElementCommand nameElement = new NameElementCommand(null, system.getUuid(), null, null, StructuresService.SYSTEM_STRUCTURE_ONLY);
namesService.createName(nameElement, when, username, holderStructures, system);
}
LOGGER.log(Level.FINE, () -> MessageFormat.format(TextUtil.DESCRIPTION_NAME_VALUE, TextUtil.CREATE_STRUCTURE, TextUtil.COMMAND, StructureCommand.CREATE));
return new StructureElementNotification(
StructureElementUtil.getStructureElementProcessed(system, holderStructures, StructureChoice.STRUCTURE),
NotificationUtil.prepareNotification(Type.SYSTEM, StructureCommand.CREATE, null, system, holderStructures));
}
@Transactional(propagation = Propagation.MANDATORY)
public StructureElementNotification updateStructure(StructureElementCommand structureElementCommand, Date when, String username,
EssNamingConvention namingConvention, HolderStructures holderStructures) {
// validation outside method
// transaction
// support a current transaction, throw an exception if none exists
// update structure
// create audit
// previous for notification
// additional - update related names
// return
// structure element for updated structure
// notification
String uuid = structureElementCommand.getUuid().toString();
String mnemonic = structureElementCommand.getMnemonic();
mnemonic = StringUtils.isEmpty(mnemonic) ? null : mnemonic;
String equivalenceClassRepresentative = namingConvention.equivalenceClassRepresentative(mnemonic);
// update structure
// create audit
System system = iSystemRepository.findByUuid(uuid);
system.setMnemonic(mnemonic);
system.setMnemonicEquivalence(equivalenceClassRepresentative);
system.setOrdering(structureElementCommand.getOrdering());
system.setDescription(structureElementCommand.getDescription());
systemRepository.updateSystem(system);
AuditStructure auditStructure = new AuditStructure(TextUtil.UPDATE, system);
auditStructureRepository.createAuditStructure(auditStructure);
// previous
AuditStructure previousAuditStructure = iAuditStructureRepository.findPreviousByAuditTableAndUuidAndAuditId(Type.SYSTEM.name().toLowerCase(), uuid, auditStructure.getAuditId());
System previous = new System(previousAuditStructure);
// additional
namesService.updateNames(previous, system, username, holderStructures);
LOGGER.log(Level.FINE, () -> MessageFormat.format(TextUtil.DESCRIPTION_NAME_VALUE, TextUtil.UPDATE_STRUCTURE, TextUtil.COMMAND, StructureCommand.UPDATE));
return new StructureElementNotification(
StructureElementUtil.getStructureElementProcessed(system, holderStructures, StructureChoice.STRUCTURE),
NotificationUtil.prepareNotification(Type.SYSTEM, StructureCommand.UPDATE, previous, system, holderStructures));
}
@Transactional(propagation = Propagation.MANDATORY)
public StructureElementNotification deleteStructure(StructureElementCommand structureElementCommand, Date when, String username,
HolderStructures holderStructures) {
// validation outside method
// transaction
// support a current transaction, throw an exception if none exists
// delete structure (update)
// create audit
// previous for notification
// additional
// delete related names
// delete sub structures (and related names)
// return
// structure element for deleted structure
// notification
String uuid = structureElementCommand.getUuid().toString();
// delete structure
// create audit
System system = iSystemRepository.findByUuid(uuid);
system.setDeleted(Boolean.TRUE);
system.setAttributesProcessed(when, username, null);
systemRepository.updateSystem(system);
AuditStructure auditStructure = new AuditStructure(TextUtil.DELETE, system);
auditStructureRepository.createAuditStructure(auditStructure);
// previous
AuditStructure previousAuditStructure = iAuditStructureRepository.findPreviousByAuditTableAndUuidAndAuditId(Type.SYSTEM.name().toLowerCase(), uuid, auditStructure.getAuditId());
System previous = new System(previousAuditStructure);
// additional
// will propagate to sub structures
namesService.deleteNames(system, username, holderStructures);
List<StructureElementCommand> commands = Lists.newArrayList();
List<Subsystem> subsystems = iSubsystemRepository.findNotDeletedByParent(system.getId());
for (Subsystem subsystem : subsystems) {
commands.add(new StructureElementCommand(subsystem.getUuid(), Type.SUBSYSTEM, null, null, null, null));
}
for (StructureElementCommand command : commands) {
subsystemService.deleteStructure(command, when, username, holderStructures);
}
LOGGER.log(Level.FINE, () -> MessageFormat.format(TextUtil.DESCRIPTION_NAME_VALUE, TextUtil.DELETE_STRUCTURE, TextUtil.COMMAND, StructureCommand.DELETE));
return new StructureElementNotification(
StructureElementUtil.getStructureElementProcessed(system, holderStructures, StructureChoice.STRUCTURE),
NotificationUtil.prepareNotification(Type.SYSTEM, StructureCommand.DELETE, previous, system, holderStructures));
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openepics.names.configuration.SecurityConfiguration;
import org.openepics.names.exception.ServiceException;
import org.openepics.names.exception.security.AuthenticationException;
import org.openepics.names.exception.security.UnauthorizedException;
import org.openepics.names.rest.beans.security.LoginResponse;
import org.openepics.names.service.security.dto.LoginTokenDto;
import org.openepics.names.service.security.dto.UserDetails;
import org.openepics.names.service.security.util.EncryptUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
* @author <a href="mailto:imre.toth@ess.eu">Imre Toth</a>
*/
@Service
public class AuthenticationService {
private static final Logger LOGGER = Logger.getLogger(AuthenticationService.class.getName());
@Value("${jwt.expire.in.minutes}")
private Integer tokenExpiration;
private final UserService userService;
private final JwtTokenService jwtTokenService;
private final EncryptUtil encryptUtil;
@Autowired
public AuthenticationService(
UserService userService, JwtTokenService jwtTokenService, EncryptUtil encryptUtil) {
this.userService = userService;
this.jwtTokenService = jwtTokenService;
this.encryptUtil = encryptUtil;
}
/**
* Checks if user has permission in RBAC, and if has, logs in. Successful login will result in
* creating a JWT for the REST API communication
*
* @param userName The login name for the user
* @param password The password for the user
* @return After successful login the backend will generate a JWT that can be user for the REST
* API communication
* @throws AuthenticationException If user has bad username/password, or doesn't have permission
* to log in
*/
public LoginResponse login(String userName, String password) throws AuthenticationException {
LoginTokenDto tokenDto = userService.loginUser(userName, password);
// checking user roles in RBAC
checkUserRights(tokenDto.getRoles(), userName);
UserDetails userDetails = new UserDetails();
userDetails.setUserName(userName);
userDetails.setToken(encryptUtil.encrypt(tokenDto.getToken()));
userDetails.setRoles(tokenDto.getRoles());
long tokenExpiresIn = Math.min(tokenExpiration, tokenDto.getExpirationDuration());
return new LoginResponse(jwtTokenService.generateToken(userDetails, tokenExpiresIn));
}
/**
* Checks if user has roles in RBAC at the beginning of the login process
*
* @param userRoles The roles of the user according to RBAC
* @param userName The name of the user
* @throws UnauthorizedException If user doesn't have permissions in RBAC
*/
private void checkUserRights(List<String> userRoles, String userName) throws UnauthorizedException {
List<String> rolesUserHas =
userRoles.stream()
.filter(SecurityConfiguration.getAllowedRolesToLogin()::contains)
.toList();
if (rolesUserHas.isEmpty()) {
LOGGER.log(Level.WARNING, "User ({}) doesn't have permission to log in", userName);
throw new UnauthorizedException("You don't have permission to log in");
}
}
public LoginResponse renewToken(UserDetails user) {
LoginTokenDto token = userService.renewToken(user.getToken());
user.setToken(encryptUtil.encrypt(user.getToken()));
long tokenExpiresIn = Math.min(tokenExpiration, token.getExpirationDuration());
return new LoginResponse(jwtTokenService.generateToken(user, tokenExpiresIn));
}
public void logout(UserDetails user) throws ServiceException {
userService.logoutUser(user.getToken());
}
public List<String> getUserRoles(UserDetails user) {
UserDetails userInfo = userService.getUserInfoFromToken(user.getToken());
return userInfo.getRoles();
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security;
import io.fusionauth.jwt.Signer;
import io.fusionauth.jwt.Verifier;
import io.fusionauth.jwt.domain.JWT;
import io.fusionauth.jwt.hmac.HMACSigner;
import io.fusionauth.jwt.hmac.HMACVerifier;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Date;
import org.openepics.names.service.security.dto.UserDetails;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
* @author <a href="mailto:imre.toth@ess.eu">Imre Toth</a>
*/
@Service
public class JwtTokenService {
private static final String TOKEN = "token";
private static final String ROLES = "roles";
@Value("${jwt.secret}")
private String secret;
/**
* Retrieves userName from JWT token.
*
* @param token the JWT token.
* @return the userName from the JWT token.
*/
public String getUsernameFromToken(String token) {
return decodeJWT(token).subject;
}
public String getRBACTokenFromToken(String token) {
return decodeJWT(token).getAllClaims().get(TOKEN).toString();
}
// check if the token has expired
private Boolean isTokenExpired(String token) {
return decodeJWT(token).isExpired();
}
/**
* Retrieving a field value from the JWT token.
*
* @param token The JWT token.
* @return The decoded value of the JWT token.
*/
public JWT decodeJWT(String token) {
Verifier verifier = HMACVerifier.newVerifier(secret);
return JWT.getDecoder().decode(token, verifier);
}
/**
* Generates JWT token from userInformation, and expiration interval.
*
* @param userDetails Information about the user.
* @param tokenExpiration JWT token expiration interval.
* @return The generated JWT token.
*/
// while creating the token -
// 1. Define claims of the token, like Issuer, Expiration, Subject, and the ID
// 2. Sign the JWT using the HS512 algorithm and secret key.
// 3. According to JWS Compact
// Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1)
// compaction of the JWT to a URL-safe string
public String generateToken(UserDetails userDetails, long tokenExpiration) {
Date jwtTokenValidity = new Date(System.currentTimeMillis() + tokenExpiration * 60 * 1000);
Signer signer = HMACSigner.newSHA512Signer(secret);
JWT jwt =
new JWT()
.setIssuer("Naming-tool")
.setIssuedAt(ZonedDateTime.now(ZoneOffset.UTC))
.setSubject(userDetails.getUserName())
.addClaim(TOKEN, userDetails.getToken())
.addClaim(ROLES, userDetails.getRoles())
.setExpiration(ZonedDateTime.ofInstant(jwtTokenValidity.toInstant(), ZoneId.systemDefault()));
return JWT.getEncoder().encode(jwt, signer);
}
// validate token
/**
* Checks if the JWT token is valid.
*
* @param token The JWT token that has to be checked.
* @param userDetails The users details.
* @return <code>true</code> if the JWT token is valid <code>false</code> if the JWT token is not
* valid
*/
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUserName()) && !isTokenExpired(token));
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security;
import org.openepics.names.service.security.dto.UserDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
/**
* @author <a href="mailto:imre.toth@ess.eu">Imre Toth</a>
*/
@Service
public class JwtUserDetailsService implements UserDetailsService {
private final UserService userService;
@Autowired
public JwtUserDetailsService(UserService userService) {
this.userService = userService;
}
public UserDetails loadUserByToken(String token) {
return userService.getUserInfoFromToken(token);
}
@Override
public org.springframework.security.core.userdetails.UserDetails loadUserByUsername(String s)
throws UsernameNotFoundException {
return null;
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security;
import java.util.Base64;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openepics.names.exception.security.AuthenticationException;
import org.openepics.names.exception.security.EntityNotFoundException;
import org.openepics.names.exception.security.ParseException;
import org.openepics.names.exception.security.RemoteException;
import org.openepics.names.exception.security.RemoteServiceException;
import org.openepics.names.exception.security.UnauthorizedException;
import org.openepics.names.service.LogService;
import org.openepics.names.service.security.rbac.RBACToken;
import org.openepics.names.service.security.rbac.RBACUserInfo;
import org.openepics.names.service.security.rbac.RBACUserRoles;
import org.openepics.names.service.security.util.HttpClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import okhttp3.Headers;
/**
* @author <a href="mailto:imre.toth@ess.eu">Imre Toth</a>
*/
@Service
public class RBACService {
private static final Logger LOGGER = Logger.getLogger(RBACService.class.getName());
private static final String RBAC_SERVER_BASEURL = "rbac.server.address";
private static final String AUTH_PATH = "auth";
private static final String TOKEN_PATH = AUTH_PATH + "/token";
private static final String AUTHENTICATION_EXCEPTION = "Authentication Exception";
private final Environment env;
private final HttpClientService httpClientService;
private final LogService logService;
@Autowired
public RBACService(Environment env, HttpClientService httpClientService, LogService logService) {
this.env = env;
this.httpClientService = httpClientService;
this.logService = logService;
}
/**
* Tries to log in user with specific parameters into RBAC, and if successful then give back
* userInformation, and a token.
*
* @param userName the loginName for the user
* @param password the password for the user
* @return userInformation, and token for the successfully logged in user
* @throws AuthenticationException if authentication fails
*/
public RBACToken loginUser(String userName, String password) throws RemoteException, AuthenticationException {
String secretHeader = Base64.getEncoder().encodeToString((userName + ":" + password).getBytes());
Headers headers = new Headers.Builder().add("Authorization", "BASIC " + secretHeader).build();
// trying to log in
try {
HttpClientService.ServiceResponse<RBACToken> rbacTokenServiceResponse =
httpClientService.executePostRequest(
headers,
env.getProperty(RBAC_SERVER_BASEURL) + TOKEN_PATH,
null,
HttpClientService.XML_MEDIA_TYPE,
RBACToken.class);
// successful login
if (HttpClientService.isSuccessHttpStatusCode(rbacTokenServiceResponse.getStatusCode())) {
return rbacTokenServiceResponse.getEntity();
}
} catch (ParseException | RemoteServiceException e) {
logService.logException(LOGGER, Level.WARNING, e, "Login error");
throw new RemoteException(AUTHENTICATION_EXCEPTION, "Error while trying to log in to RBAC");
}
throw new AuthenticationException("Bad username/password");
}
/**
* Gathers information about the logged in user.
*
* @param token the token for the logged in user.
* @return information about the logged in user.
* @throws UnauthorizedException if the token is not valid
*/
public RBACToken userInfoFromToken(String token) throws UnauthorizedException, RemoteException {
Headers headers = new Headers.Builder().build();
try {
HttpClientService.ServiceResponse<RBACToken> rbacTokenServiceResponse =
httpClientService.executeGetRequest(
headers,
env.getProperty(RBAC_SERVER_BASEURL) + TOKEN_PATH + "/" + token,
HttpClientService.XML_MEDIA_TYPE,
RBACToken.class);
// token check was successful
if (HttpClientService.isSuccessHttpStatusCode(rbacTokenServiceResponse.getStatusCode())) {
return rbacTokenServiceResponse.getEntity();
}
} catch (RemoteServiceException | ParseException e) {
throw new RemoteException(AUTHENTICATION_EXCEPTION, "Error while trying to getting token info from RBAC");
}
throw new UnauthorizedException("Token error");
}
/**
* Can be used to renew a valid client token in RBAC.
*
* @param token the token for the logged in user.
* @return the renewed token, and user information.
* @throws UnauthorizedException if token was not valid for renewing.
*/
public RBACToken renewToken(String token) throws UnauthorizedException, RemoteException {
Headers headers = new Headers.Builder().build();
try {
HttpClientService.ServiceResponse<RBACToken> rbacTokenServiceResponse =
httpClientService.executePostRequest(
headers,
env.getProperty(RBAC_SERVER_BASEURL) + TOKEN_PATH + "/" + token + "/renew",
null,
HttpClientService.XML_MEDIA_TYPE,
RBACToken.class);
// token renewal was successful
if (HttpClientService.isSuccessHttpStatusCode(rbacTokenServiceResponse.getStatusCode())) {
return rbacTokenServiceResponse.getEntity();
}
} catch (RemoteServiceException | ParseException e) {
throw new RemoteException(AUTHENTICATION_EXCEPTION, "Error while trying to renew token in RBAC");
}
throw new UnauthorizedException("Token renewal error");
}
/**
* Can be used to log out a user from RBAC (deletes the token).
*
* @param token the token for the logged in user.
* @throws RemoteException if token was not valid, or already deleted
*/
public void deleteToken(String token) throws RemoteException {
Headers headers = new Headers.Builder().build();
try {
Integer respCode =
httpClientService.executeDeleteRequest(
headers, env.getProperty(RBAC_SERVER_BASEURL) + TOKEN_PATH + "/" + token);
// deleting token was successful
if (HttpClientService.isSuccessHttpStatusCode(respCode)) {
return;
}
LOGGER.log(Level.WARNING, "Failed to delete RBAC token, response code: {}", respCode);
throw new RemoteException(
"RBAC error", "Failed to delete RBAC token, response code: " + respCode);
} catch (RemoteServiceException | ParseException e) {
logService.logException(LOGGER, Level.WARNING, e, "Failed to delete RBAC token");
throw new RemoteException("RBAC error", "Failed to delete RBAC token: " + e.getMessage());
}
}
public RBACUserInfo fetchUsersByRole(String role) throws AuthenticationException, RemoteException {
Headers headers = new Headers.Builder().build();
try {
HttpClientService.ServiceResponse<RBACUserInfo> rbacTokenServiceResponse =
httpClientService.executeGetRequest(
headers,
env.getProperty(RBAC_SERVER_BASEURL) + AUTH_PATH + "/" + role + "/users",
HttpClientService.XML_MEDIA_TYPE,
RBACUserInfo.class);
// fetching users was successful
if (HttpClientService.isSuccessHttpStatusCode(rbacTokenServiceResponse.getStatusCode())) {
return rbacTokenServiceResponse.getEntity();
}
if (HttpStatus.NOT_FOUND.value() == rbacTokenServiceResponse.getStatusCode()) {
throw new EntityNotFoundException("RBAC role", role);
}
} catch (RemoteServiceException | ParseException e) {
throw new RemoteException(AUTHENTICATION_EXCEPTION, "Error while trying get users from RBAC");
}
throw new UnauthorizedException("Getting user info error");
}
/**
* Fetching roles from RBAC based on username.
*
* @param username The login name to fetch the user roles.
* @throws RemoteException When error occurs when trying to fetch roles from RBAC
* @return List of roles that is associated to the user in RBAC
*/
public RBACUserRoles fetchRolesByUsername(String username) throws AuthenticationException, RemoteException {
Headers headers = new Headers.Builder().build();
try {
HttpClientService.ServiceResponse<RBACUserRoles> rbacTokenServiceResponse =
httpClientService.executeGetRequest(
headers,
env.getProperty(RBAC_SERVER_BASEURL) + AUTH_PATH + "/" + username + "/role",
HttpClientService.XML_MEDIA_TYPE,
RBACUserRoles.class);
// fetching users was successful
if (HttpClientService.isSuccessHttpStatusCode(rbacTokenServiceResponse.getStatusCode())) {
return rbacTokenServiceResponse.getEntity();
}
} catch (RemoteServiceException | ParseException e) {
throw new RemoteException(AUTHENTICATION_EXCEPTION, "Error while trying get roles by username from RBAC");
}
throw new RemoteException(AUTHENTICATION_EXCEPTION, "Error while trying to fetch user-roles");
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openepics.names.exception.ServiceException;
import org.openepics.names.exception.security.AuthenticationException;
import org.openepics.names.exception.security.UnauthorizedException;
import org.openepics.names.service.LogService;
import org.openepics.names.service.security.dto.LoginTokenDto;
import org.openepics.names.service.security.dto.UserDetails;
import org.openepics.names.service.security.rbac.RBACToken;
import org.openepics.names.service.security.util.SecurityUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author <a href="mailto:imre.toth@ess.eu">Imre Toth</a>
*/
@Service
public class UserService {
private static final Logger LOGGER = Logger.getLogger(UserService.class.getName());
private final RBACService rbacService;
private final LogService logService;
@Autowired
public UserService(RBACService rbacService, LogService logService) {
this.rbacService = rbacService;
this.logService = logService;
}
/**
* Logs in user with username/password in RBAC, and if successful, generates JWT token
*
* @param userName The user's login name
* @param password The user's password
* @return LoginTokenDto with roles that can be user for generating JWT token
* @throws AuthenticationException When user tries to log in with bad username/password
* @throws ServiceException If _other_ error occurs during the authentication process
*/
public LoginTokenDto loginUser(String userName, String password)
throws ServiceException, AuthenticationException {
try {
RBACToken rbacToken = rbacService.loginUser(userName, password);
LocalDateTime createTime = LocalDateTime.ofInstant(rbacToken.getCreationTime().toInstant(), ZoneId.systemDefault());
LocalDateTime expirationTime = LocalDateTime.ofInstant(rbacToken.getExpirationTime().toInstant(), ZoneId.systemDefault());
Duration duration = Duration.between(createTime, expirationTime);
long expireInMinutes = Math.abs(duration.toMinutes());
return new LoginTokenDto(rbacToken.getId(), expireInMinutes, rbacToken.getRoles());
} catch (AuthenticationException e) {
logService.logServiceException(LOGGER, Level.WARNING, e, "Error while trying to log in user to RBAC");
throw e;
} catch (ServiceException e) {
logService.logServiceException(LOGGER, Level.WARNING, e, "Error while trying to log in user to RBAC");
throw e;
}
}
/**
* Extracting user-info from RBAC using the token they have
*
* @param token The user's login token
* @return The user-information stored in RBAC
*/
public UserDetails getUserInfoFromToken(String token) {
return SecurityUtil.convertToUserDetails(rbacService.userInfoFromToken(token));
}
/**
* Trying to renew user's token in RBAC.
*
* @param token The user's login token.
* @return Information about the renewed token (including the new expiration date)
* @throws ServiceException When unexpected error occurs during the renewal process
* @throws UnauthorizedException When RBAC gives error during the token renewal process
*/
public LoginTokenDto renewToken(String token) throws ServiceException, UnauthorizedException {
RBACToken rbacToken = rbacService.renewToken(token);
LocalDateTime createTime = LocalDateTime.ofInstant(rbacToken.getCreationTime().toInstant(), ZoneId.systemDefault());
LocalDateTime expirationTime = LocalDateTime.ofInstant(rbacToken.getExpirationTime().toInstant(), ZoneId.systemDefault());
Duration duration = Duration.between(createTime, expirationTime);
long expireInMinutes = Math.abs(duration.toMinutes());
return new LoginTokenDto(rbacToken.getId(), expireInMinutes, rbacToken.getRoles());
}
/**
* Trying to log out user from RBAC. This will result in deleting token from the system.
*
* @param token The user's token that has to be deleted from RBAC
* @throws ServiceException When error occurs during the token logout process
*/
public void logoutUser(String token) throws ServiceException {
rbacService.deleteToken(token);
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security.dto;
import java.util.List;
/**
* @author <a href="mailto:imre.toth@ess.eu">Imre Toth</a>
*/
public class LoginTokenDto {
private String token;
private long expirationDuration;
private List<String> roles;
public LoginTokenDto(String token, long expirationDuration, List<String> roles) {
this.token = token;
this.expirationDuration = expirationDuration;
this.roles = roles;
}
public String getToken() {
return token;
}
public long getExpirationDuration() {
return expirationDuration;
}
public List<String> getRoles() {
return roles;
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security.dto;
import org.springframework.security.core.GrantedAuthority;
/**
* @author <a href="mailto:zoltan.runyo@ess.eu">Zoltan Runyo</a>
*/
public class RoleAuthority implements GrantedAuthority {
private static final long serialVersionUID = -47836193761080412L;
private String role;
public RoleAuthority(String role) {
this.role = role;
}
@Override
public String getAuthority() {
return role;
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security.dto;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.security.core.GrantedAuthority;
/**
* @author <a href="mailto:imre.toth@ess.eu">Imre Toth</a>
*/
public class UserDetails {
private String userName;
private String fullName;
private String token;
private Collection<RoleAuthority> authorities;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
public List<String> getRoles() {
return authorities != null
? authorities.stream().map(RoleAuthority::getAuthority).collect(Collectors.toList())
: Collections.emptyList();
}
public void setRoles(List<String> roles) {
this.authorities =
roles != null
? roles.stream().map(RoleAuthority::new).collect(Collectors.toList())
: Collections.emptyList();
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security.rbac;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author <a href="mailto:imre.toth@ess.eu">Imre Toth</a>
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class RBACToken {
private String id;
@JsonProperty("username")
private String userName;
private String firstName;
private String lastName;
private Date creationTime;
private Date expirationTime;
private String ip;
private List<String> roles;
private String signature;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Date getCreationTime() {
return RBACToken.cloneDate(creationTime);
}
public void setCreationTime(Date creationTime) {
this.creationTime = RBACToken.cloneDate(creationTime);
}
public Date getExpirationTime() {
return RBACToken.cloneDate(expirationTime);
}
public void setExpirationTime(Date expirationTime) {
this.expirationTime = RBACToken.cloneDate(expirationTime);
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
public String getSignature() {
return signature;
}
public void setSignature(String signature) {
this.signature = signature;
}
/**
* Clones a Date to make object immutable.
*
* @param dateToClone the date that has to be cloned.
* @return <code>null</code>, if date was null <code>cloned date</code>, if date was not null
*/
public static Date cloneDate(Date dateToClone) {
return dateToClone == null ? null : new Date(dateToClone.getTime());
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security.rbac;
import java.util.List;
/**
* @author <a href="mailto:zoltan.runyo@ess.eu">Zoltan Runyo</a>
*/
public class RBACUserInfo {
private String role;
private List<String> users;
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public List<String> getUsers() {
return users;
}
public void setUsers(List<String> users) {
this.users = users;
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security.rbac;
import java.util.List;
/**
* @author <a href="mailto:imre.toth@ess.eu">Imre Toth</a>
*/
public class RBACUserRoles {
private String username;
private List<String> roles;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security.util;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* Encryption utility.
*
* @author <a href="mailto:imre.toth@ess.eu">Imre Toth</a>
*/
@Component
public class EncryptUtil {
private static final Logger LOGGER = Logger.getLogger(EncryptUtil.class.getName());
@Value("${aes.key}")
private String secret;
private static SecretKeySpec secretKey;
private static final String ALGORITHM = "AES";
private void prepareSecretKey(String myKey) {
MessageDigest sha = null;
try {
byte[] key = myKey.getBytes(StandardCharsets.UTF_8);
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, ALGORITHM);
} catch (NoSuchAlgorithmException e) {
LOGGER.log(Level.WARNING, SecurityTextUtil.ALGORITHM_NOT_FOUND, e);
}
}
/**
* String encryption wrapper.
*
* @param strToEncrypt string to encrypt
* @return encrypted string
*/
public String encrypt(String strToEncrypt) {
try {
prepareSecretKey(secret);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.getEncoder()
.encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
LOGGER.log(Level.WARNING, SecurityTextUtil.ERROR_WHILE_ENCRYPTING, e);
}
return null;
}
/**
* String decryption wrapper.
*
* @param strToDecrypt string to decrypt
* @return decrypted string
*/
public String decrypt(String strToDecrypt) {
try {
prepareSecretKey(secret);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
} catch (Exception e) {
LOGGER.log(Level.WARNING, SecurityTextUtil.ERROR_WHILE_DECRYPTING, e);
}
return null;
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security.util;
import java.io.IOException;
import java.lang.reflect.Type;
import org.openepics.names.exception.security.ParseException;
import org.openepics.names.exception.security.RemoteServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* @author <a href="mailto:zoltan.runyo@ess.eu">Zoltan Runyo</a>
*/
@Service
public class HttpClientService {
public static final MediaType JSON_MEDIA_TYPE = MediaType.get("application/json; charset=utf-8");
public static final MediaType XML_MEDIA_TYPE = MediaType.get("application/xml; charset=utf-8");
private static final String UNABLE_TO_CALL_SERVICE = "Unable to call service";
private final OkHttpClient okHttpClient;
@Autowired
public HttpClientService(OkHttpClient okHttpClient) {
this.okHttpClient = okHttpClient;
}
public static class ServiceResponse<T> {
private final T entity;
private final int statusCode;
private final String errorMessage;
private final Headers headers;
public ServiceResponse(T entity, int statusCode, Headers headers) {
this.entity = entity;
this.statusCode = statusCode;
this.errorMessage = null;
this.headers = headers;
}
public ServiceResponse(int statusCode, String errorMessage, Headers headers) {
this.entity = null;
this.statusCode = statusCode;
this.errorMessage = errorMessage;
this.headers = headers;
}
public T getEntity() {
return entity;
}
public int getStatusCode() {
return statusCode;
}
public String getErrorMessage() {
return errorMessage;
}
public Headers getHeaders() {
return headers;
}
}
public <T> ServiceResponse<T> executeGetRequest(
Headers headers, String url, Class<T> responseClass) throws RemoteServiceException {
return executeGetRequest(headers, url, responseClass, null);
}
public <T> ServiceResponse<T> executeGetRequest(
Headers headers, String url, Class<T> responseClass, String formatDate)
throws RemoteServiceException {
Request request;
request = new Request.Builder().headers(headers).url(url).build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (isSuccessHttpStatusCode(response.code())) {
Gson gson = new Gson();
if (formatDate != null) {
gson = new GsonBuilder().setDateFormat(formatDate).create();
}
return new ServiceResponse<>(
gson.fromJson(response.body().string(), responseClass),
response.code(),
response.headers());
}
return new ServiceResponse<>(null, response.code(), response.headers());
} catch (IOException e) {
throw new RemoteServiceException(UNABLE_TO_CALL_SERVICE, e);
}
}
public <T> ServiceResponse<T> executeGetRequest(
Headers headers, String url, Type type, String formatDate) throws RemoteServiceException {
Request request;
request = new Request.Builder().headers(headers).url(url).build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (isSuccessHttpStatusCode(response.code())) {
return new ServiceResponse<>(
new GsonBuilder()
.setDateFormat(formatDate)
.create()
.fromJson(response.body().string(), type),
response.code(),
response.headers());
}
return new ServiceResponse<>(null, response.code(), response.headers());
} catch (IOException e) {
throw new RemoteServiceException(UNABLE_TO_CALL_SERVICE, e);
}
}
public <T> ServiceResponse<T> executeGetRequest(Headers headers, String url, Type type)
throws RemoteServiceException {
Request request;
request = new Request.Builder().headers(headers).url(url).build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (isSuccessHttpStatusCode(response.code())) {
return new ServiceResponse<>(
new GsonBuilder().create().fromJson(response.body().string(), type),
response.code(),
response.headers());
}
return new ServiceResponse<>(null, response.code(), response.headers());
} catch (IOException e) {
throw new RemoteServiceException(UNABLE_TO_CALL_SERVICE, e);
}
}
public <T> ServiceResponse<T> executeGetRequest(
Headers headers, String url, MediaType mediaType, Class<T> responseClass)
throws RemoteServiceException, ParseException {
if (JSON_MEDIA_TYPE.equals(mediaType)) {
return executeGetRequest(headers, url, responseClass);
}
Request request;
request = new Request.Builder().headers(headers).url(url).build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (isSuccessHttpStatusCode(response.code())) {
return new ServiceResponse<>(
new XmlMapper().readValue(response.body().string(), responseClass),
response.code(),
response.headers());
}
return new ServiceResponse<>(null, response.code(), response.headers());
} catch (IOException e) {
throw new RemoteServiceException("Unable to call service with GET method", e);
}
}
public ServiceResponse<String> executePlainGetRequest(Headers headers, String url)
throws RemoteServiceException {
Request request;
request = new Request.Builder().headers(headers).url(url).build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (isSuccessHttpStatusCode(response.code())) {
return new ServiceResponse<>(response.body().string(), response.code(), response.headers());
}
return new ServiceResponse<>(null, response.code(), response.headers());
} catch (IOException e) {
throw new RemoteServiceException(UNABLE_TO_CALL_SERVICE, e);
}
}
public <T> ServiceResponse<T> executePostRequest(
Headers headers, String url, Object requestBody, Class<T> responseClass)
throws RemoteServiceException {
RequestBody body = RequestBody.create(new Gson().toJson(requestBody), JSON_MEDIA_TYPE);
Request request;
request = new Request.Builder().headers(headers).url(url).post(body).build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (isSuccessHttpStatusCode(response.code())) {
return new ServiceResponse<>(
new Gson().fromJson(response.body().string(), responseClass),
response.code(),
response.headers());
}
return new ServiceResponse<>(
response.code(),
response.body() != null ? response.body().string() : null,
response.headers());
} catch (IOException e) {
throw new RemoteServiceException(UNABLE_TO_CALL_SERVICE, e);
}
}
public ServiceResponse<String> executePlainPostRequest(
Headers headers, String url, Object requestBody) throws RemoteServiceException {
RequestBody body = RequestBody.create(new Gson().toJson(requestBody), JSON_MEDIA_TYPE);
Request request;
request = new Request.Builder().headers(headers).url(url).post(body).build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (isSuccessHttpStatusCode(response.code())) {
return new ServiceResponse<>(response.body().string(), response.code(), response.headers());
}
return new ServiceResponse<>(null, response.code(), response.headers());
} catch (IOException e) {
throw new RemoteServiceException(UNABLE_TO_CALL_SERVICE, e);
}
}
public <T> ServiceResponse<T> executePostRequest(
Headers headers, String url, Object requestBody, MediaType mediaType, Class<T> responseClass)
throws RemoteServiceException, ParseException {
if (JSON_MEDIA_TYPE.equals(mediaType)) {
return executePostRequest(headers, url, requestBody, responseClass);
}
Request request;
try {
RequestBody body =
RequestBody.create(new XmlMapper().writeValueAsString(requestBody), XML_MEDIA_TYPE);
request = new Request.Builder().headers(headers).url(url).post(body).build();
} catch (JsonProcessingException e) {
throw new ParseException("Unable to parse object");
}
try (Response response = okHttpClient.newCall(request).execute()) {
if (isSuccessHttpStatusCode(response.code())) {
return new ServiceResponse<>(
new XmlMapper().readValue(response.body().string(), responseClass),
response.code(),
response.headers());
}
return new ServiceResponse<>(null, response.code(), response.headers());
} catch (IOException e) {
throw new RemoteServiceException("Unable to call service with POST method", e);
}
}
public Integer executeDeleteRequest(Headers headers, String url)
throws RemoteServiceException, ParseException {
Request request;
request = new Request.Builder().headers(headers).url(url).delete().build();
try (Response response = okHttpClient.newCall(request).execute()) {
return response.code();
} catch (IOException e) {
throw new RemoteServiceException("Unable to call service with POST method", e);
}
}
public static boolean isSuccessHttpStatusCode(int code) {
return code >= 200 && code < 300;
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security.util;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
/**
* @author <a href="mailto:imre.toth@ess.eu">Imre Toth</a>
*/
@Configuration
public class OkHttpConfiguration {
public static final String ALLOW_UNTRUSTED_CERTS = "allow.untrusted.certs";
private final Environment env;
@Autowired
public OkHttpConfiguration(Environment env) {
this.env = env;
}
@Bean
public OkHttpClient okHttpClient() {
OkHttpClient.Builder clientBuilder =
new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.connectionPool(pool());
boolean allowUntrusted = BooleanUtils.toBoolean(env.getProperty(ALLOW_UNTRUSTED_CERTS));
if (allowUntrusted) {
final X509TrustManager trustManager =
new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkServerTrusted(final X509Certificate[] chain, final String authType) {}
@Override
public void checkClientTrusted(final X509Certificate[] chain, final String authType) {}
};
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] {trustManager}, new java.security.SecureRandom());
} catch (NoSuchAlgorithmException | KeyManagementException e) {
return null;
}
clientBuilder.sslSocketFactory(sslContext.getSocketFactory(), trustManager);
HostnameVerifier hostnameVerifier = (hostname, session) -> true;
clientBuilder.hostnameVerifier(hostnameVerifier);
}
return clientBuilder.build();
}
@Bean
public ConnectionPool pool() {
return new ConnectionPool(40, 10, TimeUnit.SECONDS);
}
}
/*
* Copyright (C) 2024 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.service.security.util;
/**
* Utility class to assist in handling of text, in particular for security related matters.
*
* @author Lars Johansson
*/
public class SecurityTextUtil {
// authentication & authorization
public static final String AUTHENTICATION_HEADER_PREFIX = "Bearer ";
public static final String COOKIE_AUTH_HEADER = "naming-auth";
// encryption
public static final String ALGORITHM_NOT_FOUND = "Algorithm not found";
public static final String ERROR_WHILE_DECRYPTING = "Error while decrypting";
public static final String ERROR_WHILE_ENCRYPTING = "Error while encrypting";
/**
* This class is not to be instantiated.
*/
private SecurityTextUtil() {
throw new IllegalStateException("Utility class");
}
}
package org.openepics.names.service.security.util;
import org.openepics.names.service.security.dto.UserDetails;
import org.openepics.names.service.security.rbac.RBACToken;
/**
* @author <a href="mailto:zoltan.runyo@ess.eu">Zoltan Runyo</a>
* @author Lars Johansson
*/
public class SecurityUtil {
/**
* This class is not to be instantiated.
*/
private SecurityUtil() {
throw new IllegalStateException("Utility class");
}
/**
* Converts RBAC token to user details DTO
*
* @param rbacInfo RBAC token descriptor
* @return User details DTO
*/
public static UserDetails convertToUserDetails(RBACToken rbacInfo) {
UserDetails result = null;
if (rbacInfo != null) {
result = new UserDetails();
result.setUserName(rbacInfo.getUserName());
result.setFullName(rbacInfo.getFirstName() + " " + rbacInfo.getLastName());
result.setToken(rbacInfo.getId());
result.setRoles(rbacInfo.getRoles());
}
return result;
}
/**
* Return username for a user.
*
* @param userDetails user details
* @return username
*/
public static String getUsername(UserDetails userDetails) {
return userDetails != null ? userDetails.getUserName() : null;
}
}
......@@ -51,8 +51,6 @@ public class EssNamingConvention implements NamingConvention {
// mnemonic for instance index may be omitted for ess name
// ----------------------------------------------------------------------------------------------------
// Note revision history of file in repository.
private static final String MNEMONIC_ALPHABETIC_LOWERCASE = "^[a-z]+$";
private static final String MNEMONIC_ALPHANUMERIC = "^[a-zA-Z0-9]+$";
private static final String MNEMONIC_NUMERIC = "^[0-9]+$";
......@@ -76,6 +74,8 @@ public class EssNamingConvention implements NamingConvention {
@Override
public boolean isInstanceIndexValid(String conventionName, boolean overrideRuleset) {
// ability to override ruleset for administrator
String instanceIndex = NamingConventionUtil.extractInstanceIndex(conventionName);
if (overrideRuleset) {
// previous rules, less restrictions
......@@ -150,6 +150,7 @@ public class EssNamingConvention implements NamingConvention {
// valid if
// length 1, 2, 3
// same mnemonic only once in mnemonic path
if (!StringUtils.isEmpty(mnemonicPath)) {
String[] values = NamingConventionUtil.string2MnemonicPath(mnemonicPath.trim());
if (values.length > 3) {
......
......@@ -50,8 +50,6 @@ import org.openepics.names.rest.beans.element.NameElement;
import org.openepics.names.rest.beans.element.NameElementCommand;
import org.openepics.names.rest.beans.element.StructureElement;
import org.openepics.names.rest.beans.element.StructureElementCommand;
import org.openepics.names.rest.beans.response.ResponsePageNameElements;
import org.openepics.names.rest.beans.response.ResponsePageStructureElements;
import org.springframework.web.multipart.MultipartFile;
import com.google.common.collect.Lists;
......@@ -65,55 +63,50 @@ public class ExcelUtil {
private static final Logger LOGGER = Logger.getLogger(ExcelUtil.class.getName());
public static final String MIME_TYPE_EXCEL = "application/vnd.ms-excel";
public static final String MIME_TYPE_OPENXML_SPREADSHEET = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
public static final String MIME_TYPE_EXCEL = "application/vnd.ms-excel";
public static final String MIME_TYPE_OPENXML_SPREADSHEET = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
// width (in units of 1/256th of a character width)
// column width 2.75 inch (to fit header text and uuid)
private static final int SHEET_COLUMN_WIDTH = 28 * 256 + 64;
// column width 3.44 inch (to fit header text and uuid)
private static final int SHEET_COLUMN_WIDTH = 35 * 256 + 64;
private static final int NAMEELEMENTCOMMAND_LENGTH = 6;
private static final int NAMEELEMENTCOMMAND_LENGTH = 5;
private static final String[][] NAMEELEMENT_HEADER_COMMENT = {
{"Uuid", "Identity (uuid) of the name entry. Value is created server-side."},
{"Parentsystemstructure", "Identity (uuid) for the system structure parent."},
{"Parentdevicestructure", "Identity (uuid) for the device structure parent (if the name entry refers to device structure)."},
{"ParentSystemStructure", "Identity (uuid) for the system structure parent."},
{"ParentDeviceStructure", "Identity (uuid) for the device structure parent (if the name entry refers to device structure)."},
{"Index", "Index (instance) of the name entry (if the name entry refers to device structure)."},
{"Description", "Description of the name entry."},
{"Comment", "Comment of the name entry command."},
{"Description", "Description (verbose) of the name entry."},
// above NameElementCommand
{"Systemstructure", "Mnemonic path for for the system structure."},
{"Devicestructure", "Mnemonic path for for the device structure."},
{"SystemStructure", "Mnemonic path for for the system structure."},
{"DeviceStructure", "Mnemonic path for for the device structure."},
{"Name", "Name (verbose) of the name entry."},
{"Status", "Status of the name entry."},
{"Latest", "If the name entry is latest (with status APPROVED) in its line of entries."},
{"Deleted", "If the name entry is deleted."},
{"When", "Date and time when the name entry was created."},
{"Who", "Name (user) of who created the name entry."} };
{"Who", "Name (user) of who created the name entry."},
{"Comment", "Comment of the name entry command."} };
private static final int STRUCTUREELEMENTCOMMAND_LENGTH = 7;
private static final int STRUCTUREELEMENTCOMMAND_LENGTH = 6;
private static final String[][] STRUCTUREELEMENT_HEADER_COMMENT = {
{"Uuid", "Identity (uuid) of the structure entry. Value is created server-side."},
{"Type", "Type of the structure entry."},
{"Type", "Type of the structure entry. Valid values - SYSTEMGROUP, SYSTEM, SUBSYSTEM, DISCIPLINE, DEVICEGROUP, DEVICETYPE."},
{"Parent", "Identity (uuid) for the structure entry parent (if the structure entry has a parent)."},
{"Name", "Name (verbose) of the structure entry."},
{"Mnemonic", "Mnemonic of the structure entry."},
{"Description", "Description of the structure entry."},
{"Comment", "Comment of the structure entry command."},
{"Ordering", "Ordering of the structure entry."},
{"Description", "Description (verbose) of the structure entry."},
// above StructureElementCommand
{"Mnemonicpath", "Mnemonic path of the structure entry."},
{"MnemonicPath", "Mnemonic path of the structure entry."},
{"Level", "Level of the structure entry."},
{"Status", "Status of the structure entry."},
{"Latest", "If the structure entry is latest (with status APPROVED) in its line of entries."},
{"Deleted", "If the structure entry is deleted."},
{"When", "Date and time when the structure entry was created."},
{"Who", "Name (user) of who created the structure entry."} };
{"Who", "Name (user) of who created the structure entry."},
{"Comment", "Comment of the structure entry command."} };
private static final String ARIAL = "Arial";
private static final String SHEET = "Entries";
private static final String ENTRIES_EXCEL_TO_NAME_ELEMENT_COMMANDS = "excelToNameElementCommands, # entries: {0}";
private static final String ENTRIES_EXCEL_TO_STRUCTURE_ELEMENT_COMMANDS = "excelToStructureElementCommands, # entries: {0}";
private static final String ENTRIES_NAME_ELEMENTS_TO_EXCEL = "nameElementsToExcel, # entries: {0}";
private static final String ENTRIES_STRUCTURE_ELEMENTS_TO_EXCEL = "structureElementsToExcel, # entries: {0}";
private static final String TEXT = "text";
private static final String FAILED_TO_EXPORT_VALUES_TO_FILE = "Failed to export values to file";
private static final String FILE_COULD_NOT_BE_PARSED_FOR_VALUE_AT_ROW_CELL = "File could not be parsed for value at row: {0} cell: {1}";
......@@ -139,17 +132,23 @@ public class ExcelUtil {
}
/**
* Utility method to convert an Excel file to a list of name elements.
* Utility method to convert an Excel file to a list of name element commands given how Excel file is to be interpreted.
*
* @param is input stream
* @param nameCommand name command
* @return list of name elements
*/
public static List<NameElementCommand> excelToNameElementCommands(InputStream is) {
public static List<NameElementCommand> excelToNameElementCommands(InputStream is, NameCommand nameCommand) {
// rules for conversion
// Excel as NameElementCommand accepted
// Excel as NameElement not accepted
// ( Excel as NameElement accepted )
// see validateHeaderNameElementCommand
// NameElementCommand
// create - parentSystemStructure, parentDeviceStructure, index, description
// update - uuid, parentSystemStructure, parentDeviceStructure, index, description
// delete - uuid
int rowIndex = 0;
int columnIndex = 0;
......@@ -160,6 +159,7 @@ public class ExcelUtil {
Sheet sheet = workbook.getSheet(SHEET);
Iterator<Row> rows = sheet.iterator();
List<NameElementCommand> list = Lists.newArrayList();
boolean hasCell = false;
// iterate over rows and columns (cells)
while (rows.hasNext()) {
......@@ -168,6 +168,7 @@ public class ExcelUtil {
NameElementCommand nameElementCommand = new NameElementCommand();
while (cells.hasNext()) {
hasCell = true;
Cell cell = cells.next();
rowIndex = cell.getRowIndex();
columnIndex = cell.getColumnIndex();
......@@ -184,10 +185,10 @@ public class ExcelUtil {
nameElementCommand.setUuid(!StringUtils.isEmpty(value) ? UUID.fromString(value) : null);
break;
case 1:
nameElementCommand.setParentsystemstructure(!StringUtils.isEmpty(value) ? UUID.fromString(value) : null);
nameElementCommand.setParentSystemStructure(!StringUtils.isEmpty(value) ? UUID.fromString(value) : null);
break;
case 2:
nameElementCommand.setParentdevicestructure(!StringUtils.isEmpty(value) ? UUID.fromString(value) : null);
nameElementCommand.setParentDeviceStructure(!StringUtils.isEmpty(value) ? UUID.fromString(value) : null);
break;
case 3:
nameElementCommand.setIndex(value);
......@@ -195,9 +196,6 @@ public class ExcelUtil {
case 4:
nameElementCommand.setDescription(value);
break;
case 5:
nameElementCommand.setComment(value);
break;
default:
if (columnIndex > NAMEELEMENT_HEADER_COMMENT.length - 1) {
throw new IllegalArgumentException(
......@@ -212,8 +210,18 @@ public class ExcelUtil {
}
}
if (!hasCell) {
throw new ServiceException(
MessageFormat.format(FILE_COULD_NOT_BE_PARSED_FOR_VALUE_AT_ROW_CELL, rowIndex, columnIndex),
null, null, null);
}
workbook.close();
LOGGER.log(Level.FINE, ENTRIES_EXCEL_TO_NAME_ELEMENT_COMMANDS, list.size());
LOGGER.log(Level.FINE,
() -> MessageFormat.format(
TextUtil.DESCRIPTION_NUMBER_ELEMENTS,
"Excel to name element commands",
list.size()));
return list;
} catch (IllegalArgumentException | IOException e) {
throw new ServiceException(
......@@ -223,17 +231,23 @@ public class ExcelUtil {
}
/**
* Utility method to convert an Excel file to a list of structure elements.
* Utility method to convert an Excel file to a list of structure element commands given how Excel file is to be interpreted..
*
* @param is input stream
* @param structureCommand structure command
* @return list of structure elements
*/
public static List<StructureElementCommand> excelToStructureElementCommands(InputStream is) {
public static List<StructureElementCommand> excelToStructureElementCommands(InputStream is, StructureCommand structureCommand) {
// rules for conversion
// Excel as StructureElementCommand accepted
// Excel as StructureElement not accepted
// ( Excel as StructureElement accepted )
// see validateHeaderStructureElementCommand
// StructureElementCommand
// create - type, parent, mnemonic, ordering, description
// update - uuid, type, parent, mnemonic, ordering, description
// delete - uuid, type
int rowIndex = 0;
int columnIndex = 0;
......@@ -244,6 +258,7 @@ public class ExcelUtil {
Sheet sheet = workbook.getSheet(SHEET);
Iterator<Row> rows = sheet.iterator();
List<StructureElementCommand> list = Lists.newArrayList();
boolean hasCell = false;
// iterate over rows and columns (cells)
while (rows.hasNext()) {
......@@ -252,6 +267,7 @@ public class ExcelUtil {
StructureElementCommand structureElementCommand = new StructureElementCommand();
while (cells.hasNext()) {
hasCell = true;
Cell cell = cells.next();
rowIndex = cell.getRowIndex();
columnIndex = cell.getColumnIndex();
......@@ -274,17 +290,14 @@ public class ExcelUtil {
structureElementCommand.setParent(!StringUtils.isEmpty(value) ? UUID.fromString(value) : null);
break;
case 3:
structureElementCommand.setName(value);
structureElementCommand.setMnemonic(value);
break;
case 4:
structureElementCommand.setMnemonic(value);
structureElementCommand.setOrdering(!StringUtils.isEmpty(value) ? Integer.parseInt(value) : null);
break;
case 5:
structureElementCommand.setDescription(value);
break;
case 6:
structureElementCommand.setComment(value);
break;
default:
if (columnIndex > STRUCTUREELEMENT_HEADER_COMMENT.length - 1) {
throw new IllegalArgumentException(
......@@ -300,8 +313,18 @@ public class ExcelUtil {
}
}
if (!hasCell) {
throw new ServiceException(
MessageFormat.format(FILE_COULD_NOT_BE_PARSED_FOR_VALUE_AT_ROW_CELL, rowIndex, columnIndex),
null, null, null);
}
workbook.close();
LOGGER.log(Level.FINE, ENTRIES_EXCEL_TO_STRUCTURE_ELEMENT_COMMANDS, list.size());
LOGGER.log(Level.FINE,
() -> MessageFormat.format(
TextUtil.DESCRIPTION_NUMBER_ELEMENTS,
"Excel to structure element commands",
list.size()));
return list;
} catch (IllegalArgumentException | IOException e) {
throw new ServiceException(
......@@ -367,16 +390,6 @@ public class ExcelUtil {
}
}
/**
* Utility method to convert a list of name elements to an Excel file.
*
* @param nameElements name elements
* @return Excel file
*/
public static ByteArrayInputStream nameElementsToExcel(ResponsePageNameElements nameElements) {
return nameElementsToExcel(nameElements.getList());
}
/**
* Utility method to convert a list of name elements to an Excel file.
*
......@@ -384,18 +397,36 @@ public class ExcelUtil {
* @return Excel file
*/
public static ByteArrayInputStream nameElementsToExcel(List<NameElement> nameElements) {
LOGGER.log(Level.FINE, ENTRIES_NAME_ELEMENTS_TO_EXCEL, nameElements.size());
LOGGER.log(Level.FINE,
() -> MessageFormat.format(
TextUtil.DESCRIPTION_NUMBER_ELEMENTS,
"Name elements to Excel",
nameElements.size()));
try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream out = new ByteArrayOutputStream();) {
Sheet sheet = workbook.createSheet(SHEET);
// prepare header
// prepare
Drawing<?> drawing = sheet.createDrawingPatriarch();
CreationHelper factory = workbook.getCreationHelper();
ClientAnchor anchor = factory.createClientAnchor();
// format, font, style
DataFormat format = workbook.createDataFormat();
short textFormat = format.getFormat("text");
short textFormat = format.getFormat(TEXT);
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerFont.setFontHeightInPoints((short)12);
headerFont.setFontName(ARIAL);
Font dataFont = workbook.createFont();
dataFont.setBold(false);
dataFont.setFontHeightInPoints((short)12);
dataFont.setFontName(ARIAL);
CellStyle headerCellStyle = workbook.createCellStyle();
headerCellStyle.setDataFormat(textFormat);
headerCellStyle.setFont(headerFont);
CellStyle dataCellStyle = workbook.createCellStyle();
dataCellStyle.setDataFormat(textFormat);
dataCellStyle.setFont(dataFont);
// header
Row headerRow = sheet.createRow(0);
......@@ -409,10 +440,7 @@ public class ExcelUtil {
sheet.setColumnWidth(columnIndex, SHEET_COLUMN_WIDTH);
// style
CellStyle cellStyle = cell.getCellStyle();
cellStyle.setDataFormat(textFormat);
cellStyle.setFont(headerFont);
cell.setCellStyle(cellStyle);
cell.setCellStyle(headerCellStyle);
// comment
anchor.setRow1(headerRow.getRowNum());
......@@ -429,21 +457,54 @@ public class ExcelUtil {
// data
int rowIndex = 1;
for (NameElement nameElement : nameElements) {
Row row = sheet.createRow(rowIndex++);
row.createCell(0).setCellValue(nameElement.getUuid() != null ? nameElement.getUuid().toString() : null);
row.createCell(1).setCellValue(nameElement.getParentsystemstructure() != null ? nameElement.getParentsystemstructure().toString() : null);
row.createCell(2).setCellValue(nameElement.getParentdevicestructure() != null ? nameElement.getParentdevicestructure().toString() : null);
row.createCell(3).setCellValue(nameElement.getIndex());
row.createCell(4).setCellValue(nameElement.getDescription());
row.createCell(5).setCellValue(nameElement.getComment());
row.createCell(6).setCellValue(nameElement.getSystemstructure());
row.createCell(7).setCellValue(nameElement.getDevicestructure());
row.createCell(8).setCellValue(nameElement.getName());
row.createCell(9).setCellValue(nameElement.getStatus() != null ? nameElement.getStatus().toString() : null);
row.createCell(10).setCellValue(nameElement.isLatest());
row.createCell(11).setCellValue(nameElement.isDeleted());
row.createCell(12).setCellValue(DateFormat.getDateTimeInstance().format(nameElement.getWhen()));
row.createCell(13).setCellValue(nameElement.getWho());
Row row = sheet.createRow(rowIndex++);
for (int columnIndex = 0; columnIndex < NAMEELEMENT_HEADER_COMMENT.length; columnIndex++) {
Cell cell = row.createCell(columnIndex);
cell.setCellStyle(dataCellStyle);
switch (columnIndex) {
case 0:
cell.setCellValue(nameElement.getUuid() != null ? nameElement.getUuid().toString() : null);
break;
case 1:
cell.setCellValue(nameElement.getParentSystemStructure() != null ? nameElement.getParentSystemStructure().toString() : null);
break;
case 2:
cell.setCellValue(nameElement.getParentDeviceStructure() != null ? nameElement.getParentDeviceStructure().toString() : null);
break;
case 3:
cell.setCellValue(nameElement.getIndex());
break;
case 4:
cell.setCellValue(nameElement.getDescription());
break;
case 5:
cell.setCellValue(nameElement.getSystemStructure());
break;
case 6:
cell.setCellValue(nameElement.getDeviceStructure());
break;
case 7:
cell.setCellValue(nameElement.getName());
break;
case 8:
cell.setCellValue(nameElement.getStatus() != null ? nameElement.getStatus().toString() : null);
break;
case 9:
cell.setCellValue(nameElement.isDeleted());
break;
case 10:
cell.setCellValue(DateFormat.getDateTimeInstance().format(nameElement.getWhen()));
break;
case 11:
cell.setCellValue(nameElement.getWho());
break;
case 12:
cell.setCellValue(nameElement.getComment());
break;
default:
break;
}
}
}
workbook.write(out);
return new ByteArrayInputStream(out.toByteArray());
......@@ -453,16 +514,6 @@ public class ExcelUtil {
}
}
/**
* Utility method to convert a list of structure elements to an Excel file.
*
* @param structureElements structure elements
* @return Excel file
*/
public static ByteArrayInputStream structureElementsToExcel(ResponsePageStructureElements structureElements) {
return structureElementsToExcel(structureElements.getList());
}
/**
* Utility method to convert a list of structure elements to an Excel file.
*
......@@ -470,18 +521,36 @@ public class ExcelUtil {
* @return Excel file
*/
public static ByteArrayInputStream structureElementsToExcel(List<StructureElement> structureElements) {
LOGGER.log(Level.FINE, ENTRIES_STRUCTURE_ELEMENTS_TO_EXCEL, structureElements.size());
LOGGER.log(Level.FINE,
() -> MessageFormat.format(
TextUtil.DESCRIPTION_NUMBER_ELEMENTS,
"Structure elements to Excel",
structureElements.size()));
try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream out = new ByteArrayOutputStream();) {
Sheet sheet = workbook.createSheet(SHEET);
// prepare header
// prepare
Drawing<?> drawing = sheet.createDrawingPatriarch();
CreationHelper factory = workbook.getCreationHelper();
ClientAnchor anchor = factory.createClientAnchor();
// format, font, style
DataFormat format = workbook.createDataFormat();
short textFormat = format.getFormat("text");
short textFormat = format.getFormat(TEXT);
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerFont.setFontHeightInPoints((short)12);
headerFont.setFontName(ARIAL);
Font dataFont = workbook.createFont();
dataFont.setBold(false);
dataFont.setFontHeightInPoints((short)12);
dataFont.setFontName(ARIAL);
CellStyle headerCellStyle = workbook.createCellStyle();
headerCellStyle.setDataFormat(textFormat);
headerCellStyle.setFont(headerFont);
CellStyle dataCellStyle = workbook.createCellStyle();
dataCellStyle.setDataFormat(textFormat);
dataCellStyle.setFont(dataFont);
// header
Row headerRow = sheet.createRow(0);
......@@ -495,10 +564,7 @@ public class ExcelUtil {
sheet.setColumnWidth(columnIndex, SHEET_COLUMN_WIDTH);
// style
CellStyle cellStyle = cell.getCellStyle();
cellStyle.setDataFormat(textFormat);
cellStyle.setFont(headerFont);
cell.setCellStyle(cellStyle);
cell.setCellStyle(headerCellStyle);
// comment
anchor.setRow1(headerRow.getRowNum());
......@@ -515,21 +581,54 @@ public class ExcelUtil {
// data
int rowIndex = 1;
for (StructureElement structureElement : structureElements) {
Row row = sheet.createRow(rowIndex++);
row.createCell(0).setCellValue(structureElement.getUuid() != null ? structureElement.getUuid().toString() : null);
row.createCell(1).setCellValue(structureElement.getType() != null ? structureElement.getType().toString() : null);
row.createCell(2).setCellValue(structureElement.getParent() != null ? structureElement.getParent().toString() : null);
row.createCell(3).setCellValue(structureElement.getName());
row.createCell(4).setCellValue(structureElement.getMnemonic());
row.createCell(5).setCellValue(structureElement.getDescription());
row.createCell(6).setCellValue(structureElement.getComment());
row.createCell(7).setCellValue(structureElement.getMnemonicpath());
row.createCell(8).setCellValue(structureElement.getLevel());
row.createCell(9).setCellValue(structureElement.getStatus() != null ? structureElement.getStatus().toString() : null);
row.createCell(10).setCellValue(structureElement.isLatest());
row.createCell(11).setCellValue(structureElement.isDeleted());
row.createCell(12).setCellValue(DateFormat.getDateTimeInstance().format(structureElement.getWhen()));
row.createCell(13).setCellValue(structureElement.getWho());
Row row = sheet.createRow(rowIndex++);
for (int columnIndex = 0; columnIndex < NAMEELEMENT_HEADER_COMMENT.length; columnIndex++) {
Cell cell = row.createCell(columnIndex);
cell.setCellStyle(dataCellStyle);
switch (columnIndex) {
case 0:
cell.setCellValue(structureElement.getUuid() != null ? structureElement.getUuid().toString() : null);
break;
case 1:
cell.setCellValue(structureElement.getType() != null ? structureElement.getType().toString() : null);
break;
case 2:
cell.setCellValue(structureElement.getParent() != null ? structureElement.getParent().toString() : null);
break;
case 3:
cell.setCellValue(structureElement.getMnemonic());
break;
case 4:
cell.setCellValue(structureElement.getOrdering() != null ? structureElement.getOrdering().toString() : null);
break;
case 5:
cell.setCellValue(structureElement.getDescription());
break;
case 6:
cell.setCellValue(structureElement.getMnemonicPath());
break;
case 7:
cell.setCellValue(structureElement.getLevel());
break;
case 8:
cell.setCellValue(structureElement.getStatus() != null ? structureElement.getStatus().toString() : null);
break;
case 9:
cell.setCellValue(structureElement.isDeleted());
break;
case 10:
cell.setCellValue(DateFormat.getDateTimeInstance().format(structureElement.getWhen()));
break;
case 11:
cell.setCellValue(structureElement.getWho());
break;
case 12:
cell.setCellValue(structureElement.getComment());
break;
default:
break;
}
}
}
workbook.write(out);
return new ByteArrayInputStream(out.toByteArray());
......
......@@ -146,11 +146,7 @@ public class ExceptionUtil {
* @return input not available exception
*/
public static InputNotAvailableException createInputNotAvailableException(String message, String details, String field) {
if (message == null && field != null) {
return new InputNotAvailableException(TextUtil.VALUE_IS_NOT_AVAILABLE, details, field);
} else {
return new InputNotAvailableException(message, details, field);
}
return new InputNotAvailableException(message, details, field);
}
/**
......@@ -164,11 +160,7 @@ public class ExceptionUtil {
* @return input not correct exception
*/
public static InputNotCorrectException createInputNotCorrectException(String message, String details, String field) {
if (message == null && field != null) {
return new InputNotCorrectException(TextUtil.VALUE_IS_NOT_CORRECT, details, field);
} else {
return new InputNotCorrectException(message, details, field);
}
return new InputNotCorrectException(message, details, field);
}
/**
......@@ -182,11 +174,7 @@ public class ExceptionUtil {
* @return input not empty exception
*/
public static InputNotEmptyException createInputNotEmptyException(String message, String details, String field) {
if (message == null && field != null) {
return new InputNotEmptyException(TextUtil.VALUE_IS_NOT_EMPTY, details, field);
} else {
return new InputNotEmptyException(message, details, field);
}
return new InputNotEmptyException(message, details, field);
}
/**
......@@ -200,11 +188,7 @@ public class ExceptionUtil {
* @return input not valid exception
*/
public static InputNotValidException createInputNotValidException(String message, String details, String field) {
if (message == null && field != null) {
return new InputNotValidException(TextUtil.VALUE_IS_NOT_VALID, details, field);
} else {
return new InputNotValidException(message, details, field);
}
return new InputNotValidException(message, details, field);
}
// ----------------------------------------------------------------------------------------------------
......@@ -318,11 +302,7 @@ public class ExceptionUtil {
*/
public static void validateConditionInputNotAvailableException(boolean condition, String message, String details, String field) {
if (!condition) {
if (message == null && field != null) {
throw ExceptionUtil.createInputNotAvailableException(TextUtil.VALUE_IS_NOT_AVAILABLE, details, field);
} else {
throw ExceptionUtil.createInputNotAvailableException(message, details, field);
}
throw ExceptionUtil.createInputNotAvailableException(message, details, field);
}
}
......@@ -337,11 +317,7 @@ public class ExceptionUtil {
*/
public static void validateConditionInputNotCorrectException(boolean condition, String message, String details, String field) {
if (!condition) {
if (message == null && field != null) {
throw ExceptionUtil.createInputNotCorrectException(TextUtil.VALUE_IS_NOT_CORRECT, details, field);
} else {
throw ExceptionUtil.createInputNotCorrectException(message, details, field);
}
throw ExceptionUtil.createInputNotCorrectException(message, details, field);
}
}
......@@ -356,11 +332,7 @@ public class ExceptionUtil {
*/
public static void validateConditionInputNotEmptyException(boolean condition, String message, String details, String field) {
if (!condition) {
if (message == null && field != null) {
throw ExceptionUtil.createInputNotEmptyException(TextUtil.VALUE_IS_NOT_EMPTY, details, field);
} else {
throw ExceptionUtil.createInputNotEmptyException(message, details, field);
}
throw ExceptionUtil.createInputNotEmptyException(message, details, field);
}
}
......@@ -375,11 +347,7 @@ public class ExceptionUtil {
*/
public static void validateConditionInputNotValidException(boolean condition, String message, String details, String field) {
if (!condition) {
if (message == null && field != null) {
throw ExceptionUtil.createInputNotValidException(TextUtil.VALUE_IS_NOT_VALID, details, field);
} else {
throw ExceptionUtil.createInputNotValidException(message, details, field);
}
throw ExceptionUtil.createInputNotValidException(message, details, field);
}
}
......