Commit c5de9aa2 authored by Sonia Zorba's avatar Sonia Zorba
Browse files

Added endpoints on JWTWebServiceController

parent 65a91b9f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -60,3 +60,5 @@ nbactions.xml
/gms-client/gms-client-lib/target/
/gms-client/gms-cli/target/
/gms/node/

nb-configuration.xml
+191 −9
Original line number Diff line number Diff line
@@ -2,10 +2,19 @@ package it.inaf.ia2.gms.controller;

import it.inaf.ia2.gms.authn.RapPrincipal;
import it.inaf.ia2.gms.exception.BadRequestException;
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.LoggingDAO;
import it.inaf.ia2.gms.persistence.MembershipsDAO;
import it.inaf.ia2.gms.persistence.PermissionsDAO;
import it.inaf.ia2.gms.persistence.model.GroupEntity;
import it.inaf.ia2.gms.persistence.model.PermissionEntity;
import it.inaf.ia2.gms.service.GroupsService;
import it.inaf.ia2.gms.service.JoinService;
import it.inaf.ia2.gms.service.MembersService;
import it.inaf.ia2.gms.service.PermissionUtils;
import it.inaf.ia2.gms.service.PermissionsService;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.Principal;
@@ -16,6 +25,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
@@ -42,6 +52,21 @@ public class JWTWebServiceController {
    @Autowired
    private GroupsDAO groupsDAO;

    @Autowired
    private GroupsService groupsService;

    @Autowired
    private MembersService membersService;

    @Autowired
    private PermissionsService permissionsService;

    @Autowired
    private PermissionsDAO permissionsDAO;

    @Autowired
    private LoggingDAO loggingDAO;

    /**
     * This endpoint is compliant with the IVOA GMS standard.
     */
@@ -50,18 +75,12 @@ public class JWTWebServiceController {

        List<GroupEntity> memberships = membershipsDAO.getUserMemberships(principal.getName());

        // 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(memberships);
        for (GroupEntity group : groupsDAO.findGroupsByIds(allIdentifiers)) {
            idNameMap.put(group.getId(), group.getName());
        }
        List<String> names = getGroupsNames(memberships);

        try (PrintWriter pw = new PrintWriter(response.getOutputStream())) {

            for (GroupEntity group : memberships) {
                pw.println(getGroupCompleteName(group, idNameMap));
            for (String name : names) {
                pw.println(name);
            }
        }
    }
@@ -105,8 +124,171 @@ public class JWTWebServiceController {
        // else: empty response (as defined by GMS standard)
    }

    @GetMapping(value = {"/list/{group:.+}", "/list"}, produces = MediaType.TEXT_PLAIN_VALUE)
    public void listGroups(@PathVariable("group") Optional<String> group, Principal principal, HttpServletResponse response) throws IOException {

        String userId = principal.getName();

        List<String> groupNames = extractGroupNames(group);
        GroupEntity parentGroup = getGroupFromNames(groupNames);

        List<GroupEntity> allSubGroups = groupsDAO.getDirectSubGroups(parentGroup.getPath());

        // Select only the groups visible to the user
        List<PermissionEntity> permissions = permissionsDAO.findUserPermissions(userId);
        List<GroupEntity> visibleSubgroups = new ArrayList<>();

        for (GroupEntity subgroup : allSubGroups) {
            PermissionUtils.getGroupPermission(subgroup, permissions).ifPresent(permission -> {
                visibleSubgroups.add(subgroup);
            });
        }

        try (PrintWriter pw = new PrintWriter(response.getOutputStream())) {
            for (String groupName : getGroupsNames(visibleSubgroups)) {
                if (group.isPresent()) {
                    String shortName = groupName.substring(group.get().length() + 1);
                    pw.println(shortName);
                } else {
                    pw.println(groupName);
                }
            }
        }
    }

    @PostMapping(value = "/{group:.+}", produces = MediaType.TEXT_PLAIN_VALUE)
    public void createGroup(@PathVariable("group") String group, Principal principal, HttpServletRequest request, HttpServletResponse response) throws IOException {

        String userId = principal.getName();

        List<String> groupNames = extractGroupNames(group);
        GroupEntity parent = getParentFromNames(groupNames);

        String newGroupName = groupNames.get(groupNames.size() - 1);

        if (permissionsService.getUserPermissionForGroup(parent, userId) != Permission.ADMIN) {
            loggingDAO.logAction("Unauthorized create group request, group_name=" + newGroupName);
            throw new UnauthorizedException("Missing admin permission");
        }

        String leafParam = request.getParameter("leaf");
        boolean leaf = leafParam == null ? false : Boolean.valueOf(leafParam);

        groupsService.addGroup(parent, newGroupName, leaf);
        loggingDAO.logAction("Added group: parent_path=" + parent.getPath() + ", group_name=" + newGroupName);

        response.setStatus(HttpServletResponse.SC_CREATED);
        try (PrintWriter pw = new PrintWriter(response.getOutputStream())) {
            pw.println(group);
        }
    }

    @PostMapping(value = {"/membership/{group:.+}", "/membership"}, produces = MediaType.TEXT_PLAIN_VALUE)
    public void addMember(@PathVariable("group") Optional<String> group, Principal principal, HttpServletRequest request, HttpServletResponse response) throws IOException {

        GroupEntity groupEntity = getGroupFromNames(extractGroupNames(group));

        String userId = principal.getName();
        membersService.verifyUserCanManageMembers(groupEntity, userId);

        String targetUserId = request.getParameter("user_id");
        if (targetUserId == null) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing user_id parameter");
            return;
        }
        membersService.addMember(groupEntity.getId(), targetUserId);
        loggingDAO.logAction("Added member, group_id=" + groupEntity.getId() + ", user_id=" + targetUserId);
    }

    @PostMapping(value = {"/permission/{group:.+}", "/permission/"}, produces = MediaType.TEXT_PLAIN_VALUE)
    public void addPermission(@PathVariable("group") Optional<String> groupNames, Principal principal, HttpServletRequest request, HttpServletResponse response) throws IOException {

        GroupEntity groupEntity = getGroupFromNames(extractGroupNames(groupNames));

        String userId = principal.getName();
        permissionsService.verifyUserCanManagePermissions(groupEntity, userId);

        String targetUserId = request.getParameter("user_id");
        if (targetUserId == null) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing user_id parameter");
            return;
        }
        String permissionParam = request.getParameter("permission");
        if (permissionParam == null) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing permission parameter");
            return;
        }
        Permission permission = Permission.valueOf(permissionParam);
        permissionsService.addPermission(groupEntity, targetUserId, permission);
        loggingDAO.logAction("Permission added, group_id=" + groupEntity.getId() + ", user_id="
                + targetUserId + ", permission=" + permission);
    }

    private GroupEntity getGroupFromNames(List<String> groupNames) {
        if (groupNames.isEmpty()) {
            return getRoot();
        }
        return getGroupFromNamesAndIndex(groupNames, groupNames.size() - 1);
    }

    private GroupEntity getParentFromNames(List<String> groupNames) {
        if (groupNames.size() == 1) {
            return getRoot();
        }
        return getGroupFromNamesAndIndex(groupNames, groupNames.size() - 2);
    }

    private GroupEntity getGroupFromNamesAndIndex(List<String> groupNames, int index) {
        String parentPath = ""; // starting from ROOT
        GroupEntity group = null;
        for (int i = 0; i < index + 1; i++) {
            String groupName = groupNames.get(i);
            group = groupsDAO.findGroupByParentAndName(parentPath, groupName)
                    .orElseThrow(() -> new IllegalArgumentException("Unable to find group " + groupName));
            parentPath = group.getPath();
        }
        if (group == null) {
            throw new IllegalStateException();
        }
        return group;
    }

    private GroupEntity getRoot() {
        return groupsDAO.findGroupById("ROOT")
                .orElseThrow(() -> new IllegalStateException("Missing root group"));
    }

    /**
     * Returns the list of the group complete names, given a list of GroupEntity
     * objects.
     */
    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) {
        return extractGroupNames(group.orElse(null));
    }

    private List<String> extractGroupNames(String groupStr) {

        if (groupStr == null || groupStr.isEmpty()) {
            return new ArrayList<>();
        }

        List<String> names = new ArrayList<>();
        String currentName = "";
        for (int i = 0; i < groupStr.length(); i++) {
+3 −10
Original line number Diff line number Diff line
@@ -64,9 +64,10 @@ public class MembersController {
    public ResponseEntity<PaginatedData<RapUser>> addMember(@Valid @RequestBody AddMemberRequest request) {

        GroupEntity group = groupsService.getGroupById(request.getGroupId());
        Permission currentUserPermission = verifyCurrentUserCanManageMembers(group);
        Permission currentUserPermission = membersService.verifyUserCanManageMembers(group, session.getUserId());

        membersService.addMember(request.getGroupId(), request.getUserId());
        loggingDAO.logAction("Added member, group_id=" + group.getId() + ", user_id=" + request.getUserId());
        if (currentUserPermission == Permission.MANAGE_MEMBERS) {
            // Automatically assign the VIEW_MEMBERS permission ("Add collaborator" feature)
            permissionsService.addPermission(group, request.getUserId(), Permission.VIEW_MEMBERS);
@@ -86,7 +87,7 @@ public class MembersController {
    public ResponseEntity<PaginatedData<RapUser>> removeMember(@Valid RemoveMemberRequest request) {

        GroupEntity group = groupsService.getGroupById(request.getGroupId());
        Permission currentUserPermission = verifyCurrentUserCanManageMembers(group);
        Permission currentUserPermission = membersService.verifyUserCanManageMembers(group, session.getUserId());

        membersService.removeMember(group.getId(), request.getUserId());
        loggingDAO.logAction("Member removed, group_id=" + group.getId() + ", user_id=" + request.getUserId());
@@ -110,14 +111,6 @@ public class MembersController {
        return ResponseEntity.ok(getMembersPanel(request));
    }

    private Permission verifyCurrentUserCanManageMembers(GroupEntity group) {
        Permission currentNodePermission = permissionsService.getUserPermissionForGroup(group, session.getUserId());
        if (currentNodePermission != Permission.ADMIN && currentNodePermission != Permission.MANAGE_MEMBERS) {
            throw new UnauthorizedException("Missing admin or manage members permissions");
        }
        return currentNodePermission;
    }

    private PaginatedData<RapUser> getMembersPanel(MemberRequest request) {
        List<RapUser> members = membersService.getMembers(request.getGroupId());
        return new PaginatedData<>(members, request.getPaginatorPage(), request.getPaginatorPageSize());
+3 −11
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ public class PermissionsController {
    public ResponseEntity<Map<String, Permission>> getUserPermission(@RequestParam("groupId") String groupId, @RequestParam("userId") String userId) {

        GroupEntity group = groupsService.getGroupById(groupId);
        verifyCurrentUserCanManagePermissions(group);
        permissionsService.verifyUserCanManagePermissions(group, session.getUserId());

        Permission permission = permissionsService.getUserPermissionForGroup(group, userId);
        if (permission == null) {
@@ -80,7 +80,7 @@ public class PermissionsController {
    public ResponseEntity<PaginatedData<UserPermission>> addPermission(@Valid @RequestBody AddPermissionRequest request) {

        GroupEntity group = groupsService.getGroupById(request.getGroupId());
        verifyCurrentUserCanManagePermissions(group);
        permissionsService.verifyUserCanManagePermissions(group, session.getUserId());

        permissionsService.addPermission(group, request.getUserId(), request.getPermission());
        loggingDAO.logAction("Permission added, group_id=" + request.getGroupId() + ", user_id="
@@ -93,7 +93,7 @@ public class PermissionsController {
    public ResponseEntity<PaginatedData<UserPermission>> deletePermission(@Valid MemberRequest request) {

        GroupEntity group = groupsService.getGroupById(request.getGroupId());
        verifyCurrentUserCanManagePermissions(group);
        permissionsService.verifyUserCanManagePermissions(group, session.getUserId());

        permissionsService.removePermission(group, request.getUserId());
        loggingDAO.logAction("Permission removed, group_id=" + request.getGroupId() + ", user_id=" + request.getUserId());
@@ -101,14 +101,6 @@ public class PermissionsController {
        return ResponseEntity.ok(getPermissionsPanel(group, request));
    }

    private void verifyCurrentUserCanManagePermissions(GroupEntity group) {
        Permission currentNodePermissions = permissionsService.getUserPermissionForGroup(group, session.getUserId());
        if (currentNodePermissions != Permission.ADMIN) {
            loggingDAO.logAction("Unauthorized attempt to manage permissions");
            throw new UnauthorizedException("Only admin users can handle permissions");
        }
    }

    private PaginatedData<UserPermission> getPermissionsPanel(GroupEntity group, PaginatedModelRequest request) {
        List<UserPermission> permissions = permissionsService.getAllPermissions(group);
        return new PaginatedData<>(permissions, request.getPaginatorPage(), request.getPaginatorPageSize());
+14 −13
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.apache.commons.codec.binary.Base64;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
@@ -41,19 +41,18 @@ public class RapClient {
    @Value("${security.oauth2.client.scope}")
    private String scope;

    /* Use basic auth instead of JWT when asking for users */
    @Value("${rap.ws.basic-auth}")
    private boolean basicAuth;
    @Autowired
    private HttpServletRequest request;

    private final SessionData sessionData;
    @Autowired(required = false)
    private SessionData sessionData;

    private final RestTemplate rapRestTemplate;

    private final RestTemplate refreshTokenRestTemplate;

    @Autowired
    public RapClient(SessionData sessionData, RestTemplate rapRestTemplate) {
        this.sessionData = sessionData;
    public RapClient(RestTemplate rapRestTemplate) {
        this.rapRestTemplate = rapRestTemplate;
        this.refreshTokenRestTemplate = new RestTemplate();
    }
@@ -94,6 +93,10 @@ public class RapClient {
        try {
            return function.apply(getEntity(body));
        } catch (HttpClientErrorException.Unauthorized ex) {
            if (sessionData == null) {
                // we can't refresh the token without a session
                throw ex;
            }
            refreshToken();
            return function.apply(getEntity(body));
        }
@@ -103,13 +106,11 @@ public class RapClient {

        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));

        if (basicAuth) {
            String auth = clientId + ":" + clientSecret;
            String encodedAuth = Base64.encodeBase64String(auth.getBytes());
            headers.add("Authorization", "Basic " + encodedAuth);
        } else {
        if (sessionData != null) {
            headers.add("Authorization", "Bearer " + sessionData.getAccessToken());
        } else {
            // from JWT web service
            headers.add("Authorization", request.getHeader("Authorization"));
        }

        return new HttpEntity<>(body, headers);
Loading