GUACAMOLE-220: Merge add database support for user groups.

This commit is contained in:
Nick Couchman
2018-10-01 13:33:07 -04:00
133 changed files with 8377 additions and 1132 deletions

View File

@@ -109,33 +109,33 @@
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
<version>3.4.6</version>
</dependency>
<!-- MyBatis Guice -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-guice</artifactId>
<version>3.6</version>
<version>3.10</version>
</dependency>
<!-- Guice -->
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-multibindings</artifactId>
<version>3.0</version>
<version>4.1.0</version>
</dependency>
<!-- Guava - Utility Library -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
<version>19.0</version>
</dependency>
</dependencies>

View File

@@ -59,10 +59,15 @@ import org.apache.guacamole.auth.jdbc.activeconnection.ActiveConnectionPermissio
import org.apache.guacamole.auth.jdbc.activeconnection.ActiveConnectionPermissionSet;
import org.apache.guacamole.auth.jdbc.activeconnection.ActiveConnectionService;
import org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection;
import org.apache.guacamole.auth.jdbc.base.EntityMapper;
import org.apache.guacamole.auth.jdbc.base.EntityService;
import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper;
import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionMapper;
import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionService;
import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionSet;
import org.apache.guacamole.auth.jdbc.permission.UserGroupPermissionMapper;
import org.apache.guacamole.auth.jdbc.permission.UserGroupPermissionService;
import org.apache.guacamole.auth.jdbc.permission.UserGroupPermissionSet;
import org.apache.guacamole.auth.jdbc.security.PasswordPolicyService;
import org.apache.guacamole.auth.jdbc.sharing.ConnectionSharingService;
import org.apache.guacamole.auth.jdbc.sharing.HashSharedConnectionMap;
@@ -77,8 +82,16 @@ import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileService;
import org.apache.guacamole.auth.jdbc.tunnel.RestrictedGuacamoleTunnelService;
import org.apache.guacamole.auth.jdbc.user.PasswordRecordMapper;
import org.apache.guacamole.auth.jdbc.user.UserRecordMapper;
import org.apache.guacamole.auth.jdbc.usergroup.ModeledUserGroup;
import org.apache.guacamole.auth.jdbc.usergroup.UserGroupDirectory;
import org.apache.guacamole.auth.jdbc.usergroup.UserGroupMapper;
import org.apache.guacamole.auth.jdbc.usergroup.UserGroupMemberUserGroupMapper;
import org.apache.guacamole.auth.jdbc.usergroup.UserGroupMemberUserMapper;
import org.apache.guacamole.auth.jdbc.usergroup.UserGroupParentUserGroupMapper;
import org.apache.guacamole.auth.jdbc.usergroup.UserGroupService;
import org.mybatis.guice.MyBatisModule;
import org.mybatis.guice.datasource.builtin.PooledDataSourceProvider;
import org.apache.guacamole.auth.jdbc.user.UserParentUserGroupMapper;
/**
* Guice module which configures the injections used by the JDBC authentication
@@ -120,12 +133,19 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule {
addMapperClass(ConnectionPermissionMapper.class);
addMapperClass(ConnectionRecordMapper.class);
addMapperClass(ConnectionParameterMapper.class);
addMapperClass(EntityMapper.class);
addMapperClass(PasswordRecordMapper.class);
addMapperClass(SystemPermissionMapper.class);
addMapperClass(SharingProfileMapper.class);
addMapperClass(SharingProfileParameterMapper.class);
addMapperClass(SharingProfilePermissionMapper.class);
addMapperClass(UserGroupMapper.class);
addMapperClass(UserGroupMemberUserGroupMapper.class);
addMapperClass(UserGroupMemberUserMapper.class);
addMapperClass(UserGroupParentUserGroupMapper.class);
addMapperClass(UserGroupPermissionMapper.class);
addMapperClass(UserMapper.class);
addMapperClass(UserParentUserGroupMapper.class);
addMapperClass(UserPermissionMapper.class);
addMapperClass(UserRecordMapper.class);
@@ -143,12 +163,15 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule {
bind(ModeledSharingProfile.class);
bind(ModeledUser.class);
bind(ModeledUserContext.class);
bind(ModeledUserGroup.class);
bind(RootConnectionGroup.class);
bind(SharingProfileDirectory.class);
bind(SharingProfilePermissionSet.class);
bind(SystemPermissionSet.class);
bind(TrackedActiveConnection.class);
bind(UserDirectory.class);
bind(UserGroupDirectory.class);
bind(UserGroupPermissionSet.class);
bind(UserPermissionSet.class);
// Bind services
@@ -159,6 +182,7 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule {
bind(ConnectionPermissionService.class);
bind(ConnectionSharingService.class);
bind(ConnectionService.class);
bind(EntityService.class);
bind(GuacamoleTunnelService.class).to(RestrictedGuacamoleTunnelService.class);
bind(PasswordEncryptionService.class).to(SHA256PasswordEncryptionService.class);
bind(PasswordPolicyService.class);
@@ -168,6 +192,8 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule {
bind(SharingProfilePermissionService.class);
bind(SharingProfileService.class);
bind(SystemPermissionService.class);
bind(UserGroupService.class);
bind(UserGroupPermissionService.class);
bind(UserPermissionService.class);
bind(UserService.class);

View File

@@ -22,6 +22,7 @@ package org.apache.guacamole.auth.jdbc;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
import org.apache.ibatis.session.SqlSession;
/**
* A JDBC-specific implementation of Environment that defines generic properties
@@ -137,4 +138,18 @@ public abstract class JDBCEnvironment extends LocalEnvironment {
*/
public abstract PasswordPolicy getPasswordPolicy();
/**
* Returns whether the database supports recursive queries. Many database
* engines support recursive queries through CTEs. If recursive queries are
* not supported, queries that are intended to be recursive may need to be
* invoked multiple times to retrieve the same data.
*
* @param session
* The SqlSession provided by MyBatis for the current transaction.
*
* @return
* true if the database supports recursive queries, false otherwise.
*/
public abstract boolean isRecursiveQuerySupported(SqlSession session);
}

View File

