Skip to content

Commit dd13954

Browse files
committed
Merge branch 'develop' into
feature/503-improve-performance-of-applying-users-and-groups Conflicts: accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/authorizableinstaller/impl/AuthorizableInstallerServiceImplTest.java
2 parents 5838ea1 + 29d10d5 commit dd13954

File tree

3 files changed

+87
-15
lines changed

3 files changed

+87
-15
lines changed

accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableinstaller/impl/AuthorizableInstallerServiceImpl.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import javax.jcr.Node;
2626
import javax.jcr.RepositoryException;
2727
import javax.jcr.Session;
28+
import javax.jcr.SimpleCredentials;
2829
import javax.jcr.UnsupportedRepositoryOperationException;
2930
import javax.jcr.ValueFactory;
3031

@@ -43,6 +44,7 @@
4344
import org.apache.sling.api.resource.Resource;
4445
import org.apache.sling.api.resource.ResourceResolver;
4546
import org.apache.sling.api.resource.ResourceResolverFactory;
47+
import org.apache.sling.jcr.api.SlingRepository;
4648
import org.apache.sling.jcr.resource.JcrResourceConstants;
4749
import org.osgi.service.component.annotations.Reference;
4850
import org.osgi.service.component.annotations.ReferenceCardinality;
@@ -99,6 +101,9 @@ public class AuthorizableInstallerServiceImpl implements
99101
@Reference(policyOption = ReferencePolicyOption.GREEDY)
100102
ResourceResolverFactory resourceResolverFactory;
101103

