mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
GUACAMOLE-292: Merge attribute editing permissioning.
This commit is contained in:
@@ -97,9 +97,12 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* An object which is backed by the given model object.
|
* An object which is backed by the given model object.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the object instance cannot be created.
|
||||||
*/
|
*/
|
||||||
protected abstract InternalType getObjectInstance(ModeledAuthenticatedUser currentUser,
|
protected abstract InternalType getObjectInstance(ModeledAuthenticatedUser currentUser,
|
||||||
ModelType model);
|
ModelType model) throws GuacamoleException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an instance of a model object which is based on the given
|
* Returns an instance of a model object which is based on the given
|
||||||
@@ -113,9 +116,12 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* A model object which is based on the given object.
|
* A model object which is based on the given object.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the model object instance cannot be created.
|
||||||
*/
|
*/
|
||||||
protected abstract ModelType getModelInstance(ModeledAuthenticatedUser currentUser,
|
protected abstract ModelType getModelInstance(ModeledAuthenticatedUser currentUser,
|
||||||
ExternalType object);
|
ExternalType object) throws GuacamoleException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the given user has permission to create the type of
|
* Returns whether the given user has permission to create the type of
|
||||||
@@ -199,9 +205,12 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
|
|||||||
* @return
|
* @return
|
||||||
* A collection of objects which are backed by the models in the given
|
* A collection of objects which are backed by the models in the given
|
||||||
* collection.
|
* collection.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If any of the object instances cannot be created.
|
||||||
*/
|
*/
|
||||||
protected Collection<InternalType> getObjectInstances(ModeledAuthenticatedUser currentUser,
|
protected Collection<InternalType> getObjectInstances(ModeledAuthenticatedUser currentUser,
|
||||||
Collection<ModelType> models) {
|
Collection<ModelType> models) throws GuacamoleException {
|
||||||
|
|
||||||
// Create new collection of objects by manually converting each model
|
// Create new collection of objects by manually converting each model
|
||||||
Collection<InternalType> objects = new ArrayList<InternalType>(models.size());
|
Collection<InternalType> objects = new ArrayList<InternalType>(models.size());
|
||||||
|
@@ -176,6 +176,34 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
|
|||||||
@Inject
|
@Inject
|
||||||
private UserPermissionService userPermissionService;
|
private UserPermissionService userPermissionService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether attributes which control access restrictions should be exposed
|
||||||
|
* via getAttributes() or allowed to be set via setAttributes().
|
||||||
|
*/
|
||||||
|
private boolean exposeRestrictedAttributes = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes this ModeledUser, associating it with the current
|
||||||
|
* authenticated user and populating it with data from the given user
|
||||||
|
* 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, UserModel model,
|
||||||
|
boolean exposeRestrictedAttributes) {
|
||||||
|
super.init(currentUser, model);
|
||||||
|
this.exposeRestrictedAttributes = exposeRestrictedAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The plaintext password previously set by a call to setPassword(), if
|
* The plaintext password previously set by a call to setPassword(), if
|
||||||
* any. The password of a user cannot be retrieved once saved into the
|
* any. The password of a user cannot be retrieved once saved into the
|
||||||
@@ -309,10 +337,16 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
|
|||||||
return userPermissionService.getPermissionSet(getCurrentUser(), this);
|
return userPermissionService.getPermissionSet(getCurrentUser(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public Map<String, String> getAttributes() {
|
* Stores all restricted (privileged) attributes within the given Map,
|
||||||
|
* pulling the values of those attributes from the underlying user model.
|
||||||
Map<String, String> attributes = new HashMap<String, String>();
|
* 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
|
// Set disabled attribute
|
||||||
attributes.put(DISABLED_ATTRIBUTE_NAME, getModel().isDisabled() ? "true" : null);
|
attributes.put(DISABLED_ATTRIBUTE_NAME, getModel().isDisabled() ? "true" : null);
|
||||||
@@ -335,7 +369,6 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
|
|||||||
// Set timezone attribute
|
// Set timezone attribute
|
||||||
attributes.put(TIMEZONE_ATTRIBUTE_NAME, getModel().getTimeZone());
|
attributes.put(TIMEZONE_ATTRIBUTE_NAME, getModel().getTimeZone());
|
||||||
|
|
||||||
return attributes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -396,8 +429,14 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void setAttributes(Map<String, String> attributes) {
|
* Stores all restricted (privileged) attributes within the underlying user
|
||||||
|
* 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
|
// Translate disabled attribute
|
||||||
getModel().setDisabled("true".equals(attributes.get(DISABLED_ATTRIBUTE_NAME)));
|
getModel().setDisabled("true".equals(attributes.get(DISABLED_ATTRIBUTE_NAME)));
|
||||||
@@ -438,6 +477,27 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getAttributes() {
|
||||||
|
|
||||||
|
Map<String, String> attributes = new HashMap<String, String>();
|
||||||
|
|
||||||
|
// Include restricted attributes only if they should be exposed
|
||||||
|
if (exposeRestrictedAttributes)
|
||||||
|
putRestrictedAttributes(attributes);
|
||||||
|
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttributes(Map<String, String> attributes) {
|
||||||
|
|
||||||
|
// Assign restricted attributes only if they are exposed
|
||||||
|
if (exposeRestrictedAttributes)
|
||||||
|
setRestrictedAttributes(attributes);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the time zone associated with this user. This time zone must be
|
* Returns the time zone associated with this user. This time zone must be
|
||||||
* used when interpreting all date/time restrictions related to this user.
|
* used when interpreting all date/time restrictions related to this user.
|
||||||
|
@@ -147,15 +147,35 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ModeledUser getObjectInstance(ModeledAuthenticatedUser currentUser,
|
protected ModeledUser getObjectInstance(ModeledAuthenticatedUser currentUser,
|
||||||
UserModel model) {
|
UserModel model) throws GuacamoleException {
|
||||||
|
|
||||||
|
boolean exposeRestrictedAttributes;
|
||||||
|
|
||||||
|
// Expose restricted attributes if the user does not yet exist
|
||||||
|
if (model.getObjectID() == null)
|
||||||
|
exposeRestrictedAttributes = true;
|
||||||
|
|
||||||
|
// Otherwise, if the user permissions are available, expose restricted
|
||||||
|
// attributes only if the user has ADMINISTER permission
|
||||||
|
else if (currentUser != null)
|
||||||
|
exposeRestrictedAttributes = hasObjectPermission(currentUser,
|
||||||
|
model.getIdentifier(), ObjectPermission.Type.ADMINISTER);
|
||||||
|
|
||||||
|
// If user permissions are not available, do not expose anything
|
||||||
|
else
|
||||||
|
exposeRestrictedAttributes = false;
|
||||||
|
|
||||||
|
// Produce ModeledUser exposing only those attributes for which the
|
||||||
|
// current user has permission
|
||||||
ModeledUser user = userProvider.get();
|
ModeledUser user = userProvider.get();
|
||||||
user.init(currentUser, model);
|
user.init(currentUser, model, exposeRestrictedAttributes);
|
||||||
return user;
|
return user;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected UserModel getModelInstance(ModeledAuthenticatedUser currentUser,
|
protected UserModel getModelInstance(ModeledAuthenticatedUser currentUser,
|
||||||
final User object) {
|
final User object) throws GuacamoleException {
|
||||||
|
|
||||||
// Create new ModeledUser backed by blank model
|
// Create new ModeledUser backed by blank model
|
||||||
UserModel model = new UserModel();
|
UserModel model = new UserModel();
|
||||||
@@ -362,9 +382,13 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
|
|||||||
* @return
|
* @return
|
||||||
* The ModeledUser which corresponds to the given AuthenticatedUser, or
|
* The ModeledUser which corresponds to the given AuthenticatedUser, or
|
||||||
* null if no such user exists.
|
* null if no such user exists.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If a ModeledUser object for the user corresponding to the given
|
||||||
|
* AuthenticatedUser cannot be created.
|
||||||
*/
|
*/
|
||||||
public ModeledUser retrieveUser(AuthenticationProvider authenticationProvider,
|
public ModeledUser retrieveUser(AuthenticationProvider authenticationProvider,
|
||||||
AuthenticatedUser authenticatedUser) {
|
AuthenticatedUser authenticatedUser) throws GuacamoleException {
|
||||||
|
|
||||||
// If we already queried this user, return that rather than querying again
|
// If we already queried this user, return that rather than querying again
|
||||||
if (authenticatedUser instanceof ModeledAuthenticatedUser)
|
if (authenticatedUser instanceof ModeledAuthenticatedUser)
|
||||||
|
@@ -55,7 +55,16 @@ angular.module('form').directive('guacForm', [function form() {
|
|||||||
*
|
*
|
||||||
* @type Object.<String, String>
|
* @type Object.<String, String>
|
||||||
*/
|
*/
|
||||||
model : '='
|
model : '=',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the contents of the form should be restricted to those
|
||||||
|
* fields/forms which match properties defined within the given
|
||||||
|
* model object. By default, all fields will be shown.
|
||||||
|
*
|
||||||
|
* @type Boolean
|
||||||
|
*/
|
||||||
|
modelOnly : '='
|
||||||
|
|
||||||
},
|
},
|
||||||
templateUrl: 'app/form/templates/form.html',
|
templateUrl: 'app/form/templates/form.html',
|
||||||
@@ -163,6 +172,55 @@ angular.module('form').directive('guacForm', [function form() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given field should be displayed to the
|
||||||
|
* current user.
|
||||||
|
*
|
||||||
|
* @param {Field} field
|
||||||
|
* The field to check.
|
||||||
|
*
|
||||||
|
* @returns {Boolean}
|
||||||
|
* true if the given field should be visible, false otherwise.
|
||||||
|
*/
|
||||||
|
$scope.isVisible = function isVisible(field) {
|
||||||
|
|
||||||
|
// All fields are visible if contents are not restricted to
|
||||||
|
// model properties only
|
||||||
|
if (!$scope.modelOnly)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Otherwise, fields are only visible if they are present
|
||||||
|
// within the model
|
||||||
|
return field && (field.name in $scope.values);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether at least one of the given fields should be
|
||||||
|
* displayed to the current user.
|
||||||
|
*
|
||||||
|
* @param {Field[]} fields
|
||||||
|
* The array of fields to check.
|
||||||
|
*
|
||||||
|
* @returns {Boolean}
|
||||||
|
* true if at least one field within the given array should be
|
||||||
|
* visible, false otherwise.
|
||||||
|
*/
|
||||||
|
$scope.containsVisible = function containsVisible(fields) {
|
||||||
|
|
||||||
|
// If fields are defined, check whether at least one is visible
|
||||||
|
if (fields) {
|
||||||
|
for (var i = 0; i < fields.length; i++) {
|
||||||
|
if ($scope.isVisible(fields[i]))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, there are no visible fields
|
||||||
|
return false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
}] // end controller
|
}] // end controller
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div ng-repeat="form in forms" class="form">
|
<div ng-repeat="form in forms" class="form"
|
||||||
|
ng-show="containsVisible(form.fields)">
|
||||||
|
|
||||||
<!-- Form name -->
|
<!-- Form name -->
|
||||||
<h3 ng-show="form.name">{{getSectionHeader(form) | translate}}</h3>
|
<h3 ng-show="form.name">{{getSectionHeader(form) | translate}}</h3>
|
||||||
@@ -7,6 +8,7 @@
|
|||||||
<!-- All fields in form -->
|
<!-- All fields in form -->
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<guac-form-field ng-repeat="field in form.fields" namespace="namespace"
|
<guac-form-field ng-repeat="field in form.fields" namespace="namespace"
|
||||||
|
ng-if="isVisible(field)"
|
||||||
field="field" model="values[field.name]"></guac-form-field>
|
field="field" model="values[field.name]"></guac-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -299,6 +299,23 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
|
|||||||
$scope.parameters = {};
|
$scope.parameters = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the current user can change/set all connection
|
||||||
|
* attributes for the connection being edited, regardless of whether those
|
||||||
|
* attributes are already explicitly associated with that connection.
|
||||||
|
*
|
||||||
|
* @returns {Boolean}
|
||||||
|
* true if the current user can change all attributes for the
|
||||||
|
* connection being edited, regardless of whether those attributes are
|
||||||
|
* already explicitly associated with that connection, false otherwise.
|
||||||
|
*/
|
||||||
|
$scope.canChangeAllAttributes = function canChangeAllAttributes() {
|
||||||
|
|
||||||
|
// All attributes can be set if we are creating the connection
|
||||||
|
return !identifier;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the translation string namespace for the protocol having the
|
* Returns the translation string namespace for the protocol having the
|
||||||
* given name. The namespace will be of the form:
|
* given name. The namespace will be of the form:
|
||||||
|
@@ -195,7 +195,26 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
|
|||||||
value : ConnectionGroup.Type.BALANCING
|
value : ConnectionGroup.Type.BALANCING
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the current user can change/set all connection group
|
||||||
|
* attributes for the connection group being edited, regardless of whether
|
||||||
|
* those attributes are already explicitly associated with that connection
|
||||||
|
* group.
|
||||||
|
*
|
||||||
|
* @returns {Boolean}
|
||||||
|
* true if the current user can change all attributes for the
|
||||||
|
* connection group being edited, regardless of whether those
|
||||||
|
* attributes are already explicitly associated with that connection
|
||||||
|
* group, false otherwise.
|
||||||
|
*/
|
||||||
|
$scope.canChangeAllAttributes = function canChangeAllAttributes() {
|
||||||
|
|
||||||
|
// All attributes can be set if we are creating the connection group
|
||||||
|
return !identifier;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels all pending edits, returning to the management page.
|
* Cancels all pending edits, returning to the management page.
|
||||||
*/
|
*/
|
||||||
|
@@ -280,6 +280,25 @@ angular.module('manage').controller('manageSharingProfileController', ['$scope',
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the current user can change/set all sharing profile
|
||||||
|
* attributes for the sharing profile being edited, regardless of whether
|
||||||
|
* those attributes are already explicitly associated with that sharing
|
||||||
|
* profile.
|
||||||
|
*
|
||||||
|
* @returns {Boolean}
|
||||||
|
* true if the current user can change all attributes for the sharing
|
||||||
|
* profile being edited, regardless of whether those attributes are
|
||||||
|
* already explicitly associated with that sharing profile, false
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
$scope.canChangeAllAttributes = function canChangeAllAttributes() {
|
||||||
|
|
||||||
|
// All attributes can be set if we are creating the sharing profile
|
||||||
|
return !identifier;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the translation string namespace for the protocol having the
|
* Returns the translation string namespace for the protocol having the
|
||||||
* given name. The namespace will be of the form:
|
* given name. The namespace will be of the form:
|
||||||
|
@@ -225,8 +225,8 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the current user can change attributes associated with
|
* Returns whether the current user can change attributes explicitly
|
||||||
* the user being edited within the given data source.
|
* associated with the user being edited within the given data source.
|
||||||
*
|
*
|
||||||
* @param {String} [dataSource]
|
* @param {String} [dataSource]
|
||||||
* The identifier of the data source to check. If omitted, this will
|
* The identifier of the data source to check. If omitted, this will
|
||||||
@@ -260,6 +260,23 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the current user can change/set all user attributes for
|
||||||
|
* the user being edited, regardless of whether those attributes are
|
||||||
|
* already explicitly associated with that user.
|
||||||
|
*
|
||||||
|
* @returns {Boolean}
|
||||||
|
* true if the current user can change all attributes for the user
|
||||||
|
* being edited, regardless of whether those attributes are already
|
||||||
|
* explicitly associated with that user, false otherwise.
|
||||||
|
*/
|
||||||
|
$scope.canChangeAllAttributes = function canChangeAllAttributes() {
|
||||||
|
|
||||||
|
// All attributes can be set if we are creating the user
|
||||||
|
return !$scope.userExists(selectedDataSource);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the current user can change permissions of any kind
|
* Returns whether the current user can change permissions of any kind
|
||||||
* which are associated with the user being edited within the given data
|
* which are associated with the user being edited within the given data
|
||||||
|
@@ -40,7 +40,8 @@
|
|||||||
|
|
||||||
<!-- Connection attributes section -->
|
<!-- Connection attributes section -->
|
||||||
<div class="attributes">
|
<div class="attributes">
|
||||||
<guac-form namespace="'CONNECTION_ATTRIBUTES'" content="attributes" model="connection.attributes"></guac-form>
|
<guac-form namespace="'CONNECTION_ATTRIBUTES'" content="attributes"
|
||||||
|
model="connection.attributes" model-only="!canChangeAllAttributes()"></guac-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Connection parameters -->
|
<!-- Connection parameters -->
|
||||||
|
@@ -40,7 +40,8 @@
|
|||||||
|
|
||||||
<!-- Connection group attributes section -->
|
<!-- Connection group attributes section -->
|
||||||
<div class="attributes">
|
<div class="attributes">
|
||||||
<guac-form namespace="'CONNECTION_GROUP_ATTRIBUTES'" content="attributes" model="connectionGroup.attributes"></guac-form>
|
<guac-form namespace="'CONNECTION_GROUP_ATTRIBUTES'" content="attributes"
|
||||||
|
model="connectionGroup.attributes" model-only="!canChangeAllAttributes()"></guac-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Form action buttons -->
|
<!-- Form action buttons -->
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
<!-- Sharing profile attributes section -->
|
<!-- Sharing profile attributes section -->
|
||||||
<div class="attributes">
|
<div class="attributes">
|
||||||
<guac-form namespace="'SHARING_PROFILE_ATTRIBUTES'" content="attributes"
|
<guac-form namespace="'SHARING_PROFILE_ATTRIBUTES'" content="attributes"
|
||||||
model="sharingProfile.attributes"></guac-form>
|
model="sharingProfile.attributes" model-only="!canChangeAllAttributes()"></guac-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sharing profile parameters -->
|
<!-- Sharing profile parameters -->
|
||||||
|
@@ -41,7 +41,8 @@
|
|||||||
|
|
||||||
<!-- User attributes section -->
|
<!-- User attributes section -->
|
||||||
<div class="attributes" ng-show="canChangeAttributes()">
|
<div class="attributes" ng-show="canChangeAttributes()">
|
||||||
<guac-form namespace="'USER_ATTRIBUTES'" content="attributes" model="user.attributes"></guac-form>
|
<guac-form namespace="'USER_ATTRIBUTES'" content="attributes"
|
||||||
|
model="user.attributes" model-only="!canChangeAllAttributes()"></guac-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- System permissions section -->
|
<!-- System permissions section -->
|
||||||
|
Reference in New Issue
Block a user