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

Added /search/{groups} isMemberOf IVOA GMS endpoint

parent bc79d18c
Loading
Loading
Loading
Loading
+58 −0
Original line number Diff line number Diff line
@@ -14,12 +14,14 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -64,6 +66,62 @@ public class JWTWebServiceController {
        }
    }

    /**
     * This endpoint is compliant with the IVOA GMS standard. Warning: for
     * supporting the groups of groups (with dots inside) the path variable must
     * be defined adding ".+", otherwise Spring will think it is a file
     * extension (thanks https://stackoverflow.com/a/16333149/771431)
     */
    @GetMapping(value = "/search/{group:.+}", produces = MediaType.TEXT_PLAIN_VALUE)
    public void isMemberOf(@PathVariable("group") String group, Principal principal, HttpServletResponse response) throws IOException {

        String userId = principal.getName();

        List<String> groupNames = extractGroupNames(group);

        boolean isMemeber = false;
        String parentPath = ""; // starting from ROOT
        for (String groupName : groupNames) {
            Optional<GroupEntity> optionalGroup = groupsDAO.findGroupByParentAndName(parentPath, groupName);
            if (optionalGroup.isPresent()) {
                GroupEntity groupEntity = optionalGroup.get();
                parentPath = groupEntity.getPath();
                isMemeber = membershipsDAO.isMemberOf(userId, groupEntity.getId());
                if (isMemeber) {
                    break;
                }
            } else {
                break;
            }
        }

        if (isMemeber) {
            try (PrintWriter pw = new PrintWriter(response.getOutputStream())) {
                pw.println(group);
            }
        }
        // else: empty response (as defined by GMS standard)
    }

    private List<String> extractGroupNames(String groupStr) {

        List<String> names = new ArrayList<>();
        String currentName = "";
        for (int i = 0; i < groupStr.length(); i++) {
            char c = groupStr.charAt(i);
            // dot is the group separator and it must be escaped if used inside
            // group names
            if (c == '.' && groupStr.charAt(i - 1) != '\\') {
                names.add(currentName.replace("\\.", "."));
                currentName = "";
            } else {
                currentName += c;
            }
        }
        names.add(currentName);
        return names;
    }

    private Set<String> getAllIdentifiers(List<GroupEntity> groups) {

        Set<String> allIdentifiers = new HashSet<>();
+13 −9
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ public class LoggingDAO {
    }

    public void logAction(String action) {
        try {
            String sql = "INSERT INTO audit_log (\"user\", action, ip_address) VALUES (?, ?, ?)";

            jdbcTemplate.update(conn -> {
@@ -63,6 +64,9 @@ public class LoggingDAO {
                ps.setString(++i, getIPAddress());
                return ps;
            });
        } catch (Throwable t) {
            LOG.error("Exception while trying to save audit log", t);
        }
    }

    private String getIPAddress() {
+82 −0
Original line number Diff line number Diff line
package it.inaf.ia2.gms.controller;

import it.inaf.ia2.gms.persistence.GroupsDAO;
import it.inaf.ia2.gms.persistence.MembershipsDAO;
import it.inaf.ia2.gms.persistence.model.GroupEntity;
import java.security.Principal;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

@RunWith(MockitoJUnitRunner.class)
public class JWTWebServiceControllerTest {

    @Mock
    private MembershipsDAO membershipsDAO;

    @Mock
    private GroupsDAO groupsDAO;

    @InjectMocks
    private JWTWebServiceController controller;

    private final Principal principal = new Principal() {
        @Override
        public String getName() {
            return "TEST_PRINCIPAL";
        }
    };

    private MockMvc mockMvc;

    @Before
    public void init() {
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }

    @Test
    public void testIsMemberOf() throws Exception {

        GroupEntity group1 = new GroupEntity();
        group1.setId("1");
        group1.setPath("1");
        group1.setName("group.1");

        GroupEntity group2 = new GroupEntity();
        group2.setId("2");
        group2.setPath("1.2");
        group2.setName("subgroup");

        GroupEntity group3 = new GroupEntity();
        group3.setId("3");
        group3.setPath("1.2.3");
        group3.setName("subsubgroup");

        when(groupsDAO.findGroupByParentAndName(eq(""), eq("group.1")))
                .thenReturn(Optional.of(group1));
        when(groupsDAO.findGroupByParentAndName(eq("1"), eq("subgroup")))
                .thenReturn(Optional.of(group2));
        when(groupsDAO.findGroupByParentAndName(eq("1.2"), eq("subsubgroup")))
                .thenReturn(Optional.of(group3));

        when(membershipsDAO.isMemberOf(anyString(), eq(group3.getId()))).thenReturn(true);

        String group = "group\\.1.subgroup.subsubgroup";

        mockMvc.perform(get("/ws/jwt/search/" + group).principal(principal))
                .andExpect(status().isOk())
                .andExpect(content().string(group + "\n"));
    }
}