diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/CASAuthenticationProvider.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/CASAuthenticationProvider.java index 28122f9b8..5c80bcaf5 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/CASAuthenticationProvider.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/CASAuthenticationProvider.java @@ -107,6 +107,20 @@ public class CASAuthenticationProvider implements AuthenticationProvider { } + @Override + public UserContext decorate(UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + @Override public void shutdown() { // Do nothing diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java index 1c84046cc..7d27a75fc 100644 --- a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java +++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java @@ -102,6 +102,20 @@ public class DuoAuthenticationProvider implements AuthenticationProvider { return context; } + @Override + public UserContext decorate(UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + @Override public void shutdown() { // Do nothing diff --git a/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/HTTPHeaderAuthenticationProvider.java b/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/HTTPHeaderAuthenticationProvider.java index b3385b1db..ca19b3999 100644 --- a/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/HTTPHeaderAuthenticationProvider.java +++ b/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/HTTPHeaderAuthenticationProvider.java @@ -107,6 +107,20 @@ public class HTTPHeaderAuthenticationProvider implements AuthenticationProvider } + @Override + public UserContext decorate(UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + @Override public void shutdown() { // Do nothing diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/InjectedAuthenticationProvider.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/InjectedAuthenticationProvider.java index e73b3dfce..0e3e84b41 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/InjectedAuthenticationProvider.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/InjectedAuthenticationProvider.java @@ -104,6 +104,20 @@ public abstract class InjectedAuthenticationProvider implements AuthenticationPr authenticatedUser, credentials); } + @Override + public UserContext decorate(UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + @Override public void shutdown() { // Do nothing diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProvider.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProvider.java index f9c4a7d2c..2d8dfd748 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProvider.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProvider.java @@ -103,6 +103,20 @@ public class LDAPAuthenticationProvider implements AuthenticationProvider { return context; } + @Override + public UserContext decorate(UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + @Override public void shutdown() { // Do nothing diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProvider.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProvider.java index 57b483183..cf0b96e41 100644 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProvider.java +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProvider.java @@ -107,6 +107,20 @@ public class OpenIDAuthenticationProvider implements AuthenticationProvider { } + @Override + public UserContext decorate(UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + @Override public void shutdown() { // Do nothing diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticationProvider.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticationProvider.java index 448f495f1..fd7d84478 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticationProvider.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticationProvider.java @@ -168,6 +168,79 @@ public interface AuthenticationProvider { AuthenticatedUser authenticatedUser, Credentials credentials) throws GuacamoleException; + /** + * Given a UserContext returned from getUserContext() of a different + * AuthenticationProvider, returns a UserContext instance which decorates + * (wraps) that UserContext, delegating and overriding implemented + * functions as necessary. Each UserContext created via getUserContext() + * will be passed to the decorate() functions of all other + * AuthenticationProviders, allowing those AuthenticationProviders to + * augment (or perhaps even limit) the functionality or data provided. + * + * @param context + * An existing UserContext generated by getUserContext() of a different + * AuthenticationProvider. + * + * @param authenticatedUser + * The AuthenticatedUser object representing the user associated with + * the given UserContext. + * + * @param credentials + * The credentials which were most recently submitted for the given + * AuthenticatedUser. These are not guaranteed to be the same as the + * credentials associated with the AuthenticatedUser object, which are + * the credentials provided when the user originally authenticated. + * + * @return + * A decorated (wrapped) UserContext object, or the original, + * undecorated UserContext. + * + * @throws GuacamoleException + * If the UserContext cannot be decorated due to an error. + */ + UserContext decorate(UserContext context, + AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException; + + /** + * Given a UserContext returned by updateUserContext() of a different + * AuthenticationProvider, returns a UserContext instance which decorates + * (wraps) that UserContext, delegating and overriding implemented + * functions as necessary. Each UserContext created via updateUserContext() + * will be passed to the decorate() functions of all other + * AuthenticationProviders, allowing those AuthenticationProviders to + * augment (or perhaps even limit) the functionality or data provided. + * + * @param decorated + * The UserContext returned when decorate() was invoked on this + * AuthenticationProvider for the UserContext which was just updated + * via a call to updateUserContext(). + * + * @param context + * An existing UserContext generated by updateUserContext() of a + * different AuthenticationProvider. + * + * @param authenticatedUser + * The AuthenticatedUser object representing the user associated with + * the given UserContext. + * + * @param credentials + * The credentials which were most recently submitted for the given + * AuthenticatedUser. These are not guaranteed to be the same as the + * credentials associated with the AuthenticatedUser object, which are + * the credentials provided when the user originally authenticated. + * + * @return + * A decorated (wrapped) UserContext object, or the original, + * undecorated UserContext. + * + * @throws GuacamoleException + * If the UserContext cannot be decorated due to an error. + */ + UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException; + /** * Frees all resources associated with this AuthenticationProvider. This * function will be automatically invoked when the Guacamole server is diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DecoratingDirectory.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DecoratingDirectory.java new file mode 100644 index 000000000..dc35e3b6c --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DecoratingDirectory.java @@ -0,0 +1,134 @@ +/* + * 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.net.auth; + +import java.util.ArrayList; +import java.util.Collection; +import org.apache.guacamole.GuacamoleException; + +/** + * Directory implementation which simplifies decorating the objects within an + * underlying Directory. The decorate() and undecorate() functions must be + * implemented to define how each object is decorated, and how that decoration + * may be removed. + * + * @param + * The type of objects stored within this Directory. + */ +public abstract class DecoratingDirectory + extends DelegatingDirectory { + + /** + * Creates a new DecoratingDirectory which decorates the objects within + * the given directory. + * + * @param directory + * The Directory whose objects are being decorated. + */ + public DecoratingDirectory(Directory directory) { + super(directory); + } + + /** + * Given an object retrieved from a Directory which originates from a + * different AuthenticationProvider, returns an identical type of object + * optionally wrapped with additional information, functionality, etc. If + * this directory chooses to decorate the object provided, it is up to the + * implementation of that decorated object to properly pass through + * operations as appropriate, as well as provide for an eventual + * undecorate() operation. All objects retrieved from this + * DecoratingDirectory will first be passed through this function. + * + * @param object + * An object from a Directory which originates from a different + * AuthenticationProvider. + * + * @return + * An object which may have been decorated by this + * DecoratingDirectory. If the object was not decorated, the original, + * unmodified object may be returned instead. + * + * @throws GuacamoleException + * If the provided object cannot be decorated due to an error. + */ + protected abstract ObjectType decorate(ObjectType object) + throws GuacamoleException; + + /** + * Given an object originally returned from a call to this + * DecoratingDirectory's decorate() function, reverses the decoration + * operation, returning the original object. This function is effectively + * the exact inverse of the decorate() function. The return value of + * undecorate(decorate(X)) must be identically X. All objects given to this + * DecoratingDirectory via add() or update() will first be passed through + * this function. + * + * @param object + * An object which was originally returned by a call to this + * DecoratingDirectory's decorate() function. + * + * @return + * The original object which was provided to this DecoratingDirectory's + * decorate() function. + * + * @throws GuacamoleException + * If the provided object cannot be undecorated due to an error. + */ + protected abstract ObjectType undecorate(ObjectType object) + throws GuacamoleException; + + @Override + public ObjectType get(String identifier) throws GuacamoleException { + + // Decorate only if object exists + ObjectType object = super.get(identifier); + if (object != null) + return decorate(object); + + return null; + + } + + @Override + public Collection getAll(Collection identifiers) + throws GuacamoleException { + + Collection objects = super.getAll(identifiers); + + // Decorate all retrieved objects, if any + Collection decorated = new ArrayList(objects.size()); + for (ObjectType object : objects) + decorated.add(decorate(object)); + + return decorated; + + } + + @Override + public void add(ObjectType object) throws GuacamoleException { + super.add(decorate(object)); + } + + @Override + public void update(ObjectType object) throws GuacamoleException { + super.update(undecorate(object)); + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnection.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnection.java new file mode 100644 index 000000000..c5e9a1f9a --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnection.java @@ -0,0 +1,131 @@ +/* + * 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.net.auth; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.GuacamoleTunnel; +import org.apache.guacamole.protocol.GuacamoleClientInformation; +import org.apache.guacamole.protocol.GuacamoleConfiguration; + +/** + * Connection implementation which simply delegates all function calls to an + * underlying Connection. + */ +public class DelegatingConnection implements Connection { + + /** + * The wrapped Connection. + */ + private final Connection connection; + + /** + * Wraps the given Connection such that all function calls against this + * DelegatingConnection will be delegated to it. + * + * @param connection + * The Connection to wrap. + */ + public DelegatingConnection(Connection connection) { + this.connection = connection; + } + + @Override + public String getIdentifier() { + return connection.getIdentifier(); + } + + @Override + public void setIdentifier(String identifier) { + connection.setIdentifier(identifier); + } + + @Override + public String getName() { + return connection.getName(); + } + + @Override + public void setName(String name) { + connection.setName(name); + } + + @Override + public String getParentIdentifier() { + return connection.getParentIdentifier(); + } + + @Override + public void setParentIdentifier(String parentIdentifier) { + connection.setParentIdentifier(parentIdentifier); + } + + @Override + public GuacamoleConfiguration getConfiguration() { + return connection.getConfiguration(); + } + + @Override + public void setConfiguration(GuacamoleConfiguration config) { + connection.setConfiguration(config); + } + + @Override + public Map getAttributes() { + return connection.getAttributes(); + } + + @Override + public void setAttributes(Map attributes) { + connection.setAttributes(attributes); + } + + @Override + public Date getLastActive() { + return connection.getLastActive(); + } + + @Override + public List getHistory() + throws GuacamoleException { + return connection.getHistory(); + } + + @Override + public Set getSharingProfileIdentifiers() + throws GuacamoleException { + return connection.getSharingProfileIdentifiers(); + } + + @Override + public GuacamoleTunnel connect(GuacamoleClientInformation info) + throws GuacamoleException { + return connection.connect(info); + } + + @Override + public int getActiveConnections() { + return connection.getActiveConnections(); + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnectionGroup.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnectionGroup.java new file mode 100644 index 000000000..9f71ebbda --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnectionGroup.java @@ -0,0 +1,120 @@ +/* + * 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.net.auth; + +import java.util.Map; +import java.util.Set; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.GuacamoleTunnel; +import org.apache.guacamole.protocol.GuacamoleClientInformation; + +/** + * ConnectionGroup implementation which simply delegates all function calls to + * an underlying ConnectionGroup. + */ +public class DelegatingConnectionGroup implements ConnectionGroup { + + /** + * The wrapped ConnectionGroup. + */ + private final ConnectionGroup connectionGroup; + + /** + * Wraps the given ConnectionGroup such that all function calls against this + * DelegatingConnectionGroup will be delegated to it. + * + * @param connectionGroup + * The ConnectionGroup to wrap. + */ + public DelegatingConnectionGroup(ConnectionGroup connectionGroup) { + this.connectionGroup = connectionGroup; + } + + @Override + public String getIdentifier() { + return connectionGroup.getIdentifier(); + } + + @Override + public void setIdentifier(String identifier) { + connectionGroup.setIdentifier(identifier); + } + + @Override + public String getName() { + return connectionGroup.getName(); + } + + @Override + public void setName(String name) { + connectionGroup.setName(name); + } + + @Override + public String getParentIdentifier() { + return connectionGroup.getParentIdentifier(); + } + + @Override + public void setParentIdentifier(String parentIdentifier) { + connectionGroup.setParentIdentifier(parentIdentifier); + } + + @Override + public void setType(Type type) { + connectionGroup.setType(type); + } + + @Override + public Type getType() { + return connectionGroup.getType(); + } + + @Override + public Set getConnectionIdentifiers() throws GuacamoleException { + return connectionGroup.getConnectionIdentifiers(); + } + + @Override + public Set getConnectionGroupIdentifiers() throws GuacamoleException { + return connectionGroup.getConnectionGroupIdentifiers(); + } + + @Override + public Map getAttributes() { + return connectionGroup.getAttributes(); + } + + @Override + public void setAttributes(Map attributes) { + connectionGroup.setAttributes(attributes); + } + + @Override + public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException { + return connectionGroup.connect(info); + } + + @Override + public int getActiveConnections() { + return connectionGroup.getActiveConnections(); + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingDirectory.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingDirectory.java new file mode 100644 index 000000000..f1bf27494 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingDirectory.java @@ -0,0 +1,83 @@ +/* + * 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.net.auth; + +import java.util.Collection; +import java.util.Set; +import org.apache.guacamole.GuacamoleException; + +/** + * Directory implementation which simply delegates all function calls to an + * underlying Directory. + * + * @param + * The type of objects stored within this Directory. + */ +public class DelegatingDirectory + implements Directory { + + /** + * The wrapped Directory. + */ + private final Directory directory; + + /** + * Wraps the given Directory such that all function calls against this + * DelegatingDirectory will be delegated to it. + * + * @param directory + * The directory to wrap. + */ + public DelegatingDirectory(Directory directory) { + this.directory = directory; + } + + @Override + public ObjectType get(String identifier) throws GuacamoleException { + return directory.get(identifier); + } + + @Override + public Collection getAll(Collection identifiers) + throws GuacamoleException { + return directory.getAll(identifiers); + } + + @Override + public Set getIdentifiers() throws GuacamoleException { + return directory.getIdentifiers(); + } + + @Override + public void add(ObjectType object) throws GuacamoleException { + directory.add(object); + } + + @Override + public void update(ObjectType object) throws GuacamoleException { + directory.update(object); + } + + @Override + public void remove(String identifier) throws GuacamoleException { + directory.remove(identifier); + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingSharingProfile.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingSharingProfile.java new file mode 100644 index 000000000..576e882de --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingSharingProfile.java @@ -0,0 +1,96 @@ +/* + * 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.net.auth; + +import java.util.Map; + +/** + * SharingProfile implementation which simply delegates all function calls to an + * underlying SharingProfile. + */ +public class DelegatingSharingProfile implements SharingProfile { + + /** + * The wrapped SharingProfile. + */ + private final SharingProfile sharingProfile; + + /** + * Wraps the given SharingProfile such that all function calls against this + * DelegatingSharingProfile will be delegated to it. + * + * @param sharingProfile + * The SharingProfile to wrap. + */ + public DelegatingSharingProfile(SharingProfile sharingProfile) { + this.sharingProfile = sharingProfile; + } + + @Override + public String getIdentifier() { + return sharingProfile.getIdentifier(); + } + + @Override + public void setIdentifier(String identifier) { + sharingProfile.setIdentifier(identifier); + } + + @Override + public String getName() { + return sharingProfile.getName(); + } + + @Override + public void setName(String name) { + sharingProfile.setName(name); + } + + @Override + public String getPrimaryConnectionIdentifier() { + return sharingProfile.getPrimaryConnectionIdentifier(); + } + + @Override + public void setPrimaryConnectionIdentifier(String identifier) { + sharingProfile.setPrimaryConnectionIdentifier(identifier); + } + + @Override + public Map getParameters() { + return sharingProfile.getParameters(); + } + + @Override + public void setParameters(Map parameters) { + sharingProfile.setParameters(parameters); + } + + @Override + public Map getAttributes() { + return sharingProfile.getAttributes(); + } + + @Override + public void setAttributes(Map attributes) { + sharingProfile.setAttributes(attributes); + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUser.java new file mode 100644 index 000000000..65f057778 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUser.java @@ -0,0 +1,127 @@ +/* + * 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.net.auth; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; +import org.apache.guacamole.net.auth.permission.SystemPermissionSet; + +/** + * User implementation which simply delegates all function calls to an + * underlying User. + */ +public class DelegatingUser implements User { + + /** + * The wrapped User. + */ + private final User user; + + /** + * Wraps the given User such that all function calls against this + * DelegatingUser will be delegated to it. + * + * @param user + * The User to wrap. + */ + public DelegatingUser(User user) { + this.user = user; + } + + @Override + public String getIdentifier() { + return user.getIdentifier(); + } + + @Override + public void setIdentifier(String identifier) { + user.setIdentifier(identifier); + } + + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public void setPassword(String password) { + user.setPassword(password); + } + + @Override + public Map getAttributes() { + return user.getAttributes(); + } + + @Override + public void setAttributes(Map attributes) { + user.setAttributes(attributes); + } + + @Override + public Date getLastActive() { + return user.getLastActive(); + } + + @Override + public List getHistory() + throws GuacamoleException { + return user.getHistory(); + } + + @Override + public SystemPermissionSet getSystemPermissions() + throws GuacamoleException { + return user.getSystemPermissions(); + } + + @Override + public ObjectPermissionSet getConnectionPermissions() + throws GuacamoleException { + return user.getConnectionPermissions(); + } + + @Override + public ObjectPermissionSet getConnectionGroupPermissions() + throws GuacamoleException { + return user.getConnectionGroupPermissions(); + } + + @Override + public ObjectPermissionSet getSharingProfilePermissions() + throws GuacamoleException { + return user.getSharingProfilePermissions(); + } + + @Override + public ObjectPermissionSet getActiveConnectionPermissions() + throws GuacamoleException { + return user.getActiveConnectionPermissions(); + } + + @Override + public ObjectPermissionSet getUserPermissions() throws GuacamoleException { + return user.getUserPermissions(); + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java new file mode 100644 index 000000000..a37faf952 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java @@ -0,0 +1,134 @@ +/* + * 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.net.auth; + +import java.util.Collection; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.form.Form; + +/** + * UserContext implementation which simply delegates all function calls to + * an underlying UserContext. + */ +public class DelegatingUserContext implements UserContext { + + /** + * The wrapped UserContext. + */ + private final UserContext userContext; + + /** + * Wraps the given UserContext such that all function calls against this + * DelegatingUserContext will be delegated to it. + * + * @param userContext + * The UserContext to wrap. + */ + public DelegatingUserContext(UserContext userContext) { + this.userContext = userContext; + } + + @Override + public User self() { + return userContext.self(); + } + + @Override + public Object getResource() throws GuacamoleException { + return userContext.getResource(); + } + + @Override + public AuthenticationProvider getAuthenticationProvider() { + return userContext.getAuthenticationProvider(); + } + + @Override + public Directory getUserDirectory() throws GuacamoleException { + return userContext.getUserDirectory(); + } + + @Override + public Directory getConnectionDirectory() + throws GuacamoleException { + return userContext.getConnectionDirectory(); + } + + @Override + public Directory getConnectionGroupDirectory() + throws GuacamoleException { + return userContext.getConnectionGroupDirectory(); + } + + @Override + public Directory getActiveConnectionDirectory() + throws GuacamoleException { + return userContext.getActiveConnectionDirectory(); + } + + @Override + public Directory getSharingProfileDirectory() + throws GuacamoleException { + return userContext.getSharingProfileDirectory(); + } + + @Override + public ActivityRecordSet getConnectionHistory() + throws GuacamoleException { + return userContext.getConnectionHistory(); + } + + @Override + public ActivityRecordSet getUserHistory() + throws GuacamoleException { + return userContext.getUserHistory(); + } + + @Override + public ConnectionGroup getRootConnectionGroup() throws GuacamoleException { + return userContext.getRootConnectionGroup(); + } + + @Override + public Collection
getUserAttributes() { + return userContext.getUserAttributes(); + } + + @Override + public Collection getConnectionAttributes() { + return userContext.getConnectionAttributes(); + } + + @Override + public Collection getConnectionGroupAttributes() { + return userContext.getConnectionGroupAttributes(); + } + + @Override + public Collection getSharingProfileAttributes() { + return userContext.getSharingProfileAttributes(); + } + + @Override + public void invalidate() { + userContext.invalidate(); + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java index 83ac79476..b29c2d794 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java @@ -260,6 +260,23 @@ public abstract class SimpleAuthenticationProvider } + @Override + public UserContext decorate(UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + + // Simply return the given context, decorating nothing + return context; + + } + + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return decorate(context, authenticatedUser, credentials); + } + @Override public void shutdown() { // Do nothing diff --git a/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java b/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java index e723c0a9e..24ea196c5 100644 --- a/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java +++ b/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java @@ -28,6 +28,7 @@ import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.rest.auth.DecoratedUserContext; import org.apache.guacamole.tunnel.UserTunnel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,7 +53,7 @@ public class GuacamoleSession { * All UserContexts associated with this session. Each * AuthenticationProvider may provide its own UserContext. */ - private List userContexts; + private List userContexts; /** * All currently-active tunnels, indexed by tunnel UUID. @@ -84,7 +85,7 @@ public class GuacamoleSession { */ public GuacamoleSession(Environment environment, AuthenticatedUser authenticatedUser, - List userContexts) + List userContexts) throws GuacamoleException { this.lastAccessedTime = System.currentTimeMillis(); this.authenticatedUser = authenticatedUser; @@ -121,7 +122,7 @@ public class GuacamoleSession { * An unmodifiable list of all UserContexts associated with this * session. */ - public List getUserContexts() { + public List getUserContexts() { return Collections.unmodifiableList(userContexts); } @@ -141,12 +142,12 @@ public class GuacamoleSession { * @throws GuacamoleException * If no such UserContext exists. */ - public UserContext getUserContext(String authProviderIdentifier) + public DecoratedUserContext getUserContext(String authProviderIdentifier) throws GuacamoleException { // Locate and return the UserContext associated with the // AuthenticationProvider having the given identifier, if any - for (UserContext userContext : getUserContexts()) { + for (DecoratedUserContext userContext : getUserContexts()) { // Get AuthenticationProvider associated with current UserContext AuthenticationProvider authProvider = userContext.getAuthenticationProvider(); @@ -170,7 +171,7 @@ public class GuacamoleSession { * @param userContexts * The List of UserContexts to associate with this session. */ - public void setUserContexts(List userContexts) { + public void setUserContexts(List userContexts) { this.userContexts = userContexts; } diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/AuthenticationProviderFacade.java b/guacamole/src/main/java/org/apache/guacamole/extension/AuthenticationProviderFacade.java index 8dfbe7fee..a86893179 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/AuthenticationProviderFacade.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/AuthenticationProviderFacade.java @@ -158,6 +158,35 @@ public class AuthenticationProviderFacade implements AuthenticationProvider { } + @Override + public UserContext decorate(UserContext context, + AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // Do nothing if underlying auth provider could not be loaded + if (authProvider == null) + return context; + + // Delegate to underlying auth provider + return authProvider.decorate(context, authenticatedUser, credentials); + + } + + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // Do nothing if underlying auth provider could not be loaded + if (authProvider == null) + return context; + + // Delegate to underlying auth provider + return authProvider.redecorate(decorated, context, + authenticatedUser, credentials); + + } + @Override public void shutdown() { if (authProvider != null) diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java index 587d8338e..98c5c7a63 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java @@ -33,6 +33,7 @@ import org.codehaus.jackson.jaxrs.JacksonJsonProvider; import org.apache.guacamole.rest.auth.TokenRESTService; import org.apache.guacamole.rest.auth.AuthTokenGenerator; import org.apache.guacamole.rest.auth.AuthenticationService; +import org.apache.guacamole.rest.auth.DecorationService; import org.apache.guacamole.rest.auth.SecureRandomAuthTokenGenerator; import org.apache.guacamole.rest.auth.TokenSessionMap; import org.apache.guacamole.rest.connection.ConnectionModule; @@ -80,6 +81,7 @@ public class RESTServiceModule extends ServletModule { bind(ListenerService.class); bind(AuthenticationService.class); bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class); + bind(DecorationService.class); // Automatically translate GuacamoleExceptions for REST methods MethodInterceptor interceptor = new RESTExceptionWrapper(); diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java index fb118e126..7f388572a 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java @@ -78,6 +78,12 @@ public class AuthenticationService { @Inject private AuthTokenGenerator authTokenGenerator; + /** + * Service for applying or reapplying layers of decoration. + */ + @Inject + private DecorationService decorationService; + /** * The service to use to notify registered authentication listeners. */ @@ -343,26 +349,30 @@ public class AuthenticationService { * @throws GuacamoleException * If an error occurs while creating or updating any UserContext. */ - private List getUserContexts(GuacamoleSession existingSession, + private List getUserContexts(GuacamoleSession existingSession, AuthenticatedUser authenticatedUser, Credentials credentials) throws GuacamoleException { - List userContexts = new ArrayList(authProviders.size()); + List userContexts = + new ArrayList(authProviders.size()); // If UserContexts already exist, update them and add to the list if (existingSession != null) { // Update all old user contexts - List oldUserContexts = existingSession.getUserContexts(); - for (UserContext oldUserContext : oldUserContexts) { + List oldUserContexts = existingSession.getUserContexts(); + for (DecoratedUserContext userContext : oldUserContexts) { + + UserContext oldUserContext = userContext.getUndecoratedUserContext(); // Update existing UserContext AuthenticationProvider authProvider = oldUserContext.getAuthenticationProvider(); - UserContext userContext = authProvider.updateUserContext(oldUserContext, authenticatedUser, credentials); + UserContext updatedUserContext = authProvider.updateUserContext(oldUserContext, authenticatedUser, credentials); // Add to available data, if successful - if (userContext != null) - userContexts.add(userContext); + if (updatedUserContext != null) + userContexts.add(decorationService.redecorate(userContext, + updatedUserContext, authenticatedUser, credentials)); // If unsuccessful, log that this happened, as it may be a bug else @@ -384,7 +394,8 @@ public class AuthenticationService { // Add to available data, if successful if (userContext != null) - userContexts.add(userContext); + userContexts.add(decorationService.decorate(userContext, + authenticatedUser, credentials)); } @@ -428,7 +439,7 @@ public class AuthenticationService { // Get up-to-date AuthenticatedUser and associated UserContexts AuthenticatedUser authenticatedUser = getAuthenticatedUser(existingSession, credentials); - List userContexts = getUserContexts(existingSession, authenticatedUser, credentials); + List userContexts = getUserContexts(existingSession, authenticatedUser, credentials); // Update existing session, if it exists String authToken; @@ -513,7 +524,7 @@ public class AuthenticationService { * @throws GuacamoleException * If the auth token does not correspond to any logged in user. */ - public List getUserContexts(String authToken) + public List getUserContexts(String authToken) throws GuacamoleException { return getGuacamoleSession(authToken).getUserContexts(); } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecoratedUserContext.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecoratedUserContext.java new file mode 100644 index 000000000..24ad8b35b --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecoratedUserContext.java @@ -0,0 +1,361 @@ +/* + * 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.rest.auth; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.AuthenticatedUser; +import org.apache.guacamole.net.auth.AuthenticationProvider; +import org.apache.guacamole.net.auth.Credentials; +import org.apache.guacamole.net.auth.DelegatingUserContext; +import org.apache.guacamole.net.auth.UserContext; + +/** + * A UserContext which has been decorated by an AuthenticationProvider through + * invoking decorate() or redecorate(). + */ +public class DecoratedUserContext extends DelegatingUserContext { + + /** + * The original, undecorated UserContext. + */ + private final UserContext undecoratedUserContext; + + /** + * The AuthenticationProvider which applied this layer of decoration. + */ + private final AuthenticationProvider decoratingAuthenticationProvider; + + /** + * The DecoratedUserContext which applies the layer of decoration + * immediately beneath this DecoratedUserContext. If no further decoration + * has been applied, this will be null. + */ + private final DecoratedUserContext decoratedUserContext; + + /** + * Decorates a newly-created UserContext (as would be returned by + * getUserContext()), invoking the decorate() function of the given + * AuthenticationProvider to apply an additional layer of decoration. If the + * AuthenticationProvider originated the given UserContext, this function + * has no effect. + * + * @param authProvider + * The AuthenticationProvider which should be used to decorate the + * given UserContext. + * + * @param userContext + * The UserContext to decorate. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. + * + * @return + * A UserContext instance which has been decorated (wrapped) by the + * given AuthenticationProvider, or the original UserContext if the + * given AuthenticationProvider originated the UserContext. + * + * @throws GuacamoleException + * If the given AuthenticationProvider fails while decorating the + * UserContext. + */ + private static UserContext decorate(AuthenticationProvider authProvider, + UserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // Skip the AuthenticationProvider which produced the UserContext + // being decorated + if (authProvider != userContext.getAuthenticationProvider()) { + + // Apply layer of wrapping around UserContext + UserContext decorated = authProvider.decorate(userContext, + authenticatedUser, credentials); + + // Do not allow misbehaving extensions to wipe out the + // UserContext entirely + if (decorated != null) + return decorated; + + } + + return userContext; + + } + + /** + * Redecorates an updated UserContext (as would be returned by + * updateUserContext()), invoking the redecorate() function of the given + * AuthenticationProvider to apply an additional layer of decoration. If the + * AuthenticationProvider originated the given UserContext, this function + * has no effect. + * + * @param decorated + * The DecoratedUserContext associated with an older version of the + * given UserContext. + * + * @param userContext + * The new version of the UserContext which should be decorated. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. + * + * @return + * A UserContext instance which has been decorated (wrapped) by the + * given AuthenticationProvider, or the original UserContext if the + * given AuthenticationProvider originated the UserContext. + * + * @throws GuacamoleException + * If the given AuthenticationProvider fails while decorating the + * UserContext. + */ + private static UserContext redecorate(DecoratedUserContext decorated, + UserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + AuthenticationProvider authProvider = decorated.getDecoratingAuthenticationProvider(); + + // Skip the AuthenticationProvider which produced the UserContext + // being decorated + if (authProvider != userContext.getAuthenticationProvider()) { + + // Apply next layer of wrapping around UserContext + UserContext redecorated = authProvider.redecorate(decorated, + userContext, authenticatedUser, credentials); + + // Do not allow misbehaving extensions to wipe out the + // UserContext entirely + if (redecorated != null) + return redecorated; + + } + + return userContext; + + } + + /** + * Creates a new DecoratedUserContext, invoking the the decorate() function + * of the given AuthenticationProvider to decorate the provided, undecorated + * UserContext. If the AuthenticationProvider originated the given + * UserContext, then the given UserContext is wrapped without any + * decoration. + * + * @param authProvider + * The AuthenticationProvider which should be used to decorate the + * given UserContext. + * + * @param userContext + * The undecorated UserContext to decorate. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. + * + * @throws GuacamoleException + * If any of the given AuthenticationProviders fails while decorating + * the UserContext. + */ + public DecoratedUserContext(AuthenticationProvider authProvider, + UserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // Wrap the result of invoking decorate() on the given AuthenticationProvider + super(decorate(authProvider, userContext, authenticatedUser, credentials)); + this.decoratingAuthenticationProvider = authProvider; + + // The wrapped UserContext is undecorated + this.undecoratedUserContext = userContext; + this.decoratedUserContext = null; + + } + + /** + * Creates a new DecoratedUserContext, invoking the the decorate() function + * of the given AuthenticationProvider to apply an additional layer of + * decoration to a DecoratedUserContext. If the AuthenticationProvider + * originated the given UserContext, then the given UserContext is wrapped + * without any decoration. + * + * @param authProvider + * The AuthenticationProvider which should be used to decorate the + * given UserContext. + * + * @param userContext + * The DecoratedUserContext to decorate. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. + * + * @throws GuacamoleException + * If any of the given AuthenticationProviders fails while decorating + * the UserContext. + */ + public DecoratedUserContext(AuthenticationProvider authProvider, + DecoratedUserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // Wrap the result of invoking decorate() on the given AuthenticationProvider + super(decorate(authProvider, userContext, authenticatedUser, credentials)); + this.decoratingAuthenticationProvider = authProvider; + + // The wrapped UserContext has at least one layer of decoration + this.undecoratedUserContext = userContext.getUndecoratedUserContext(); + this.decoratedUserContext = userContext; + + } + + /** + * Creates a new DecoratedUserContext, invoking the the redecorate() + * function of the given AuthenticationProvider to reapply decoration to the + * provided, undecorated UserContext, which has been updated relative to a + * past version which was decorated. If the AuthenticationProvider + * originated the given UserContext, then the given UserContext is wrapped + * without any decoration. + * + * @param decorated + * The DecoratedUserContext associated with the older version of the + * given UserContext. + * + * @param userContext + * The undecorated UserContext to decorate. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. + * + * @throws GuacamoleException + * If any of the given AuthenticationProviders fails while decorating + * the UserContext. + */ + public DecoratedUserContext(DecoratedUserContext decorated, + UserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // Wrap the result of invoking redecorate() on the given AuthenticationProvider + super(redecorate(decorated, userContext, authenticatedUser, credentials)); + this.decoratingAuthenticationProvider = decorated.getDecoratingAuthenticationProvider(); + + // The wrapped UserContext is undecorated + this.undecoratedUserContext = userContext; + this.decoratedUserContext = null; + + } + + /** + * Creates a new DecoratedUserContext, invoking the the redecorate() + * function of the given AuthenticationProvider to reapply decoration to a + * DecoratedUserContext which already has at least one layer of decoration + * applied, and which is associated with a UserContext which was updated + * relative to a past version which was decorated. If the + * AuthenticationProvider originated the given UserContext, then the given + * UserContext is wrapped without any decoration. + * + * @param decorated + * The DecoratedUserContext associated with the older version of the + * UserContext wrapped within one or more layers of decoration. + * + * @param userContext + * The DecoratedUserContext to decorate. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. + * + * @throws GuacamoleException + * If any of the given AuthenticationProviders fails while decorating + * the UserContext. + */ + public DecoratedUserContext(DecoratedUserContext decorated, + DecoratedUserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // Wrap the result of invoking redecorate() on the given AuthenticationProvider + super(redecorate(decorated, userContext, authenticatedUser, credentials)); + this.decoratingAuthenticationProvider = decorated.getDecoratingAuthenticationProvider(); + + // The wrapped UserContext has at least one layer of decoration + this.undecoratedUserContext = userContext.getUndecoratedUserContext(); + this.decoratedUserContext = userContext; + + } + + /** + * Returns the original UserContext with absolutely no layers of decoration + * applied. + * + * @return + * The original, undecorated UserContext. + */ + public UserContext getUndecoratedUserContext() { + return undecoratedUserContext; + } + + /** + * Returns the AuthenticationProvider which applied the layer of decoration + * represented by this DecoratedUserContext. + * + * @return + * The AuthenticationProvider which applied this layer of decoration. + */ + public AuthenticationProvider getDecoratingAuthenticationProvider() { + return decoratingAuthenticationProvider; + } + + /** + * Returns the DecoratedUserContext representing the next layer of + * decoration, itself decorated by this DecoratedUserContext. If no further + * layers of decoration exist, this will be null. + * + * @return + * The DecoratedUserContext which applies the layer of decoration + * immediately beneath this DecoratedUserContext, or null if no further + * decoration has been applied. + */ + public DecoratedUserContext getDecoratedUserContext() { + return decoratedUserContext; + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecorationService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecorationService.java new file mode 100644 index 000000000..b63b037b3 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecorationService.java @@ -0,0 +1,145 @@ +/* + * 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.rest.auth; + +import com.google.inject.Inject; +import java.util.Iterator; +import java.util.List; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.AuthenticatedUser; +import org.apache.guacamole.net.auth.AuthenticationProvider; +import org.apache.guacamole.net.auth.Credentials; +import org.apache.guacamole.net.auth.UserContext; + +/** + * A service for applying or reapplying layers of decoration to UserContexts. + * The semantics of UserContext decoration/redecoration is defined by the + * AuthenticationProvider interface. + */ +public class DecorationService { + + /** + * All configured authentication providers which can be used to + * authenticate users or retrieve data associated with authenticated users. + */ + @Inject + private List authProviders; + + /** + * Creates a new DecoratedUserContext, invoking the the decorate() function + * of all AuthenticationProviders to decorate the provided UserContext. + * Decoration by each AuthenticationProvider will occur in the order that + * the AuthenticationProviders were loaded. Only AuthenticationProviders + * which did not originate the given UserContext will be used. + * + * @param userContext + * The UserContext to decorate. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. + * + * @return + * A new DecoratedUserContext which has been decorated by all + * AuthenticationProviders. + * + * @throws GuacamoleException + * If any AuthenticationProvider fails while decorating the UserContext. + */ + public DecoratedUserContext decorate(UserContext userContext, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + + // Get first AuthenticationProvider in list + Iterator current = authProviders.iterator(); + if (!current.hasNext()) + return null; + + // Use first AuthenticationProvider to produce the root-level + // decorated UserContext + DecoratedUserContext decorated = new DecoratedUserContext(current.next(), + userContext, authenticatedUser, credentials); + + // Repeatedly wrap the decorated UserContext with additional layers of + // decoration for each remaining AuthenticationProvider + while (current.hasNext()) { + decorated = new DecoratedUserContext(current.next(), decorated, + authenticatedUser, credentials); + } + + return decorated; + + } + + /** + * Creates a new DecoratedUserContext, invoking the the redecorate() + * function of all AuthenticationProviders to reapply decoration. Decoration + * by each AuthenticationProvider will occur in the order that the + * AuthenticationProviders were loaded. Only AuthenticationProviders which + * did not originate the given UserContext will be used. + * + * @param decorated + * The DecoratedUserContext associated with an older version of the + * given UserContext. + * + * @param userContext + * The new version of the UserContext which should be decorated. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. + * + * @return + * A new DecoratedUserContext which has been decorated by all + * AuthenticationProviders. + * + * @throws GuacamoleException + * If any AuthenticationProvider fails while decorating the UserContext. + */ + public DecoratedUserContext redecorate(DecoratedUserContext decorated, + UserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // If the given DecoratedUserContext contains further decorated layers, + // redecorate those first + DecoratedUserContext next = decorated.getDecoratedUserContext(); + if (next != null) { + return new DecoratedUserContext(decorated, + redecorate(next, userContext, authenticatedUser, credentials), + authenticatedUser, credentials); + } + + // If only one layer of decoration is present, simply redecorate that + // layer + return new DecoratedUserContext(decorated, userContext, + authenticatedUser, credentials); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java index 6366d287c..cea0315e9 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java @@ -186,7 +186,7 @@ public class TokenRESTService { throw new GuacamoleResourceNotFoundException("No such token."); // Build list of all available auth providers - List userContexts = session.getUserContexts(); + List userContexts = session.getUserContexts(); List authProviderIdentifiers = new ArrayList(userContexts.size()); for (UserContext userContext : userContexts) authProviderIdentifiers.add(userContext.getAuthenticationProvider().getIdentifier());