package com.thinkbiganalytics.metadata.modeshape.user;
/*-
* #%L
* thinkbig-metadata-modeshape
* %%
* Copyright (C) 2017 ThinkBig Analytics
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import com.thinkbiganalytics.metadata.api.user.User;
import com.thinkbiganalytics.metadata.api.user.UserGroup;
import com.thinkbiganalytics.metadata.modeshape.MetadataRepositoryException;
import com.thinkbiganalytics.metadata.modeshape.common.AbstractJcrAuditableSystemEntity;
import com.thinkbiganalytics.metadata.modeshape.common.JcrEntity;
import com.thinkbiganalytics.metadata.modeshape.support.JcrPropertyUtil;
import com.thinkbiganalytics.metadata.modeshape.support.JcrUtil;
import com.thinkbiganalytics.security.GroupPrincipal;
import org.apache.commons.lang3.reflect.ConstructorUtils;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.Principal;
import java.util.Iterator;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.annotation.Nonnull;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
/**
*
*/
public class JcrUserGroup extends AbstractJcrAuditableSystemEntity implements UserGroup {
/**
* JCR node type for users
*/
public static final String NODE_TYPE = "tba:userGroup";
/**
* The groups property from the mixin tba:userGroupable
*/
public static final String GROUPS = "tba:groups";
/**
* Encoding for properties
*/
static final String ENCODING = "UTF-8";
/**
* Name of the {@code enabled} property
*/
private static final String ENABLED = "tba:enabled";
/**
* @param node
*/
public JcrUserGroup(Node node) {
super(node);
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.extension.ExtensibleEntity#getId()
*/
@Nonnull
@Override
public UserGroupId getId() {
try {
return new UserGroupId(this.node.getIdentifier());
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to retrieve the entity id", e);
}
}
@Nonnull
@Override
public String getSystemName() {
try {
return URLDecoder.decode(JcrPropertyUtil.getName(this.node), ENCODING);
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("Unsupported encoding for system name of user: " + this.node, e);
}
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#isEnabled()
*/
@Override
public boolean isEnabled() {
return getProperty(ENABLED, true);
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#setEnabled(boolean)
*/
@Override
public void setEnabled(final boolean enabled) {
setProperty(ENABLED, enabled);
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#getUsers()
*/
@Nonnull
@Override
public Iterable<User> getUsers() {
return iterateReferances(JcrUser.NODE_TYPE, User.class, JcrUser.class);
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#streamAllUsers()
*/
@Nonnull
public Stream<User> streamAllUsers() {
return streamAllGroups().flatMap(g -> StreamSupport.stream(g.getUsers().spliterator(), false));
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#addUser(com.thinkbiganalytics.metadata.api.user.User)
*/
@Override
public boolean addUser(@Nonnull User user) {
JcrUser jcrUser = (JcrUser) user;
return JcrPropertyUtil.addToSetProperty(jcrUser.getNode(), GROUPS, this.node, true);
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#removeUser(com.thinkbiganalytics.metadata.api.user.User)
*/
@Override
public boolean removeUser(@Nonnull User user) {
JcrUser jcrUser = (JcrUser) user;
return JcrPropertyUtil.removeFromSetProperty(jcrUser.getNode(), GROUPS, this.node);
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#getContainingGroups()
*/
@Nonnull
@Override
public Set<UserGroup> getContainingGroups() {
return streamContainingGroupNodes(this.node)
.map(node -> (UserGroup) JcrUtil.toJcrObject(node, JcrUserGroup.NODE_TYPE, JcrUserGroup.class))
.collect(Collectors.toSet());
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#getAllContainingGroups()
*/
@Override
public Set<UserGroup> getAllContainingGroups() {
return streamAllContainingGroupNodes(this.node)
.map(node -> JcrUtil.toJcrObject(node, JcrUserGroup.NODE_TYPE, JcrUserGroup.class))
.collect(Collectors.toSet());
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#getGroups()
*/
@Nonnull
@Override
public Iterable<UserGroup> getGroups() {
return iterateReferances(JcrUserGroup.NODE_TYPE, UserGroup.class, JcrUserGroup.class);
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#streamAllGroups()
*/
@Nonnull
public Stream<UserGroup> streamAllGroups() {
return StreamSupport.stream(getGroups().spliterator(), false).flatMap(g -> g.streamAllGroups());
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#addGroup(com.thinkbiganalytics.metadata.api.user.UserGroup)
*/
@Override
public boolean addGroup(@Nonnull UserGroup group) {
JcrUserGroup jcrGrp = (JcrUserGroup) group;
return JcrPropertyUtil.addToSetProperty(jcrGrp.getNode(), GROUPS, this.node, true);
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#removeGroup(com.thinkbiganalytics.metadata.api.user.UserGroup)
*/
@Override
public boolean removeGroup(@Nonnull UserGroup group) {
JcrUserGroup jcrGrp = (JcrUserGroup) group;
return JcrPropertyUtil.removeFromSetProperty(jcrGrp.getNode(), GROUPS, this.node);
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#getPrincial()
*/
@Override
public GroupPrincipal getPrincial() {
Set<Principal> members = StreamSupport.stream(getGroups().spliterator(), false)
.map(g -> g.getPrincial())
.collect(Collectors.toSet());
return new GroupPrincipal(getSystemName(), members);
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.UserGroup#getRootPrincial()
*/
@Override
public GroupPrincipal getRootPrincial() {
return new GroupPrincipal(getSystemName());
}
private <C, J> Iterable<C> iterateReferances(String nodeType, Class<C> modelClass, Class<J> jcrClass) {
return () -> {
@SuppressWarnings("unchecked")
Iterable<Property> propItr = () -> {
try {
return (Iterator<Property>) this.node.getWeakReferences();
} catch (Exception e) {
throw new MetadataRepositoryException("Failed to retrieve the users in the group node: " + this.node, e);
}
};
return StreamSupport.stream(propItr.spliterator(), false)
.map(p -> JcrPropertyUtil.getParent(p))
.filter(n -> JcrUtil.isNodeType(n, nodeType))
.map(n -> {
try {
@SuppressWarnings("unchecked")
C entity = (C) ConstructorUtils.invokeConstructor(jcrClass, n);
return entity;
} catch (Exception e) {
throw new MetadataRepositoryException("Failed to retrieve create entity: " + jcrClass, e);
}
})
.iterator();
};
}
private Stream<Node> streamContainingGroupNodes(Node groupNode) {
return JcrPropertyUtil.<Node>getSetProperty(groupNode, JcrUserGroup.GROUPS).stream();
}
private Stream<Node> streamAllContainingGroupNodes(Node groupNode) {
Set<Node> referenced = JcrPropertyUtil.<Node>getSetProperty(groupNode, JcrUserGroup.GROUPS);
return Stream.concat(referenced.stream(),
referenced.stream().flatMap(node -> streamAllContainingGroupNodes(node)));
}
/**
* A {@link com.thinkbiganalytics.metadata.api.user.UserGroup.ID} implementation identifying a UserGroup.
*/
static class UserGroupId extends JcrEntity.EntityId implements UserGroup.ID {
private static final long serialVersionUID = 1L;
UserGroupId(@Nonnull final Serializable ser) {
super(ser);
}
}
}