diff --git a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/TOTPAuthenticationProvider.java b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/TOTPAuthenticationProvider.java index 835ba87d3..28e2380b2 100644 --- a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/TOTPAuthenticationProvider.java +++ b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/TOTPAuthenticationProvider.java @@ -19,9 +19,11 @@ package org.apache.guacamole.auth.totp; +import org.apache.guacamole.auth.totp.user.UserVerificationService; import com.google.inject.Guice; import com.google.inject.Injector; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.totp.user.TOTPUserContext; import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.Credentials; @@ -104,7 +106,7 @@ public class TOTPAuthenticationProvider implements AuthenticationProvider { // User has been verified, and authentication should be allowed to // continue - return context; + return new TOTPUserContext(context); } @@ -112,7 +114,7 @@ public class TOTPAuthenticationProvider implements AuthenticationProvider { public UserContext redecorate(UserContext decorated, UserContext context, AuthenticatedUser authenticatedUser, Credentials credentials) throws GuacamoleException { - return context; + return new TOTPUserContext(context); } @Override diff --git a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/TOTPAuthenticationProviderModule.java b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/TOTPAuthenticationProviderModule.java index e72beeca6..94b723223 100644 --- a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/TOTPAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/TOTPAuthenticationProviderModule.java @@ -19,6 +19,7 @@ package org.apache.guacamole.auth.totp; +import org.apache.guacamole.auth.totp.user.UserVerificationService; import com.google.inject.AbstractModule; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.auth.totp.conf.ConfigurationService; diff --git a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/form/AuthenticationCodeField.java b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/form/AuthenticationCodeField.java index c3ca20710..764fe9568 100644 --- a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/form/AuthenticationCodeField.java +++ b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/form/AuthenticationCodeField.java @@ -32,7 +32,7 @@ import java.net.URI; import javax.ws.rs.core.UriBuilder; import javax.xml.bind.DatatypeConverter; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.auth.totp.UserTOTPKey; +import org.apache.guacamole.auth.totp.user.UserTOTPKey; import org.apache.guacamole.auth.totp.conf.ConfigurationService; import org.apache.guacamole.form.Field; import org.codehaus.jackson.annotate.JsonProperty; diff --git a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/TOTPUser.java b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/TOTPUser.java new file mode 100644 index 000000000..4199d4380 --- /dev/null +++ b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/TOTPUser.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.totp.user; + +import java.util.HashMap; +import java.util.Map; +import org.apache.guacamole.net.auth.DelegatingUser; +import org.apache.guacamole.net.auth.User; + +/** + * TOTP-specific User implementation which wraps a User from another extension, + * hiding and blocking access to the core attributes used by TOTP. + */ +public class TOTPUser extends DelegatingUser { + + /** + * The name of the user attribute which stores the TOTP key. + */ + public static final String TOTP_KEY_SECRET_ATTRIBUTE_NAME = "guac-totp-key-secret"; + + /** + * The name of the user attribute defines whether the TOTP key has been + * confirmed by the user, and the user is thus fully enrolled. + */ + public static final String TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME = "guac-totp-key-confirmed"; + + /** + * The User object wrapped by this TOTPUser. + */ + private final User undecorated; + + /** + * Wraps the given User object, hiding and blocking access to the core + * attributes used by TOTP. + * + * @param user + * The User object to wrap. + */ + public TOTPUser(User user) { + super(user); + this.undecorated = user; + } + + /** + * Returns the User object wrapped by this TOTPUser. + * + * @return + * The wrapped User object. + */ + public User getUndecorated() { + return undecorated; + } + + @Override + public Map getAttributes() { + + // Create independent, mutable copy of attributes + Map attributes = + new HashMap(super.getAttributes()); + + // Do not expose any TOTP-related attributes outside this extension + attributes.remove(TOTP_KEY_SECRET_ATTRIBUTE_NAME); + attributes.remove(TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME); + + // Expose only non-TOTP attributes + return attributes; + + } + + @Override + public void setAttributes(Map attributes) { + + // Create independent, mutable copy of attributes + attributes = new HashMap(attributes); + + // Do not expose any TOTP-related attributes outside this extension + attributes.remove(TOTP_KEY_SECRET_ATTRIBUTE_NAME); + attributes.remove(TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME); + + // Set only non-TOTP attributes + super.setAttributes(attributes); + + } + +} diff --git a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/TOTPUserContext.java b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/TOTPUserContext.java new file mode 100644 index 000000000..980bbf782 --- /dev/null +++ b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/TOTPUserContext.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.totp.user; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.DecoratingDirectory; +import org.apache.guacamole.net.auth.DelegatingUserContext; +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.net.auth.UserContext; + +/** + * TOTP-specific UserContext implementation which wraps the UserContext of + * some other extension, providing (or hiding) additional data. + */ +public class TOTPUserContext extends DelegatingUserContext { + + /** + * Creates a new TOTPUserContext which wraps the given UserContext, + * providing (or hiding) additional TOTP-specific data. + * + * @param userContext + * The UserContext to wrap. + */ + public TOTPUserContext(UserContext userContext) { + super(userContext); + } + + @Override + public Directory getUserDirectory() throws GuacamoleException { + return new DecoratingDirectory(super.getUserDirectory()) { + + @Override + protected User decorate(User object) { + return new TOTPUser(object); + } + + @Override + protected User undecorate(User object) { + assert(object instanceof TOTPUser); + return ((TOTPUser) object).getUndecorated(); + } + + }; + } + +} diff --git a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserTOTPKey.java b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserTOTPKey.java similarity index 98% rename from extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserTOTPKey.java rename to extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserTOTPKey.java index 3de378581..d7bc903e7 100644 --- a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserTOTPKey.java +++ b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserTOTPKey.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.guacamole.auth.totp; +package org.apache.guacamole.auth.totp.user; import java.security.SecureRandom; import java.util.Random; diff --git a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserVerificationService.java b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserVerificationService.java similarity index 91% rename from extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserVerificationService.java rename to extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserVerificationService.java index 851bb9484..8264efd1a 100644 --- a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserVerificationService.java +++ b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserVerificationService.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.guacamole.auth.totp; +package org.apache.guacamole.auth.totp.user; import com.google.common.io.BaseEncoding; import com.google.inject.Inject; @@ -53,17 +53,6 @@ public class UserVerificationService { */ private final Logger logger = LoggerFactory.getLogger(UserVerificationService.class); - /** - * The name of the user attribute which stores the TOTP key. - */ - private static final String TOTP_KEY_SECRET_ATTRIBUTE_NAME = "guac-totp-key-secret"; - - /** - * The name of the user attribute defines whether the TOTP key has been - * confirmed by the user, and the user is thus fully enrolled. - */ - private static final String TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME = "guac-totp-key-confirmed"; - /** * BaseEncoding instance which decoded/encodes base32. */ @@ -111,7 +100,7 @@ public class UserVerificationService { Map attributes = context.self().getAttributes(); // If no key is defined, attempt to generate a new key - String secret = attributes.get(TOTP_KEY_SECRET_ATTRIBUTE_NAME); + String secret = attributes.get(TOTPUser.TOTP_KEY_SECRET_ATTRIBUTE_NAME); if (secret == null) { // Generate random key for user @@ -140,7 +129,7 @@ public class UserVerificationService { } // Otherwise, parse value from attributes - boolean confirmed = "true".equals(attributes.get(TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME)); + boolean confirmed = "true".equals(attributes.get(TOTPUser.TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME)); return new UserTOTPKey(username, key, confirmed); } @@ -173,14 +162,14 @@ public class UserVerificationService { Map attributes = new HashMap(); // Set/overwrite current TOTP key state - attributes.put(TOTP_KEY_SECRET_ATTRIBUTE_NAME, BASE32.encode(key.getSecret())); - attributes.put(TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME, key.isConfirmed() ? "true" : "false"); + attributes.put(TOTPUser.TOTP_KEY_SECRET_ATTRIBUTE_NAME, BASE32.encode(key.getSecret())); + attributes.put(TOTPUser.TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME, key.isConfirmed() ? "true" : "false"); self.setAttributes(attributes); // Confirm that attributes have actually been set Map setAttributes = self.getAttributes(); - if (!setAttributes.containsKey(TOTP_KEY_SECRET_ATTRIBUTE_NAME) - || !setAttributes.containsKey(TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME)) + if (!setAttributes.containsKey(TOTPUser.TOTP_KEY_SECRET_ATTRIBUTE_NAME) + || !setAttributes.containsKey(TOTPUser.TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME)) return false; // Update user object