diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java index 052849520..418ffad81 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java @@ -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 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.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 implements Us * logical forms. */ public static final Collection
ATTRIBUTES = Collections.unmodifiableCollection(Arrays.asList( + PROFILE, ACCOUNT_RESTRICTIONS )); @@ -371,6 +385,31 @@ public class ModeledUser extends ModeledDirectoryObject 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 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 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 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 getAttributes() { Map attributes = new HashMap(); + // 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 implements Us @Override public void setAttributes(Map attributes) { + // Always assign unrestricted attributes + setUnrestrictedAttributes(attributes); + // Assign restricted attributes only if they are exposed if (exposeRestrictedAttributes) setRestrictedAttributes(attributes); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java index 4dfb27596..2376caed9 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java @@ -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; + } + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/resources/translations/en.json b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/resources/translations/en.json index f182aacb7..bf73c35d8 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/resources/translations/en.json +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/resources/translations/en.json @@ -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" } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql index 37a825917..29bc47a7e 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql @@ -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`) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.9.13.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.9.13.sql index bb37c6c31..95bbc1c05 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.9.13.sql +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.9.13.sql @@ -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); + diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml index 3530b0b51..0ddb0517a 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml @@ -25,17 +25,21 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + @@ -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 @@ -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} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql index 199b4bdc2..f9b235108 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql @@ -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 diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.13.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.13.sql index 015ec9a6c..0fac52846 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.13.sql +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.13.sql @@ -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); + diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml index 39ec05a01..569a8ac45 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml @@ -25,18 +25,22 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -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 @@ -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} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/form/EmailField.java b/guacamole-ext/src/main/java/org/apache/guacamole/form/EmailField.java new file mode 100644 index 000000000..e56a75786 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/form/EmailField.java @@ -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); + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java b/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java index dba10654c..19f1ead09 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java @@ -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. diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/User.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/User.java index 90167e4e2..88756e495 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/User.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/User.java @@ -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. diff --git a/guacamole/src/main/webapp/app/form/services/formService.js b/guacamole/src/main/webapp/app/form/services/formService.js index 0d12259ad..c117bbf7f 100644 --- a/guacamole/src/main/webapp/app/form/services/formService.js +++ b/guacamole/src/main/webapp/app/form/services/formService.js @@ -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. * diff --git a/guacamole/src/main/webapp/app/form/templates/emailField.html b/guacamole/src/main/webapp/app/form/templates/emailField.html new file mode 100644 index 000000000..db6d3beda --- /dev/null +++ b/guacamole/src/main/webapp/app/form/templates/emailField.html @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/index/styles/input.css b/guacamole/src/main/webapp/app/index/styles/input.css index 1eb8d9b71..4141c768c 100644 --- a/guacamole/src/main/webapp/app/index/styles/input.css +++ b/guacamole/src/main/webapp/app/index/styles/input.css @@ -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; diff --git a/guacamole/src/main/webapp/app/manage/styles/attributes.css b/guacamole/src/main/webapp/app/manage/styles/attributes.css index 136ec5dee..2b5bc92fc 100644 --- a/guacamole/src/main/webapp/app/manage/styles/attributes.css +++ b/guacamole/src/main/webapp/app/manage/styles/attributes.css @@ -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; diff --git a/guacamole/src/main/webapp/app/manage/styles/connection-parameter.css b/guacamole/src/main/webapp/app/manage/styles/connection-parameter.css index a005703a1..8fe19d691 100644 --- a/guacamole/src/main/webapp/app/manage/styles/connection-parameter.css +++ b/guacamole/src/main/webapp/app/manage/styles/connection-parameter.css @@ -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; diff --git a/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js b/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js index f94d006c7..9534dd444 100644 --- a/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js +++ b/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js @@ -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'); /** @@ -55,7 +59,59 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu() * @type String */ $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. * diff --git a/guacamole/src/main/webapp/app/navigation/navigationModule.js b/guacamole/src/main/webapp/app/navigation/navigationModule.js index 332dcb8b7..24c63e3c3 100644 --- a/guacamole/src/main/webapp/app/navigation/navigationModule.js +++ b/guacamole/src/main/webapp/app/navigation/navigationModule.js @@ -22,6 +22,7 @@ */ angular.module('navigation', [ 'auth', + 'form', 'notification', 'rest' ]); diff --git a/guacamole/src/main/webapp/app/navigation/styles/user-menu.css b/guacamole/src/main/webapp/app/navigation/styles/user-menu.css index b7fce98e6..d7759acd5 100644 --- a/guacamole/src/main/webapp/app/navigation/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/navigation/styles/user-menu.css @@ -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; +} \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html b/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html index 4ffd937ca..9869cd6e8 100644 --- a/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html +++ b/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html @@ -1,6 +1,13 @@
+ +
+ +
{{ role }}
+
{{ organization }}
+
+
  • diff --git a/guacamole/src/main/webapp/app/rest/types/Field.js b/guacamole/src/main/webapp/app/rest/types/Field.js index 5204268d3..84dfe13b5 100644 --- a/guacamole/src/main/webapp/app/rest/types/Field.js +++ b/guacamole/src/main/webapp/app/rest/types/Field.js @@ -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 diff --git a/guacamole/src/main/webapp/app/rest/types/User.js b/guacamole/src/main/webapp/app/rest/types/User.js index 4a808b164..9edd1f258 100644 --- a/guacamole/src/main/webapp/app/rest/types/User.js +++ b/guacamole/src/main/webapp/app/rest/types/User.js @@ -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; }]); \ No newline at end of file diff --git a/guacamole/src/main/webapp/translations/en.json b/guacamole/src/main/webapp/translations/en.json index 512524a89..4995f54c8 100644 --- a/guacamole/src/main/webapp/translations/en.json +++ b/guacamole/src/main/webapp/translations/en.json @@ -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",