104+
@Reference(policyOption = ReferencePolicyOption.GREEDY)
105+
SlingRepository repository;
106+
102107
@Override
103108
public void installAuthorizables(
104109
AcConfiguration acConfiguration,
@@ -156,7 +161,7 @@ private void installAuthorizableConfigurationBean(final Session session,
156161
// update password for users
157162
if (!authorizableToInstall.isGroup() && !authorizableConfigBean.isSystemUser()
158163
&& StringUtils.isNotBlank(authorizableConfigBean.getPassword())) {
159-
setUserPassword(authorizableConfigBean, (User) authorizableToInstall);
164+
setUserPassword(authorizableConfigBean, (User) authorizableToInstall, installLog);
160165
}
161166

162167
// move authorizable if path changed (retaining existing members)
@@ -227,11 +232,25 @@ private void installKeys(Map<String, Key> keys, String userId, ResourceResolver
227232
}
228233
}
229234

230-
231235
void setUserPassword(final AuthorizableConfigBean authorizableConfigBean,
232-
final User authorizableToInstall) throws RepositoryException, AuthorizableCreatorException {
236+
final User authorizableToInstall, InstallationLogger installLog) throws RepositoryException, AuthorizableCreatorException {
237+
238+
String userId = authorizableToInstall.getID();
233239
String password = getPassword(authorizableConfigBean);
234-
authorizableToInstall.changePassword(password);
240+
Session sessionForUser = null;
241+
try {
242+
sessionForUser = repository.login(new SimpleCredentials(userId, password.toCharArray()));
243+
LOG.trace("Could obtain session {} for user {}, will not update password", sessionForUser, userId);
244+
installLog.addVerboseMessage(LOG, "Password of user " + userId + " has not changed");
245+
} catch (javax.jcr.LoginException e) {
246+
LOG.trace("User {} could not log in with existing password", userId, e);
247+
authorizableToInstall.changePassword(password);
248+
installLog.addMessage(LOG, "Changed password of user " + userId);
249+
} finally {
250+
if (sessionForUser != null) {
251+
sessionForUser.logout();
252+
}
253+
}
235254
}
236255

237256
private String getPassword(final AuthorizableConfigBean authorizableConfigBean)

accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/authorizableinstaller/impl/AuthorizableInstallerServiceImplTest.java

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import static org.mockito.Matchers.eq;
1515
import static org.mockito.Mockito.doAnswer;
1616
import static org.mockito.Mockito.doReturn;
17+
import static org.mockito.Mockito.doThrow;
1718
import static org.mockito.Mockito.mock;
1819
import static org.mockito.Mockito.reset;
1920
import static org.mockito.Mockito.spy;
@@ -22,6 +23,7 @@
2223
import static org.mockito.Mockito.verifyNoMoreInteractions;
2324
import static org.mockito.Mockito.verifyZeroInteractions;
2425
import static org.mockito.Mockito.when;
26+
import static org.mockito.MockitoAnnotations.initMocks;
2527

2628
import java.util.Arrays;
2729
import java.util.Collections;
@@ -30,6 +32,7 @@
3032

3133
import javax.jcr.RepositoryException;
3234
import javax.jcr.Session;
35+
import javax.jcr.SimpleCredentials;
3336
import javax.jcr.Value;
3437
import javax.jcr.ValueFactory;
3538

@@ -39,17 +42,17 @@
3942
import org.apache.jackrabbit.api.security.user.Query;
4043
import org.apache.jackrabbit.api.security.user.User;
4144
import org.apache.jackrabbit.api.security.user.UserManager;
45+
import org.apache.sling.jcr.api.SlingRepository;
4246
import org.hamcrest.BaseMatcher;
4347
import org.hamcrest.Description;
4448
import org.junit.Before;
45-
import org.junit.Ignore;
4649
import org.junit.Test;
4750
import org.junit.runner.RunWith;
4851
import org.junit.runners.Enclosed;
52+
import org.mockito.InjectMocks;
4953
import org.mockito.Matchers;
5054
import org.mockito.Mock;
5155
import org.mockito.Mockito;
52-
import org.mockito.MockitoAnnotations;
5356
import org.mockito.Spy;
5457
import org.mockito.invocation.InvocationOnMock;
5558
import org.mockito.runners.MockitoJUnitRunner;
@@ -321,6 +324,7 @@ public void describeTo(Description desc) {
321324

322325
public static final class SetUserPassword {
323326

327+
private static final String USER_ID = "userid";
324328
private static final String UNPROTECTED_PASSWORD = "unprotected_pass";
325329

326330
@Mock
@@ -329,26 +333,48 @@ public static final class SetUserPassword {
329333
@Mock
330334
private DecryptionService decryptionService;
331335

336+
@Mock
337+
private InstallationLogger installationLogger;
338+
339+
@Mock
340+
private SlingRepository repository;
341+
342+
@Mock
343+
private Session session;
344+
345+
@Spy
346+
@InjectMocks
332347
private AuthorizableInstallerServiceImpl service;
333348

349+
private AuthorizableConfigBean configBean;
350+
334351
@Before
335-
public void setUp() throws CryptoException {
336-
MockitoAnnotations.initMocks(this);
352+
public void setUp() throws CryptoException, RepositoryException {
353+
initMocks(this);
337354

338-
service = new AuthorizableInstallerServiceImpl();
339-
service.decryptionService = decryptionService;
355+
doReturn(USER_ID).when(user).getID();
340356

341357
doReturn(UNPROTECTED_PASSWORD).when(decryptionService).decrypt(anyString());
358+
359+
configBean = new AuthorizableConfigBean();
360+
configBean.setPassword("{some_protected_pass1}");
342361
}
343362

344363
@Test
345-
public void test() throws RepositoryException, AuthorizableCreatorException {
346-
final AuthorizableConfigBean bean = new AuthorizableConfigBean();
347-
bean.setPassword("{some_protected_pass1}");
364+
public void testPasswordExists() throws RepositoryException, AuthorizableCreatorException {
348365

349-
service.setUserPassword(bean, user);
366+
doReturn(session).when(repository).login(any(SimpleCredentials.class));
367+
service.setUserPassword(configBean, user, installationLogger);
368+
verify(user, times(0)).changePassword(anyString());
369+
}
350370

351-
verify(user).changePassword(eq(UNPROTECTED_PASSWORD));
371+
@Test
372+
public void testPasswordDifferent() throws RepositoryException, AuthorizableCreatorException {
373+
final AuthorizableConfigBean bean = new AuthorizableConfigBean();
374+
bean.setPassword("{some_protected_pass1}");
375+
doThrow(javax.jcr.LoginException.class).when(repository).login(any(SimpleCredentials.class));
376+
service.setUserPassword(configBean, user, installationLogger);
377+
verify(user, times(1)).changePassword(UNPROTECTED_PASSWORD);
352378
}
353379
}
354380
}

docs/ApplyConfig.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* [JMX](#jmx)
1010
* [Startup Hook](#startup-hook)
1111
* [Upload Listener Service](#upload-listener-service)
12+
* [Ad hoc installation of small fragments](#ad-hoc-installation-of-small-fragments)
1213

1314
<!--- This table of contents has been generated with https://github.com/ekalinin/github-markdown-toc#gh-md-toc -->
1415

@@ -141,5 +142,31 @@ NOTE: Usually it is better to rely on the install hook and manual executions via
141142
The upload listener service requires the `AC Tool Installation Service` (PID `biz.netcentric.cq.tools.actool.impl.AcInstallationServiceImpl`) to be configured correctly, i.e. its configuration path must point to the nodes containing the `YAML` files.
142143
<img src="images/installation-service.png">
143144

145+
### Ad hoc installation of small fragments
146+
147+
Generally it is best practice to keep the yaml files in source control and only use one of the above methods to trigger the installation of those files.
148+
However, for some support scenarios it can be useful to be able to apply small yaml fragments directly. This can be achieved by using the following
149+
groovy script (using the [AEM Groovy Console](https://github.com/icfnext/aem-groovy-console)):
150+
151+
```
152+
import static org.apache.jackrabbit.commons.JcrUtils.*
153+
import static org.apache.commons.io.IOUtils.*
154+
import biz.netcentric.cq.tools.actool.api.AcInstallationService
155+
def runAcTool(adhocFolder, adhocFile, yaml) {
156+
putFile(getOrCreateByPath(adhocFolder, "nt:folder", session), adhocFile, "text/yaml", toInputStream(yaml)); session.save();
157+
return getService(AcInstallationService.class).apply(adhocFolder)
158+
}
159+
160+
runAcTool("/tmp/actool-adhoc", "actool.yaml", """
161+
162+
- user_config:
163+
- test-user:
164+
- name: "My test user"
165+
path: /home/users/testusers
166+
167+
""")
168+
```
169+
170+
As for any executions, the log of ad hoc installations are found underneath `/var/statistics/achistory`.
144171

145172

0 commit comments

Comments
 (0)