@@ -23,17 +23,17 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
import org.apache.guacamole.auth.jdbc.permission.AbstractPermissionService;
import org.apache.guacamole.auth.jdbc.permission.ObjectPermissionService;
import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord;
import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.net.auth.permission.ObjectPermission;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
@@ -58,32 +58,32 @@ public class ActiveConnectionPermissionService
private Provider<ActiveConnectionPermissionSet> activeConnectionPermissionSetProvider;
@Override
public ObjectPermission retrievePermission(ModeledAuthenticatedUser user,
ModeledUser targetUser, ObjectPermission.Type type,
String identifier) throws GuacamoleException {
public boolean hasPermission(ModeledAuthenticatedUser user,
ModeledPermissions<? extends EntityModel> targetEntity,
ObjectPermission.Type type, String identifier,
Set<String> effectiveGroups) throws GuacamoleException {
// Retrieve permissions
Set<ObjectPermission> permissions = retrievePermissions(user, targetUser);
Set<ObjectPermission> permissions = retrievePermissions(user,
targetEntity, effectiveGroups);
// If retrieved permissions contains the requested permission, return it
// Permission is granted if retrieved permissions contains the
// requested permission
ObjectPermission permission = new ObjectPermission(type, identifier);
if (permissions.contains(permission))
return permission;
// Otherwise, no such permission
return null;
return permissions.contains(permission);
}
@Override
public Set<ObjectPermission> retrievePermissions(ModeledAuthenticatedUser user,
ModeledUser targetUser) throws GuacamoleException {
ModeledPermissions<? extends EntityModel> targetEntity,
Set<String> effectiveGroups) throws GuacamoleException {
// Retrieve permissions only if allowed
if (canReadPermissions(user, targetUser)) {
if (canReadPermissions(user, targetEntity)) {
// Only administrators may access active connections
boolean isAdmin = targetUser.isAdministrator();
boolean isAdmin = targetEntity.isAdministrator();
// Get all active connections
Collection<ActiveConnectionRecord> records = tunnelService.getActiveConnections(user);
@@ -112,10 +112,12 @@ public class ActiveConnectionPermissionService
@Override
public Collection<String> retrieveAccessibleIdentifiers(ModeledAuthenticatedUser user,
ModeledUser targetUser, Collection<ObjectPermission.Type> permissionTypes,
Collection<String> identifiers) throws GuacamoleException {
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<ObjectPermission.Type> permissionTypes,
Collection<String> identifiers, Set<String> effectiveGroups)
throws GuacamoleException {
Set<ObjectPermission> permissions = retrievePermissions(user, targetUser);
Set<ObjectPermission> permissions = retrievePermissions(user, targetEntity, effectiveGroups);
Collection<String> accessibleObjects = new ArrayList<String>(permissions.size());
// For each identifier/permission combination
@@ -138,11 +140,12 @@ public class ActiveConnectionPermissionService
@Override
public ObjectPermissionSet getPermissionSet(ModeledAuthenticatedUser user,
ModeledUser targetUser) throws GuacamoleException {
ModeledPermissions<? extends EntityModel> targetEntity,
Set<String> effectiveGroups) throws GuacamoleException {
// Create permission set for requested user
// Create permission set for requested entity
ActiveConnectionPermissionSet permissionSet = activeConnectionPermissionSetProvider.get();
permissionSet.init(user, targetUser);
permissionSet.init(user, targetEntity, effectiveGroups);
return permissionSet;
@@ -150,7 +153,8 @@ public class ActiveConnectionPermissionService
@Override
public void createPermissions(ModeledAuthenticatedUser user,
ModeledUser targetUser, Collection<ObjectPermission> permissions)
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<ObjectPermission> permissions)
throws GuacamoleException {
// Creating active connection permissions is not implemented
@@ -160,7 +164,8 @@ public class ActiveConnectionPermissionService
@Override
public void deletePermissions(ModeledAuthenticatedUser user,
ModeledUser targetUser, Collection<ObjectPermission> permissions)
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<ObjectPermission> permissions)
throws GuacamoleException {
// Deleting active connection permissions is not implemented

View File

@@ -0,0 +1,80 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.base;
import java.util.Collection;
import java.util.Set;
import org.apache.ibatis.annotations.Param;
/**
* Mapper for entities. An entity is the base concept behind a user or user
* group, and serves as a common point for granting permissions and defining
* group membership.
*/
public interface EntityMapper {
/**
* Inserts the given entity into the database. If the entity already
* exists, this will result in an error.
*
* @param entity
* The entity to insert.
*
* @return
* The number of rows inserted.
*/
int insert(@Param("entity") EntityModel entity);
/**
* Returns the set of all group identifiers of which the given entity is a
* member, taking into account the given collection of known group
* memberships which are not necessarily defined within the database.
*
* NOTE: This query is expected to handle recursion through the membership
* graph on its own. If the database engine does not support recursive
* queries (isRecursiveQuerySupported() of JDBCEnvironment returns false),
* then this query will only return one level of depth past the effective
* groups given and will need to be invoked multiple times.
*
* @param entity
* The entity whose effective groups should be returned.
*
* @param effectiveGroups
* The identifiers of any known effective groups that should be taken
* into account, such as those defined externally to the database.
*
* @param recursive
* Whether the query should leverage database engine features to return
* absolutely all effective groups, including those inherited through
* group membership. If false, this query will return only one level of
* depth and may need to be executed multiple times. If it is known
* that the database engine in question will always support (or always
* not support) recursive queries, this parameter may be ignored.
*
* @return
* The set of identifiers of all groups that the given entity is a
* member of, including those where membership is inherited through
* membership in other groups.
*/
Set<String> selectEffectiveGroupIdentifiers(@Param("entity") EntityModel entity,
@Param("effectiveGroups") Collection<String> effectiveGroups,
@Param("recursive") boolean recursive);
}

View File

@@ -0,0 +1,113 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.base;
/**
* Base representation of a Guacamole object that can be granted permissions
* (an "entity"), such as a user or user group, as represented in the database.
* Each entity has three base properties:
*
* 1. The "entityID", which points to the common entry in the
* guacamole_entity table and is common to any type of entity.
*
* 2. The "objectID", which points to the type-specific entry for the object
* in question (ie: an entry in guacamole_user or guacamole_user_group).
*
* 3. The "identifier", which contains the unique "name" value defined for
* the entity within the guacamole_entity table.
*/
public abstract class EntityModel extends ObjectModel {
/**
* The ID of the entity entry which corresponds to this object in the
* database, if any. Note that this is distinct from the objectID,
* inherited from ObjectModel, which is specific to the actual type of
* object represented by the entity.
*/
private Integer entityID;
/**
* The type of object represented by the entity (user or user group).
*/
private EntityType type;
/**
* Creates a new, empty entity.
*/
public EntityModel() {
}
/**
* Creates a new entity of the given type which is otherwise empty.
*
* @param type
* The type to assign to the new entity.
*/
public EntityModel(EntityType type) {
this.type = type;
}
/**
* Returns the ID of the entity entry which corresponds to this object in
* the database, if it exists. Note that this is distinct from the objectID,
* inherited from ObjectModel, which is specific to the actual type of
* object represented by the entity.
*
* @return
* The ID of this entity in the database, or null if this entity was
* not retrieved from the database.
*/
public Integer getEntityID() {
return entityID;
}
/**
* Sets the ID of this entity to the given value.
*
* @param entityID
* The ID to assign to this entity.
*/
public void setEntityID(Integer entityID) {
this.entityID = entityID;
}
/**
* Returns the type of object represented by the entity. Each entity may be
* either a user or a user group.
*
* @return
* The type of object represented by the entity.
*/
public EntityType getEntityType() {
return type;
}
/**
* Sets the type of object represented by the entity. Each entity may be
* either a user or a user group.
*
* @param type
* The type of object represented by the entity.
*/
public void setEntityType(EntityType type) {
this.type = type;
}
}

View File

@@ -0,0 +1,98 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.base;
import com.google.inject.Inject;
import java.util.Collection;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.guice.transactional.Transactional;
/**
* Service which provides convenience methods for creating, retrieving, and
* manipulating entities.
*/
public class EntityService {
/**
* The Guacamole server environment.
*/
@Inject
private JDBCEnvironment environment;
/**
* Mapper for Entity model objects.
*/
@Inject
private EntityMapper entityMapper;
/**
* The current SQL session used by MyBatis.
*/
@Inject
private SqlSession sqlSession;
/**
* Returns the set of all group identifiers of which the given entity is a
* member, taking into account the given collection of known group
* memberships which are not necessarily defined within the database.
*
* Note that group visibility with respect to the queried entity is NOT
* taken into account. If the entity is a member of a group, the identifier
* of that group will be included in the returned set even if the current
* user lacks "READ" permission for that group.
*
* @param entity
* The entity whose effective groups should be returned.
*
* @param effectiveGroups
* The identifiers of any known effective groups that should be taken
* into account, such as those defined externally to the database.
*
* @return
* The set of identifiers of all groups that the given entity is a
* member of, including those where membership is inherited through
* membership in other groups.
*/
@Transactional
public Set<String> retrieveEffectiveGroups(ModeledPermissions<? extends EntityModel> entity,
Collection<String> effectiveGroups) {
// Retrieve the effective user groups of the given entity, recursively if possible
boolean recursive = environment.isRecursiveQuerySupported(sqlSession);
Set<String> identifiers = entityMapper.selectEffectiveGroupIdentifiers(entity.getModel(), effectiveGroups, recursive);
// If the set of user groups retrieved was not produced recursively,
// manually repeat the query to expand the set until all effective
// groups have been found
if (!recursive && !identifiers.isEmpty()) {
Set<String> previousIdentifiers;
do {
previousIdentifiers = identifiers;
identifiers = entityMapper.selectEffectiveGroupIdentifiers(entity.getModel(), previousIdentifiers, false);
} while (identifiers.size() > previousIdentifiers.size());
}
return identifiers;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.base;
/**
* The type of object represented by an entity. Each entity may represent
* either a user or a user group.
*/
public enum EntityType {
/**
* An individual user.
*/
USER,
/**
* A group of users and/or other groups.
*/
USER_GROUP
}

View File

@@ -53,7 +53,8 @@ public abstract class ModeledChildDirectoryObjectService<InternalType extends Mo
/**
* Returns the permission set associated with the given user and related
* to the type of objects which can be parents of the child objects handled
* by this directory object service.
* by this directory object service, taking into account permission
* inheritance via user groups.
*
* @param user
* The user whose permissions are being retrieved.
@@ -66,7 +67,7 @@ public abstract class ModeledChildDirectoryObjectService<InternalType extends Mo
* @throws GuacamoleException
* If permission to read the user's permissions is denied.
*/
protected abstract ObjectPermissionSet getParentPermissionSet(
protected abstract ObjectPermissionSet getParentEffectivePermissionSet(
ModeledAuthenticatedUser user) throws GuacamoleException;
/**
@@ -155,7 +156,7 @@ public abstract class ModeledChildDirectoryObjectService<InternalType extends Mo
Collection<String> modifiedParents = getModifiedParents(user, identifier, model);
if (!modifiedParents.isEmpty()) {
ObjectPermissionSet permissionSet = getParentPermissionSet(user);
ObjectPermissionSet permissionSet = getParentEffectivePermissionSet(user);
Collection<String> updateableParents = permissionSet.getAccessibleObjects(
Collections.singleton(ObjectPermission.Type.UPDATE),
modifiedParents

View File

@@ -57,10 +57,15 @@ public interface ModeledDirectoryObjectMapper<ModelType> {
* The user whose permissions should determine whether an identifier
* is returned.
*
* @param effectiveGroups
* The identifiers of any known effective groups that should be taken
* into account, such as those defined externally to the database.
*
* @return
* A Set containing all identifiers of all readable objects.
*/
Set<String> selectReadableIdentifiers(@Param("user") UserModel user);
Set<String> selectReadableIdentifiers(@Param("user") UserModel user,
@Param("effectiveGroups") Collection<String> effectiveGroups);
/**
* Selects all objects which have the given identifiers. If an identifier
@@ -91,11 +96,16 @@ public interface ModeledDirectoryObjectMapper<ModelType> {
* @param identifiers
* The identifiers of the objects to return.
*
* @param effectiveGroups
* The identifiers of any known effective groups that should be taken
* into account, such as those defined externally to the database.
*
* @return
* A Collection of all objects having the given identifiers.
*/
Collection<ModelType> selectReadable(@Param("user") UserModel user,
@Param("identifiers") Collection<String> identifiers);
@Param("identifiers") Collection<String> identifiers,
@Param("effectiveGroups") Collection<String> effectiveGroups);
/**
* Inserts the given object into the database. If the object already

View File

@@ -126,7 +126,8 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
/**
* Returns whether the given user has permission to create the type of
* objects that this directory object service manages.
* objects that this directory object service manages, taking into account
* permission inheritance through user groups.
*
* @param user
* The user being checked.
@@ -143,7 +144,8 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
/**
* Returns whether the given user has permission to perform a certain
* action on a specific object managed by this directory object service.
* action on a specific object managed by this directory object service,
* taking into account permission inheritance through user groups.
*
* @param user
* The user being checked.
@@ -166,7 +168,7 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
throws GuacamoleException {
// Get object permissions
ObjectPermissionSet permissionSet = getPermissionSet(user);
ObjectPermissionSet permissionSet = getEffectivePermissionSet(user);
// Return whether permission is granted
return user.getUser().isAdministrator()
@@ -176,7 +178,8 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
/**
* Returns the permission set associated with the given user and related
* to the type of objects handled by this directory object service.
* to the type of objects handled by this directory object service, taking
* into account permission inheritance via user groups.
*
* @param user
* The user whose permissions are being retrieved.
@@ -189,7 +192,7 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
* @throws GuacamoleException
* If permission to read the user's permissions is denied.
*/
protected abstract ObjectPermissionSet getPermissionSet(ModeledAuthenticatedUser user)
protected abstract ObjectPermissionSet getEffectivePermissionSet(ModeledAuthenticatedUser user)
throws GuacamoleException;
/**
@@ -398,7 +401,8 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
// Otherwise only return explicitly readable identifiers
else
objects = getObjectMapper().selectReadable(user.getUser().getModel(), identifiers);
objects = getObjectMapper().selectReadable(user.getUser().getModel(),
identifiers, user.getEffectiveUserGroups());
// Return collection of requested objects
return getObjectInstances(user, objects);
@@ -432,8 +436,7 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
// Create model which grants this permission to the current user
ObjectPermissionModel permissionModel = new ObjectPermissionModel();
permissionModel.setUserID(userModel.getObjectID());
permissionModel.setUsername(userModel.getIdentifier());
permissionModel.setEntityID(userModel.getEntityID());
permissionModel.setType(permission);
permissionModel.setObjectIdentifier(model.getIdentifier());
@@ -510,7 +513,8 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
// Otherwise only return explicitly readable identifiers
else
return getObjectMapper().selectReadableIdentifiers(user.getUser().getModel());
return getObjectMapper().selectReadableIdentifiers(user.getUser().getModel(),
user.getEffectiveUserGroups());
}

View File

@@ -0,0 +1,271 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.base;
import com.google.inject.Inject;
import java.util.Collections;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.permission.SystemPermissionService;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.activeconnection.ActiveConnectionPermissionService;
import org.apache.guacamole.auth.jdbc.permission.ConnectionGroupPermissionService;
import org.apache.guacamole.auth.jdbc.permission.ConnectionPermissionService;
import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionService;
import org.apache.guacamole.auth.jdbc.permission.UserGroupPermissionService;
import org.apache.guacamole.auth.jdbc.permission.UserPermissionService;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.net.auth.Permissions;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
import org.apache.guacamole.net.auth.permission.SystemPermission;
import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
/**
* An implementation of the base Permissions interface which is common to both
* Users and UserGroups, backed by a database model.
*
* @param <ModelType>
* The type of model object that corresponds to this object.
*/
public abstract class ModeledPermissions<ModelType extends EntityModel>
extends ModeledDirectoryObject<ModelType> implements Permissions {
/**
* Service for retrieving entity details.
*/
@Inject
private EntityService entityService;
/**
* Service for retrieving system permissions.
*/
@Inject
private SystemPermissionService systemPermissionService;
/**
* Service for retrieving connection permissions.
*/
@Inject
private ConnectionPermissionService connectionPermissionService;
/**
* Service for retrieving connection group permissions.
*/
@Inject
private ConnectionGroupPermissionService connectionGroupPermissionService;
/**
* Service for retrieving sharing profile permissions.
*/
@Inject
private SharingProfilePermissionService sharingProfilePermissionService;
/**
* Service for retrieving active connection permissions.
*/
@Inject
private ActiveConnectionPermissionService activeConnectionPermissionService;
/**
* Service for retrieving user permissions.
*/
@Inject
private UserPermissionService userPermissionService;
/**
* Service for retrieving user group permissions.
*/
@Inject
private UserGroupPermissionService userGroupPermissionService;
/**
* Returns whether the underlying entity is a user. Entities may be either
* users or user groups.
*
* @return
* true if the underlying entity is a user, false otherwise.
*/
public boolean isUser() {
return getModel().getEntityType() == EntityType.USER;
}
/**
* Returns whether the underlying entity is a user group. Entities may be
* either users or user groups.
*
* @return
* true if the underlying entity is a user group, false otherwise.
*/
public boolean isUserGroup() {
return getModel().getEntityType() == EntityType.USER_GROUP;
}
/**
* Returns whether this entity is a system administrator, and thus is not
* restricted by permissions, taking into account permission inheritance
* via user groups.
*
* @return
* true if this entity is a system administrator, false otherwise.
*
* @throws GuacamoleException
* If an error occurs while determining the entity's system administrator
* status.
*/
public boolean isAdministrator() throws GuacamoleException {
SystemPermissionSet systemPermissionSet = getEffective().getSystemPermissions();
return systemPermissionSet.hasPermission(SystemPermission.Type.ADMINISTER);
}
@Override
public SystemPermissionSet getSystemPermissions()
throws GuacamoleException {
return systemPermissionService.getPermissionSet(getCurrentUser(), this,
Collections.<String>emptySet());
}
@Override
public ObjectPermissionSet getConnectionPermissions()
throws GuacamoleException {
return connectionPermissionService.getPermissionSet(getCurrentUser(),
this, Collections.<String>emptySet());
}
@Override
public ObjectPermissionSet getConnectionGroupPermissions()
throws GuacamoleException {
return connectionGroupPermissionService.getPermissionSet(
getCurrentUser(), this, Collections.<String>emptySet());
}
@Override
public ObjectPermissionSet getSharingProfilePermissions()
throws GuacamoleException {
return sharingProfilePermissionService.getPermissionSet(
getCurrentUser(), this, Collections.<String>emptySet());
}
@Override
public ObjectPermissionSet getActiveConnectionPermissions()
throws GuacamoleException {
return activeConnectionPermissionService.getPermissionSet(
getCurrentUser(), this, Collections.<String>emptySet());
}
@Override
public ObjectPermissionSet getUserPermissions()
throws GuacamoleException {
return userPermissionService.getPermissionSet(getCurrentUser(), this,
Collections.<String>emptySet());
}
@Override
public ObjectPermissionSet getUserGroupPermissions() throws GuacamoleException {
return userGroupPermissionService.getPermissionSet(getCurrentUser(),
this, Collections.<String>emptySet());
}
/**
* Returns the identifiers of all user groups defined within the database
* which apply to this user, including any groups inherited through
* membership in yet more groups.
*
* @return
* The identifiers of all user groups defined within the database which
* apply to this user.
*/
public Set<String> getEffectiveUserGroups() {
return entityService.retrieveEffectiveGroups(this,
Collections.<String>emptySet());
}
/**
* Returns a Permissions object which represents all permissions granted to
* this entity, including any permissions inherited through group
* membership.
*
* @return
* A Permissions object which represents all permissions granted to
* this entity.
*/
public Permissions getEffective() {
final ModeledAuthenticatedUser authenticatedUser = getCurrentUser();
final Set<String> effectiveGroups;
// If this user is the currently-authenticated user, include any
// additional effective groups declared by the authentication system
if (authenticatedUser.getIdentifier().equals(getIdentifier()))
effectiveGroups = entityService.retrieveEffectiveGroups(this,
authenticatedUser.getEffectiveUserGroups());
// Otherwise, just include effective groups from the database
else
effectiveGroups = getEffectiveUserGroups();
// Return a permissions object which describes all effective
// permissions, including any permissions inherited via user groups
return new Permissions() {
@Override
public ObjectPermissionSet getActiveConnectionPermissions()
throws GuacamoleException {
return activeConnectionPermissionService.getPermissionSet(authenticatedUser, ModeledPermissions.this, effectiveGroups);
}
@Override
public ObjectPermissionSet getConnectionGroupPermissions()
throws GuacamoleException {
return connectionGroupPermissionService.getPermissionSet(authenticatedUser, ModeledPermissions.this, effectiveGroups);
}
@Override
public ObjectPermissionSet getConnectionPermissions()
throws GuacamoleException {
return connectionPermissionService.getPermissionSet(authenticatedUser, ModeledPermissions.this, effectiveGroups);
}
@Override
public ObjectPermissionSet getSharingProfilePermissions()
throws GuacamoleException {
return sharingProfilePermissionService.getPermissionSet(authenticatedUser, ModeledPermissions.this, effectiveGroups);
}
@Override
public SystemPermissionSet getSystemPermissions()
throws GuacamoleException {
return systemPermissionService.getPermissionSet(authenticatedUser, ModeledPermissions.this, effectiveGroups);
}
@Override
public ObjectPermissionSet getUserPermissions()
throws GuacamoleException {
return userPermissionService.getPermissionSet(authenticatedUser, ModeledPermissions.this, effectiveGroups);
}
@Override
public ObjectPermissionSet getUserGroupPermissions()
throws GuacamoleException {
return userGroupPermissionService.getPermissionSet(getCurrentUser(), ModeledPermissions.this, effectiveGroups);
}
};
}
}

View File

@@ -0,0 +1,126 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.base;
import java.util.Collection;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.UserModel;
import org.apache.ibatis.annotations.Param;
/**
* Mapper for the relations represented by a particular RelatedObjectSet
* implementation.
*
* @param <ParentModelType>
* The underlying database model of the object on the parent side of the
* one-to-many relationship represented by the RelatedObjectSet mapped by
* this ObjectRelationMapper.
*/
public interface ObjectRelationMapper<ParentModelType extends ObjectModel> {
/**
* Inserts rows as necessary to establish the one-to-many relationship
* represented by the RelatedObjectSet between the given parent and
* children. If the relation for any parent/child pair is already present,
* no attempt is made to insert a new row for that relation.
*
* @param parent
* The model of the object on the parent side of the one-to-many
* relationship represented by the RelatedObjectSet.
*
* @param children
* The identifiers of the objects on the child side of the one-to-many
* relationship represented by the RelatedObjectSet.
*
* @return
* The number of rows inserted.
*/
int insert(@Param("parent") ParentModelType parent,
@Param("children") Collection<String> children);
/**
* Deletes rows as necessary to modify the one-to-many relationship
* represented by the RelatedObjectSet between the given parent and
* children. If the relation for any parent/child pair does not exist,
* that specific relation is ignored, and deletion proceeds with the
* remaining relations.
*
* @param parent
* The model of the object on the parent side of the one-to-many
* relationship represented by the RelatedObjectSet.
*
* @param children
* The identifiers of the objects on the child side of the one-to-many
* relationship represented by the RelatedObjectSet.
*
* @return
* The number of rows deleted.
*/
int delete(@Param("parent") ParentModelType parent,
@Param("children") Collection<String> children);
/**
* Retrieves the identifiers of all objects on the child side of the
* one-to-many relationship represented by the RelatedObjectSet mapped by
* this ObjectRelationMapper. This should only be called on behalf of a
* system administrator. If identifiers are needed by a non-administrative
* user who must have explicit read rights, use
* selectReadableChildIdentifiers() instead.
*
* @param parent
* The model of the object on the parent side of the one-to-many
* relationship represented by the RelatedObjectSet.
*
* @return
* A Set containing the identifiers of all objects on the child side
* of the one-to-many relationship.
*/
Set<String> selectChildIdentifiers(@Param("parent") ParentModelType parent);
/**
* Retrieves the identifiers of all objects on the child side of the
* one-to-many relationship represented by the RelatedObjectSet mapped by
* this ObjectRelationMapper, including only those objects which are
* explicitly readable by the given user. If identifiers are needed by a
* system administrator (who, by definition, does not need explicit read
* rights), use selectChildIdentifiers() instead.
*
* @param user
* The user whose permissions should determine whether an identifier
* is returned.
*
* @param effectiveGroups
* The identifiers of any known effective groups that should be taken
* into account, such as those defined externally to the database.
*
* @param parent
* The model of the object on the parent side of the one-to-many
* relationship represented by the RelatedObjectSet.
*
* @return
* A Set containing the identifiers of all readable objects on the
* child side of the one-to-many relationship.
*/
Set<String> selectReadableChildIdentifiers(@Param("user") UserModel user,
@Param("effectiveGroups") Collection<String> effectiveGroups,
@Param("parent") ParentModelType parent);
}

View File

@@ -0,0 +1,211 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.base;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.net.auth.permission.ObjectPermission;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
/**
* A database implementation of RelatedObjectSet which provides access to a
* parent object and corresponding set of objects related to the parent, subject
* to object-level permissions. Though the parent and child objects have
* specific types, only the parent object's type is enforced through type
* parameters, as child objects are represented by identifiers only.
*
* @param <ParentObjectType>
* The type of object that represents the parent side of the relation.
*
* @param <ParentModelType>
* The underlying database model of the parent object.
*/
public abstract class RelatedObjectSet<ParentObjectType extends ModeledDirectoryObject<ParentModelType>, ParentModelType extends ObjectModel>
extends RestrictedObject implements org.apache.guacamole.net.auth.RelatedObjectSet {
/**
* The parent object which shares some arbitrary relation with the objects
* within this set.
*/
private ParentObjectType parent;
/**
* Creates a new RelatedObjectSet. The resulting object set must still be
* initialized by a call to init().
*/
public RelatedObjectSet() {
}
/**
* Initializes this RelatedObjectSet with the current user and the single
* object on the parent side of the one-to-many relation represented by the
* set.
*
* @param currentUser
* The user who queried this RelatedObjectSet, and whose permissions
* dictate the access level of all operations performed on this set.
*
* @param parent
* The parent object which shares some arbitrary relation with the
* objects within this set.
*/
public void init(ModeledAuthenticatedUser currentUser, ParentObjectType parent) {
super.init(currentUser);
this.parent = parent;
}
/**
* Returns the mapper which provides low-level access to the the database
* models which drive the relation represented by this RelatedObjectSet.
*
* @return
* The mapper which provides low-level access to the the database
* models which drive the relation represented by this
* RelatedObjectSet.
*/
protected abstract ObjectRelationMapper<ParentModelType> getObjectRelationMapper();
/**
* Returns the permission set which exposes the effective permissions
* available to the current user regarding the objects on the parent side
* of the one-to-many relationship represented by this RelatedObjectSet.
* Permission inheritance through user groups is taken into account.
*
* @return
* The permission set which exposes the effective permissions
* available to the current user regarding the objects on the parent
* side of the one-to-many relationship represented by this
* RelatedObjectSet.
*
* @throws GuacamoleException
* If permission to query permission status is denied.
*/
protected abstract ObjectPermissionSet getParentObjectEffectivePermissionSet()
throws GuacamoleException;
/**
* Returns the permission set which exposes the effective permissions
* available to the current user regarding the objects on the child side
* of the one-to-many relationship represented by this RelatedObjectSet.
* Permission inheritance through user groups is taken into account.
*
* @return
* The permission set which exposes the effective permissions
* available to the current user regarding the objects on the child
* side of the one-to-many relationship represented by this
* RelatedObjectSet.
*
* @throws GuacamoleException
* If permission to query permission status is denied.
*/
protected abstract ObjectPermissionSet getChildObjectEffectivePermissionSet()
throws GuacamoleException;
/**
* Returns whether the current user has permission to alter the status of
* the relation between the parent object and the given child objects.
*
* @param identifiers
* The identifiers of all objects on the child side of the one-to-many
* relation being changed.
*
* @return
* true if the user has permission to make the described changes,
* false otherwise.
*
* @throws GuacamoleException
* If permission to query permission status is denied.
*/
private boolean canAlterRelation(Collection<String> identifiers)
throws GuacamoleException {
// System administrators may alter any relations
if (getCurrentUser().getUser().isAdministrator())
return true;
// Non-admin users require UPDATE permission on the parent object ...
if (!getParentObjectEffectivePermissionSet().hasPermission(
ObjectPermission.Type.UPDATE, parent.getIdentifier()))
return false;
// ... as well as UPDATE permission on all child objects being changed
Collection<String> accessibleIdentifiers =
getChildObjectEffectivePermissionSet().getAccessibleObjects(
Collections.singleton(ObjectPermission.Type.UPDATE),
identifiers);
return accessibleIdentifiers.size() == identifiers.size();
}
@Override
public Set<String> getObjects() throws GuacamoleException {
// Bypass permission checks if the user is a system admin
ModeledAuthenticatedUser user = getCurrentUser();
if (user.getUser().isAdministrator())
return getObjectRelationMapper().selectChildIdentifiers(parent.getModel());
// Otherwise only return explicitly readable identifiers
return getObjectRelationMapper().selectReadableChildIdentifiers(
user.getUser().getModel(), user.getEffectiveUserGroups(),
parent.getModel());
}
@Override
public void addObjects(Set<String> identifiers) throws GuacamoleException {
// Nothing to do if nothing provided
if (identifiers.isEmpty())
return;
// Create relations only if permission is granted
if (canAlterRelation(identifiers))
getObjectRelationMapper().insert(parent.getModel(), identifiers);
// User lacks permission to add user groups
else
throw new GuacamoleSecurityException("Permission denied.");
}
@Override
public void removeObjects(Set<String> identifiers) throws GuacamoleException {
// Nothing to do if nothing provided
if (identifiers.isEmpty())
return;
// Delete relations only if permission is granted
if (canAlterRelation(identifiers))
getObjectRelationMapper().delete(parent.getModel(), identifiers);
// User lacks permission to remove user groups
else
throw new GuacamoleSecurityException("Permission denied.");
}
}

View File

@@ -19,6 +19,7 @@
package org.apache.guacamole.auth.jdbc.connection;
import java.util.Collection;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper;
import org.apache.guacamole.auth.jdbc.user.UserModel;
@@ -61,11 +62,18 @@ public interface ConnectionMapper extends ModeledDirectoryObjectMapper<Connectio
* The identifier of the parent connection group, or null if the root
* connection group is to be queried.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the user. If
* no groups are given, only permissions directly granted to the user
* will be used.
*
* @return
* A Set containing all identifiers of all readable objects.
*/
Set<String> selectReadableIdentifiersWithin(@Param("user") UserModel user,
@Param("parentIdentifier") String parentIdentifier);
@Param("parentIdentifier") String parentIdentifier,
@Param("effectiveGroups") Collection<String> effectiveGroups);
/**
* Selects the connection within the given parent group and having the

View File

@@ -102,12 +102,19 @@ public interface ConnectionRecordMapper {
* @param limit
* The maximum number of records that should be returned.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the user. If
* no groups are given, only permissions directly granted to the user
* will be used.
*
* @return
* The results of the search performed with the given parameters.
*/
List<ConnectionRecordModel> searchReadable(@Param("user") UserModel user,
@Param("terms") Collection<ActivityRecordSearchTerm> terms,
@Param("sortPredicates") List<ActivityRecordSortPredicate> sortPredicates,
@Param("limit") int limit);
@Param("limit") int limit,
@Param("effectiveGroups") Collection<String> effectiveGroups);
}

View File

@@ -131,26 +131,26 @@ public class ConnectionService extends ModeledChildDirectoryObjectService<Modele
throws GuacamoleException {
// Return whether user has explicit connection creation permission
SystemPermissionSet permissionSet = user.getUser().getSystemPermissions();
SystemPermissionSet permissionSet = user.getUser().getEffectivePermissions().getSystemPermissions();
return permissionSet.hasPermission(SystemPermission.Type.CREATE_CONNECTION);
}
@Override
protected ObjectPermissionSet getPermissionSet(ModeledAuthenticatedUser user)
protected ObjectPermissionSet getEffectivePermissionSet(ModeledAuthenticatedUser user)
throws GuacamoleException {
// Return permissions related to connections
return user.getUser().getConnectionPermissions();
return user.getUser().getEffectivePermissions().getConnectionPermissions();
}
@Override
protected ObjectPermissionSet getParentPermissionSet(ModeledAuthenticatedUser user)
protected ObjectPermissionSet getParentEffectivePermissionSet(ModeledAuthenticatedUser user)
throws GuacamoleException {
// Connections are contained by connection groups
return user.getUser().getConnectionGroupPermissions();
return user.getUser().getEffectivePermissions().getConnectionGroupPermissions();
}
@@ -303,7 +303,9 @@ public class ConnectionService extends ModeledChildDirectoryObjectService<Modele
// Otherwise only return explicitly readable identifiers
else
return connectionMapper.selectReadableIdentifiersWithin(user.getUser().getModel(), identifier);
return connectionMapper.selectReadableIdentifiersWithin(
user.getUser().getModel(), identifier,
user.getEffectiveUserGroups());
}
@@ -475,8 +477,9 @@ public class ConnectionService extends ModeledChildDirectoryObjectService<Modele
// Otherwise only return explicitly readable history records
else
searchResults = connectionRecordMapper.searchReadable(user.getUser().getModel(),
requiredContents, sortPredicates, limit);
searchResults = connectionRecordMapper.searchReadable(
user.getUser().getModel(), requiredContents, sortPredicates,
limit, user.getEffectiveUserGroups());
return getObjectInstances(searchResults);

View File

@@ -19,6 +19,7 @@
package org.apache.guacamole.auth.jdbc.connectiongroup;
import java.util.Collection;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper;
import org.apache.guacamole.auth.jdbc.user.UserModel;
@@ -61,11 +62,18 @@ public interface ConnectionGroupMapper extends ModeledDirectoryObjectMapper<Conn
* The identifier of the parent connection group, or null if the root
* connection group is to be queried.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the user. If
* no groups are given, only permissions directly granted to the user
* will be used.
*
* @return
* A Set containing all identifiers of all readable objects.
*/
Set<String> selectReadableIdentifiersWithin(@Param("user") UserModel user,
@Param("parentIdentifier") String parentIdentifier);
@Param("parentIdentifier") String parentIdentifier,
@Param("effectiveGroups") Collection<String> effectiveGroups);
/**
* Selects the connection group within the given parent group and having

View File

@@ -112,26 +112,26 @@ public class ConnectionGroupService extends ModeledChildDirectoryObjectService<M
throws GuacamoleException {
// Return whether user has explicit connection group creation permission
SystemPermissionSet permissionSet = user.getUser().getSystemPermissions();
SystemPermissionSet permissionSet = user.getUser().getEffectivePermissions().getSystemPermissions();
return permissionSet.hasPermission(SystemPermission.Type.CREATE_CONNECTION_GROUP);
}
@Override
protected ObjectPermissionSet getPermissionSet(ModeledAuthenticatedUser user)
protected ObjectPermissionSet getEffectivePermissionSet(ModeledAuthenticatedUser user)
throws GuacamoleException {
// Return permissions related to connection groups
return user.getUser().getConnectionGroupPermissions();
return user.getUser().getEffectivePermissions().getConnectionGroupPermissions();
}
@Override
protected ObjectPermissionSet getParentPermissionSet(ModeledAuthenticatedUser user)
protected ObjectPermissionSet getParentEffectivePermissionSet(ModeledAuthenticatedUser user)
throws GuacamoleException {
// Connection groups are contained by other connection groups
return user.getUser().getConnectionGroupPermissions();
return user.getUser().getEffectivePermissions().getConnectionGroupPermissions();
}
@@ -223,7 +223,9 @@ public class ConnectionGroupService extends ModeledChildDirectoryObjectService<M
// Otherwise only return explicitly readable identifiers
else
return connectionGroupMapper.selectReadableIdentifiersWithin(user.getUser().getModel(), identifier);
return connectionGroupMapper.selectReadableIdentifiersWithin(
user.getUser().getModel(), identifier,
user.getEffectiveUserGroups());
}

View File

@@ -20,8 +20,10 @@
package org.apache.guacamole.auth.jdbc.permission;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.net.auth.permission.ObjectPermission;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
import org.apache.guacamole.net.auth.permission.Permission;
@@ -41,17 +43,51 @@ public abstract class AbstractPermissionService<PermissionSetType extends Permis
PermissionType extends Permission>
implements PermissionService<PermissionSetType, PermissionType> {
/**
* Returns the ObjectPermissionSet related to the type of the given entity.
* If the given entity represents a user, then the ObjectPermissionSet
* containing user permissions is returned. If the given entity represents
* a user group, then the ObjectPermissionSet containing user group
* permissions is returned.
*
* @param user
* The user to retrieve the ObjectPermissionSet from.
*
* @param targetEntity
* The entity whose type dictates the ObjectPermissionSet returned.
*
* @return
* The ObjectPermissionSet related to the type of the given entity.
*
* @throws GuacamoleException
* If the relevant ObjectPermissionSet cannot be retrieved.
*/
protected ObjectPermissionSet getRelevantPermissionSet(ModeledUser user,
ModeledPermissions<? extends EntityModel> targetEntity)
throws GuacamoleException {
if (targetEntity.isUser())
return user.getUserPermissions();
if (targetEntity.isUserGroup())
return user.getUserGroupPermissions();
// Entities should be only users or groups
throw new UnsupportedOperationException("Unexpected entity type.");
}
/**
* Determines whether the given user can read the permissions currently
* granted to the given target user. If the reading user and the target
* user are not the same, then explicit READ or SYSTEM_ADMINISTER access is
* required.
* granted to the given target entity. If the reading user and the target
* entity are not the same, then explicit READ or SYSTEM_ADMINISTER access
* is required. Permission inheritance via user groups is taken into account.
*
* @param user
* The user attempting to read permissions.
*
* @param targetUser
* The user whose permissions are being read.
* @param targetEntity
* The entity whose permissions are being read.
*
* @return
* true if permission is granted, false otherwise.
@@ -61,19 +97,20 @@ public abstract class AbstractPermissionService<PermissionSetType extends Permis
* permission is denied to read the current user's permissions.
*/
protected boolean canReadPermissions(ModeledAuthenticatedUser user,
ModeledUser targetUser) throws GuacamoleException {
ModeledPermissions<? extends EntityModel> targetEntity)
throws GuacamoleException {
// A user can always read their own permissions
if (user.getUser().getIdentifier().equals(targetUser.getIdentifier()))
if (targetEntity.isUser() && user.getUser().getIdentifier().equals(targetEntity.getIdentifier()))
return true;
// A system adminstrator can do anything
if (user.getUser().isAdministrator())
return true;
// Can read permissions on target user if explicit READ is granted
ObjectPermissionSet userPermissionSet = user.getUser().getUserPermissions();
return userPermissionSet.hasPermission(ObjectPermission.Type.READ, targetUser.getIdentifier());
// Can read permissions on target entity if explicit READ is granted
ObjectPermissionSet permissionSet = getRelevantPermissionSet(user.getUser(), targetEntity);
return permissionSet.hasPermission(ObjectPermission.Type.READ, targetEntity.getIdentifier());
}

View File

@@ -21,9 +21,11 @@ package org.apache.guacamole.auth.jdbc.permission;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
/**
* Service which provides convenience methods for creating, retrieving, and
@@ -51,11 +53,12 @@ public class ConnectionGroupPermissionService extends ModeledObjectPermissionSer
@Override
public ObjectPermissionSet getPermissionSet(ModeledAuthenticatedUser user,
ModeledUser targetUser) throws GuacamoleException {
ModeledPermissions<? extends EntityModel> targetEntity,
Set<String> effectiveGroups) throws GuacamoleException {
// Create permission set for requested user
// Create permission set for requested entity
ObjectPermissionSet permissionSet = connectionGroupPermissionSetProvider.get();
permissionSet.init(user, targetUser);
permissionSet.init(user, targetEntity, effectiveGroups);
return permissionSet;

View File

@@ -21,9 +21,11 @@ package org.apache.guacamole.auth.jdbc.permission;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
/**
* Service which provides convenience methods for creating, retrieving, and
@@ -51,11 +53,12 @@ public class ConnectionPermissionService extends ModeledObjectPermissionService
@Override
public ObjectPermissionSet getPermissionSet(ModeledAuthenticatedUser user,
ModeledUser targetUser) throws GuacamoleException {
ModeledPermissions<? extends EntityModel> targetEntity,
Set<String> effectiveGroups) throws GuacamoleException {
// Create permission set for requested user
// Create permission set for requested entity
ObjectPermissionSet permissionSet = connectionPermissionSetProvider.get();
permissionSet.init(user, targetUser);
permissionSet.init(user, targetEntity, effectiveGroups);
return permissionSet;

View File

@@ -22,10 +22,12 @@ package org.apache.guacamole.auth.jdbc.permission;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
import org.apache.guacamole.net.auth.permission.ObjectPermission;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
@@ -47,14 +49,14 @@ public abstract class ModeledObjectPermissionService
}
@Override
protected ObjectPermissionModel getModelInstance(ModeledUser targetUser,
protected ObjectPermissionModel getModelInstance(
ModeledPermissions<? extends EntityModel> targetEntity,
ObjectPermission permission) {
ObjectPermissionModel model = new ObjectPermissionModel();
// Populate model object with data from user and permission
model.setUserID(targetUser.getModel().getObjectID());
model.setUsername(targetUser.getModel().getIdentifier());
// Populate model object with data from entity and permission
model.setEntityID(targetEntity.getModel().getEntityID());
model.setType(permission.getType());
model.setObjectIdentifier(permission.getObjectIdentifier());
@@ -64,30 +66,32 @@ public abstract class ModeledObjectPermissionService
/**
* Determines whether the current user has permission to update the given
* target user, adding or removing the given permissions. Such permission
* target entity, adding or removing the given permissions. Such permission
* depends on whether the current user is a system administrator, whether
* they have explicit UPDATE permission on the target user, and whether
* they have explicit UPDATE permission on the target entity, and whether
* they have explicit ADMINISTER permission on all affected objects.
* Permission inheritance via user groups is taken into account.
*
* @param user
* The user who is changing permissions.
*
* @param targetUser
* The user whose permissions are being changed.
* @param targetEntity
* The entity whose permissions are being changed.
*
* @param permissions
* The permissions that are being added or removed from the target
* user.
* entity.
*
* @return
* true if the user has permission to change the target users
* true if the user has permission to change the target entity's
* permissions as specified, false otherwise.
*
* @throws GuacamoleException
* If an error occurs while checking permission status, or if
* permission is denied to read the current user's permissions.
*/
protected boolean canAlterPermissions(ModeledAuthenticatedUser user, ModeledUser targetUser,
protected boolean canAlterPermissions(ModeledAuthenticatedUser user,
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<ObjectPermission> permissions)
throws GuacamoleException {
@@ -95,9 +99,9 @@ public abstract class ModeledObjectPermissionService
if (user.getUser().isAdministrator())
return true;
// Verify user has update permission on the target user
ObjectPermissionSet userPermissionSet = user.getUser().getUserPermissions();
if (!userPermissionSet.hasPermission(ObjectPermission.Type.UPDATE, targetUser.getIdentifier()))
// Verify user has update permission on the target entity
ObjectPermissionSet permissionSet = getRelevantPermissionSet(user.getUser(), targetEntity);
if (!permissionSet.hasPermission(ObjectPermission.Type.UPDATE, targetEntity.getIdentifier()))
return false;
// Produce collection of affected identifiers
@@ -106,7 +110,7 @@ public abstract class ModeledObjectPermissionService
affectedIdentifiers.add(permission.getObjectIdentifier());
// Determine subset of affected identifiers that we have admin access to
ObjectPermissionSet affectedPermissionSet = getPermissionSet(user, user.getUser());
ObjectPermissionSet affectedPermissionSet = getPermissionSet(user, user.getUser(), user.getEffectiveUserGroups());
Collection<String> allowedSubset = affectedPermissionSet.getAccessibleObjects(
Collections.singleton(ObjectPermission.Type.ADMINISTER),
affectedIdentifiers
@@ -121,13 +125,14 @@ public abstract class ModeledObjectPermissionService
}
@Override
public void createPermissions(ModeledAuthenticatedUser user, ModeledUser targetUser,
public void createPermissions(ModeledAuthenticatedUser user,
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<ObjectPermission> permissions)
throws GuacamoleException {
// Create permissions only if user has permission to do so
if (canAlterPermissions(user, targetUser, permissions)) {
Collection<ObjectPermissionModel> models = getModelInstances(targetUser, permissions);
if (canAlterPermissions(user, targetEntity, permissions)) {
Collection<ObjectPermissionModel> models = getModelInstances(targetEntity, permissions);
getPermissionMapper().insert(models);
return;
}
@@ -138,13 +143,14 @@ public abstract class ModeledObjectPermissionService
}
@Override
public void deletePermissions(ModeledAuthenticatedUser user, ModeledUser targetUser,
public void deletePermissions(ModeledAuthenticatedUser user,
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<ObjectPermission> permissions)
throws GuacamoleException {
// Delete permissions only if user has permission to do so
if (canAlterPermissions(user, targetUser, permissions)) {
Collection<ObjectPermissionModel> models = getModelInstances(targetUser, permissions);
if (canAlterPermissions(user, targetEntity, permissions)) {
Collection<ObjectPermissionModel> models = getModelInstances(targetEntity, permissions);
getPermissionMapper().delete(models);
return;
}
@@ -155,49 +161,43 @@ public abstract class ModeledObjectPermissionService
}
@Override
public ObjectPermission retrievePermission(ModeledAuthenticatedUser user,
ModeledUser targetUser, ObjectPermission.Type type,
String identifier) throws GuacamoleException {
public boolean hasPermission(ModeledAuthenticatedUser user,
ModeledPermissions<? extends EntityModel> targetEntity,
ObjectPermission.Type type, String identifier,
Set<String> effectiveGroups) throws GuacamoleException {
// Retrieve permissions only if allowed
if (canReadPermissions(user, targetUser)) {
if (canReadPermissions(user, targetEntity))
return getPermissionMapper().selectOne(targetEntity.getModel(),
type, identifier, effectiveGroups) != null;
// Read permission from database, return null if not found
ObjectPermissionModel model = getPermissionMapper().selectOne(targetUser.getModel(), type, identifier);
if (model == null)
return null;
return getPermissionInstance(model);
}
// User cannot read this user's permissions
// User cannot read this entity's permissions
throw new GuacamoleSecurityException("Permission denied.");
}
@Override
public Collection<String> retrieveAccessibleIdentifiers(ModeledAuthenticatedUser user,
ModeledUser targetUser, Collection<ObjectPermission.Type> permissions,
Collection<String> identifiers) throws GuacamoleException {
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<ObjectPermission.Type> permissions,
Collection<String> identifiers, Set<String> effectiveGroups)
throws GuacamoleException {
// Nothing is always accessible
if (identifiers.isEmpty())
return identifiers;
// Retrieve permissions only if allowed
if (canReadPermissions(user, targetUser)) {
// If user is an admin, everything is accessible
if (user.getUser().isAdministrator())
return identifiers;
// If user is an admin, everything is accessible
if (user.getUser().isAdministrator())
return identifiers;
// Otherwise, return explicitly-retrievable identifiers only if allowed
if (canReadPermissions(user, targetEntity))
return getPermissionMapper().selectAccessibleIdentifiers(
targetEntity.getModel(), permissions, identifiers,
effectiveGroups);
// Otherwise, return explicitly-retrievable identifiers
return getPermissionMapper().selectAccessibleIdentifiers(targetUser.getModel(), permissions, identifiers);
}
// User cannot read this user's permissions
// User cannot read this entity's permissions
throw new GuacamoleSecurityException("Permission denied.");
}

View File

@@ -24,9 +24,10 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
import org.apache.guacamole.net.auth.permission.Permission;
import org.apache.guacamole.net.auth.permission.PermissionSet;
@@ -92,47 +93,49 @@ public abstract class ModeledPermissionService<PermissionSetType extends Permiss
permissions.add(getPermissionInstance(model));
return permissions;
}
/**
* Returns an instance of a model object which is based on the given
* permission and target user.
* permission and target entity.
*
* @param targetUser
* The user to whom this permission is granted.
* @param targetEntity
* The entity to whom this permission is granted.
*
* @param permission
* The permission to use to produce the returned model object.
*
* @return
* A model object which is based on the given permission and target
* user.
* entity.
*/
protected abstract ModelType getModelInstance(ModeledUser targetUser,
protected abstract ModelType getModelInstance(
ModeledPermissions<? extends EntityModel> targetEntity,
PermissionType permission);
/**
* Returns a collection of model objects which are based on the given
* permissions and target user.
* permissions and target entity.
*
* @param targetUser
* The user to whom this permission is granted.
* @param targetEntity
* The entity to whom this permission is granted.
*
* @param permissions
* The permissions to use to produce the returned model objects.
*
* @return
* A collection of model objects which are based on the given
* permissions and target user.
* permissions and target entity.
*/
protected Collection<ModelType> getModelInstances(ModeledUser targetUser,
protected Collection<ModelType> getModelInstances(
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<PermissionType> permissions) {
// Create new collection of models by manually converting each permission
// Create new collection of models by manually converting each permission
Collection<ModelType> models = new ArrayList<ModelType>(permissions.size());
for (PermissionType permission : permissions)
models.add(getModelInstance(targetUser, permission));
models.add(getModelInstance(targetEntity, permission));
return models;
@@ -140,15 +143,16 @@ public abstract class ModeledPermissionService<PermissionSetType extends Permiss
@Override
public Set<PermissionType> retrievePermissions(ModeledAuthenticatedUser user,
ModeledUser targetUser) throws GuacamoleException {
ModeledPermissions<? extends EntityModel> targetEntity,
Set<String> effectiveGroups) throws GuacamoleException {
// Retrieve permissions only if allowed
if (canReadPermissions(user, targetUser))
return getPermissionInstances(getPermissionMapper().select(targetUser.getModel()));
if (canReadPermissions(user, targetEntity))
return getPermissionInstances(getPermissionMapper().select(targetEntity.getModel(), effectiveGroups));
// User cannot read this user's permissions
// User cannot read this entity's permissions
throw new GuacamoleSecurityException("Permission denied.");
}
}

View File

@@ -20,8 +20,8 @@
package org.apache.guacamole.auth.jdbc.permission;
import java.util.Collection;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.ibatis.annotations.Param;
import org.apache.guacamole.auth.jdbc.user.UserModel;
import org.apache.guacamole.net.auth.permission.ObjectPermission;
/**
@@ -31,32 +31,39 @@ public interface ObjectPermissionMapper extends PermissionMapper<ObjectPermissio
/**
* Retrieve the permission of the given type associated with the given
* user and object, if it exists. If no such permission exists, null is
* entity and object, if it exists. If no such permission exists, null is
* returned.
*
* @param user
* The user to retrieve permissions for.
*
* @param entity
* The entity to retrieve permissions for.
*
* @param type
* The type of permission to return.
*
*
* @param identifier
* The identifier of the object affected by the permission to return.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the user. If
* no groups are given, only permissions directly granted to the user
* will be used.
*
* @return
* The requested permission, or null if no such permission is granted
* to the given user for the given object.
* to the given entity for the given object.
*/
ObjectPermissionModel selectOne(@Param("user") UserModel user,
ObjectPermissionModel selectOne(@Param("entity") EntityModel entity,
@Param("type") ObjectPermission.Type type,
@Param("identifier") String identifier);
@Param("identifier") String identifier,
@Param("effectiveGroups") Collection<String> effectiveGroups);
/**
* Retrieves the subset of the given identifiers for which the given user
* Retrieves the subset of the given identifiers for which the given entity
* has at least one of the given permissions.
*
* @param user
* The user to check permissions of.
* @param entity
* The entity to check permissions of.
*
* @param permissions
* The permissions to check. An identifier will be included in the
@@ -67,12 +74,19 @@ public interface ObjectPermissionMapper extends PermissionMapper<ObjectPermissio
* The identifiers of the objects affected by the permissions being
* checked.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the user. If
* no groups are given, only permissions directly granted to the user
* will be used.
*
* @return
* A collection containing the subset of identifiers for which at least
* one of the specified permissions is granted.
*/
Collection<String> selectAccessibleIdentifiers(@Param("user") UserModel user,
Collection<String> selectAccessibleIdentifiers(@Param("entity") EntityModel entity,
@Param("permissions") Collection<ObjectPermission.Type> permissions,
@Param("identifiers") Collection<String> identifiers);
@Param("identifiers") Collection<String> identifiers,
@Param("effectiveGroups") Collection<String> effectiveGroups);
}

View File

@@ -20,9 +20,11 @@
package org.apache.guacamole.auth.jdbc.permission;
import java.util.Collection;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
import org.apache.guacamole.net.auth.permission.ObjectPermission;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
@@ -35,41 +37,48 @@ public interface ObjectPermissionService
extends PermissionService<ObjectPermissionSet, ObjectPermission> {
/**
* Retrieves the permission of the given type associated with the given
* user and object, if it exists. If no such permission exists, null is
* Returns whether the permission of the given type and associated with the
* given object has been granted to the given entity.
*
* @param user
* The user retrieving the permission.
*
* @param targetUser
* The user associated with the permission to be retrieved.
*
* @param targetEntity
* The entity associated with the permission to be retrieved.
*
* @param type
* The type of permission to retrieve.
*
* @param identifier
* The identifier of the object affected by the permission to return.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the entity.
* If no groups are given, only permissions directly granted to the
* entity will be used.
*
* @return
* The permission of the given type associated with the given user and
* object, or null if no such permission exists.
* true if permission of the given type and associated with the given
* object has been granted to the given entity, false otherwise.
*
* @throws GuacamoleException
* If an error occurs while retrieving the requested permission.
*/
ObjectPermission retrievePermission(ModeledAuthenticatedUser user,
ModeledUser targetUser, ObjectPermission.Type type,
String identifier) throws GuacamoleException;
boolean hasPermission(ModeledAuthenticatedUser user,
ModeledPermissions<? extends EntityModel> targetEntity,
ObjectPermission.Type type, String identifier,
Set<String> effectiveGroups) throws GuacamoleException;
/**
* Retrieves the subset of the given identifiers for which the given user
* Retrieves the subset of the given identifiers for which the given entity
* has at least one of the given permissions.
*
* @param user
* The user checking the permissions.
*
* @param targetUser
* The user to check permissions of.
* @param targetEntity
* The entity to check permissions of.
*
* @param permissions
* The permissions to check. An identifier will be included in the
@@ -80,6 +89,12 @@ public interface ObjectPermissionService
* The identifiers of the objects affected by the permissions being
* checked.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the entity.
* If no groups are given, only permissions directly granted to the
* entity will be used.
*
* @return
* A collection containing the subset of identifiers for which at least
* one of the specified permissions is granted.
@@ -88,7 +103,9 @@ public interface ObjectPermissionService
* If an error occurs while retrieving permissions.
*/
Collection<String> retrieveAccessibleIdentifiers(ModeledAuthenticatedUser user,
ModeledUser targetUser, Collection<ObjectPermission.Type> permissions,
Collection<String> identifiers) throws GuacamoleException;
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<ObjectPermission.Type> permissions,
Collection<String> identifiers, Set<String> effectiveGroups)
throws GuacamoleException;
}

View File

@@ -19,28 +19,35 @@
package org.apache.guacamole.auth.jdbc.permission;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
import org.apache.guacamole.auth.jdbc.base.RestrictedObject;
import org.apache.guacamole.net.auth.permission.ObjectPermission;
/**
* A database implementation of ObjectPermissionSet which uses an injected
* service to query and manipulate the object-level permissions associated with
* a particular user.
* a particular entity.
*/
public abstract class ObjectPermissionSet extends RestrictedObject
implements org.apache.guacamole.net.auth.permission.ObjectPermissionSet {
/**
* The user associated with this permission set. Each of the permissions in
* this permission set is granted to this user.
* The entity associated with this permission set. Each of the permissions
* in this permission set is granted to this entity.
*/
private ModeledUser user;
private ModeledPermissions<? extends EntityModel> entity;
/**
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the entity.
*/
private Set<String> effectiveGroups;
/**
* Creates a new ObjectPermissionSet. The resulting permission set
@@ -51,19 +58,28 @@ public abstract class ObjectPermissionSet extends RestrictedObject
}
/**
* Initializes this permission set with the current user and the user
* Initializes this permission set with the current user and the entity
* to whom the permissions in this set are granted.
*
* @param currentUser
* The user who queried this permission set, and whose permissions
* dictate the access level of all operations performed on this set.
*
* @param user
* The user to whom the permissions in this set are granted.
* @param entity
* The entity to whom the permissions in this set are granted.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the entity.
* If no groups are given, only permissions directly granted to the
* entity will be used.
*/
public void init(ModeledAuthenticatedUser currentUser, ModeledUser user) {
public void init(ModeledAuthenticatedUser currentUser,
ModeledPermissions<? extends EntityModel> entity,
Set<String> effectiveGroups) {
super.init(currentUser);
this.user = user;
this.entity = entity;
this.effectiveGroups = effectiveGroups;
}
/**
@@ -75,16 +91,16 @@ public abstract class ObjectPermissionSet extends RestrictedObject
* permissions contained within this permission set.
*/
protected abstract ObjectPermissionService getObjectPermissionService();
@Override
public Set<ObjectPermission> getPermissions() throws GuacamoleException {
return getObjectPermissionService().retrievePermissions(getCurrentUser(), user);
return getObjectPermissionService().retrievePermissions(getCurrentUser(), entity, effectiveGroups);
}
@Override
public boolean hasPermission(ObjectPermission.Type permission,
String identifier) throws GuacamoleException {
return getObjectPermissionService().retrievePermission(getCurrentUser(), user, permission, identifier) != null;
return getObjectPermissionService().hasPermission(getCurrentUser(), entity, permission, identifier, effectiveGroups);
}
@Override
@@ -102,19 +118,19 @@ public abstract class ObjectPermissionSet extends RestrictedObject
@Override
public Collection<String> getAccessibleObjects(Collection<ObjectPermission.Type> permissions,
Collection<String> identifiers) throws GuacamoleException {
return getObjectPermissionService().retrieveAccessibleIdentifiers(getCurrentUser(), user, permissions, identifiers);
return getObjectPermissionService().retrieveAccessibleIdentifiers(getCurrentUser(), entity, permissions, identifiers, effectiveGroups);
}
@Override
public void addPermissions(Set<ObjectPermission> permissions)
throws GuacamoleException {
getObjectPermissionService().createPermissions(getCurrentUser(), user, permissions);
getObjectPermissionService().createPermissions(getCurrentUser(), entity, permissions);
}
@Override
public void removePermissions(Set<ObjectPermission> permissions)
throws GuacamoleException {
getObjectPermissionService().deletePermissions(getCurrentUser(), user, permissions);
getObjectPermissionService().deletePermissions(getCurrentUser(), entity, permissions);
}
}

View File

@@ -20,7 +20,7 @@
package org.apache.guacamole.auth.jdbc.permission;
import java.util.Collection;
import org.apache.guacamole.auth.jdbc.user.UserModel;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.ibatis.annotations.Param;
/**
@@ -32,15 +32,23 @@ import org.apache.ibatis.annotations.Param;
public interface PermissionMapper<PermissionType> {
/**
* Retrieves all permissions associated with the given user.
* Retrieves all permissions associated with the given entity (user or user
* group).
*
* @param user
* The user to retrieve permissions for.
* @param entity
* The entity to retrieve permissions for.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the user. If
* no groups are given, only permissions directly granted to the user
* will be used.
*
* @return
* All permissions associated with the given user.
* All permissions associated with the given entity.
*/
Collection<PermissionType> select(@Param("user") UserModel user);
Collection<PermissionType> select(@Param("entity") EntityModel entity,
@Param("effectiveGroups") Collection<String> effectiveGroups);
/**
* Inserts the given permissions into the database. If any permissions

View File

@@ -21,7 +21,7 @@ package org.apache.guacamole.auth.jdbc.permission;
/**
* Generic base permission model which grants a permission of a particular type
* to a specific user.
* to a specific entity (user or user group).
*
* @param <PermissionType>
* The type of permissions allowed within this model.
@@ -29,14 +29,9 @@ package org.apache.guacamole.auth.jdbc.permission;
public abstract class PermissionModel<PermissionType> {
/**
* The database ID of the user to whom this permission is granted.
* The database ID of the entity to whom this permission is granted.
*/
private Integer userID;
/**
* The username of the user to whom this permission is granted.
*/
private String username;
private Integer entityID;
/**
* The type of action granted by this permission.
@@ -44,43 +39,24 @@ public abstract class PermissionModel<PermissionType> {
private PermissionType type;
/**
* Returns the database ID of the user to whom this permission is granted.
* Returns the database ID of the entity to whom this permission is
* granted.
*
* @return
* The database ID of the user to whom this permission is granted.
* The database ID of the entity to whom this permission is granted.
*/
public Integer getUserID() {
return userID;
public Integer getEntityID() {
return entityID;
}
/**
* Sets the database ID of the user to whom this permission is granted.
* Sets the database ID of the entity to whom this permission is granted.
*
* @param userID
* The database ID of the user to whom this permission is granted.
* @param entityID
* The database ID of the entity to whom this permission is granted.
*/
public void setUserID(Integer userID) {
this.userID = userID;
}
/**
* Returns the username of the user to whom this permission is granted.
*
* @return
* The username of the user to whom this permission is granted.
*/
public String getUsername() {
return username;
}
/**
* Sets the username of the user to whom this permission is granted.
*
* @param username
* The username of the user to whom this permission is granted.
*/
public void setUsername(String username) {
this.username = username;
public void setEntityID(Integer entityID) {
this.entityID = entityID;
}
/**

View File

@@ -19,16 +19,12 @@
package org.apache.guacamole.auth.jdbc.permission;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.net.auth.permission.ObjectPermission;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
import org.apache.guacamole.net.auth.permission.Permission;
import org.apache.guacamole.net.auth.permission.PermissionSet;
@@ -49,45 +45,60 @@ public interface PermissionService<PermissionSetType extends PermissionSet<Permi
/**
* Returns a permission set that can be used to retrieve and manipulate the
* permissions of the given user.
* permissions of the given entity.
*
* @param user
* The user who will be retrieving or manipulating permissions through
* the returned permission set.
*
* @param targetUser
* The user to whom the permissions in the returned permission set are
* @param targetEntity
* The entity to whom the permissions in the returned permission set are
* granted.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the entity.
* If no groups are given, only permissions directly granted to the
* entity will be used.
*
* @return
* A permission set that contains all permissions associated with the
* given user, and can be used to manipulate that user's permissions.
* given entity, and can be used to manipulate that entity's
* permissions.
*
* @throws GuacamoleException
* If an error occurs while retrieving the permissions of the given
* user, or if permission to retrieve the permissions of the given
* user is denied.
* entity, or if permission to retrieve the permissions of the given
* entity is denied.
*/
PermissionSetType getPermissionSet(ModeledAuthenticatedUser user,
ModeledUser targetUser) throws GuacamoleException;
ModeledPermissions<? extends EntityModel> targetEntity,
Set<String> effectiveGroups) throws GuacamoleException;
/**
* Retrieves all permissions associated with the given user.
* Retrieves all permissions associated with the given entity.
*
* @param user
* The user retrieving the permissions.
*
* @param targetUser
* The user associated with the permissions to be retrieved.
* @param targetEntity
* The entity associated with the permissions to be retrieved.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the entity.
* If no groups are given, only permissions directly granted to the
* entity will be used.
*
* @return
* The permissions associated with the given user.
* The permissions associated with the given entity.
*
* @throws GuacamoleException
* If an error occurs while retrieving the requested permissions.
*/
Set<PermissionType> retrievePermissions(ModeledAuthenticatedUser user,
ModeledUser targetUser) throws GuacamoleException;
ModeledPermissions<? extends EntityModel> targetEntity,
Set<String> effectiveGroups) throws GuacamoleException;
/**
* Creates the given permissions within the database. If any permissions
@@ -96,8 +107,8 @@ public interface PermissionService<PermissionSetType extends PermissionSet<Permi
* @param user
* The user creating the permissions.
*
* @param targetUser
* The user associated with the permissions to be created.
* @param targetEntity
* The entity associated with the permissions to be created.
*
* @param permissions
* The permissions to create.
@@ -106,8 +117,10 @@ public interface PermissionService<PermissionSetType extends PermissionSet<Permi
* If the user lacks permission to create the permissions, or an error
* occurs while creating the permissions.
*/
void createPermissions(ModeledAuthenticatedUser user, ModeledUser targetUser,
Collection<PermissionType> permissions) throws GuacamoleException;
void createPermissions(ModeledAuthenticatedUser user,
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<PermissionType> permissions)
throws GuacamoleException;
/**
* Deletes the given permissions. If any permissions do not exist, they
@@ -116,8 +129,8 @@ public interface PermissionService<PermissionSetType extends PermissionSet<Permi
* @param user
* The user deleting the permissions.
*
* @param targetUser
* The user associated with the permissions to be deleted.
* @param targetEntity
* The entity associated with the permissions to be deleted.
*
* @param permissions
* The permissions to delete.
@@ -126,7 +139,9 @@ public interface PermissionService<PermissionSetType extends PermissionSet<Permi
* If the user lacks permission to delete the permissions, or an error
* occurs while deleting the permissions.
*/
void deletePermissions(ModeledAuthenticatedUser user, ModeledUser targetUser,
Collection<PermissionType> permissions) throws GuacamoleException;
void deletePermissions(ModeledAuthenticatedUser user,
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<PermissionType> permissions)
throws GuacamoleException;
}

View File

@@ -21,9 +21,11 @@ package org.apache.guacamole.auth.jdbc.permission;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
/**
* Service which provides convenience methods for creating, retrieving, and
@@ -51,11 +53,12 @@ public class SharingProfilePermissionService extends ModeledObjectPermissionServ
@Override
public ObjectPermissionSet getPermissionSet(ModeledAuthenticatedUser user,
ModeledUser targetUser) throws GuacamoleException {
ModeledPermissions<? extends EntityModel> targetEntity,
Set<String> effectiveGroups) throws GuacamoleException {
// Create permission set for requested user
// Create permission set for requested entity
ObjectPermissionSet permissionSet = sharingProfilePermissionSetProvider.get();
permissionSet.init(user, targetUser);
permissionSet.init(user, targetEntity, effectiveGroups);
return permissionSet;

View File

@@ -19,7 +19,8 @@
package org.apache.guacamole.auth.jdbc.permission;
import org.apache.guacamole.auth.jdbc.user.UserModel;
import java.util.Collection;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.ibatis.annotations.Param;
import org.apache.guacamole.net.auth.permission.SystemPermission;
@@ -30,19 +31,26 @@ public interface SystemPermissionMapper extends PermissionMapper<SystemPermissio
/**
* Retrieve the permission of the given type associated with the given
* user, if it exists. If no such permission exists, null is returned.
* entity, if it exists. If no such permission exists, null is returned.
*
* @param entity
* The entity to retrieve permissions for.
*
* @param user
* The user to retrieve permissions for.
*
* @param type
* The type of permission to return.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the user. If
* no groups are given, only permissions directly granted to the user
* will be used.
*
* @return
* The requested permission, or null if no such permission is granted
* to the given user.
* to the given entity.
*/
SystemPermissionModel selectOne(@Param("user") UserModel user,
@Param("type") SystemPermission.Type type);
SystemPermissionModel selectOne(@Param("entity") EntityModel entity,
@Param("type") SystemPermission.Type type,
@Param("effectiveGroups") Collection<String> effectiveGroups);
}

View File

@@ -22,11 +22,13 @@ package org.apache.guacamole.auth.jdbc.permission;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Collection;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.GuacamoleUnsupportedException;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
import org.apache.guacamole.net.auth.permission.SystemPermission;
/**
@@ -60,14 +62,14 @@ public class SystemPermissionService
}
@Override
protected SystemPermissionModel getModelInstance(final ModeledUser targetUser,
protected SystemPermissionModel getModelInstance(
final ModeledPermissions<? extends EntityModel> targetEntity,
final SystemPermission permission) {
SystemPermissionModel model = new SystemPermissionModel();
// Populate model object with data from user and permission
model.setUserID(targetUser.getModel().getObjectID());
model.setUsername(targetUser.getModel().getIdentifier());
// Populate model object with data from entity and permission
model.setEntityID(targetEntity.getModel().getEntityID());
model.setType(permission.getType());
return model;
@@ -76,23 +78,25 @@ public class SystemPermissionService
@Override
public SystemPermissionSet getPermissionSet(ModeledAuthenticatedUser user,
ModeledUser targetUser) throws GuacamoleException {
ModeledPermissions<? extends EntityModel> targetEntity,
Set<String> effectiveGroups) throws GuacamoleException {
// Create permission set for requested user
// Create permission set for requested entity
SystemPermissionSet permissionSet = systemPermissionSetProvider.get();
permissionSet.init(user, targetUser);
permissionSet.init(user, targetEntity, effectiveGroups);
return permissionSet;
}
@Override
public void createPermissions(ModeledAuthenticatedUser user, ModeledUser targetUser,
public void createPermissions(ModeledAuthenticatedUser user,
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<SystemPermission> permissions) throws GuacamoleException {
// Only an admin can create system permissions
if (user.getUser().isAdministrator()) {
Collection<SystemPermissionModel> models = getModelInstances(targetUser, permissions);
Collection<SystemPermissionModel> models = getModelInstances(targetEntity, permissions);
systemPermissionMapper.insert(models);
return;
}
@@ -103,17 +107,18 @@ public class SystemPermissionService
}
@Override
public void deletePermissions(ModeledAuthenticatedUser user, ModeledUser targetUser,
public void deletePermissions(ModeledAuthenticatedUser user,
ModeledPermissions<? extends EntityModel> targetEntity,
Collection<SystemPermission> permissions) throws GuacamoleException {
// Only an admin can delete system permissions
if (user.getUser().isAdministrator()) {
// Do not allow users to remove their own admin powers
if (user.getUser().getIdentifier().equals(targetUser.getIdentifier()))
if (user.getUser().getIdentifier().equals(targetEntity.getIdentifier()))
throw new GuacamoleUnsupportedException("Removing your own administrative permissions is not allowed.");
Collection<SystemPermissionModel> models = getModelInstances(targetUser, permissions);
Collection<SystemPermissionModel> models = getModelInstances(targetEntity, permissions);
systemPermissionMapper.delete(models);
return;
}
@@ -124,41 +129,42 @@ public class SystemPermissionService
}
/**
* Retrieves the permission of the given type associated with the given
* user, if it exists. If no such permission exists, null is returned.
* Retrieves whether the permission of the given type has been granted to
* the given entity. Permission inheritance through group membership is
* taken into account.
*
* @param user
* The user retrieving the permission.
*
* @param targetUser
* The user associated with the permission to be retrieved.
* @param targetEntity
* The entity associated with the permission to be retrieved.
*
* @param type
* The type of permission to retrieve.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the entity.
* If no groups are given, only permissions directly granted to the
* entity will be used.
*
* @return
* The permission of the given type associated with the given user, or
* null if no such permission exists.
* true if permission of the given type has been granted to the given
* entity, false otherwise.
*
* @throws GuacamoleException
* If an error occurs while retrieving the requested permission.
*/
public SystemPermission retrievePermission(ModeledAuthenticatedUser user,
ModeledUser targetUser, SystemPermission.Type type) throws GuacamoleException {
public boolean hasPermission(ModeledAuthenticatedUser user,
ModeledPermissions<? extends EntityModel> targetEntity,
SystemPermission.Type type, Set<String> effectiveGroups)
throws GuacamoleException {
// Retrieve permissions only if allowed
if (canReadPermissions(user, targetUser)) {
if (canReadPermissions(user, targetEntity))
return getPermissionMapper().selectOne(targetEntity.getModel(), type, effectiveGroups) != null;
// Read permission from database, return null if not found
SystemPermissionModel model = getPermissionMapper().selectOne(targetUser.getModel(), type);
if (model == null)
return null;
return getPermissionInstance(model);
}
// User cannot read this user's permissions
// User cannot read this entity's permissions
throw new GuacamoleSecurityException("Permission denied.");
}

View File

@@ -19,28 +19,35 @@
package org.apache.guacamole.auth.jdbc.permission;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import com.google.inject.Inject;
import java.util.Collections;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
import org.apache.guacamole.auth.jdbc.base.RestrictedObject;
import org.apache.guacamole.net.auth.permission.SystemPermission;
/**
* A database implementation of SystemPermissionSet which uses an injected
* service to query and manipulate the system permissions associated with a
* particular user.
* particular entity.
*/
public class SystemPermissionSet extends RestrictedObject
implements org.apache.guacamole.net.auth.permission.SystemPermissionSet {
/**
* The user associated with this permission set. Each of the permissions in
* this permission set is granted to this user.
* The entity associated with this permission set. Each of the permissions
* in this permission set is granted to this entity.
*/
private ModeledUser user;
private ModeledPermissions<? extends EntityModel> entity;
/**
* The identifiers of all groups that should be taken into account when
* determining the permissions effectively granted to the entity.
*/
private Set<String> effectiveGroups;
/**
* Service for reading and manipulating system permissions.
@@ -57,30 +64,39 @@ public class SystemPermissionSet extends RestrictedObject
}
/**
* Initializes this permission set with the current user and the user
* Initializes this permission set with the current user and the entity
* to whom the permissions in this set are granted.
*
* @param currentUser
* The user who queried this permission set, and whose permissions
* dictate the access level of all operations performed on this set.
*
* @param user
* The user to whom the permissions in this set are granted.
* @param entity
* The entity to whom the permissions in this set are granted.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the entity.
* If no groups are given, only permissions directly granted to the
* entity will be used.
*/
public void init(ModeledAuthenticatedUser currentUser, ModeledUser user) {
public void init(ModeledAuthenticatedUser currentUser,
ModeledPermissions<? extends EntityModel> entity,
Set<String> effectiveGroups) {
super.init(currentUser);
this.user = user;
this.entity = entity;
this.effectiveGroups = effectiveGroups;
}
@Override
public Set<SystemPermission> getPermissions() throws GuacamoleException {
return systemPermissionService.retrievePermissions(getCurrentUser(), user);
return systemPermissionService.retrievePermissions(getCurrentUser(), entity, effectiveGroups);
}
@Override
public boolean hasPermission(SystemPermission.Type permission)
throws GuacamoleException {
return systemPermissionService.retrievePermission(getCurrentUser(), user, permission) != null;
return systemPermissionService.hasPermission(getCurrentUser(), entity, permission, effectiveGroups);
}
@Override
@@ -98,13 +114,13 @@ public class SystemPermissionSet extends RestrictedObject
@Override
public void addPermissions(Set<SystemPermission> permissions)
throws GuacamoleException {
systemPermissionService.createPermissions(getCurrentUser(), user, permissions);
systemPermissionService.createPermissions(getCurrentUser(), entity, permissions);
}
@Override
public void removePermissions(Set<SystemPermission> permissions)
throws GuacamoleException {
systemPermissionService.deletePermissions(getCurrentUser(), user, permissions);
systemPermissionService.deletePermissions(getCurrentUser(), entity, permissions);
}
}

View File

@@ -0,0 +1,25 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.permission;
/**
* Mapper for user group permissions.
*/
public interface UserGroupPermissionMapper extends ObjectPermissionMapper {}

View File

@@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.permission;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
/**
* Service which provides convenience methods for creating, retrieving, and
* deleting user group permissions. This service will automatically enforce the
* permissions of the current user.
*/
public class UserGroupPermissionService extends ModeledObjectPermissionService {
/**
* Mapper for user group permissions.
*/
@Inject
private UserGroupPermissionMapper userGroupPermissionMapper;
/**
* Provider for user group permission sets.
*/
@Inject
private Provider<UserGroupPermissionSet> userGroupPermissionSetProvider;
@Override
protected ObjectPermissionMapper getPermissionMapper() {
return userGroupPermissionMapper;
}
@Override
public ObjectPermissionSet getPermissionSet(ModeledAuthenticatedUser user,
ModeledPermissions<? extends EntityModel> targetEntity,
Set<String> effectiveGroups) throws GuacamoleException {
// Create permission set for requested entity
ObjectPermissionSet permissionSet = userGroupPermissionSetProvider.get();
permissionSet.init(user, targetEntity, effectiveGroups);
return permissionSet;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.permission;
import com.google.inject.Inject;
/**
* A database implementation of ObjectPermissionSet which uses an injected
* service to query and manipulate the user group permissions associated with a
* particular user.
*/
public class UserGroupPermissionSet extends ObjectPermissionSet {
/**
* Service for querying and manipulating user group permissions.
*/
@Inject
private UserGroupPermissionService userGroupPermissionService;
@Override
protected ObjectPermissionService getObjectPermissionService() {
return userGroupPermissionService;
}
}

View File

@@ -21,9 +21,11 @@ package org.apache.guacamole.auth.jdbc.permission;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
/**
* Service which provides convenience methods for creating, retrieving, and
@@ -51,11 +53,12 @@ public class UserPermissionService extends ModeledObjectPermissionService {
@Override
public ObjectPermissionSet getPermissionSet(ModeledAuthenticatedUser user,
ModeledUser targetUser) throws GuacamoleException {
ModeledPermissions<? extends EntityModel> targetEntity,
Set<String> effectiveGroups) throws GuacamoleException {
// Create permission set for requested user
// Create permission set for requested entity
ObjectPermissionSet permissionSet = userPermissionSetProvider.get();
permissionSet.init(user, targetUser);
permissionSet.init(user, targetEntity, effectiveGroups);
return permissionSet;

View File

@@ -20,7 +20,6 @@
package org.apache.guacamole.auth.jdbc.sharing.user;
import java.util.Collections;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
@@ -52,7 +51,8 @@ public class SharedAuthenticatedUser extends RemoteAuthenticatedUser {
* The AuthenticatedUser to copy.
*/
public SharedAuthenticatedUser(AuthenticatedUser authenticatedUser) {
super(authenticatedUser.getAuthenticationProvider(), authenticatedUser.getCredentials());
super(authenticatedUser.getAuthenticationProvider(),
authenticatedUser.getCredentials(), Collections.<String>emptySet());
this.shareKey = null;
this.identifier = authenticatedUser.getIdentifier();
}
@@ -75,7 +75,7 @@ public class SharedAuthenticatedUser extends RemoteAuthenticatedUser {
*/
public SharedAuthenticatedUser(AuthenticationProvider authenticationProvider,
Credentials credentials, String shareKey) {
super(authenticationProvider, credentials);
super(authenticationProvider, credentials, Collections.<String>emptySet());
this.shareKey = shareKey;
this.identifier = AuthenticatedUser.ANONYMOUS_IDENTIFIER;
}
@@ -102,9 +102,4 @@ public class SharedAuthenticatedUser extends RemoteAuthenticatedUser {
throw new UnsupportedOperationException("Users authenticated via share keys are immutable.");
}
@Override
public Set<String> getEffectiveUserGroups() {
return Collections.<String>emptySet();
}
}

View File

@@ -112,26 +112,26 @@ public class SharingProfileService
throws GuacamoleException {
// Return whether user has explicit sharing profile creation permission
SystemPermissionSet permissionSet = user.getUser().getSystemPermissions();
SystemPermissionSet permissionSet = user.getUser().getEffectivePermissions().getSystemPermissions();
return permissionSet.hasPermission(SystemPermission.Type.CREATE_SHARING_PROFILE);
}
@Override
protected ObjectPermissionSet getPermissionSet(ModeledAuthenticatedUser user)
protected ObjectPermissionSet getEffectivePermissionSet(ModeledAuthenticatedUser user)
throws GuacamoleException {
// Return permissions related to sharing profiles
return user.getUser().getSharingProfilePermissions();
return user.getUser().getEffectivePermissions().getSharingProfilePermissions();
}
@Override
protected ObjectPermissionSet getParentPermissionSet(ModeledAuthenticatedUser user)
protected ObjectPermissionSet getParentEffectivePermissionSet(ModeledAuthenticatedUser user)
throws GuacamoleException {
// Sharing profiles are children of connections
return user.getUser().getConnectionPermissions();
return user.getUser().getEffectivePermissions().getConnectionPermissions();
}

View File

@@ -628,7 +628,9 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
identifiers.add(record.getConnection().getIdentifier());
// Produce collection of readable connection identifiers
Collection<ConnectionModel> connections = connectionMapper.selectReadable(user.getUser().getModel(), identifiers);
Collection<ConnectionModel> connections =
connectionMapper.selectReadable(user.getUser().getModel(),
identifiers, user.getEffectiveUserGroups());
// Ensure set contains only identifiers of readable connections
identifiers.clear();

View File

@@ -76,7 +76,7 @@ public class ModeledAuthenticatedUser extends RemoteAuthenticatedUser {
*/
public ModeledAuthenticatedUser(AuthenticatedUser authenticatedUser,
AuthenticationProvider modelAuthenticationProvider, ModeledUser user) {
super(authenticatedUser.getAuthenticationProvider(), authenticatedUser.getCredentials());
super(authenticatedUser.getAuthenticationProvider(), authenticatedUser.getCredentials(), authenticatedUser.getEffectiveUserGroups());
this.modelAuthenticationProvider = modelAuthenticationProvider;
this.user = user;
}
@@ -98,7 +98,7 @@ public class ModeledAuthenticatedUser extends RemoteAuthenticatedUser {
*/
public ModeledAuthenticatedUser(AuthenticationProvider authenticationProvider,
ModeledUser user, Credentials credentials) {
super(authenticationProvider, credentials);
super(authenticationProvider, credentials, user.getEffectiveUserGroups());
this.modelAuthenticationProvider = authenticationProvider;
this.user = user;
}
@@ -169,9 +169,4 @@ public class ModeledAuthenticatedUser extends RemoteAuthenticatedUser {
user.setIdentifier(identifier);
}
@Override
public Set<String> getEffectiveUserGroups() {
return Collections.<String>emptySet();
}
}

View File

@@ -20,6 +20,7 @@
package org.apache.guacamole.auth.jdbc.user;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
@@ -33,16 +34,10 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObject;
import org.apache.guacamole.auth.jdbc.security.PasswordEncryptionService;
import org.apache.guacamole.auth.jdbc.security.SaltService;
import org.apache.guacamole.auth.jdbc.permission.SystemPermissionService;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.activeconnection.ActiveConnectionPermissionService;
import org.apache.guacamole.auth.jdbc.permission.ConnectionGroupPermissionService;
import org.apache.guacamole.auth.jdbc.permission.ConnectionPermissionService;
import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionService;
import org.apache.guacamole.auth.jdbc.permission.UserPermissionService;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
import org.apache.guacamole.form.BooleanField;
import org.apache.guacamole.form.DateField;
import org.apache.guacamole.form.EmailField;
@@ -55,18 +50,13 @@ import org.apache.guacamole.net.auth.ActivityRecord;
import org.apache.guacamole.net.auth.Permissions;
import org.apache.guacamole.net.auth.RelatedObjectSet;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
import org.apache.guacamole.net.auth.permission.SystemPermission;
import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
import org.apache.guacamole.net.auth.simple.SimpleObjectPermissionSet;
import org.apache.guacamole.net.auth.simple.SimpleRelatedObjectSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An implementation of the User object which is backed by a database model.
*/
public class ModeledUser extends ModeledDirectoryObject<UserModel> implements User {
public class ModeledUser extends ModeledPermissions<UserModel> implements User {
/**
* Logger for this class.
@@ -187,40 +177,11 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
private SaltService saltService;
/**
* Service for retrieving system permissions.
* Provider for RelatedObjectSets containing the user groups of which this
* user is a member.
*/
@Inject
private SystemPermissionService systemPermissionService;
/**
* Service for retrieving connection permissions.
*/
@Inject
private ConnectionPermissionService connectionPermissionService;
/**
* Service for retrieving connection group permissions.
*/
@Inject
private ConnectionGroupPermissionService connectionGroupPermissionService;
/**
* Service for retrieving sharing profile permissions.
*/
@Inject
private SharingProfilePermissionService sharingProfilePermissionService;
/**
* Service for retrieving active connection permissions.
*/
@Inject
private ActiveConnectionPermissionService activeConnectionPermissionService;
/**
* Service for retrieving user permissions.
*/
@Inject
private UserPermissionService userPermissionService;
private Provider<UserParentUserGroupSet> parentUserGroupSetProvider;
/**
* Whether attributes which control access restrictions should be exposed
@@ -331,63 +292,6 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
return passwordRecord;
}
/**
* Returns whether this user is a system administrator, and thus is not
* restricted by permissions.
*
* @return
* true if this user is a system administrator, false otherwise.
*
* @throws GuacamoleException
* If an error occurs while determining the user's system administrator
* status.
*/
public boolean isAdministrator() throws GuacamoleException {
SystemPermissionSet systemPermissionSet = getSystemPermissions();
return systemPermissionSet.hasPermission(SystemPermission.Type.ADMINISTER);
}
@Override
public SystemPermissionSet getSystemPermissions()
throws GuacamoleException {
return systemPermissionService.getPermissionSet(getCurrentUser(), this);
}
@Override
public ObjectPermissionSet getConnectionPermissions()
throws GuacamoleException {
return connectionPermissionService.getPermissionSet(getCurrentUser(), this);
}
@Override
public ObjectPermissionSet getConnectionGroupPermissions()
throws GuacamoleException {
return connectionGroupPermissionService.getPermissionSet(getCurrentUser(), this);
}
@Override
public ObjectPermissionSet getSharingProfilePermissions()
throws GuacamoleException {
return sharingProfilePermissionService.getPermissionSet(getCurrentUser(), this);
}
@Override
public ObjectPermissionSet getActiveConnectionPermissions()
throws GuacamoleException {
return activeConnectionPermissionService.getPermissionSet(getCurrentUser(), this);
}
@Override
public ObjectPermissionSet getUserPermissions()
throws GuacamoleException {
return userPermissionService.getPermissionSet(getCurrentUser(), this);
}
@Override
public ObjectPermissionSet getUserGroupPermissions() throws GuacamoleException {
return new SimpleObjectPermissionSet();
}
/**
* Stores all restricted (privileged) attributes within the given Map,
* pulling the values of those attributes from the underlying user model.
@@ -850,12 +754,14 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
@Override
public RelatedObjectSet getUserGroups() throws GuacamoleException {
return new SimpleRelatedObjectSet();
UserParentUserGroupSet parentUserGroupSet = parentUserGroupSetProvider.get();
parentUserGroupSet.init(getCurrentUser(), this);
return parentUserGroupSet;
}
@Override
public Permissions getEffectivePermissions() throws GuacamoleException {
return this;
return super.getEffective();
}
}

View File

@@ -26,7 +26,6 @@ import org.apache.guacamole.auth.jdbc.connection.ConnectionDirectory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.RestrictedObject;
@@ -37,6 +36,8 @@ import org.apache.guacamole.auth.jdbc.connection.ModeledConnection;
import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileDirectory;
import org.apache.guacamole.auth.jdbc.usergroup.ModeledUserGroup;
import org.apache.guacamole.auth.jdbc.usergroup.UserGroupDirectory;
import org.apache.guacamole.form.Form;
import org.apache.guacamole.net.auth.ActiveConnection;
import org.apache.guacamole.net.auth.ActivityRecord;
@@ -48,7 +49,6 @@ import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.SharingProfile;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.UserGroup;
import org.apache.guacamole.net.auth.simple.SimpleDirectory;
/**
* UserContext implementation which is driven by an arbitrary, underlying
@@ -63,6 +63,13 @@ public class ModeledUserContext extends RestrictedObject
*/
@Inject
private UserDirectory userDirectory;
/**
* User group directory restricted by the permissions of the user associated
* with this context.
*/
@Inject
private UserGroupDirectory userGroupDirectory;
/**
* Connection directory restricted by the permissions of the user
@@ -128,6 +135,7 @@ public class ModeledUserContext extends RestrictedObject
// Init directories
userDirectory.init(currentUser);
userGroupDirectory.init(currentUser);
connectionDirectory.init(currentUser);
connectionGroupDirectory.init(currentUser);
sharingProfileDirectory.init(currentUser);
@@ -166,7 +174,7 @@ public class ModeledUserContext extends RestrictedObject
@Override
public Directory<UserGroup> getUserGroupDirectory() throws GuacamoleException {
return new SimpleDirectory<UserGroup>();
return userGroupDirectory;
}
@Override
@@ -224,7 +232,7 @@ public class ModeledUserContext extends RestrictedObject
@Override
public Collection<Form> getUserGroupAttributes() {
return Collections.<Form>emptyList();
return ModeledUserGroup.ATTRIBUTES;
}
@Override

View File

@@ -19,6 +19,8 @@
package org.apache.guacamole.auth.jdbc.user;
import java.util.Collections;
import java.util.Set;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
@@ -43,6 +45,12 @@ public abstract class RemoteAuthenticatedUser implements AuthenticatedUser {
*/
private final String remoteHost;
/**
* The identifiers of any groups of which this user is a member, including
* groups inherited through membership in other groups.
*/
private final Set<String> effectiveGroups;
/**
* Creates a new RemoteAuthenticatedUser, deriving the associated remote
* host from the given credentials.
@@ -52,12 +60,17 @@ public abstract class RemoteAuthenticatedUser implements AuthenticatedUser {
*
* @param credentials
* The credentials given by the user when they authenticated.
*
* @param effectiveGroups
* The identifiers of any groups of which this user is a member,
* including groups inherited through membership in other groups.
*/
public RemoteAuthenticatedUser(AuthenticationProvider authenticationProvider,
Credentials credentials) {
Credentials credentials, Set<String> effectiveGroups) {
this.authenticationProvider = authenticationProvider;
this.credentials = credentials;
this.remoteHost = credentials.getRemoteAddress();
this.effectiveGroups = Collections.unmodifiableSet(effectiveGroups);
}
@Override
@@ -75,6 +88,11 @@ public abstract class RemoteAuthenticatedUser implements AuthenticatedUser {
return remoteHost;
}
@Override
public Set<String> getEffectiveUserGroups() {
return effectiveGroups;
}
@Override
public AuthenticationProvider getAuthenticationProvider() {
return authenticationProvider;

View File

@@ -38,5 +38,5 @@ public interface UserMapper extends ModeledDirectoryObjectMapper<UserModel> {
* The user having the given username, or null if no such user exists.
*/
UserModel selectOne(@Param("username") String username);
}

View File

@@ -22,12 +22,13 @@ package org.apache.guacamole.auth.jdbc.user;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import org.apache.guacamole.auth.jdbc.base.ObjectModel;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.EntityType;
/**
* Object representation of a Guacamole user, as represented in the database.
*/
public class UserModel extends ObjectModel {
public class UserModel extends EntityModel {
/**
* The SHA-256 hash of the password and salt.
@@ -124,6 +125,7 @@ public class UserModel extends ObjectModel {
* Creates a new, empty user.
*/
public UserModel() {
super(EntityType.USER);
}
/**

View File

@@ -0,0 +1,28 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.user;
import org.apache.guacamole.auth.jdbc.base.ObjectRelationMapper;
/**
* Mapper for the one-to-many relationship between a user and the user groups
* of which it is a member.
*/
public interface UserParentUserGroupMapper extends ObjectRelationMapper<UserModel> {}

View File

@@ -0,0 +1,59 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.user;
import com.google.inject.Inject;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.ObjectRelationMapper;
import org.apache.guacamole.auth.jdbc.base.RelatedObjectSet;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
/**
* RelatedObjectSet implementation which represents the one-to-many
* relationship between a particular user and the user groups of which it is a
* member.
*/
public class UserParentUserGroupSet extends RelatedObjectSet<ModeledUser, UserModel> {
/**
* Mapper for the relations between users and the user groups of which they
* are members.
*/
@Inject
private UserParentUserGroupMapper userParentUserGroupMapper;
@Override
protected ObjectRelationMapper<UserModel> getObjectRelationMapper() {
return userParentUserGroupMapper;
}
@Override
protected ObjectPermissionSet
getParentObjectEffectivePermissionSet() throws GuacamoleException {
return getCurrentUser().getUser().getEffectivePermissions().getUserPermissions();
}
@Override
protected ObjectPermissionSet getChildObjectEffectivePermissionSet()
throws GuacamoleException {
return getCurrentUser().getUser().getEffectivePermissions().getUserGroupPermissions();
}
}

View File

@@ -113,12 +113,19 @@ public interface UserRecordMapper {
* @param limit
* The maximum number of records that should be returned.
*
* @param effectiveGroups
* The identifiers of all groups that should be taken into account
* when determining the permissions effectively granted to the user. If
* no groups are given, only permissions directly granted to the user
* will be used.
*
* @return
* The results of the search performed with the given parameters.
*/
List<ActivityRecordModel> searchReadable(@Param("user") UserModel user,
@Param("terms") Collection<ActivityRecordSearchTerm> terms,
@Param("sortPredicates") List<ActivityRecordSortPredicate> sortPredicates,
@Param("limit") int limit);
@Param("limit") int limit,
@Param("effectiveGroups") Collection<String> effectiveGroups);
}

View File

@@ -37,8 +37,8 @@ import org.apache.guacamole.GuacamoleUnsupportedException;
import org.apache.guacamole.auth.jdbc.base.ActivityRecordModel;
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm;
import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate;
import org.apache.guacamole.auth.jdbc.base.EntityMapper;
import org.apache.guacamole.auth.jdbc.base.ModeledActivityRecord;
import org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel;
import org.apache.guacamole.auth.jdbc.permission.ObjectPermissionMapper;
import org.apache.guacamole.auth.jdbc.permission.ObjectPermissionModel;
import org.apache.guacamole.auth.jdbc.permission.UserPermissionMapper;
@@ -49,7 +49,6 @@ import org.apache.guacamole.form.PasswordField;
import org.apache.guacamole.net.auth.ActivityRecord;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.ConnectionRecord;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
@@ -115,6 +114,12 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
CONFIRM_NEW_PASSWORD
));
/**
* Mapper for creating/deleting entities.
*/
@Inject
private EntityMapper entityMapper;
/**
* Mapper for accessing users.
*/
@@ -211,17 +216,17 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
throws GuacamoleException {
// Return whether user has explicit user creation permission
SystemPermissionSet permissionSet = user.getUser().getSystemPermissions();
SystemPermissionSet permissionSet = user.getUser().getEffectivePermissions().getSystemPermissions();
return permissionSet.hasPermission(SystemPermission.Type.CREATE_USER);
}
@Override
protected ObjectPermissionSet getPermissionSet(ModeledAuthenticatedUser user)
protected ObjectPermissionSet getEffectivePermissionSet(ModeledAuthenticatedUser user)
throws GuacamoleException {
// Return permissions related to users
return user.getUser().getUserPermissions();
return user.getUser().getEffectivePermissions().getUserPermissions();
}
@@ -244,6 +249,9 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
if (object.getPassword() != null)
passwordPolicyService.verifyPassword(object.getIdentifier(), object.getPassword());
// Create base entity object, implicitly populating underlying entity ID
entityMapper.insert(model);
}
@Override
@@ -294,8 +302,7 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
for (ObjectPermission.Type permissionType : IMPLICIT_USER_PERMISSIONS) {
ObjectPermissionModel permissionModel = new ObjectPermissionModel();
permissionModel.setUserID(model.getObjectID());
permissionModel.setUsername(model.getIdentifier());
permissionModel.setEntityID(model.getEntityID());
permissionModel.setType(permissionType);
permissionModel.setObjectIdentifier(model.getIdentifier());
@@ -584,11 +591,10 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
// Otherwise only return explicitly readable history records
else
searchResults = userRecordMapper.searchReadable(user.getUser().getModel(),
requiredContents, sortPredicates, limit);
requiredContents, sortPredicates, limit, user.getEffectiveUserGroups());
return getObjectInstances(searchResults);
}
}

View File

@@ -0,0 +1,206 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.usergroup;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.form.BooleanField;
import org.apache.guacamole.form.Field;
import org.apache.guacamole.form.Form;
import org.apache.guacamole.net.auth.RelatedObjectSet;
import org.apache.guacamole.net.auth.UserGroup;
/**
* An implementation of the UserGroup object which is backed by a database model.
*/
public class ModeledUserGroup extends ModeledPermissions<UserGroupModel>
implements UserGroup {
/**
* The name of the attribute which controls whether a user group is
* disabled.
*/
public static final String DISABLED_ATTRIBUTE_NAME = "disabled";
/**
* All attributes related to restricting user groups, within a logical
* form.
*/
public static final Form ACCOUNT_RESTRICTIONS = new Form("restrictions", Arrays.<Field>asList(
new BooleanField(DISABLED_ATTRIBUTE_NAME, "true")
));
/**
* All possible attributes of user groups organized as individual,
* logical forms.
*/
public static final Collection<Form> ATTRIBUTES = Collections.unmodifiableCollection(Arrays.asList(
ACCOUNT_RESTRICTIONS
));
/**
* The names of all attributes which are explicitly supported by this
* extension's UserGroup objects.
*/
public static final Set<String> ATTRIBUTE_NAMES =
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
DISABLED_ATTRIBUTE_NAME
)));
/**
* Provider for RelatedObjectSets containing the user groups of which this
* user group is a member.
*/
@Inject
private Provider<UserGroupParentUserGroupSet> parentUserGroupSetProvider;
/**
* Provider for RelatedObjectSets containing the users that are members of
* this user group.
*/
@Inject
private Provider<UserGroupMemberUserSet> memberUserSetProvider;
/**
* Provider for RelatedObjectSets containing the user groups that are
* members of this user group.
*/
@Inject
private Provider<UserGroupMemberUserGroupSet> memberUserGroupSetProvider;
/**
* Whether attributes which control access restrictions should be exposed
* via getAttributes() or allowed to be set via setAttributes().
*/
private boolean exposeRestrictedAttributes = false;
/**
* Initializes this ModeledUserGroup, associating it with the current
* authenticated user and populating it with data from the given user group
* model.
*
* @param currentUser
* The user that created or retrieved this object.
*
* @param model
* The backing model object.
*
* @param exposeRestrictedAttributes
* Whether attributes which control access restrictions should be
* exposed via getAttributes() or allowed to be set via
* setAttributes().
*/
public void init(ModeledAuthenticatedUser currentUser, UserGroupModel model,
boolean exposeRestrictedAttributes) {
super.init(currentUser, model);
this.exposeRestrictedAttributes = exposeRestrictedAttributes;
}
/**
* Stores all restricted (privileged) attributes within the given Map,
* pulling the values of those attributes from the underlying user group
* model. If no value is yet defined for an attribute, that attribute will
* be set to null.
*
* @param attributes
* The Map to store all restricted attributes within.
*/
private void putRestrictedAttributes(Map<String, String> attributes) {
// Set disabled attribute
attributes.put(DISABLED_ATTRIBUTE_NAME, getModel().isDisabled() ? "true" : null);
}
/**
* Stores all restricted (privileged) attributes within the underlying user
* group model, pulling the values of those attributes from the given Map.
*
* @param attributes
* The Map to pull all restricted attributes from.
*/
private void setRestrictedAttributes(Map<String, String> attributes) {
// Translate disabled attribute
getModel().setDisabled("true".equals(attributes.get(DISABLED_ATTRIBUTE_NAME)));
}
@Override
public Set<String> getSupportedAttributeNames() {
return ATTRIBUTE_NAMES;
}
@Override
public Map<String, String> getAttributes() {
// Include any defined arbitrary attributes
Map<String, String> attributes = super.getAttributes();
// Include restricted attributes only if they should be exposed
if (exposeRestrictedAttributes)
putRestrictedAttributes(attributes);
return attributes;
}
@Override
public void setAttributes(Map<String, String> attributes) {
// Set arbitrary attributes
super.setAttributes(attributes);
// Assign restricted attributes only if they are exposed
if (exposeRestrictedAttributes)
setRestrictedAttributes(attributes);
}
@Override
public RelatedObjectSet getUserGroups() throws GuacamoleException {
UserGroupParentUserGroupSet parentUserGroupSet = parentUserGroupSetProvider.get();
parentUserGroupSet.init(getCurrentUser(), this);
return parentUserGroupSet;
}
@Override
public RelatedObjectSet getMemberUsers() throws GuacamoleException {
UserGroupMemberUserSet memberUserSet = memberUserSetProvider.get();
memberUserSet.init(getCurrentUser(), this);
return memberUserSet;
}
@Override
public RelatedObjectSet getMemberUserGroups() throws GuacamoleException {
UserGroupMemberUserGroupSet memberUserGroupSet = memberUserGroupSetProvider.get();
memberUserGroupSet.init(getCurrentUser(), this);
return memberUserGroupSet;
}
}

View File

@@ -0,0 +1,82 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.usergroup;
import com.google.inject.Inject;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.RestrictedObject;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.UserGroup;
import org.mybatis.guice.transactional.Transactional;
/**
* Implementation of the UserGroup Directory which is driven by an underlying,
* arbitrary database.
*/
public class UserGroupDirectory extends RestrictedObject
implements Directory<UserGroup> {
/**
* Service for managing user group objects.
*/
@Inject
private UserGroupService userGroupService;
@Override
public UserGroup get(String identifier) throws GuacamoleException {
return userGroupService.retrieveObject(getCurrentUser(), identifier);
}
@Override
@Transactional
public Collection<UserGroup> getAll(Collection<String> identifiers) throws GuacamoleException {
Collection<ModeledUserGroup> objects = userGroupService.retrieveObjects(getCurrentUser(), identifiers);
return Collections.<UserGroup>unmodifiableCollection(objects);
}
@Override
@Transactional
public Set<String> getIdentifiers() throws GuacamoleException {
return userGroupService.getIdentifiers(getCurrentUser());
}
@Override
@Transactional
public void add(UserGroup object) throws GuacamoleException {
userGroupService.createObject(getCurrentUser(), object);
}
@Override
@Transactional
public void update(UserGroup object) throws GuacamoleException {
ModeledUserGroup group = (ModeledUserGroup) object;
userGroupService.updateObject(getCurrentUser(), group);
}
@Override
@Transactional
public void remove(String identifier) throws GuacamoleException {
userGroupService.deleteObject(getCurrentUser(), identifier);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.usergroup;
import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper;
import org.apache.ibatis.annotations.Param;
/**
* Mapper for user group objects.
*/
public interface UserGroupMapper extends ModeledDirectoryObjectMapper<UserGroupModel> {
/**
* Returns the group having the given name, if any. If no such group
* exists, null is returned.
*
* @param name
* The name of the group to return.
*
* @return
* The group having the given name, or null if no such group exists.
*/
UserGroupModel selectOne(@Param("name") String name);
}

View File

@@ -0,0 +1,28 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.usergroup;
import org.apache.guacamole.auth.jdbc.base.ObjectRelationMapper;
/**
* Mapper for the one-to-many relationship between a user group and its user
* group members.
*/
public interface UserGroupMemberUserGroupMapper extends ObjectRelationMapper<UserGroupModel> {}

View File

@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.usergroup;
import com.google.inject.Inject;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.ObjectRelationMapper;
import org.apache.guacamole.auth.jdbc.base.RelatedObjectSet;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
/**
* RelatedObjectSet implementation which represents the one-to-many
* relationship between a particular user group and its user group members.
*/
public class UserGroupMemberUserGroupSet extends RelatedObjectSet<ModeledUserGroup, UserGroupModel> {
/**
* Mapper for the relation between user groups and their user group members.
*/
@Inject
private UserGroupMemberUserGroupMapper userGroupMemberUserGroupMapper;
@Override
protected ObjectRelationMapper<UserGroupModel> getObjectRelationMapper() {
return userGroupMemberUserGroupMapper;
}
@Override
protected ObjectPermissionSet
getParentObjectEffectivePermissionSet() throws GuacamoleException {
return getCurrentUser().getUser().getEffectivePermissions().getUserGroupPermissions();
}
@Override
protected ObjectPermissionSet getChildObjectEffectivePermissionSet()
throws GuacamoleException {
return getCurrentUser().getUser().getEffectivePermissions().getUserGroupPermissions();
}
}

View File

@@ -0,0 +1,28 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.usergroup;
import org.apache.guacamole.auth.jdbc.base.ObjectRelationMapper;
/**
* Mapper for the one-to-many relationship between a user group and its user
* members.
*/
public interface UserGroupMemberUserMapper extends ObjectRelationMapper<UserGroupModel> {}

View File

@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.usergroup;
import com.google.inject.Inject;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.ObjectRelationMapper;
import org.apache.guacamole.auth.jdbc.base.RelatedObjectSet;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
/**
* RelatedObjectSet implementation which represents the one-to-many
* relationship between a particular user group and its user members.
*/
public class UserGroupMemberUserSet extends RelatedObjectSet<ModeledUserGroup, UserGroupModel> {
/**
* Mapper for the relation between user groups and their user members.
*/
@Inject
private UserGroupMemberUserMapper userGroupMemberUserMapper;
@Override
protected ObjectRelationMapper<UserGroupModel> getObjectRelationMapper() {
return userGroupMemberUserMapper;
}
@Override
protected ObjectPermissionSet
getParentObjectEffectivePermissionSet() throws GuacamoleException {
return getCurrentUser().getUser().getEffectivePermissions().getUserGroupPermissions();
}
@Override
protected ObjectPermissionSet getChildObjectEffectivePermissionSet()
throws GuacamoleException {
return getCurrentUser().getUser().getEffectivePermissions().getUserPermissions();
}
}

View File

@@ -0,0 +1,68 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.usergroup;
import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.EntityType;
/**
* Object representation of a Guacamole user group, as represented in the
* database.
*/
public class UserGroupModel extends EntityModel {
/**
* Whether the user group is disabled. Disabled accounts exist and can
* be modified, but cannot be used.
*/
private boolean disabled;
/**
* Creates a new, empty user group.
*/
public UserGroupModel() {
super(EntityType.USER_GROUP);
}
/**
* Returns whether this user group has been disabled. Memberships of
* disabled user groups are treated as non-existent, effectively disabling
* membership in that group.
*
* @return
* true if this user group is disabled, false otherwise.
*/
public boolean isDisabled() {
return disabled;
}
/**
* Sets whether this user group has been disabled. Memberships of disabled
* user groups are treated as non-existent, effectively disabling
* membership in that group.
*
* @param disabled
* true if this user group should be disabled, false otherwise.
*/
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.usergroup;
import org.apache.guacamole.auth.jdbc.base.ObjectRelationMapper;
/**
* Mapper for the one-to-many relationship between a user group and its
* containing user groups.
*/
public interface UserGroupParentUserGroupMapper extends ObjectRelationMapper<UserGroupModel> {}

View File

@@ -0,0 +1,58 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.usergroup;
import com.google.inject.Inject;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.ObjectRelationMapper;
import org.apache.guacamole.auth.jdbc.base.RelatedObjectSet;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
/**
* RelatedObjectSet implementation which represents the one-to-many
* relationship between a particular user group and its containing user groups.
*/
public class UserGroupParentUserGroupSet extends RelatedObjectSet<ModeledUserGroup, UserGroupModel> {
/**
* Mapper for the relation between user groups and their containing user
* groups.
*/
@Inject
private UserGroupParentUserGroupMapper userGroupParentUserGroupMapper;
@Override
protected ObjectRelationMapper<UserGroupModel> getObjectRelationMapper() {
return userGroupParentUserGroupMapper;
}
@Override
protected ObjectPermissionSet
getParentObjectEffectivePermissionSet() throws GuacamoleException {
return getCurrentUser().getUser().getEffectivePermissions().getUserGroupPermissions();
}
@Override
protected ObjectPermissionSet getChildObjectEffectivePermissionSet()
throws GuacamoleException {
return getCurrentUser().getUser().getEffectivePermissions().getUserGroupPermissions();
}
}

View File

@@ -0,0 +1,183 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.guacamole.auth.jdbc.usergroup;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper;
import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectService;
import org.apache.guacamole.GuacamoleClientException;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.base.EntityMapper;
import org.apache.guacamole.auth.jdbc.permission.ObjectPermissionMapper;
import org.apache.guacamole.auth.jdbc.permission.UserGroupPermissionMapper;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.net.auth.UserGroup;
import org.apache.guacamole.net.auth.permission.ObjectPermission;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
import org.apache.guacamole.net.auth.permission.SystemPermission;
import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
/**
* Service which provides convenience methods for creating, retrieving, and
* manipulating user groups.
*/
public class UserGroupService extends ModeledDirectoryObjectService<ModeledUserGroup, UserGroup, UserGroupModel> {
/**
* Mapper for creating/deleting entities.
*/
@Inject
private EntityMapper entityMapper;
/**
* Mapper for accessing user groups.
*/
@Inject
private UserGroupMapper userGroupMapper;
/**
* Mapper for manipulating user group permissions.
*/
@Inject
private UserGroupPermissionMapper userGroupPermissionMapper;
/**
* Provider for creating user groups.
*/
@Inject
private Provider<ModeledUserGroup> userGroupProvider;
@Override
protected ModeledDirectoryObjectMapper<UserGroupModel> getObjectMapper() {
return userGroupMapper;
}
@Override
protected ObjectPermissionMapper getPermissionMapper() {
return userGroupPermissionMapper;
}
@Override
protected ModeledUserGroup getObjectInstance(ModeledAuthenticatedUser currentUser,
UserGroupModel model) throws GuacamoleException {
boolean exposeRestrictedAttributes;
// Expose restricted attributes if the user group does not yet exist
if (model.getObjectID() == null)
exposeRestrictedAttributes = true;
// Otherwise, expose restricted attributes only if the user has
// ADMINISTER permission
else
exposeRestrictedAttributes = hasObjectPermission(currentUser,
model.getIdentifier(), ObjectPermission.Type.ADMINISTER);
// Produce ModeledUserGroup exposing only those attributes for which the
// current user has permission
ModeledUserGroup group = userGroupProvider.get();
group.init(currentUser, model, exposeRestrictedAttributes);
return group;
}
@Override
protected UserGroupModel getModelInstance(ModeledAuthenticatedUser currentUser,
final UserGroup object) throws GuacamoleException {
// Create new ModeledUserGroup backed by blank model
UserGroupModel model = new UserGroupModel();
ModeledUserGroup group = getObjectInstance(currentUser, model);
// Set model contents through ModeledUser, copying the provided group
group.setIdentifier(object.getIdentifier());
group.setAttributes(object.getAttributes());
return model;
}
@Override
protected boolean hasCreatePermission(ModeledAuthenticatedUser user)
throws GuacamoleException {
// Return whether user has explicit user group creation permission
SystemPermissionSet permissionSet = user.getUser().getEffectivePermissions().getSystemPermissions();
return permissionSet.hasPermission(SystemPermission.Type.CREATE_USER);
}
@Override
protected ObjectPermissionSet getEffectivePermissionSet(ModeledAuthenticatedUser user)
throws GuacamoleException {
// Return permissions related to user groups
return user.getUser().getEffectivePermissions().getUserGroupPermissions();
}
@Override
protected void beforeCreate(ModeledAuthenticatedUser user, UserGroup object,
UserGroupModel model) throws GuacamoleException {
super.beforeCreate(user, object, model);
// Group name must not be blank
if (model.getIdentifier() == null || model.getIdentifier().trim().isEmpty())
throw new GuacamoleClientException("The group name must not be blank.");
// Do not create duplicate user groups
UserGroupModel existing = userGroupMapper.selectOne(model.getIdentifier());
if (existing != null)
throw new GuacamoleClientException("Group \"" + model.getIdentifier() + "\" already exists.");
// Create base entity object, implicitly populating underlying entity ID
entityMapper.insert(model);
}
@Override
protected void beforeUpdate(ModeledAuthenticatedUser user,
ModeledUserGroup object, UserGroupModel model) throws GuacamoleException {
super.beforeUpdate(user, object, model);
// Group names must not be blank
if (model.getIdentifier() == null || model.getIdentifier().trim().isEmpty())
throw new GuacamoleClientException("The group name must not be blank.");
// Do not allow groups to be renamed if the name collides with that of
// another, existing group
UserGroupModel existing = userGroupMapper.selectOne(model.getIdentifier());
if (existing != null && !existing.getObjectID().equals(model.getObjectID()))
throw new GuacamoleClientException("Group \"" + model.getIdentifier() + "\" already exists.");
}
@Override
protected boolean isValidIdentifier(String identifier) {
// All strings are valid group identifiers
return true;
}
}

View File

@@ -100,6 +100,14 @@
"SECTION_HEADER_RESTRICTIONS" : "Account Restrictions",
"SECTION_HEADER_PROFILE" : "Profile"
},
"USER_GROUP_ATTRIBUTES" : {
"FIELD_HEADER_DISABLED" : "@:USER_ATTRIBUTES.FIELD_HEADER_DISABLED",
"SECTION_HEADER_RESTRICTIONS" : "@:USER_ATTRIBUTES.SECTION_HEADER_RESTRICTIONS"
}
}