mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 21:27:40 +00:00
GUACAMOLE-96: Add base support within JDBC auth for storage of arbitrary attributes from other extensions.
This commit is contained in:
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* 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.AbstractCollection;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of arbitrary attribute name/value pairs which can alternatively be
|
||||||
|
* exposed as a collection of model objects.
|
||||||
|
*/
|
||||||
|
public class ArbitraryAttributeMap extends HashMap<String, String> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ArbitraryAttributeMap containing the name/value pairs
|
||||||
|
* within the given collection of model objects.
|
||||||
|
*
|
||||||
|
* @param models
|
||||||
|
* The model objects of all attributes which should be stored in the
|
||||||
|
* new map as name/value pairs.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A new ArbitraryAttributeMap containing the name/value pairs within
|
||||||
|
* the given collection of model objects.
|
||||||
|
*/
|
||||||
|
public static ArbitraryAttributeMap fromModelCollection(Collection<ArbitraryAttributeModel> models) {
|
||||||
|
|
||||||
|
// Add all name/value pairs from the given collection to the map
|
||||||
|
ArbitraryAttributeMap map = new ArbitraryAttributeMap();
|
||||||
|
for (ArbitraryAttributeModel model : models)
|
||||||
|
map.put(model.getName(), model.getValue());
|
||||||
|
|
||||||
|
return map;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a collection of model objects which mirrors the contents of this
|
||||||
|
* ArbitraryAttributeMap. Each name/value pair within the map is reflected
|
||||||
|
* by a corresponding model object within the returned collection. Removing
|
||||||
|
* a model object from the collection removes the corresponding name/value
|
||||||
|
* pair from the map. Adding a new model object to the collection adds a
|
||||||
|
* corresponding name/value pair to the map. Changes to a model object
|
||||||
|
* within the collection are NOT reflected on the map, however.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A collection of model objects which mirrors the contents of this
|
||||||
|
* ArbitraryAttributeMap.
|
||||||
|
*/
|
||||||
|
public Collection<ArbitraryAttributeModel> toModelCollection() {
|
||||||
|
return new AbstractCollection<ArbitraryAttributeModel>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
ArbitraryAttributeMap.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
|
||||||
|
// The Collection view of an ArbitraryAttributeMap can contain
|
||||||
|
// only ArbitraryAttributeModel objects
|
||||||
|
if (!(o instanceof ArbitraryAttributeModel))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// The attribute should be removed only if the value matches
|
||||||
|
ArbitraryAttributeModel model = (ArbitraryAttributeModel) o;
|
||||||
|
return ArbitraryAttributeMap.this.remove(model.getName(),
|
||||||
|
model.getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(ArbitraryAttributeModel e) {
|
||||||
|
|
||||||
|
String newValue = e.getValue();
|
||||||
|
String oldValue = put(e.getName(), newValue);
|
||||||
|
|
||||||
|
// If null value is being added, collection changed only if
|
||||||
|
// old value was non-null
|
||||||
|
if (newValue == null)
|
||||||
|
return oldValue != null;
|
||||||
|
|
||||||
|
// Collection changed if value changed
|
||||||
|
return !newValue.equals(oldValue);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
|
||||||
|
// The Collection view of an ArbitraryAttributeMap can contain
|
||||||
|
// only ArbitraryAttributeModel objects
|
||||||
|
if (!(o instanceof ArbitraryAttributeModel))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// No need to check the value of the attribute if the attribute
|
||||||
|
// is not even present
|
||||||
|
ArbitraryAttributeModel model = (ArbitraryAttributeModel) o;
|
||||||
|
String value = get(model.getName());
|
||||||
|
if (value == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// The name/value pair is present only if the value matches
|
||||||
|
return value.equals(model.getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<ArbitraryAttributeModel> iterator() {
|
||||||
|
|
||||||
|
// Get iterator over all string name/value entries
|
||||||
|
final Iterator<Entry<String, String>> iterator = entrySet().iterator();
|
||||||
|
|
||||||
|
// Dynamically translate each string name/value entry into a
|
||||||
|
// corresponding attribute model object as iteration continues
|
||||||
|
return new Iterator<ArbitraryAttributeModel>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return iterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArbitraryAttributeModel next() {
|
||||||
|
Entry<String, String> entry = iterator.next();
|
||||||
|
return new ArbitraryAttributeModel(entry.getKey(),
|
||||||
|
entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return ArbitraryAttributeMap.this.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single attribute name/value pair belonging to a object which implements
|
||||||
|
* the Attributes interface, such as a Connection or User. Attributes stored
|
||||||
|
* as raw name/value pairs are the attributes which are given to the database
|
||||||
|
* authentication extension for storage by other extensions. Attributes which
|
||||||
|
* are directly supported by the database authentication extension have defined
|
||||||
|
* columns and properties with proper types, constraints, etc.
|
||||||
|
*/
|
||||||
|
public class ArbitraryAttributeModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the attribute.
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value the attribute is set to.
|
||||||
|
*/
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ArbitraryAttributeModel with its name and value both set
|
||||||
|
* to null.
|
||||||
|
*/
|
||||||
|
public ArbitraryAttributeModel() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ArbitraryAttributeModel with its name and value
|
||||||
|
* initialized to the given values.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* The name of the attribute.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The value the attribute is set to.
|
||||||
|
*/
|
||||||
|
public ArbitraryAttributeModel(String name, String value) {
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of this attribute.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The name of this attribute.
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the name of this attribute.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* The name of this attribute.
|
||||||
|
*/
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of this attribute.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The value of this attribute.
|
||||||
|
*/
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of this attribute.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The value of this attribute.
|
||||||
|
*/
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -65,35 +65,32 @@ public abstract class ModeledDirectoryObject<ModelType extends ObjectModel>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getAttributes() {
|
public Map<String, String> getAttributes() {
|
||||||
|
return new HashMap<String, String>(getModel().getArbitraryAttributeMap());
|
||||||
// If no arbitrary attributes are defined, just return an empty map
|
|
||||||
Map<String, String> arbitraryAttributes = getModel().getArbitraryAttributes();
|
|
||||||
if (arbitraryAttributes == null)
|
|
||||||
return new HashMap<String, String>();
|
|
||||||
|
|
||||||
// Otherwise include any defined arbitrary attributes
|
|
||||||
return new HashMap<String, String>(arbitraryAttributes);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAttributes(Map<String, String> attributes) {
|
public void setAttributes(Map<String, String> attributes) {
|
||||||
|
|
||||||
|
ArbitraryAttributeMap arbitraryAttributes = getModel().getArbitraryAttributeMap();
|
||||||
|
|
||||||
// Get set of all supported attribute names
|
// Get set of all supported attribute names
|
||||||
Set<String> supportedAttributes = getSupportedAttributeNames();
|
Set<String> supportedAttributes = getSupportedAttributeNames();
|
||||||
|
|
||||||
// Initialize model with empty map if no such map is already present
|
|
||||||
Map<String, String> arbitraryAttributes = getModel().getArbitraryAttributes();
|
|
||||||
if (arbitraryAttributes == null) {
|
|
||||||
arbitraryAttributes = new HashMap<String, String>();
|
|
||||||
getModel().setArbitraryAttributes(arbitraryAttributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store remaining attributes only if not directly mapped to model
|
// Store remaining attributes only if not directly mapped to model
|
||||||
for (Map.Entry<String, String> attribute : attributes.entrySet()) {
|
for (Map.Entry<String, String> attribute : attributes.entrySet()) {
|
||||||
|
|
||||||
String name = attribute.getKey();
|
String name = attribute.getKey();
|
||||||
if (!supportedAttributes.contains(name))
|
String value = attribute.getValue();
|
||||||
arbitraryAttributes.put(name, attribute.getValue());
|
|
||||||
|
// Handle null attributes as explicit removal of that attribute,
|
||||||
|
// as the underlying model cannot store null attribute values
|
||||||
|
if (!supportedAttributes.contains(name)) {
|
||||||
|
if (value == null)
|
||||||
|
arbitraryAttributes.remove(name);
|
||||||
|
else
|
||||||
|
arbitraryAttributes.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -132,5 +132,28 @@ public interface ModeledDirectoryObjectMapper<ModelType> {
|
|||||||
* The number of rows updated.
|
* The number of rows updated.
|
||||||
*/
|
*/
|
||||||
int update(@Param("object") ModelType object);
|
int update(@Param("object") ModelType object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes any arbitrary attributes currently associated with the given
|
||||||
|
* object in the database.
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* The object whose arbitrary attributes should be deleted.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The number of rows deleted.
|
||||||
|
*/
|
||||||
|
int deleteAttributes(@Param("object") ModelType object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts all arbitrary attributes associated with the given object.
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* The object whose arbitrary attributes should be inserted.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The number of rows inserted.
|
||||||
|
*/
|
||||||
|
int insertAttributes(@Param("object") ModelType object);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -32,6 +32,7 @@ import org.apache.guacamole.auth.jdbc.user.UserModel;
|
|||||||
import org.apache.guacamole.net.auth.Identifiable;
|
import org.apache.guacamole.net.auth.Identifiable;
|
||||||
import org.apache.guacamole.net.auth.permission.ObjectPermission;
|
import org.apache.guacamole.net.auth.permission.ObjectPermission;
|
||||||
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
|
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
|
||||||
|
import org.mybatis.guice.transactional.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service which provides convenience methods for creating, retrieving, and
|
* Service which provides convenience methods for creating, retrieving, and
|
||||||
@@ -446,6 +447,7 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional
|
||||||
public InternalType createObject(ModeledAuthenticatedUser user, ExternalType object)
|
public InternalType createObject(ModeledAuthenticatedUser user, ExternalType object)
|
||||||
throws GuacamoleException {
|
throws GuacamoleException {
|
||||||
|
|
||||||
@@ -461,6 +463,10 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
|
|||||||
// Add implicit permissions
|
// Add implicit permissions
|
||||||
getPermissionMapper().insert(getImplicitPermissions(user, model));
|
getPermissionMapper().insert(getImplicitPermissions(user, model));
|
||||||
|
|
||||||
|
// Add any arbitrary attributes
|
||||||
|
if (model.hasArbitraryAttributes())
|
||||||
|
getObjectMapper().insertAttributes(model);
|
||||||
|
|
||||||
return getObjectInstance(user, model);
|
return getObjectInstance(user, model);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -477,6 +483,7 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional
|
||||||
public void updateObject(ModeledAuthenticatedUser user, InternalType object)
|
public void updateObject(ModeledAuthenticatedUser user, InternalType object)
|
||||||
throws GuacamoleException {
|
throws GuacamoleException {
|
||||||
|
|
||||||
@@ -486,6 +493,11 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
|
|||||||
// Update object
|
// Update object
|
||||||
getObjectMapper().update(model);
|
getObjectMapper().update(model);
|
||||||
|
|
||||||
|
// Replace any existing arbitrary attributes
|
||||||
|
getObjectMapper().deleteAttributes(model);
|
||||||
|
if (model.hasArbitraryAttributes())
|
||||||
|
getObjectMapper().insertAttributes(model);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
package org.apache.guacamole.auth.jdbc.base;
|
package org.apache.guacamole.auth.jdbc.base;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object representation of a Guacamole object, such as a user or connection,
|
* Object representation of a Guacamole object, such as a user or connection,
|
||||||
@@ -41,7 +41,8 @@ public abstract class ObjectModel {
|
|||||||
* Map of all arbitrary attributes associated with this object but not
|
* Map of all arbitrary attributes associated with this object but not
|
||||||
* directly mapped to a particular column.
|
* directly mapped to a particular column.
|
||||||
*/
|
*/
|
||||||
private Map<String, String> arbitraryAttributes;
|
private ArbitraryAttributeMap arbitraryAttributes =
|
||||||
|
new ArbitraryAttributeMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new, empty object.
|
* Creates a new, empty object.
|
||||||
@@ -102,23 +103,47 @@ public abstract class ObjectModel {
|
|||||||
* with this model which do not otherwise have explicit mappings to
|
* with this model which do not otherwise have explicit mappings to
|
||||||
* properties.
|
* properties.
|
||||||
*/
|
*/
|
||||||
public Map<String, String> getArbitraryAttributes() {
|
public ArbitraryAttributeMap getArbitraryAttributeMap() {
|
||||||
return arbitraryAttributes;
|
return arbitraryAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets all arbitrary attribute name/value pairs associated with this
|
* Returns whether at least one arbitrary attribute name/value pair has
|
||||||
* model. The provided map should contain only attributes which are not
|
* been associated with this object.
|
||||||
* explicitly supported by the model, as any explicitly-supported
|
*
|
||||||
* attributes should instead be mapped to corresponding properties.
|
* @return
|
||||||
|
* true if this object has at least one arbitrary attribute set, false
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasArbitraryAttributes() {
|
||||||
|
return !arbitraryAttributes.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Collection view of the equivalent attribute model objects
|
||||||
|
* which make up the map of arbitrary attribute name/value pairs returned
|
||||||
|
* by getArbitraryAttributeMap(). Additions and removals on the returned
|
||||||
|
* Collection directly affect the attribute map.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A Collection view of the map returned by
|
||||||
|
* getArbitraryAttributeMap().
|
||||||
|
*/
|
||||||
|
public Collection<ArbitraryAttributeModel> getArbitraryAttributes() {
|
||||||
|
return arbitraryAttributes.toModelCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces all arbitrary attributes associated with this object with the
|
||||||
|
* attribute name/value pairs within the given collection of model objects.
|
||||||
*
|
*
|
||||||
* @param arbitraryAttributes
|
* @param arbitraryAttributes
|
||||||
* A map of attribute name/value pairs for all attributes associated
|
* The Collection of model objects containing the attribute name/value
|
||||||
* with this model which do not otherwise have explicit mappings to
|
* pairs which should replace all currently-stored arbitrary attributes,
|
||||||
* properties.
|
* if any.
|
||||||
*/
|
*/
|
||||||
public void setArbitraryAttributes(Map<String, String> arbitraryAttributes) {
|
public void setArbitraryAttributes(Collection<ArbitraryAttributeModel> arbitraryAttributes) {
|
||||||
this.arbitraryAttributes = arbitraryAttributes;
|
this.arbitraryAttributes = ArbitraryAttributeMap.fromModelCollection(arbitraryAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user