private String getGroupAuthorities(ScimGroupMember member) { if (member.getRoles() != null && !member.getRoles().isEmpty()) { return StringUtils.collectionToCommaDelimitedString(member.getRoles()); } else { return StringUtils.collectionToCommaDelimitedString(ScimGroupMember.GROUP_MEMBER); } }
@Override public List<ScimGroupMember> updateOrAddMembers(String groupId, List<ScimGroupMember> members) throws ScimResourceNotFoundException { List<ScimGroupMember> currentMembers = getMembers(groupId); logger.debug("current-members: " + currentMembers + ", in request: " + members); List<ScimGroupMember> currentMembersToRemove = new ArrayList<>(currentMembers); currentMembersToRemove.removeAll(members); logger.debug("removing members: " + currentMembersToRemove); for (ScimGroupMember member : currentMembersToRemove) { removeMemberById(groupId, member.getMemberId()); } List<ScimGroupMember> newMembersToAdd = new ArrayList<>(members); newMembersToAdd.removeAll(currentMembers); logger.debug("adding new members: " + newMembersToAdd); for (ScimGroupMember member : newMembersToAdd) { addMember(groupId, member); } List<ScimGroupMember> membersToUpdate = new ArrayList<>(members); membersToUpdate.retainAll(currentMembers); logger.debug("updating members: " + membersToUpdate); for (ScimGroupMember member : membersToUpdate) { updateMember(groupId, member); } return getMembers(groupId); }
@Override public ScimGroupMember updateMember(final String groupId, final ScimGroupMember member) throws ScimResourceNotFoundException, MemberNotFoundException { validateRequest(groupId, member); final String authorities = getGroupAuthorities(member); int updated = jdbcTemplate.update( UPDATE_MEMBER_SQL, new PreparedStatementSetter() { @Override public void setValues(PreparedStatement ps) throws SQLException { ps.setString(1, authorities); ps.setString(2, groupId); ps.setString(3, member.getMemberId()); } }); if (updated == 0) { throw new MemberNotFoundException( "Member " + member.getMemberId() + " does not exist in group " + groupId); } if (updated != 1) { throw new IncorrectResultSizeDataAccessException( "unexpected number of members updated", 1, updated); } return getMemberById(groupId, member.getMemberId()); }
@Override public ScimGroupMember addMember(final String groupId, final ScimGroupMember member) throws ScimResourceNotFoundException, MemberAlreadyExistsException { if (isDefaultGroup(groupId)) { throw new MemberAlreadyExistsException("Trying to add member to default group"); } // first validate the supplied groupId, memberId validateRequest(groupId, member); final String authorities = getGroupAuthorities(member); final String type = (member.getType() == null ? ScimGroupMember.Type.USER : member.getType()).toString(); try { logger.debug("Associating group:" + groupId + " with member:" + member); jdbcTemplate.update( ADD_MEMBER_SQL, new PreparedStatementSetter() { @Override public void setValues(PreparedStatement ps) throws SQLException { ps.setString(1, groupId); ps.setString(2, member.getMemberId()); ps.setString(3, type); ps.setString(4, authorities); ps.setTimestamp(5, new Timestamp(new Date().getTime())); ps.setString(6, member.getOrigin()); } }); } catch (DuplicateKeyException e) { throw new MemberAlreadyExistsException( member.getMemberId() + " is already part of the group: " + groupId); } return getMemberById(groupId, member.getMemberId()); }
@Test public void canGetMemberById() throws Exception { addMember("g3", "m2", "USER", "READER,WRITER"); ScimGroupMember m = dao.getMemberById("g3", "m2"); assertEquals(ScimGroupMember.Type.USER, m.getType()); assertEquals(ScimGroupMember.GROUP_ADMIN, m.getRoles()); }
@Test(expected = ScimResourceNotFoundException.class) public void addMember_In_Different_Zone_Causes_Issues() throws Exception { String subdomain = generator.generate(); IdentityZone otherZone = MultitenancyFixture.identityZone(subdomain, subdomain); IdentityZoneHolder.set(otherZone); ScimGroupMember m1 = new ScimGroupMember("m1", ScimGroupMember.Type.USER, null); m1.setOrigin(OriginKeys.UAA); dao.addMember("g2", m1); }
@Override public ScimGroup deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { ScimGroup group = new ScimGroup(); Map<ScimGroupMember.Role, List<ScimGroupMember>> roles = new HashMap<ScimGroupMember.Role, List<ScimGroupMember>>(); for (ScimGroupMember.Role role : ScimGroupMember.Role.values()) { roles.put(role, new ArrayList<ScimGroupMember>()); } Set<ScimGroupMember> allMembers = new HashSet<ScimGroupMember>(); while (jp.nextToken() != JsonToken.END_OBJECT) { if (jp.getCurrentToken() == JsonToken.FIELD_NAME) { String fieldName = jp.getCurrentName(); jp.nextToken(); if ("id".equalsIgnoreCase(fieldName)) { group.setId(jp.readValueAs(String.class)); } else if ("displayname".equalsIgnoreCase(fieldName)) { group.setDisplayName(jp.readValueAs(String.class)); } else if ("meta".equalsIgnoreCase(fieldName)) { group.setMeta(jp.readValueAs(ScimMeta.class)); } else if ("schemas".equalsIgnoreCase(fieldName)) { group.setSchemas(jp.readValueAs(String[].class)); } else { String value = fieldName.substring(0, fieldName.length() - 1); ScimGroupMember.Role role; try { role = ScimGroupMember.Role.valueOf(value.toUpperCase()); } catch (IllegalArgumentException ex) { role = null; } if (role != null) { ScimGroupMember[] members = jp.readValueAs(ScimGroupMember[].class); for (ScimGroupMember member : members) { member.setRoles(new ArrayList<ScimGroupMember.Role>()); } roles.get(role).addAll(Arrays.asList(members)); allMembers.addAll(Arrays.asList(members)); } } } } for (ScimGroupMember member : allMembers) { for (ScimGroupMember.Role role : roles.keySet()) { if (roles.get(role).contains(member)) { member.getRoles().add(role); } } } group.setMembers(new ArrayList<ScimGroupMember>(allMembers)); return group; }
@Test(expected = ScimResourceNotFoundException.class) public void canAddMember_Validate_Origin_and_ZoneId() throws Exception { String subdomain = generator.generate(); IdentityZone otherZone = MultitenancyFixture.identityZone(subdomain, subdomain); IdentityZoneHolder.set(otherZone); validateCount(0); ScimGroupMember m1 = new ScimGroupMember("m1", ScimGroupMember.Type.USER, null); m1.setOrigin(OriginKeys.UAA); dao.addMember("g2", m1); }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ScimGroupMember member = (ScimGroupMember) o; if (getMemberId() != null ? !getMemberId().equals(member.getMemberId()) : member.getMemberId() != null) return false; return getType() == member.getType(); }
@Test public void canAddMember() throws Exception { validateCount(0); ScimGroupMember m1 = new ScimGroupMember("m1", ScimGroupMember.Type.USER, null); ScimGroupMember m2 = dao.addMember("g2", m1); validateCount(1); assertEquals(ScimGroupMember.Type.USER, m2.getType()); assertEquals(ScimGroupMember.GROUP_MEMBER, m2.getRoles()); assertEquals("m1", m2.getMemberId()); validateUserGroups("m1", "test2"); }
@Test public void canAddNestedGroupMember() { addMember("g2", "m1", "USER", "READER"); ScimGroupMember g2 = new ScimGroupMember("g2", ScimGroupMember.Type.GROUP, ScimGroupMember.GROUP_ADMIN); g2 = dao.addMember("g1", g2); assertEquals(ScimGroupMember.Type.GROUP, g2.getType()); assertEquals(ScimGroupMember.GROUP_ADMIN, g2.getRoles()); assertEquals("g2", g2.getMemberId()); validateUserGroups("m1", "test1.i", "test2"); }
@Test public void canUpdateMember() throws Exception { addMember("g1", "m1", "USER", "READER"); validateCount(1); ScimGroupMember m1 = new ScimGroupMember("m1", ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN); ScimGroupMember m2 = dao.updateMember("g1", m1); assertEquals(ScimGroupMember.GROUP_ADMIN, m2.getRoles()); assertNotSame(m1, m2); validateCount(1); validateUserGroups("m1", "test1"); }
@Override public ScimGroupMember mapRow(ResultSet rs, int rowNum) throws SQLException { String memberId = rs.getString(2); String memberType = rs.getString(3); String authorities = rs.getString(4); Date added = rs.getDate(5); String origin = rs.getString(6); ScimGroupMember sgm = new ScimGroupMember( memberId, ScimGroupMember.Type.valueOf(memberType), getAuthorities(authorities)); sgm.setOrigin(origin); return sgm; }
private void validateRequest(String groupId, ScimGroupMember member) { if (!StringUtils.hasText(groupId) || !StringUtils.hasText(member.getMemberId()) || !StringUtils.hasText(member.getOrigin())) { throw new InvalidScimResourceException( "group-id, member-id, origin and member-type must be non-empty"); } if (groupId.equals(member.getMemberId())) { // oops! cycle detected throw new InvalidScimResourceException("trying to nest group within itself, aborting"); } // check if the group exists and the member-id is a valid group or user // id ScimGroup group = groupProvisioning.retrieve(groupId); // this will throw a ScimException String memberZoneId; // if the group does not exist // this will throw a ScimException if the group or user does not exist if (member.getType() == ScimGroupMember.Type.GROUP) { memberZoneId = groupProvisioning.retrieve(member.getMemberId()).getZoneId(); } else { memberZoneId = userProvisioning.retrieve(member.getMemberId()).getZoneId(); } if (!memberZoneId.equals(group.getZoneId())) { throw new ScimResourceConstraintFailedException( "The zone of the group and the member must be the same."); } if (!memberZoneId.equals(IdentityZoneHolder.get().getId())) { throw new ScimResourceConstraintFailedException( "Unable to make membership changes in a different zone"); } }
@Test public void testBackwardsCompatibilityToMemberAuthorities() { addMember("g1", "m1", "USER", "READ"); addMember("g1", "g2", "GROUP", "member"); addMember("g1", "m2", "USER", "READER,write"); List<ScimGroupMember> members = dao.getMembers("g1", null, false); assertNotNull(members); assertEquals(3, members.size()); List<ScimGroupMember> readers = new ArrayList<ScimGroupMember>(), writers = new ArrayList<ScimGroupMember>(); for (ScimGroupMember member : members) { if (member.getRoles().contains(ScimGroupMember.Role.READER)) { readers.add(member); } if (member.getRoles().contains(ScimGroupMember.Role.WRITER)) { writers.add(member); } } assertEquals(2, readers.size()); assertEquals(1, writers.size()); }