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.extension.JcrExtensiblePropertyCollection;
import com.thinkbiganalytics.metadata.modeshape.support.JcrPropertyUtil;
import com.thinkbiganalytics.metadata.modeshape.support.JcrUtil;
import com.thinkbiganalytics.security.GroupPrincipal;
import com.thinkbiganalytics.security.UsernamePrincipal;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.Principal;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.jcr.Node;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
/**
* A {@link User} stored in a JCR repository.
*/
public class JcrUser extends AbstractJcrAuditableSystemEntity implements User {
/**
* Encoding for properties
*/
static final String ENCODING = "UTF-8";
/**
* JCR node type for users
*/
static final String NODE_TYPE = "tba:user";
/**
* Name of the {@code displayName} property
*/
private static final String DISPLAY_NAME = "tba:displayName";
/**
* Name of the {@code email} property
*/
private static final String EMAIL = "tba:email";
/**
* Name of the {@code enabled} property
*/
private static final String ENABLED = "tba:enabled";
/**
* Name of the {@code groups} property
*/
private static final String GROUPS = "tba:groups";
/**
* Name of the {@code password} property
*/
private static final String PASSWORD = "tba:password";
/**
* Constructs a {@code JcrUser} using the specified node.
*
* @param node the JCR node for the user
*/
public JcrUser(@Nonnull final Node node) {
super(node);
}
@Nullable
@Override
public String getDisplayName() {
return getProperty(DISPLAY_NAME, String.class);
}
@Override
public void setDisplayName(@Nullable final String displayName) {
setProperty(DISPLAY_NAME, displayName);
}
@Nullable
@Override
public String getEmail() {
return getProperty(EMAIL, String.class);
}
@Override
public void setEmail(@Nullable final String email) {
setProperty(EMAIL, email);
}
@Nonnull
@Override
public UserId getId() {
try {
return new UserId(getObjectId());
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to retrieve the entity id", e);
}
}
@Override
public boolean isEnabled() {
return getProperty(ENABLED, true);
}
@Override
public void setEnabled(final boolean enabled) {
setProperty(ENABLED, enabled);
}
@Nullable
@Override
public String getPassword() {
return getProperty(PASSWORD, String.class);
}
@Override
public void setPassword(@Nullable final String password) {
setProperty(PASSWORD, password);
}
@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.User#getAllContainingGroups()
*/
@Override
public Set<UserGroup> getAllContainingGroups() {
return streamAllContainingGroups().collect(Collectors.toSet());
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.User#getContainingGroups()
*/
@Override
public Set<UserGroup> getContainingGroups() {
return JcrPropertyUtil.<Node>getSetProperty(this.node, JcrUserGroup.GROUPS).stream()
.filter(node -> node != null)
.map(node -> (UserGroup) JcrUtil.toJcrObject(node, JcrUserGroup.NODE_TYPE, JcrUserGroup.class))
.collect(Collectors.toSet());
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.User#getPrincipal()
*/
@Override
public Principal getPrincipal() {
return new UsernamePrincipal(getSystemName());
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.api.user.User#getGroupPrincipals()
*/
@Nonnull
@Override
public Set<GroupPrincipal> getAllGroupPrincipals() {
return streamAllContainingGroups()
.map(group -> group.getRootPrincial())
.collect(Collectors.toSet());
}
private Stream<UserGroup> streamAllContainingGroups() {
Set<UserGroup> groups = getContainingGroups();
return Stream.concat(groups.stream(),
groups.stream().flatMap(group -> group.getAllContainingGroups().stream()));
}
@Nonnull
@Override
@SuppressWarnings("unchecked")
public Set<UserGroup> getGroups() {
return getContainingGroups();
}
@Override
public void setGroups(@Nonnull final Set<UserGroup> groups) {
final JcrExtensiblePropertyCollection collection = new JcrExtensiblePropertyCollection(PropertyType.WEAKREFERENCE, groups);
try {
JcrPropertyUtil.setProperties(node.getSession(), node, Collections.singletonMap(GROUPS, collection));
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to set groups", e);
}
}
/**
* A {@link com.thinkbiganalytics.metadata.api.user.User.ID} representing a JCR User.
*/
static class UserId extends JcrEntity.EntityId implements User.ID {
private static final long serialVersionUID = 1780033096808176536L;
/**
* Constructs a {@code UserId} with the specified username.
*
* @param ser the username
*/
UserId(@Nonnull final Serializable ser) {
super(ser);
}
}
}