Skip to content
Snippets Groups Projects
Commit 17c4724e authored by Sonia Zorba's avatar Sonia Zorba
Browse files

#4 Implemented invited registration

parent ba6b02eb
Branches
No related tags found
No related merge requests found
Showing
with 458 additions and 94 deletions
package it.inaf.ia2.gms.client; package it.inaf.ia2.gms.client;
import it.inaf.ia2.gms.client.call.AddInvitedRegistrationCall;
import it.inaf.ia2.gms.client.call.HttpClientWrapper; import it.inaf.ia2.gms.client.call.HttpClientWrapper;
import it.inaf.ia2.gms.client.call.AddMemberCall; import it.inaf.ia2.gms.client.call.AddMemberCall;
import it.inaf.ia2.gms.client.call.AddPermissionCall; import it.inaf.ia2.gms.client.call.AddPermissionCall;
...@@ -13,6 +14,7 @@ import it.inaf.ia2.gms.client.call.RemovePermissionCall; ...@@ -13,6 +14,7 @@ import it.inaf.ia2.gms.client.call.RemovePermissionCall;
import it.inaf.ia2.gms.client.model.Permission; import it.inaf.ia2.gms.client.model.Permission;
import it.inaf.ia2.gms.client.model.UserPermission; import it.inaf.ia2.gms.client.model.UserPermission;
import java.util.List; import java.util.List;
import java.util.Map;
public class GmsClient { public class GmsClient {
...@@ -71,4 +73,8 @@ public class GmsClient { ...@@ -71,4 +73,8 @@ public class GmsClient {
public List<UserPermission> getUserPermissions(String userId) { public List<UserPermission> getUserPermissions(String userId) {
return new GetUserPermissionsCall(httpClientWrapper).getUserPermissions(userId); return new GetUserPermissionsCall(httpClientWrapper).getUserPermissions(userId);
} }
public void addInvitedRegistration(String token, String email, Map<String, Permission> groupsPermissions) {
new AddInvitedRegistrationCall(httpClientWrapper).addInvitedRegistration(token, email, groupsPermissions);
}
} }
package it.inaf.ia2.gms.client.call;
import it.inaf.ia2.gms.client.model.Permission;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Map;
import java.util.stream.Collectors;
public class AddInvitedRegistrationCall extends BaseGmsCall {
public AddInvitedRegistrationCall(HttpClientWrapper clientWrapper) {
super(clientWrapper);
}
public void addInvitedRegistration(String token, String email, Map<String, Permission> groupsPermissions) {
String tokenHash = getTokenHash(token);
String endpoint = "invited-registration";
String bodyParams = "token_hash=" + tokenHash
+ "&email=" + email + "&groups="
+ String.join("\n", groupsPermissions.entrySet()
.stream().map(e -> e.getKey() + " " + e.getValue())
.collect(Collectors.toList()));
HttpRequest groupsRequest = newHttpRequest(endpoint)
.header("Accept", "text/plain")
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(bodyParams))
.build();
getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream())
.thenApply(response -> {
if (response.statusCode() == 201) {
return true;
}
logServerErrorInputStream(groupsRequest, response);
throw new IllegalStateException("Unable to create invited registration");
}).join();
}
private String getTokenHash(String token) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(token.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException(ex);
}
}
}
...@@ -64,7 +64,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { ...@@ -64,7 +64,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
*/ */
@Override @Override
public void configure(WebSecurity web) throws Exception { public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/ws/basic/**", "/ws/jwt/**", "/error", "/logout"); web.ignoring().antMatchers("/ws/jwt/**", "/error", "/logout", "/invited-registration");
} }
/** /**
......
package it.inaf.ia2.gms.controller; package it.inaf.ia2.gms.controller;
import it.inaf.ia2.gms.authn.SessionData; import it.inaf.ia2.gms.authn.SessionData;
import it.inaf.ia2.gms.manager.InvitedRegistrationManager;
import it.inaf.ia2.gms.model.request.GroupsRequest; import it.inaf.ia2.gms.model.request.GroupsRequest;
import it.inaf.ia2.gms.model.response.GroupsTabResponse; import it.inaf.ia2.gms.model.response.GroupsTabResponse;
import it.inaf.ia2.gms.model.response.HomePageResponse; import it.inaf.ia2.gms.model.response.HomePageResponse;
import it.inaf.ia2.gms.persistence.model.InvitedRegistration;
import java.io.IOException; import java.io.IOException;
import java.util.Optional;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import javax.validation.Valid; import javax.validation.Valid;
...@@ -25,6 +30,9 @@ public class HomePageController { ...@@ -25,6 +30,9 @@ public class HomePageController {
@Autowired @Autowired
private GroupsTabResponseBuilder groupsTabResponseBuilder; private GroupsTabResponseBuilder groupsTabResponseBuilder;
@Autowired
private InvitedRegistrationManager invitedRegistrationManager;
@ResponseBody @ResponseBody
@GetMapping(value = "/home", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) @GetMapping(value = "/home", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<HomePageResponse> getMainPage(@Valid GroupsRequest request) { public ResponseEntity<HomePageResponse> getMainPage(@Valid GroupsRequest request) {
...@@ -42,7 +50,15 @@ public class HomePageController { ...@@ -42,7 +50,15 @@ public class HomePageController {
} }
@GetMapping(value = "/", produces = MediaType.TEXT_HTML_VALUE) @GetMapping(value = "/", produces = MediaType.TEXT_HTML_VALUE)
public String index() { public String index(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Optional<InvitedRegistration> optReg = invitedRegistrationManager.completeInvitedRegistrationIfNecessary();
if (optReg.isPresent()) {
request.setAttribute("invited-registration", optReg.get());
return "/registration-completed";
//request.getRequestDispatcher("/registration-completed").forward(request, response);
}
return "index.html"; return "index.html";
} }
......
package it.inaf.ia2.gms.controller;
import it.inaf.ia2.gms.manager.InvitedRegistrationManager;
import it.inaf.ia2.gms.persistence.model.InvitedRegistration;
import it.inaf.ia2.gms.service.GroupNameService;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
@Controller
public class InvitedRegistrationController {
@Autowired
private InvitedRegistrationManager invitedRegistrationManager;
@Autowired
private GroupNameService groupNameService;
@GetMapping(value = "/invited-registration", produces = MediaType.TEXT_HTML_VALUE)
public void index(@RequestParam("token") String token, HttpServletRequest request, HttpServletResponse response) throws IOException {
InvitedRegistration invitedRegistration = invitedRegistrationManager.getInvitedRegistrationFromToken(token);
String html = getFileContent("invited-registration.html")
.replace("#EMAIL#", invitedRegistration.getEmail())
.replace("#GROUPS#", getGroupsList(invitedRegistration))
.replace("#HOME#", request.getContextPath());
response.getOutputStream().print(html);
}
@GetMapping(value = "/registration-completed", produces = MediaType.TEXT_HTML_VALUE)
public void completed(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=UTF-8");
InvitedRegistration invitedRegistration = (InvitedRegistration) request.getAttribute("invited-registration");
if (invitedRegistration == null) {
// redirect to home
String baseUrl = ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();
response.sendRedirect(baseUrl);
} else {
String html = getFileContent("registration-completed.html")
.replace("#GROUPS#", getGroupsList(invitedRegistration))
.replace("#HOME#", request.getContextPath());
response.getOutputStream().print(html);
}
}
private String getFileContent(String templateFileName) throws IOException {
try (InputStream in = InvitedRegistrationController.class.getClassLoader().getResourceAsStream("templates/" + templateFileName)) {
Scanner s = new Scanner(in).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
}
private String getGroupsList(InvitedRegistration invitedRegistration) {
String groups = "<ul>";
for (String groupName : groupNameService.getGroupsNamesFromIdentifiers(
invitedRegistration.getGroupsPermissions().keySet())) {
groups += "<li>" + groupName + "</li>";
}
groups += "</ul>";
return groups;
}
}
...@@ -3,6 +3,7 @@ package it.inaf.ia2.gms.controller; ...@@ -3,6 +3,7 @@ package it.inaf.ia2.gms.controller;
import it.inaf.ia2.gms.authn.RapPrincipal; import it.inaf.ia2.gms.authn.RapPrincipal;
import it.inaf.ia2.gms.exception.BadRequestException; import it.inaf.ia2.gms.exception.BadRequestException;
import it.inaf.ia2.gms.manager.GroupsManager; import it.inaf.ia2.gms.manager.GroupsManager;
import it.inaf.ia2.gms.manager.InvitedRegistrationManager;
import it.inaf.ia2.gms.manager.MembershipManager; import it.inaf.ia2.gms.manager.MembershipManager;
import it.inaf.ia2.gms.manager.PermissionsManager; import it.inaf.ia2.gms.manager.PermissionsManager;
import it.inaf.ia2.gms.model.Permission; import it.inaf.ia2.gms.model.Permission;
...@@ -11,6 +12,7 @@ import it.inaf.ia2.gms.persistence.GroupsDAO; ...@@ -11,6 +12,7 @@ import it.inaf.ia2.gms.persistence.GroupsDAO;
import it.inaf.ia2.gms.persistence.PermissionsDAO; import it.inaf.ia2.gms.persistence.PermissionsDAO;
import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.GroupEntity;
import it.inaf.ia2.gms.persistence.model.PermissionEntity; import it.inaf.ia2.gms.persistence.model.PermissionEntity;
import it.inaf.ia2.gms.service.GroupNameService;
import it.inaf.ia2.gms.service.GroupsService; import it.inaf.ia2.gms.service.GroupsService;
import it.inaf.ia2.gms.service.JoinService; import it.inaf.ia2.gms.service.JoinService;
import it.inaf.ia2.gms.service.PermissionUtils; import it.inaf.ia2.gms.service.PermissionUtils;
...@@ -20,11 +22,9 @@ import java.io.PrintWriter; ...@@ -20,11 +22,9 @@ import java.io.PrintWriter;
import java.security.Principal; import java.security.Principal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -57,6 +57,9 @@ public class JWTWebServiceController { ...@@ -57,6 +57,9 @@ public class JWTWebServiceController {
@Autowired @Autowired
private GroupsService groupsService; private GroupsService groupsService;
@Autowired
private GroupNameService groupNameService;
@Autowired @Autowired
private MembershipManager membershipManager; private MembershipManager membershipManager;
...@@ -69,6 +72,9 @@ public class JWTWebServiceController { ...@@ -69,6 +72,9 @@ public class JWTWebServiceController {
@Autowired @Autowired
private SearchService searchService; private SearchService searchService;
@Autowired
private InvitedRegistrationManager invitedRegistrationManager;
/** /**
* This endpoint is compliant with the IVOA GMS standard. * This endpoint is compliant with the IVOA GMS standard.
*/ */
...@@ -77,7 +83,7 @@ public class JWTWebServiceController { ...@@ -77,7 +83,7 @@ public class JWTWebServiceController {
List<GroupEntity> memberships = membershipManager.getCurrentUserMemberships(); List<GroupEntity> memberships = membershipManager.getCurrentUserMemberships();
List<String> names = getGroupsNames(memberships); List<String> names = groupNameService.getGroupsNames(memberships);
try (PrintWriter pw = new PrintWriter(response.getOutputStream())) { try (PrintWriter pw = new PrintWriter(response.getOutputStream())) {
...@@ -145,7 +151,7 @@ public class JWTWebServiceController { ...@@ -145,7 +151,7 @@ public class JWTWebServiceController {
} }
try (PrintWriter pw = new PrintWriter(response.getOutputStream())) { try (PrintWriter pw = new PrintWriter(response.getOutputStream())) {
for (String groupName : getGroupsNames(visibleSubgroups)) { for (String groupName : groupNameService.getGroupsNames(visibleSubgroups)) {
pw.println(getShortGroupName(groupName, group)); pw.println(getShortGroupName(groupName, group));
} }
} }
...@@ -195,7 +201,7 @@ public class JWTWebServiceController { ...@@ -195,7 +201,7 @@ public class JWTWebServiceController {
List<GroupEntity> groups = membershipManager.getUserGroups(parent, userId); List<GroupEntity> groups = membershipManager.getUserGroups(parent, userId);
try (PrintWriter pw = new PrintWriter(response.getOutputStream())) { try (PrintWriter pw = new PrintWriter(response.getOutputStream())) {
for (String groupName : getGroupsNames(groups)) { for (String groupName : groupNameService.getGroupsNames(groups)) {
pw.println(getShortGroupName(groupName, group)); pw.println(getShortGroupName(groupName, group));
} }
} }
...@@ -265,6 +271,27 @@ public class JWTWebServiceController { ...@@ -265,6 +271,27 @@ public class JWTWebServiceController {
response.setStatus(HttpServletResponse.SC_NO_CONTENT); response.setStatus(HttpServletResponse.SC_NO_CONTENT);
} }
@PostMapping(value = "/invited-registration", produces = MediaType.TEXT_PLAIN_VALUE)
public void addInvitedRegistration(@RequestParam("token_hash") String tokenHash, @RequestParam("email") String email,
@RequestParam("groups") String groupNamesAndPermissionsParam, HttpServletResponse response) {
Map<GroupEntity, Permission> groupsPermissions = new HashMap<>();
for (String param : groupNamesAndPermissionsParam.split("\n")) {
if (!param.isEmpty()) {
int lastSpaceIndex = param.lastIndexOf(" ");
String groupName = param.substring(0, lastSpaceIndex);
Permission permission = Permission.valueOf(param.substring(lastSpaceIndex + 1));
GroupEntity groupEntity = getGroupFromNames(extractGroupNames(groupName));
groupsPermissions.put(groupEntity, permission);
}
}
invitedRegistrationManager.addInvitedRegistration(tokenHash, email, groupsPermissions);
response.setStatus(HttpServletResponse.SC_CREATED);
}
private GroupEntity getGroupFromNames(List<String> groupNames) { private GroupEntity getGroupFromNames(List<String> groupNames) {
if (groupNames.isEmpty()) { if (groupNames.isEmpty()) {
return getRoot(); return getRoot();
...@@ -292,27 +319,6 @@ public class JWTWebServiceController { ...@@ -292,27 +319,6 @@ public class JWTWebServiceController {
.orElseThrow(() -> new IllegalStateException("Missing root group")); .orElseThrow(() -> new IllegalStateException("Missing root group"));
} }
/**
* Returns the list of the group complete names, given a list of GroupEntity
* objects. TODO: probably this logic is duplicated inside GroupNameService.
* Check this.
*/
private List<String> getGroupsNames(List<GroupEntity> groups) {
// We need to return the complete group name, so it is necessary to load
// all the parents too.
Map<String, String> idNameMap = new HashMap<>();
Set<String> allIdentifiers = getAllIdentifiers(groups);
for (GroupEntity group : groupsDAO.findGroupsByIds(allIdentifiers)) {
idNameMap.put(group.getId(), group.getName());
}
List<String> names = new ArrayList<>();
for (GroupEntity group : groups) {
names.add(getGroupCompleteName(group, idNameMap));
}
return names;
}
private List<String> extractGroupNames(Optional<String> group) { private List<String> extractGroupNames(Optional<String> group) {
return extractGroupNames(group.orElse(null)); return extractGroupNames(group.orElse(null));
...@@ -341,44 +347,6 @@ public class JWTWebServiceController { ...@@ -341,44 +347,6 @@ public class JWTWebServiceController {
return names; return names;
} }
private Set<String> getAllIdentifiers(List<GroupEntity> groups) {
Set<String> allIdentifiers = new HashSet<>();
for (GroupEntity group : groups) {
if (!"".equals(group.getPath())) {
String[] ids = group.getPath().split("\\.");
for (String id : ids) {
allIdentifiers.add(id);
}
}
}
return allIdentifiers;
}
private String getGroupCompleteName(GroupEntity group, Map<String, String> idNameMap) {
if ("ROOT".equals(group.getId())) {
return group.getName();
}
List<String> names = new ArrayList<>();
for (String groupId : group.getPath().split("\\.")) {
String groupName = idNameMap.get(groupId);
// Dot inside names is considered a special character (because it is
// used to separate the group from its parents), so we use a
// backslash to escape it (client apps need to be aware of this).
groupName = groupName.replace("\\.", "\\\\.");
names.add(groupName);
}
return String.join(".", names);
}
private String getShortGroupName(String completeGroupName, Optional<String> groupPrefix) { private String getShortGroupName(String completeGroupName, Optional<String> groupPrefix) {
if (groupPrefix.isPresent()) { if (groupPrefix.isPresent()) {
return completeGroupName.substring(groupPrefix.get().length() + 1); return completeGroupName.substring(groupPrefix.get().length() + 1);
......
package it.inaf.ia2.gms.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {
public NotFoundException(String message) {
super(message);
}
}
package it.inaf.ia2.gms.manager;
import it.inaf.ia2.gms.exception.NotFoundException;
import it.inaf.ia2.gms.exception.UnauthorizedException;
import it.inaf.ia2.gms.model.Permission;
import it.inaf.ia2.gms.persistence.GroupsDAO;
import it.inaf.ia2.gms.persistence.InvitedRegistrationDAO;
import it.inaf.ia2.gms.persistence.LoggingDAO;
import it.inaf.ia2.gms.persistence.MembershipsDAO;
import it.inaf.ia2.gms.persistence.model.GroupEntity;
import it.inaf.ia2.gms.persistence.model.InvitedRegistration;
import it.inaf.ia2.gms.persistence.model.MembershipEntity;
import it.inaf.ia2.gms.service.PermissionsService;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class InvitedRegistrationManager extends UserAwareComponent {
private static final String INVITED_REGISTRATION = "invited-registration";
@Autowired
private GroupsDAO groupsDAO;
@Autowired
private MembershipsDAO membershipsDAO;
@Autowired
private PermissionsService permissionsService;
@Autowired
private PermissionsManager permissionsManager;
@Autowired
private InvitedRegistrationDAO invitedRegistrationDAO;
@Autowired
private LoggingDAO loggingDAO;
@Autowired
private HttpSession httpSession;
public void addInvitedRegistration(String tokenHash, String email, Map<GroupEntity, Permission> groupsPermissions) {
Map<String, Permission> groupIdsPermissions = new HashMap<>();
for (Map.Entry<GroupEntity, Permission> entry : groupsPermissions.entrySet()) {
GroupEntity group = entry.getKey();
if (permissionsManager.getCurrentUserPermission(group) != Permission.ADMIN) {
throw new UnauthorizedException("You don't have the permission to perform invited registrations");
}
groupIdsPermissions.put(group.getId(), entry.getValue());
}
InvitedRegistration invitedRegistration = new InvitedRegistration()
.setId(UUID.randomUUID().toString().replaceAll("-", ""))
.setEmail(email)
.setTokenHash(tokenHash)
.setGroupsPermissions(groupIdsPermissions);
invitedRegistrationDAO.addInvitedRegistration(invitedRegistration);
}
public InvitedRegistration getInvitedRegistrationFromToken(String token) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(token.getBytes(StandardCharsets.UTF_8));
String tokenHash = Base64.getEncoder().encodeToString(hash);
InvitedRegistration invitedRegistration = invitedRegistrationDAO.getInvitedRegistrationFromToken(tokenHash)
.orElseThrow(() -> new NotFoundException("No invited registrations found for this token"));
httpSession.setAttribute(INVITED_REGISTRATION, invitedRegistration);
loggingDAO.logAction("Started invited registration for email " + invitedRegistration.getEmail());
return invitedRegistration;
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException(ex);
}
}
public Optional<InvitedRegistration> completeInvitedRegistrationIfNecessary() {
InvitedRegistration invitedRegistration = (InvitedRegistration) httpSession.getAttribute(INVITED_REGISTRATION);
if (invitedRegistration != null) {
for (Map.Entry<String, Permission> entry : invitedRegistration.getGroupsPermissions().entrySet()) {
String groupId = entry.getKey();
String userId = getCurrentUserId();
GroupEntity groupEntity = groupsDAO.findGroupById(groupId).get();
MembershipEntity membershipEntity = new MembershipEntity();
membershipEntity.setUserId(userId);
membershipEntity.setGroupId(groupId);
membershipsDAO.addMember(membershipEntity);
permissionsService.addPermission(groupEntity, userId, entry.getValue());
}
invitedRegistrationDAO.setRegistrationDone(invitedRegistration);
httpSession.removeAttribute(INVITED_REGISTRATION);
}
return Optional.ofNullable(invitedRegistration);
}
}
package it.inaf.ia2.gms.persistence; package it.inaf.ia2.gms.persistence;
import it.inaf.ia2.gms.model.Permission;
import it.inaf.ia2.gms.persistence.model.InvitedRegistration; import it.inaf.ia2.gms.persistence.model.InvitedRegistration;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.ArrayList; import java.sql.Types;
import java.util.List; import java.util.HashMap;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -34,13 +36,15 @@ public class InvitedRegistrationDAO { ...@@ -34,13 +36,15 @@ public class InvitedRegistrationDAO {
return ps; return ps;
}); });
for (String groupId : invitedRegistration.getGroupIds()) { for (Map.Entry<String, Permission> entry : invitedRegistration.getGroupsPermissions().entrySet()) {
String sqlReqGroup = "INSERT INTO invited_registration_request_group (request_id, group_id) VALUES (?, ?)";
String sqlReqGroup = "INSERT INTO invited_registration_request_group (request_id, group_id, permission) VALUES (?, ?, ?)";
jdbcTemplate.update(conn -> { jdbcTemplate.update(conn -> {
PreparedStatement ps = conn.prepareStatement(sqlReqGroup); PreparedStatement ps = conn.prepareStatement(sqlReqGroup);
ps.setString(1, invitedRegistration.getId()); ps.setString(1, invitedRegistration.getId());
ps.setString(2, groupId); ps.setString(2, entry.getKey());
ps.setObject(3, entry.getValue().toString(), Types.OTHER);
return ps; return ps;
}); });
} }
...@@ -48,7 +52,7 @@ public class InvitedRegistrationDAO { ...@@ -48,7 +52,7 @@ public class InvitedRegistrationDAO {
public Optional<InvitedRegistration> getInvitedRegistrationFromToken(String tokenHash) { public Optional<InvitedRegistration> getInvitedRegistrationFromToken(String tokenHash) {
String sqlReq = "SELECT id, email FROM invited_registration_request WHERE token_hash = ? AND !done"; String sqlReq = "SELECT id, email FROM invited_registration_request WHERE token_hash = ? AND done IS NOT true";
InvitedRegistration registration = jdbcTemplate.query(conn -> { InvitedRegistration registration = jdbcTemplate.query(conn -> {
PreparedStatement ps = conn.prepareStatement(sqlReq); PreparedStatement ps = conn.prepareStatement(sqlReq);
...@@ -66,33 +70,35 @@ public class InvitedRegistrationDAO { ...@@ -66,33 +70,35 @@ public class InvitedRegistrationDAO {
if (registration != null) { if (registration != null) {
String sqlReqGroup = "SELECT group_id FROM invited_registration_request_group WHERE request_id = ?"; String sqlReqGroup = "SELECT group_id, permission FROM invited_registration_request_group WHERE request_id = ?";
List<String> groupIds = jdbcTemplate.query(conn -> { Map<String, Permission> groupsPermissions = jdbcTemplate.query(conn -> {
PreparedStatement ps = conn.prepareStatement(sqlReqGroup); PreparedStatement ps = conn.prepareStatement(sqlReqGroup);
ps.setString(1, registration.getId()); ps.setString(1, registration.getId());
return ps; return ps;
}, resultSet -> { }, resultSet -> {
List<String> groups = new ArrayList<>(); Map<String, Permission> map = new HashMap<>();
while (resultSet.next()) { while (resultSet.next()) {
groups.add(resultSet.getString("group_id")); String groupId = resultSet.getString("group_id");
Permission permission = Permission.valueOf(resultSet.getString("permission"));
map.put(groupId, permission);
} }
return groups; return map;
}); });
registration.setGroupIds(groupIds); registration.setGroupsPermissions(groupsPermissions);
} }
return Optional.ofNullable(registration); return Optional.ofNullable(registration);
} }
public void setRegistrationDone(String tokenHash) { public void setRegistrationDone(InvitedRegistration invitedRegistration) {
String sql = "UPDATE invited_registration_request SET done = true WHERE token_hash = ?"; String sql = "UPDATE invited_registration_request SET done = true WHERE id = ?";
jdbcTemplate.update(conn -> { jdbcTemplate.update(conn -> {
PreparedStatement ps = conn.prepareStatement(sql); PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, tokenHash); ps.setString(1, invitedRegistration.getId());
return ps; return ps;
}); });
} }
......
...@@ -99,7 +99,8 @@ public class MembershipsDAO { ...@@ -99,7 +99,8 @@ public class MembershipsDAO {
public MembershipEntity addMember(MembershipEntity membership) { public MembershipEntity addMember(MembershipEntity membership) {
String sql = "INSERT INTO gms_membership (group_id, user_id) VALUES (?, ?)"; String sql = "INSERT INTO gms_membership (group_id, user_id) VALUES (?, ?)\n"
+ "ON CONFLICT (group_id, user_id) DO NOTHING";
jdbcTemplate.update(conn -> { jdbcTemplate.update(conn -> {
PreparedStatement ps = conn.prepareStatement(sql); PreparedStatement ps = conn.prepareStatement(sql);
......
package it.inaf.ia2.gms.persistence.model; package it.inaf.ia2.gms.persistence.model;
import java.util.List; import it.inaf.ia2.gms.model.Permission;
import java.util.Map;
public class InvitedRegistration { public class InvitedRegistration {
...@@ -8,45 +9,50 @@ public class InvitedRegistration { ...@@ -8,45 +9,50 @@ public class InvitedRegistration {
private String tokenHash; private String tokenHash;
private String email; private String email;
private boolean done; private boolean done;
private List<String> groupIds; private Map<String, Permission> groupsPermissions;
public String getId() { public String getId() {
return id; return id;
} }
public void setId(String id) { public InvitedRegistration setId(String id) {
this.id = id; this.id = id;
return this;
} }
public String getTokenHash() { public String getTokenHash() {
return tokenHash; return tokenHash;
} }
public void setTokenHash(String tokenHash) { public InvitedRegistration setTokenHash(String tokenHash) {
this.tokenHash = tokenHash; this.tokenHash = tokenHash;
return this;
} }
public String getEmail() { public String getEmail() {
return email; return email;
} }
public void setEmail(String email) { public InvitedRegistration setEmail(String email) {
this.email = email; this.email = email;
return this;
} }
public boolean isDone() { public boolean isDone() {
return done; return done;
} }
public void setDone(boolean done) { public InvitedRegistration setDone(boolean done) {
this.done = done; this.done = done;
return this;
} }
public List<String> getGroupIds() { public Map<String, Permission> getGroupsPermissions() {
return groupIds; return groupsPermissions;
} }
public void setGroupIds(List<String> groupIds) { public InvitedRegistration setGroupsPermissions(Map<String, Permission> groupsPermissions) {
this.groupIds = groupIds; this.groupsPermissions = groupsPermissions;
return this;
} }
} }
...@@ -22,6 +22,69 @@ public class GroupNameService { ...@@ -22,6 +22,69 @@ public class GroupNameService {
@Autowired @Autowired
private GroupsDAO groupsDAO; private GroupsDAO groupsDAO;
public List<String> getGroupsNamesFromIdentifiers(Set<String> groupIdentifiers) {
return getGroupsNames(groupsDAO.findGroupsByIds(groupIdentifiers));
}
/**
* Returns the list of the group complete names, given a list of GroupEntity
* objects.
*/
public List<String> getGroupsNames(List<GroupEntity> groups) {
// We need to return the complete group name, so it is necessary to load
// all the parents too.
Map<String, String> idNameMap = new HashMap<>();
Set<String> allIdentifiers = getAllIdentifiers(groups);
for (GroupEntity group : groupsDAO.findGroupsByIds(allIdentifiers)) {
idNameMap.put(group.getId(), group.getName());
}
List<String> names = new ArrayList<>();
for (GroupEntity group : groups) {
names.add(getGroupCompleteName(group, idNameMap));
}
return names;
}
private Set<String> getAllIdentifiers(List<GroupEntity> groups) {
Set<String> allIdentifiers = new HashSet<>();
for (GroupEntity group : groups) {
if (!"".equals(group.getPath())) {
String[] ids = group.getPath().split("\\.");
for (String id : ids) {
allIdentifiers.add(id);
}
}
}
return allIdentifiers;
}
private String getGroupCompleteName(GroupEntity group, Map<String, String> idNameMap) {
if ("ROOT".equals(group.getId())) {
return group.getName();
}
List<String> names = new ArrayList<>();
for (String groupId : group.getPath().split("\\.")) {
String groupName = idNameMap.get(groupId);
// Dot inside names is considered a special character (because it is
// used to separate the group from its parents), so we use a
// backslash to escape it (client apps need to be aware of this).
groupName = groupName.replace("\\.", "\\\\.");
names.add(groupName);
}
return String.join(".", names);
}
/** /**
* @param groupsIdPath map having group id as keys and group paths as values * @param groupsIdPath map having group id as keys and group paths as values
* @return map having group id as keys and group names as values * @return map having group id as keys and group names as values
......
...@@ -56,6 +56,8 @@ CREATE TABLE invited_registration_request ( ...@@ -56,6 +56,8 @@ CREATE TABLE invited_registration_request (
CREATE TABLE invited_registration_request_group ( CREATE TABLE invited_registration_request_group (
request_id varchar NOT NULL, request_id varchar NOT NULL,
group_id varchar NOT NULL, group_id varchar NOT NULL,
PRIMARY KEY (request_id, group_id), permission permission_type NOT NULL,
FOREIGN KEY (request_id) REFERENCES invited_registration_request(id) PRIMARY KEY (request_id, group_id, permission),
FOREIGN KEY (request_id) REFERENCES invited_registration_request(id),
FOREIGN KEY (group_id) REFERENCES gms_group(id)
); );
<!DOCTYPE html>
<html>
<head>
<title>Invited registration</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous" />
</head>
<body>
<div class="container mt-4">
<h1 class="mb-3">Invited registration</h1>
<p>Hi <strong>#EMAIL#</strong>, at the end of this procedure you will be added to the following groups:</p>
#GROUPS#
<p>Just perform a login!</p>
<p><a class="btn btn-primary" href="#HOME#">Login</a></p>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Invited registration completed</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous" />
</head>
<body>
<div class="container mt-4">
<h1 class="mb-3">Invited registration completed!</h1>
<p>You are now member of the following groups:</p>
#GROUPS#
<p><a class="btn btn-primary" href="#HOME#">Ok</a></p>
</div>
</body>
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment