/** * {@inheritDoc} */ @NotNull @Override public SyncContext createContext(@NotNull ExternalIdentityProvider idp, @NotNull UserManager userManager, @NotNull ValueFactory valueFactory) { if (config.user().getDynamicMembership()) { return new DynamicSyncContext(config, idp, userManager, valueFactory); } else { return new DefaultSyncContext(config, idp, userManager, valueFactory); } }
/** * Synchronize content common to both external users and external groups: * - properties * - auto-group membership * * @param external The external identity * @param authorizable The corresponding repository user/group * @param config The sync configuration * @throws RepositoryException If an error occurs. */ private void syncExternalIdentity(@NotNull ExternalIdentity external, @NotNull Authorizable authorizable, @NotNull DefaultSyncConfig.Authorizable config) throws RepositoryException { syncProperties(external, authorizable, config.getPropertyMapping()); applyMembership(authorizable, config.getAutoMembership()); }
@NotNull protected DefaultSyncResultImpl syncGroup(@NotNull ExternalGroup external, @NotNull Group group) throws RepositoryException { // make also sure the local user to be synced belongs to the same IDP. Note: 'external' has been verified before. if (!isSameIDP(group)) { return new DefaultSyncResultImpl(new DefaultSyncedIdentity(external.getId(), external.getExternalId(), false, -1), SyncResult.Status.FOREIGN); } SyncResult.Status status; // first check if group is expired if (!forceGroupSync && !isExpired(group)) { status = SyncResult.Status.NOP; } else { syncExternalIdentity(external, group, config.group()); // finally "touch" the sync property group.setProperty(REP_LAST_SYNCED, nowValue); status = SyncResult.Status.UPDATE; } return new DefaultSyncResultImpl(createSyncedIdentity(group), status); }
auth.setProperty(relPath, createValues((Collection) obj)); } else if (obj instanceof byte[] || obj instanceof char[]) { auth.setProperty(relPath, createValue(obj)); } else if (obj instanceof Object[]) { auth.setProperty(relPath, createValues(Arrays.asList((Object[]) obj))); } else { auth.setProperty(relPath, createValue(obj));
@Test public void testSyncProperties() throws Exception { ExternalUser externalUser = idp.getUser(TestIdentityProvider.ID_SECOND_USER); Authorizable a = syncCtx.createUser(externalUser); // create exact mapping Map<String, String> mapping = new HashMap<>(); Map<String, ?> extProps = externalUser.getProperties(); for (String propName : extProps.keySet()) { mapping.put(propName, propName); } syncCtx.syncProperties(externalUser, a, mapping); for (String propName : extProps.keySet()) { assertTrue(a.hasProperty(propName)); Object obj = extProps.get(propName); Value[] vs = a.getProperty(propName); if (vs.length == 1) { assertEquals(syncCtx.createValue(obj), a.getProperty(propName)[0]); } else { Value[] expected = (obj instanceof Collection) ? syncCtx.createValues((Collection) obj) : syncCtx.createValues(Arrays.asList((Object[]) obj)); assertArrayEquals(expected, a.getProperty(propName)); } } }
public SyncResult sync(@NotNull ExternalIdentity identity) throws SyncException { ExternalIdentityRef ref = identity.getExternalId(); if (!isSameIDP(ref)) { boolean created = false; if (identity instanceof ExternalUser) { User user = getAuthorizable(identity, User.class); timer.mark("find"); if (user == null) { user = createUser((ExternalUser) identity); timer.mark("create"); created = true; ret = syncUser((ExternalUser) identity, user); timer.mark("sync"); } else if (identity instanceof ExternalGroup) { Group group = getAuthorizable(identity, Group.class); timer.mark("find"); if (group == null) { group = createGroup((ExternalGroup) identity); timer.mark("create"); created = true; ret = syncGroup((ExternalGroup) identity, group); timer.mark("sync"); } else {
@Test public void testSyncMembershipDepthNoSync() throws Exception { ExternalUser externalUser = idp.listUsers().next(); Authorizable a = syncCtx.createUser(externalUser); root.commit(); assertTrue(externalUser.getDeclaredGroups().iterator().hasNext()); syncCtx.syncMembership(externalUser, a, 0); assertFalse(root.hasPendingChanges()); syncCtx.syncMembership(externalUser, a, -1); assertFalse(root.hasPendingChanges()); }
@Test public void testSyncExternalUserExistingGroups() throws Exception { syncConfig.user().setMembershipNestingDepth(1); ExternalUser externalUser = idp.getUser(USER_ID); DefaultSyncContext ctx = new DefaultSyncContext(syncConfig, idp, userManager, valueFactory); ctx.sync(externalUser); ctx.close(); Authorizable a = userManager.getAuthorizable(USER_ID); assertSyncedMembership(userManager, a, externalUser); syncContext.setForceUserSync(true); syncConfig.user().setMembershipExpirationTime(-1); syncContext.sync(externalUser); Tree t = r.getTree(a.getPath()); assertFalse(t.hasProperty(ExternalIdentityConstants.REP_EXTERNAL_PRINCIPAL_NAMES)); assertSyncedMembership(userManager, a, externalUser); }
ExternalIdentityRef ref = getIdentityRef(auth); if (ref == null || !isSameIDP(ref)) { return new DefaultSyncResultImpl(new DefaultSyncedIdentity(id, ref, auth.isGroup(), -1), SyncResult.Status.FOREIGN); timer.mark("retrieve"); if (external == null) { ret = handleMissingIdentity(id, auth, timer); } else { ret = syncGroup(external, (Group) auth); timer.mark("sync"); timer.mark("retrieve"); if (external == null) { ret = handleMissingIdentity(id, auth, timer); } else { ret = syncUser(external, (User) auth); timer.mark("sync");
@Test public void testForeign() throws Exception { // sync foreign user into the repository // NOTE: that should be considered a bug by the tool that does the sync // as it uses an IDP that is not configured with the login-chain! ExternalIdentityProvider foreign = new TestIdentityProvider("foreign"); SyncContext syncContext = new DefaultSyncContext(syncConfig, foreign, getUserManager(root), getValueFactory(root)); SyncResult result = syncContext.sync(foreign.getUser(TestIdentityProvider.ID_TEST_USER)); long lastSynced = result.getIdentity().lastSynced(); root.commit(); PreAuthCredentials creds = new PreAuthCredentials(TestIdentityProvider.ID_TEST_USER); ContentSession cs = null; try { // login should succeed due the fact that the _LoginModuleImpl_ succeeds for // an existing authorizable if _pre_auth_ is enabled. cs = login(creds); assertEquals(PreAuthCredentials.PRE_AUTH_DONE, creds.getMessage()); // foreign user _must_ not have been touched by the _ExternalLoginModule_ root.refresh(); User u = getUserManager(root).getAuthorizable(TestIdentityProvider.ID_TEST_USER, User.class); assertNotNull(u); assertEquals(lastSynced, DefaultSyncContext.createSyncedIdentity(u).lastSynced()); } finally { if (cs != null) { cs.close(); } } }
while (grpIter.hasNext()) { Group grp = grpIter.next(); if (isSameIDP(grp)) { declaredExternalGroups.put(grp.getID(), grp); Authorizable a = userManager.getAuthorizable(extGroup.getId()); if (a == null) { grp = createGroup(extGroup); log.debug("- created new group"); } else if (a.isGroup() && isSameIDP(a)) { grp = (Group) a; } else { syncGroup(extGroup, grp); syncMembership(extGroup, grp, depth - 1); } else { log.debug("- group nesting level for '{}' reached", grp.getID());
/** * Creates an array of JCR values based on the type. * @param propValues the given values * @return and array of JCR values * @throws RepositoryException if an error occurs */ @Nullable protected Value[] createValues(@NotNull Collection<?> propValues) throws RepositoryException { List<Value> values = new ArrayList<>(); for (Object obj : propValues) { Value v = createValue(obj); if (v != null) { values.add(v); } } return values.toArray(new Value[0]); }
/** * {@inheritDoc} */ @Override public SyncedIdentity findIdentity(@NotNull UserManager userManager, @NotNull String id) throws RepositoryException { return DefaultSyncContext.createSyncedIdentity(userManager.getAuthorizable(id)); }
@Test public void testSyncPropertiesEmptyMap() throws Exception { ExternalUser externalUser = idp.getUser(TestIdentityProvider.ID_SECOND_USER); Authorizable a = syncCtx.createUser(externalUser); syncCtx.syncProperties(externalUser, a, ImmutableMap.<String, String>of()); for (String propName : externalUser.getProperties().keySet()) { assertFalse(a.hasProperty(propName)); } }
/** * Checks if the given authorizable was synced from the same IDP by comparing the IDP name of the * {@value #REP_EXTERNAL_ID} property. * * @param auth the authorizable. * @return {@code true} if same IDP. */ protected boolean isSameIDP(@Nullable Authorizable auth) throws RepositoryException { ExternalIdentityRef ref = getIdentityRef(auth); return ref != null && idp.getName().equals(ref.getProviderName()); }
@Test public void testIsSameIDPNull() throws Exception { assertFalse(syncCtx.isSameIDP((Authorizable) null)); }
@Test public void testIsExpiredSyncedUser() throws Exception { ExternalIdentity externalUser = idp.listUsers().next(); sync(externalUser); Authorizable a = userManager.getAuthorizable(externalUser.getId()); assertFalse(syncCtx.isExpired(a, syncConfig.user().getExpirationTime(), "any")); assertTrue(syncCtx.isExpired(a, -1, "any")); // create a ctx with a newer 'now' DefaultSyncContext ctx = new DefaultSyncContext(syncConfig, idp, userManager, valueFactory); long expTime = ctx.now - syncCtx.now - 1; assertTrue(ctx.isExpired(a, expTime, "any")); // remove last-sync property a.removeProperty(DefaultSyncContext.REP_LAST_SYNCED); assertTrue(syncCtx.isExpired(a, syncConfig.user().getExpirationTime(), "any")); }
@Test public void testSyncByForeignId2() throws Exception { User u = userManager.getAuthorizable(getTestUser().getID(), User.class); setExternalID(u, "differentIDP"); SyncResult result = syncCtx.sync(u.getID()); assertEquals(SyncResult.Status.FOREIGN, result.getStatus()); SyncedIdentity si = result.getIdentity(); assertNotNull(si); assertEquals(DefaultSyncContext.getIdentityRef(u), si.getExternalIdRef()); }
@Test public void testLostMembershipWithExpirationSet() throws Exception { long expTime = 2; syncConfig.user().setMembershipNestingDepth(1).setMembershipExpirationTime(expTime).setExpirationTime(expTime); Group gr = createTestGroup(); setExternalID(gr, idp.getName()); SyncResult result = syncCtx.sync(idp.listUsers().next()); User user = (User) userManager.getAuthorizable(result.getIdentity().getId()); gr.addMember(user); root.commit(); waitUntilExpired(user, root, expTime); DefaultSyncContext newCtx = new DefaultSyncContext(syncConfig, idp, userManager, valueFactory); result = newCtx.sync(user.getID()); root.commit(); assertSame(SyncResult.Status.UPDATE, result.getStatus()); gr = (Group) userManager.getAuthorizable(gr.getID()); assertFalse(gr.isDeclaredMember(userManager.getAuthorizable(user.getID()))); }
@Test public void testGetAuthorizableUser() throws Exception { ExternalIdentity extUser = idp.listUsers().next(); User user = syncCtx.getAuthorizable(extUser, User.class); assertNull(user); sync(extUser); user = syncCtx.getAuthorizable(extUser, User.class); assertNotNull(user); }