GUACAMOLE-292: Merge user profile base support.

This commit is contained in:
James Muehlner
2017-05-28 11:01:47 -07:00
24 changed files with 538 additions and 39 deletions

View File

@@ -43,8 +43,10 @@ import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionService
import org.apache.guacamole.auth.jdbc.permission.UserPermissionService;
import org.apache.guacamole.form.BooleanField;
import org.apache.guacamole.form.DateField;
import org.apache.guacamole.form.EmailField;
import org.apache.guacamole.form.Field;
import org.apache.guacamole.form.Form;
import org.apache.guacamole.form.TextField;
import org.apache.guacamole.form.TimeField;
import org.apache.guacamole.form.TimeZoneField;
import org.apache.guacamole.net.auth.User;
@@ -106,6 +108,17 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
*/
public static final String TIMEZONE_ATTRIBUTE_NAME = "timezone";
/**
* All attributes related to user profile information, within a logical
* form.
*/
public static final Form PROFILE = new Form("profile", Arrays.<Field>asList(
new TextField(User.Attribute.FULL_NAME),
new EmailField(User.Attribute.EMAIL_ADDRESS),
new TextField(User.Attribute.ORGANIZATION),
new TextField(User.Attribute.ORGANIZATIONAL_ROLE)
));
/**
* All attributes related to restricting user accounts, within a logical
* form.
@@ -125,6 +138,7 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
* logical forms.
*/
public static final Collection<Form> ATTRIBUTES = Collections.unmodifiableCollection(Arrays.asList(
PROFILE,
ACCOUNT_RESTRICTIONS
));
@@ -371,6 +385,31 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
}
/**
* Stores all unrestricted (unprivileged) attributes within the given Map,
* pulling the values of those attributes from the underlying user model.
* If no value is yet defined for an attribute, that attribute will be set
* to null.
*
* @param attributes
* The Map to store all unrestricted attributes within.
*/
private void putUnrestrictedAttributes(Map<String, String> attributes) {
// Set full name attribute
attributes.put(User.Attribute.FULL_NAME, getModel().getFullName());
// Set email address attribute
attributes.put(User.Attribute.EMAIL_ADDRESS, getModel().getEmailAddress());
// Set organization attribute
attributes.put(User.Attribute.ORGANIZATION, getModel().getOrganization());
// Set role attribute
attributes.put(User.Attribute.ORGANIZATIONAL_ROLE, getModel().getOrganizationalRole());
}
/**
* Parses the given string into a corresponding date. The string must
* follow the standard format used by date attributes, as defined by
@@ -477,11 +516,37 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
}
/**
* Stores all unrestricted (unprivileged) attributes within the underlying
* user model, pulling the values of those attributes from the given Map.
*
* @param attributes
* The Map to pull all unrestricted attributes from.
*/
private void setUnrestrictedAttributes(Map<String, String> attributes) {
// Translate full name attribute
getModel().setFullName(attributes.get(User.Attribute.FULL_NAME));
// Translate email address attribute
getModel().setEmailAddress(attributes.get(User.Attribute.EMAIL_ADDRESS));
// Translate organization attribute
getModel().setOrganization(attributes.get(User.Attribute.ORGANIZATION));
// Translate role attribute
getModel().setOrganizationalRole(attributes.get(User.Attribute.ORGANIZATIONAL_ROLE));
}
@Override
public Map<String, String> getAttributes() {
Map<String, String> attributes = new HashMap<String, String>();
// Always include unrestricted attributes
putUnrestrictedAttributes(attributes);
// Include restricted attributes only if they should be exposed
if (exposeRestrictedAttributes)
putRestrictedAttributes(attributes);
@@ -492,6 +557,9 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
@Override
public void setAttributes(Map<String, String> attributes) {
// Always assign unrestricted attributes
setUnrestrictedAttributes(attributes);
// Assign restricted attributes only if they are exposed
if (exposeRestrictedAttributes)
setRestrictedAttributes(attributes);

View File

@@ -92,6 +92,28 @@ public class UserModel extends ObjectModel {
*/
private String timeZone;
/**
* The user's full name, or null if this is not known.
*/
private String fullName;
/**
* The email address of the user, or null if this is not known.
*/
private String emailAddress;
/**
* The organization, company, group, etc. that the user belongs to, or null
* if this is not known.
*/
private String organization;
/**
* The role that the user has at the organization, company, group, etc.
* they belong to, or null if this is not known.
*/
private String organizationalRole;
/**
* Creates a new, empty user.
*/
@@ -351,4 +373,93 @@ public class UserModel extends ObjectModel {
this.timeZone = timeZone;
}
/**
* Returns the user's full name, if known. If not available, null is
* returned.
*
* @return
* The user's full name, or null if this is not known.
*/
public String getFullName() {
return fullName;
}
/**
* Sets the user's full name.
*
* @param fullName
* The user's full name, or null if this is not known.
*/
public void setFullName(String fullName) {
this.fullName = fullName;
}
/**
* Returns the email address of the user, if known. If not available, null
* is returned.
*
* @return
* The email address of the user, or null if this is not known.
*/
public String getEmailAddress() {
return emailAddress;
}
/**
* Sets the email address of the user.
*
* @param emailAddress
* The email address of the user, or null if this is not known.
*/
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
/**
* Returns the organization, company, group, etc. that the user belongs to,
* if known. If not available, null is returned.
*
* @return
* The organization, company, group, etc. that the user belongs to, or
* null if this is not known.
*/
public String getOrganization() {
return organization;
}
/**
* Sets the organization, company, group, etc. that the user belongs to.
*
* @param organization
* The organization, company, group, etc. that the user belongs to, or
* null if this is not known.
*/
public void setOrganization(String organization) {
this.organization = organization;
}
/**
* Returns the role that the user has at the organization, company, group,
* etc. they belong to. If not available, null is returned.
*
* @return
* The role that the user has at the organization, company, group, etc.
* they belong to, or null if this is not known.
*/
public String getOrganizationalRole() {
return organizationalRole;
}
/**
* Sets the role that the user has at the organization, company, group,
* etc. they belong to.
*
* @param organizationalRole
* The role that the user has at the organization, company, group, etc.
* they belong to, or null if this is not known.
*/
public void setOrganizationalRole(String organizationalRole) {
this.organizationalRole = organizationalRole;
}
}

View File

@@ -85,7 +85,8 @@
"FIELD_HEADER_VALID_FROM" : "Enable account after:",
"FIELD_HEADER_VALID_UNTIL" : "Disable account after:",
"SECTION_HEADER_RESTRICTIONS" : "Account Restrictions"
"SECTION_HEADER_RESTRICTIONS" : "Account Restrictions",
"SECTION_HEADER_PROFILE" : "Profile"
}

View File

@@ -102,6 +102,12 @@ CREATE TABLE `guacamole_user` (
-- Timezone used for all date/time comparisons and interpretation
`timezone` VARCHAR(64),
-- Profile information
`full_name` VARCHAR(256),
`email_address` VARCHAR(256),
`organization` VARCHAR(256),
`organizational_role` VARCHAR(256),
PRIMARY KEY (`user_id`),
UNIQUE KEY `username` (`username`)

View File

@@ -28,3 +28,13 @@ ALTER TABLE guacamole_connection ADD COLUMN proxy_encryption_method ENUM(
'NONE',
'SSL'
);
--
-- Add new user profile columns
--
ALTER TABLE guacamole_user ADD COLUMN full_name VARCHAR(256);
ALTER TABLE guacamole_user ADD COLUMN email_address VARCHAR(256);
ALTER TABLE guacamole_user ADD COLUMN organization VARCHAR(256);
ALTER TABLE guacamole_user ADD COLUMN organizational_role VARCHAR(256);

View File

@@ -25,17 +25,21 @@
<!-- Result mapper for user objects -->
<resultMap id="UserResultMap" type="org.apache.guacamole.auth.jdbc.user.UserModel" >
<id column="user_id" property="objectID" jdbcType="INTEGER"/>
<result column="username" property="identifier" jdbcType="VARCHAR"/>
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
<result column="password_date" property="passwordDate" jdbcType="TIMESTAMP"/>
<result column="disabled" property="disabled" jdbcType="BOOLEAN"/>
<result column="access_window_start" property="accessWindowStart" jdbcType="TIME"/>
<result column="access_window_end" property="accessWindowEnd" jdbcType="TIME"/>
<result column="valid_from" property="validFrom" jdbcType="DATE"/>
<result column="valid_until" property="validUntil" jdbcType="DATE"/>
<result column="timezone" property="timeZone" jdbcType="VARCHAR"/>
<id column="user_id" property="objectID" jdbcType="INTEGER"/>
<result column="username" property="identifier" jdbcType="VARCHAR"/>
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
<result column="password_date" property="passwordDate" jdbcType="TIMESTAMP"/>
<result column="disabled" property="disabled" jdbcType="BOOLEAN"/>
<result column="access_window_start" property="accessWindowStart" jdbcType="TIME"/>
<result column="access_window_end" property="accessWindowEnd" jdbcType="TIME"/>
<result column="valid_from" property="validFrom" jdbcType="DATE"/>
<result column="valid_until" property="validUntil" jdbcType="DATE"/>
<result column="timezone" property="timeZone" jdbcType="VARCHAR"/>
<result column="full_name" property="fullName" jdbcType="VARCHAR"/>
<result column="email_address" property="emailAddress" jdbcType="VARCHAR"/>
<result column="organization" property="organization" jdbcType="VARCHAR"/>
<result column="organizational_role" property="organizationalRole" jdbcType="VARCHAR"/>
</resultMap>
<!-- Select all usernames -->
@@ -69,7 +73,11 @@
access_window_end,
valid_from,
valid_until,
timezone
timezone,
full_name,
email_address,
organization,
organizational_role
FROM guacamole_user
WHERE username IN
<foreach collection="identifiers" item="identifier"
@@ -94,7 +102,11 @@
access_window_end,
valid_from,
valid_until,
timezone
timezone,
full_name,
email_address,
organization,
organizational_role
FROM guacamole_user
JOIN guacamole_user_permission ON affected_user_id = guacamole_user.user_id
WHERE username IN
@@ -122,7 +134,11 @@
access_window_end,
valid_from,
valid_until,
timezone
timezone,
full_name,
email_address,
organization,
organizational_role
FROM guacamole_user
WHERE
username = #{username,jdbcType=VARCHAR}
@@ -150,7 +166,11 @@
access_window_end,
valid_from,
valid_until,
timezone
timezone,
full_name,
email_address,
organization,
organizational_role
)
VALUES (
#{object.identifier,jdbcType=VARCHAR},
@@ -163,7 +183,11 @@
#{object.accessWindowEnd,jdbcType=TIME},
#{object.validFrom,jdbcType=DATE},
#{object.validUntil,jdbcType=DATE},
#{object.timeZone,jdbcType=VARCHAR}
#{object.timeZone,jdbcType=VARCHAR},
#{object.fullName,jdbcType=VARCHAR},
#{object.emailAddress,jdbcType=VARCHAR},
#{object.organization,jdbcType=VARCHAR},
#{object.organizationalRole,jdbcType=VARCHAR}
)
</insert>
@@ -180,7 +204,11 @@
access_window_end = #{object.accessWindowEnd,jdbcType=TIME},
valid_from = #{object.validFrom,jdbcType=DATE},
valid_until = #{object.validUntil,jdbcType=DATE},
timezone = #{object.timeZone,jdbcType=VARCHAR}
timezone = #{object.timeZone,jdbcType=VARCHAR},
full_name = #{object.fullName,jdbcType=VARCHAR},
email_address = #{object.emailAddress,jdbcType=VARCHAR},
organization = #{object.organization,jdbcType=VARCHAR},
organizational_role = #{object.organizationalRole,jdbcType=VARCHAR}
WHERE user_id = #{object.objectID,jdbcType=VARCHAR}
</update>

View File

@@ -143,6 +143,12 @@ CREATE TABLE guacamole_user (
-- Timezone used for all date/time comparisons and interpretation
timezone varchar(64),
-- Profile information
full_name varchar(256),
email_address varchar(256),
organization varchar(256),
organizational_role varchar(256),
PRIMARY KEY (user_id),
CONSTRAINT username

View File

@@ -33,3 +33,13 @@ CREATE TYPE guacamole_proxy_encryption_method AS ENUM(
ALTER TABLE guacamole_connection ADD COLUMN proxy_port integer;
ALTER TABLE guacamole_connection ADD COLUMN proxy_hostname varchar(512);
ALTER TABLE guacamole_connection ADD COLUMN proxy_encryption_method guacamole_proxy_encryption_method;
--
-- Add new user profile columns
--
ALTER TABLE guacamole_user ADD COLUMN full_name VARCHAR(256);
ALTER TABLE guacamole_user ADD COLUMN email_address VARCHAR(256);
ALTER TABLE guacamole_user ADD COLUMN organization VARCHAR(256);
ALTER TABLE guacamole_user ADD COLUMN organizational_role VARCHAR(256);

View File

@@ -25,18 +25,22 @@
<!-- Result mapper for user objects -->
<resultMap id="UserResultMap" type="org.apache.guacamole.auth.jdbc.user.UserModel" >
<id column="user_id" property="objectID" jdbcType="INTEGER"/>
<result column="username" property="identifier" jdbcType="VARCHAR"/>
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
<result column="password_date" property="passwordDate" jdbcType="TIMESTAMP"/>
<result column="disabled" property="disabled" jdbcType="BOOLEAN"/>
<result column="expired" property="expired" jdbcType="BOOLEAN"/>
<result column="access_window_start" property="accessWindowStart" jdbcType="TIME"/>
<result column="access_window_end" property="accessWindowEnd" jdbcType="TIME"/>
<result column="valid_from" property="validFrom" jdbcType="DATE"/>
<result column="valid_until" property="validUntil" jdbcType="DATE"/>
<result column="timezone" property="timeZone" jdbcType="VARCHAR"/>
<id column="user_id" property="objectID" jdbcType="INTEGER"/>
<result column="username" property="identifier" jdbcType="VARCHAR"/>
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
<result column="password_date" property="passwordDate" jdbcType="TIMESTAMP"/>
<result column="disabled" property="disabled" jdbcType="BOOLEAN"/>
<result column="expired" property="expired" jdbcType="BOOLEAN"/>
<result column="access_window_start" property="accessWindowStart" jdbcType="TIME"/>
<result column="access_window_end" property="accessWindowEnd" jdbcType="TIME"/>
<result column="valid_from" property="validFrom" jdbcType="DATE"/>
<result column="valid_until" property="validUntil" jdbcType="DATE"/>
<result column="timezone" property="timeZone" jdbcType="VARCHAR"/>
<result column="full_name" property="fullName" jdbcType="VARCHAR"/>
<result column="email_address" property="emailAddress" jdbcType="VARCHAR"/>
<result column="organization" property="organization" jdbcType="VARCHAR"/>
<result column="organizational_role" property="organizationalRole" jdbcType="VARCHAR"/>
</resultMap>
<!-- Select all usernames -->
@@ -70,7 +74,11 @@
access_window_end,
valid_from,
valid_until,
timezone
timezone,
full_name,
email_address,
organization,
organizational_role
FROM guacamole_user
WHERE username IN
<foreach collection="identifiers" item="identifier"
@@ -95,7 +103,11 @@
access_window_end,
valid_from,
valid_until,
timezone
timezone,
full_name,
email_address,
organization,
organizational_role
FROM guacamole_user
JOIN guacamole_user_permission ON affected_user_id = guacamole_user.user_id
WHERE username IN
@@ -123,7 +135,11 @@
access_window_end,
valid_from,
valid_until,
timezone
timezone,
full_name,
email_address,
organization,
organizational_role
FROM guacamole_user
WHERE
username = #{username,jdbcType=VARCHAR}
@@ -151,7 +167,11 @@
access_window_end,
valid_from,
valid_until,
timezone
timezone,
full_name,
email_address,
organization,
organizational_role
)
VALUES (
#{object.identifier,jdbcType=VARCHAR},
@@ -164,7 +184,11 @@
#{object.accessWindowEnd,jdbcType=TIME},
#{object.validFrom,jdbcType=DATE},
#{object.validUntil,jdbcType=DATE},
#{object.timeZone,jdbcType=VARCHAR}
#{object.timeZone,jdbcType=VARCHAR},
#{object.fullName,jdbcType=VARCHAR},
#{object.emailAddress,jdbcType=VARCHAR},
#{object.organization,jdbcType=VARCHAR},
#{object.organizationalRole,jdbcType=VARCHAR}
)
</insert>
@@ -181,7 +205,11 @@
access_window_end = #{object.accessWindowEnd,jdbcType=TIME},
valid_from = #{object.validFrom,jdbcType=DATE},
valid_until = #{object.validUntil,jdbcType=DATE},
timezone = #{object.timeZone,jdbcType=VARCHAR}
timezone = #{object.timeZone,jdbcType=VARCHAR},
full_name = #{object.fullName,jdbcType=VARCHAR},
email_address = #{object.emailAddress,jdbcType=VARCHAR},
organization = #{object.organization,jdbcType=VARCHAR},
organizational_role = #{object.organizationalRole,jdbcType=VARCHAR}
WHERE user_id = #{object.objectID,jdbcType=VARCHAR}
</update>

View File

@@ -0,0 +1,37 @@
/*
* 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.form;
/**
* Represents a text field which may contain an email address.
*/
public class EmailField extends Field {
/**
* Creates a new EmailField with the given name.
*
* @param name
* The unique name to associate with this field.
*/
public EmailField(String name) {
super(name, Field.Type.EMAIL);
}
}

View File

@@ -47,6 +47,12 @@ public class Field {
*/
public static String TEXT = "TEXT";
/**
* An email address field. This field type generally behaves
* identically to arbitrary text fields, but has semantic differences.
*/
public static String EMAIL = "EMAIL";
/**
* A username field. This field type generally behaves identically to
* arbitrary text fields, but has semantic differences.

View File

@@ -30,6 +30,38 @@ import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
*/
public interface User extends Identifiable {
/**
* All standard attribute names with semantics defined by the Guacamole web
* application. Extensions may additionally define their own attributes
* with completely arbitrary names and semantics, so long as those names do
* not conflict with the names listed here. All standard attribute names
* have a "guac-" prefix to avoid such conflicts.
*/
public static class Attribute {
/**
* The user's full name.
*/
public static String FULL_NAME = "guac-full-name";
/**
* The email address of the user.
*/
public static String EMAIL_ADDRESS = "guac-email-address";
/**
* The organization, company, group, etc. that the user belongs to.
*/
public static String ORGANIZATION = "guac-organization";
/**
* The role that the user has at the organization, company, group, etc.
* they belong to.
*/
public static String ORGANIZATIONAL_ROLE = "guac-organizational-role";
}
/**
* Returns this user's password. Note that the password returned may be
* hashed or completely arbitrary.

View File

@@ -47,6 +47,16 @@ angular.module('form').provider('formService', function formServiceProvider() {
templateUrl : 'app/form/templates/textField.html'
},
/**
* Email address field type.
*
* @see {@link Field.Type.EMAIL}
* @type FieldType
*/
'EMAIL' : {
templateUrl : 'app/form/templates/emailField.html'
},
/**
* Numeric field type.
*

View File

@@ -0,0 +1,8 @@
<div class="email-field">
<input type="email"
ng-model="model"
ng-hide="readOnly"
autocorrect="off"
autocapitalize="off"/>
<a href="mailto:{{model}}" ng-show="readOnly">{{model}}</a>
</div>

View File

@@ -17,11 +17,11 @@
* under the License.
*/
input[type=checkbox], input[type=number], input[type=text], input[type=radio], label, textarea {
input[type=checkbox], input[type=number], input[type=text], input[type=email], input[type=radio], label, textarea {
-webkit-tap-highlight-color: rgba(128,192,128,0.5);
}
div.location, input[type=text], input[type=number], input[type=password], textarea {
div.location, input[type=text], input[type=email], input[type=number], input[type=password], textarea {
border: 1px solid #777;
-moz-border-radius: 0.2em;
-webkit-border-radius: 0.2em;

View File

@@ -19,6 +19,7 @@
/* Do not stretch attributes to fit available area */
.attributes input[type=text],
.attributes input[type=email],
.attributes input[type=password],
.attributes input[type=number] {
width: auto;

View File

@@ -19,6 +19,7 @@
/* Do not stretch connection parameters to fit available area */
.connection-parameters input[type=text],
.connection-parameters input[type=email],
.connection-parameters input[type=password],
.connection-parameters input[type=number] {
width: auto;

View File

@@ -43,10 +43,14 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu()
controller: ['$scope', '$injector',
function guacUserMenuController($scope, $injector) {
// Required types
var User = $injector.get('User');
// Get required services
var $location = $injector.get('$location');
var $route = $injector.get('$route');
var authenticationService = $injector.get('authenticationService');
var userService = $injector.get('userService');
var userPageService = $injector.get('userPageService');
/**
@@ -56,6 +60,58 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu()
*/
$scope.username = authenticationService.getCurrentUsername();
/**
* The user's full name. If not yet available, or if not defined,
* this will be null.
*
* @type String
*/
$scope.fullName = null;
/**
* A URL pointing to relevant user information such as the user's
* email address. If not yet available, or if no such URL can be
* determined, this will be null.
*
* @type String
*/
$scope.userURL = null;
/**
* The organization, company, group, etc. that the user belongs to.
* If not yet available, or if not defined, this will be null.
*
* @type String
*/
$scope.organization = null;
/**
* The role that the user has at the organization, company, group,
* etc. they belong to. If not yet available, or if not defined,
* this will be null.
*
* @type String
*/
$scope.role = null;
// Pull user data
userService.getUser(authenticationService.getDataSource(), $scope.username)
.success(function userRetrieved(user) {
// Store retrieved user object
$scope.user = user;
// Pull basic profile information
$scope.fullName = user.attributes[User.Attributes.FULL_NAME];
$scope.organization = user.attributes[User.Attributes.ORGANIZATION];
$scope.role = user.attributes[User.Attributes.ORGANIZATIONAL_ROLE];
// Link to email address if available
var email = user.attributes[User.Attributes.EMAIL_ADDRESS];
$scope.userURL = email ? 'mailto:' + email : null;
});
/**
* The available main pages for the current user.
*

View File

@@ -22,6 +22,7 @@
*/
angular.module('navigation', [
'auth',
'form',
'notification',
'rest'
]);

View File

@@ -82,3 +82,18 @@
.user-menu .menu-dropdown .menu-contents li a.logout {
background-image: url('images/action-icons/guac-logout-dark.png');
}
.user-menu .menu-dropdown .menu-contents .profile {
margin: 1em;
padding-bottom: 1em;
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
width: 2in;
}
.user-menu .menu-dropdown .menu-contents .profile .full-name {
font-weight: bold;
}
.user-menu .menu-dropdown .menu-contents .profile .organization,
.user-menu .menu-dropdown .menu-contents .profile .organizational-role {
font-size: 0.8em;
}

View File

@@ -1,6 +1,13 @@
<div class="user-menu" ng-show="!isAnonymous()">
<guac-menu menu-title="username">
<!-- User profile view -->
<div class="profile" ng-show="fullName">
<div class="full-name"><a ng-href="{{userURL}}">{{ fullName }}</a></div>
<div class="organizational-role" ng-show="role">{{ role }}</div>
<div class="organization" ng-show="organization">{{ organization }}</div>
</div>
<!-- Local actions -->
<ul class="action-list">
<li ng-repeat="action in localActions">

View File

@@ -75,6 +75,14 @@ angular.module('rest').factory('Field', [function defineField() {
*/
TEXT : 'TEXT',
/**
* The type string associated with parameters that may contain an email
* address.
*
* @type String
*/
EMAIL : 'EMAIL',
/**
* The type string associated with parameters that may contain an
* arbitrary string, where that string represents the username of the

View File

@@ -64,6 +64,46 @@ angular.module('rest').factory('User', [function defineUser() {
};
/**
* All standard attribute names with semantics defined by the Guacamole web
* application. Extensions may additionally define their own attributes
* with completely arbitrary names and semantics, so long as those names do
* not conflict with the names listed here. All standard attribute names
* have a "guac-" prefix to avoid such conflicts.
*/
User.Attributes = {
/**
* The user's full name.
*
* @type String
*/
FULL_NAME : 'guac-full-name',
/**
* The email address of the user.
*
* @type String
*/
EMAIL_ADDRESS : 'guac-email-address',
/**
* The organization, company, group, etc. that the user belongs to.
*
* @type String
*/
ORGANIZATION : 'guac-organization',
/**
* The role that the user has at the organization, company, group, etc.
* they belong to.
*
* @type String
*/
ORGANIZATIONAL_ROLE : 'guac-organizational-role'
};
return User;
}]);

View File

@@ -697,6 +697,15 @@
},
"USER_ATTRIBUTES" : {
"FIELD_HEADER_GUAC_EMAIL_ADDRESS" : "Email address:",
"FIELD_HEADER_GUAC_FULL_NAME" : "Full name:",
"FIELD_HEADER_GUAC_ORGANIZATION" : "Organization:",
"FIELD_HEADER_GUAC_ORGANIZATIONAL_ROLE" : "Role:"
},
"USER_MENU" : {
"ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT",