Skip to content

Commit 62e51dc

Browse files
author
Jeff Bornemann
committed
progress
1 parent ccb8beb commit 62e51dc

File tree

9 files changed

+593
-199
lines changed

9 files changed

+593
-199
lines changed
Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
package com.twcable.grabbit.client.jcr
1+
package com.twcable.grabbit.jcr
22

3-
import com.twcable.grabbit.jcr.JcrNodeDecorator
4-
import com.twcable.grabbit.jcr.ProtoNodeDecorator
3+
import com.twcable.grabbit.proto.NodeProtos.Node as ProtoNode
54
import com.twcable.grabbit.security.AuthorizablePrincipal
65
import com.twcable.grabbit.security.InsufficientGrabbitPrivilegeException
76
import groovy.transform.CompileStatic
8-
import groovy.transform.InheritConstructors
97
import groovy.util.logging.Slf4j
108
import org.apache.jackrabbit.api.security.user.Authorizable
119
import org.apache.jackrabbit.api.security.user.User
@@ -40,10 +38,16 @@ import java.lang.reflect.ReflectPermission
4038
* trees, and can not be written directly by a client.
4139
*/
4240
@CompileStatic
43-
@InheritConstructors
4441
@Slf4j
4542
class AuthorizableProtoNodeDecorator extends ProtoNodeDecorator {
4643

44+
45+
protected AuthorizableProtoNodeDecorator(@Nonnull ProtoNode node, @Nonnull Collection<ProtoPropertyDecorator> protoProperties) {
46+
this.innerProtoNode = node
47+
this.protoProperties = protoProperties
48+
}
49+
50+
4751
@Override
4852
JcrNodeDecorator writeToJcr(@Nonnull Session session) {
4953
if(!checkSecurityPermissions()) {
@@ -61,13 +65,14 @@ class AuthorizableProtoNodeDecorator extends ProtoNodeDecorator {
6165
return new JcrNodeDecorator(session.getNode(authorizable.getPath()))
6266
}
6367

68+
6469
/**
6570
* @return a new authorizable from this serialized node
6671
*/
6772
private Authorizable createNewAuthorizable(final Session session) {
68-
final UserManager userManager = AccessControlUtil.getUserManager(session)
73+
final UserManager userManager = getUserManager(session)
6974
if(isUserType()) {
70-
//When set a temporary password for now, and then set the real password later in setPasswordForUser(). See the method for why.
75+
//We set a temporary password for now, and then set the real password later in setPasswordForUser(). See the method for why.
7176
final newUser = userManager.createUser(authorizableID, 'temp', new AuthorizablePrincipal(authorizableID), getIntermediateAuthorizablePath())
7277
//This is a special protected property for disabling user access
7378
if(hasProperty('rep:disabled')) {
@@ -84,6 +89,7 @@ class AuthorizableProtoNodeDecorator extends ProtoNodeDecorator {
8489
return userManager.createGroup(authorizableID, new AuthorizablePrincipal(authorizableID), getIntermediateAuthorizablePath())
8590
}
8691

92+
8793
/**
8894
* From a client API perspective, there is really no way to truely update an existing authorizable node. All of the properties are protected, and there is no
8995
* known way to update them. What we do here is essentially remove the existing authorizable as denoted by the authorizableID, and recreate it.
@@ -96,7 +102,7 @@ class AuthorizableProtoNodeDecorator extends ProtoNodeDecorator {
96102

97103

98104
private Authorizable findAuthorizable(final Session session) {
99-
final UserManager userManager = AccessControlUtil.getUserManager(session)
105+
final UserManager userManager = getUserManager(session)
100106
return userManager.getAuthorizable(getAuthorizableID())
101107
}
102108

@@ -107,7 +113,10 @@ class AuthorizableProtoNodeDecorator extends ProtoNodeDecorator {
107113

108114

109115
private String getIntermediateAuthorizablePath() {
110-
return getName() - "/${getName().tokenize('/').last()}"
116+
final pathTokens = getName().tokenize('/')
117+
//remove last index, as this is the Authorizable node name
118+
pathTokens.remove(pathTokens.size() - 1)
119+
return "/${pathTokens.join('/')}"
111120
}
112121

113122

@@ -122,7 +131,7 @@ class AuthorizableProtoNodeDecorator extends ProtoNodeDecorator {
122131
* @return true if we can sync this Authorizable
123132
*/
124133
private boolean checkSecurityPermissions() {
125-
final SecurityManager securityManager = System.getSecurityManager()
134+
final SecurityManager securityManager = getSecurityManager()
126135
//If no security manager is present, then we are in the clear; otherwise, we need to check certain permissions
127136
if(!securityManager){
128137
log.debug "No SecurityManager found on this JVM. Sync of Users/Groups can continue"
@@ -166,6 +175,18 @@ class AuthorizableProtoNodeDecorator extends ProtoNodeDecorator {
166175
}
167176
}
168177

178+
/**
179+
* @return the system's security manager, or null if one is not present
180+
*/
181+
SecurityManager getSecurityManager() {
182+
return System.getSecurityManager()
183+
}
184+
185+
186+
UserManager getUserManager(final Session session) {
187+
return AccessControlUtil.getUserManager(session)
188+
}
189+
169190

170191
/**
171192
* Normally we would call org.apache.jackrabbit.oak.jcr.delegate.UserDelegator.changePassword(String password) to change a password (this is what is publicly available through the Jackrabbit API)
@@ -178,10 +199,10 @@ class AuthorizableProtoNodeDecorator extends ProtoNodeDecorator {
178199
*
179200
* @throws IllegalStateException if security permissions required to run this are not there. @{code checkSecurityPermissions()} should be called before calling this method
180201
**/
181-
private void setPasswordForUser(final User user, final Session session) {
202+
void setPasswordForUser(final User user, final Session session) {
182203
if(!checkSecurityPermissions()) throw new IllegalStateException("Security check failed for Grabbit. Can not set user passwords")
183204
//As a consumer we have access to org.apache.jackrabbit.oak.jcr.delegate.UserManagerDelegator below
184-
final userManager = AccessControlUtil.getUserManager(session)
205+
final userManager = getUserManager(session)
185206
Class userManagerDelegatorClass = userManager.getClass()
186207
//Reach into the class of this delegator, and grab the core Jackrabbit object we delegate to
187208
Field userManagerDelegateField = userManagerDelegatorClass.getDeclaredField('userManagerDelegate')
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2015 Time Warner Cable, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.twcable.grabbit.jcr
17+
18+
import com.twcable.grabbit.proto.NodeProtos.Node as ProtoNode
19+
import com.twcable.grabbit.proto.NodeProtos.Value as ProtoValue
20+
import groovy.transform.CompileStatic
21+
import groovy.util.logging.Slf4j
22+
import org.apache.jackrabbit.commons.JcrUtils
23+
24+
import javax.annotation.Nonnull
25+
import javax.jcr.Node as JCRNode
26+
import javax.jcr.Session
27+
28+
import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES
29+
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE
30+
31+
@CompileStatic
32+
@Slf4j
33+
class DefaultProtoNodeDecorator extends ProtoNodeDecorator {
34+
35+
36+
protected DefaultProtoNodeDecorator(@Nonnull ProtoNode node, @Nonnull Collection<ProtoPropertyDecorator> protoProperties) {
37+
this.innerProtoNode = node
38+
this.protoProperties = protoProperties
39+
}
40+
41+
42+
@Override
43+
JcrNodeDecorator writeToJcr(@Nonnull Session session) {
44+
final jcrNode = getOrCreateNode(session)
45+
//Write mixin types first to avoid InvalidConstraintExceptions
46+
final mixinProperty = getMixinProperty()
47+
if(mixinProperty) {
48+
addMixins(mixinProperty, jcrNode)
49+
}
50+
//Then add other properties
51+
writableProperties.each { it.writeToNode(jcrNode) }
52+
53+
return new JcrNodeDecorator(jcrNode)
54+
}
55+
56+
57+
private ProtoPropertyDecorator getMixinProperty() {
58+
protoProperties.find { it.isMixinType() }
59+
}
60+
61+
62+
private Collection<ProtoPropertyDecorator> getWritableProperties() {
63+
protoProperties.findAll { !(it.name in [JCR_PRIMARYTYPE, JCR_MIXINTYPES]) }
64+
}
65+
66+
67+
/**
68+
* This method is rather succinct, but helps isolate this JcrUtils static method call
69+
* so that we can get better test coverage.
70+
* @param session to create or get the node path for
71+
* @return the newly created, or found node
72+
*/
73+
JCRNode getOrCreateNode(Session session) {
74+
JcrUtils.getOrCreateByPath(innerProtoNode.name, primaryType.getStringValue(), session)
75+
}
76+
77+
78+
/**
79+
* If a property can be added as a mixin, adds it to the given node
80+
* @param property
81+
* @param node
82+
*/
83+
private static void addMixins(ProtoPropertyDecorator property, JCRNode node) {
84+
property.valuesList.each { ProtoValue value ->
85+
if (node.canAddMixin(value.stringValue)) {
86+
node.addMixin(value.stringValue)
87+
log.debug "Added mixin ${value.stringValue} for : ${node.name}."
88+
}
89+
else {
90+
log.warn "Encountered invalid mixin type while unmarshalling for Proto value : ${value}"
91+
}
92+
}
93+
}
94+
95+
}

src/main/groovy/com/twcable/grabbit/jcr/JCRNodeDecorator.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ class JcrNodeDecorator {
146146
if (hasProperty(JCR_LASTMODIFIED)) {
147147
return getProperty(JCR_LASTMODIFIED).date.time
148148
}
149-
else if (hasProperty("cq:lastModified")) {
149+
else if (hasProperty(CQ_LAST_MODIFIED)) {
150150
return getProperty(CQ_LAST_MODIFIED).date.time
151151
}
152152
else if (hasProperty(JCR_CREATED)) {
Lines changed: 19 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
package com.twcable.grabbit.jcr
2+
3+
import com.twcable.grabbit.proto.NodeProtos.Node as ProtoNode
4+
import groovy.transform.CompileStatic
5+
6+
import javax.annotation.Nonnull
7+
import javax.jcr.Session
8+
19
/*
210
* Copyright 2015 Time Warner Cable, Inc.
311
*
@@ -13,113 +21,38 @@
1321
* See the License for the specific language governing permissions and
1422
* limitations under the License.
1523
*/
16-
package com.twcable.grabbit.jcr
17-
18-
import com.twcable.grabbit.client.jcr.AuthorizableProtoNodeDecorator
19-
import com.twcable.grabbit.proto.NodeProtos.Node as ProtoNode
20-
import com.twcable.grabbit.proto.NodeProtos.Value as ProtoValue
21-
import groovy.transform.CompileStatic
22-
import groovy.util.logging.Slf4j
23-
import org.apache.jackrabbit.commons.JcrUtils
24-
25-
import javax.annotation.Nonnull
26-
import javax.jcr.Node as JCRNode
27-
import javax.jcr.Session
28-
29-
import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES
30-
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE
3124

3225
@CompileStatic
33-
@Slf4j
34-
class ProtoNodeDecorator {
26+
abstract class ProtoNodeDecorator {
3527

3628
@Delegate
37-
ProtoNode innerProtoNode
29+
protected ProtoNode innerProtoNode
3830

39-
Collection<ProtoPropertyDecorator> protoProperties
31+
protected Collection<ProtoPropertyDecorator> protoProperties
4032

33+
abstract JcrNodeDecorator writeToJcr(@Nonnull Session session)
4134

4235
static ProtoNodeDecorator createFrom(@Nonnull ProtoNode node) {
4336
if(!node) throw new IllegalArgumentException("node must not be null!")
4437
final protoProperties = node.propertiesList.collect { new ProtoPropertyDecorator(it) }
45-
if(protoProperties.any { it.userType || it.groupType }) {
38+
final primaryType = protoProperties.find { it.primaryType }
39+
if(primaryType.isUserType() || primaryType.isGroupType()) {
4640
return new AuthorizableProtoNodeDecorator(node, protoProperties)
4741
}
48-
return new ProtoNodeDecorator(node, protoProperties)
42+
return new DefaultProtoNodeDecorator(node, protoProperties)
4943
}
5044

51-
52-
protected ProtoNodeDecorator(@Nonnull ProtoNode node, @Nonnull Collection<ProtoPropertyDecorator> protoProperties) {
53-
this.innerProtoNode = node
54-
this.protoProperties = protoProperties
55-
}
56-
57-
5845
boolean hasProperty(String propertyName) {
5946
propertiesList.any{ it.name == propertyName }
6047
}
6148

6249

63-
protected String getStringValueFrom(String propertyName) {
64-
protoProperties.find { it.name == propertyName }.stringValue
50+
protected ProtoPropertyDecorator getPrimaryType() {
51+
protoProperties.find { it.isPrimaryType() }
6552
}
6653

6754

68-
private String getPrimaryType() {
69-
protoProperties.find { it.isPrimaryType() }.stringValue
70-
}
71-
72-
73-
private ProtoPropertyDecorator getMixinProperty() {
74-
protoProperties.find { it.isMixinType() }
75-
}
76-
77-
78-
private Collection<ProtoPropertyDecorator> getWritableProperties() {
79-
protoProperties.findAll { !(it.name in [JCR_PRIMARYTYPE, JCR_MIXINTYPES]) }
80-
}
81-
82-
83-
JcrNodeDecorator writeToJcr(@Nonnull Session session) {
84-
final jcrNode = getOrCreateNode(session)
85-
//Write mixin types first to avoid InvalidConstraintExceptions
86-
final mixinProperty = getMixinProperty()
87-
if(mixinProperty) {
88-
addMixins(mixinProperty, jcrNode)
89-
}
90-
//Then add other properties
91-
writableProperties.each { it.writeToNode(jcrNode) }
92-
93-
return new JcrNodeDecorator(jcrNode)
94-
}
95-
96-
97-
/**
98-
* This method is rather succinct, but helps isolate this JcrUtils static method call
99-
* so that we can get better test coverage.
100-
* @param session to create or get the node path for
101-
* @return the newly created, or found node
102-
*/
103-
private JCRNode getOrCreateNode(Session session) {
104-
JcrUtils.getOrCreateByPath(innerProtoNode.name, primaryType, session)
105-
}
106-
107-
108-
/**
109-
* If a property can be added as a mixin, adds it to the given node
110-
* @param property
111-
* @param node
112-
*/
113-
private static void addMixins(ProtoPropertyDecorator property, JCRNode node) {
114-
property.valuesList.each { ProtoValue value ->
115-
if (node.canAddMixin(value.stringValue)) {
116-
node.addMixin(value.stringValue)
117-
log.debug "Added mixin ${value.stringValue} for : ${node.name}."
118-
}
119-
else {
120-
log.warn "Encountered invalid mixin type while unmarshalling for Proto value : ${value}"
121-
}
122-
}
55+
protected String getStringValueFrom(String propertyName) {
56+
protoProperties.find { it.name == propertyName }.stringValue
12357
}
124-
12558
}

src/main/groovy/com/twcable/grabbit/jcr/ProtoPropertyDecorator.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,12 @@ class ProtoPropertyDecorator {
8585

8686

8787
boolean isUserType() {
88-
innerProtoProperty.name == 'rep:User'
88+
getStringValue() == 'rep:User'
8989
}
9090

9191

9292
boolean isGroupType() {
93-
innerProtoProperty.name == 'rep:Group'
93+
getStringValue() == 'rep:Group'
9494
}
9595

9696

0 commit comments

Comments
 (0)