mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-1219: Add support for disabling TOTP for specific users and groups.
This commit is contained in:
@@ -33,6 +33,12 @@ import org.apache.guacamole.net.auth.User;
|
||||
*/
|
||||
public class TOTPUser extends DelegatingUser {
|
||||
|
||||
/**
|
||||
* The name of the user attribute which disables the TOTP requirement
|
||||
* for that specific user.
|
||||
*/
|
||||
public static final String TOTP_KEY_DISABLED_ATTRIBUTE_NAME = "guac-totp-disabled";
|
||||
|
||||
/**
|
||||
* The name of the user attribute which stores the TOTP key.
|
||||
*/
|
||||
@@ -56,13 +62,14 @@ public class TOTPUser extends DelegatingUser {
|
||||
* The string value used by TOTP user attributes to represent the boolean
|
||||
* value "true".
|
||||
*/
|
||||
private static final String TRUTH_VALUE = "true";
|
||||
public static final String TRUTH_VALUE = "true";
|
||||
|
||||
/**
|
||||
* The form which contains all configurable properties for this user.
|
||||
*/
|
||||
public static final Form TOTP_ENROLLMENT_STATUS = new Form("totp-enrollment-status",
|
||||
Arrays.asList(
|
||||
new BooleanField(TOTP_KEY_DISABLED_ATTRIBUTE_NAME, TRUTH_VALUE),
|
||||
new BooleanField(TOTP_KEY_SECRET_GENERATED_ATTRIBUTE_NAME, TRUTH_VALUE),
|
||||
new BooleanField(TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME, TRUTH_VALUE)
|
||||
)
|
||||
@@ -95,6 +102,9 @@ public class TOTPUser extends DelegatingUser {
|
||||
|
||||
// Create independent, mutable copy of attributes
|
||||
Map<String, String> attributes = new HashMap<>(super.getAttributes());
|
||||
|
||||
if (!attributes.containsKey(TOTP_KEY_DISABLED_ATTRIBUTE_NAME))
|
||||
attributes.put(TOTP_KEY_DISABLED_ATTRIBUTE_NAME, null);
|
||||
|
||||
// Replace secret key with simple boolean attribute representing
|
||||
// whether a key has been generated at all
|
||||
|
@@ -23,12 +23,14 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.totp.usergroup.TOTPUserGroup;
|
||||
import org.apache.guacamole.form.Form;
|
||||
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;
|
||||
import org.apache.guacamole.net.auth.UserGroup;
|
||||
|
||||
/**
|
||||
* TOTP-specific UserContext implementation which wraps the UserContext of
|
||||
@@ -65,11 +67,36 @@ public class TOTPUserContext extends DelegatingUserContext {
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Directory<UserGroup> getUserGroupDirectory() throws GuacamoleException {
|
||||
return new DecoratingDirectory<UserGroup>(super.getUserGroupDirectory()) {
|
||||
|
||||
@Override
|
||||
protected UserGroup decorate(UserGroup object) {
|
||||
return new TOTPUserGroup(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserGroup undecorate(UserGroup object) {
|
||||
assert(object instanceof TOTPUserGroup);
|
||||
return ((TOTPUserGroup) object).getUndecorated();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Form> getUserAttributes() {
|
||||
Collection<Form> userAttrs = new HashSet<>(super.getUserAttributes());
|
||||
userAttrs.add(TOTPUser.TOTP_ENROLLMENT_STATUS);
|
||||
return Collections.unmodifiableCollection(userAttrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Form> getUserGroupAttributes() {
|
||||
Collection<Form> userGroupAttrs = new HashSet<>(super.getUserGroupAttributes());
|
||||
userGroupAttrs.add(TOTPUserGroup.TOTP_USER_GROUP_CONFIG);
|
||||
return Collections.unmodifiableCollection(userGroupAttrs);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -26,19 +26,23 @@ import java.security.InvalidKeyException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleSecurityException;
|
||||
import org.apache.guacamole.GuacamoleUnsupportedException;
|
||||
import org.apache.guacamole.auth.totp.conf.ConfigurationService;
|
||||
import org.apache.guacamole.auth.totp.form.AuthenticationCodeField;
|
||||
import org.apache.guacamole.auth.totp.usergroup.TOTPUserGroup;
|
||||
import org.apache.guacamole.form.Field;
|
||||
import org.apache.guacamole.language.TranslatableGuacamoleClientException;
|
||||
import org.apache.guacamole.language.TranslatableGuacamoleInsufficientCredentialsException;
|
||||
import org.apache.guacamole.net.auth.AuthenticatedUser;
|
||||
import org.apache.guacamole.net.auth.Credentials;
|
||||
import org.apache.guacamole.net.auth.Directory;
|
||||
import org.apache.guacamole.net.auth.User;
|
||||
import org.apache.guacamole.net.auth.UserContext;
|
||||
import org.apache.guacamole.net.auth.UserGroup;
|
||||
import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
|
||||
import org.apache.guacamole.totp.TOTPGenerator;
|
||||
import org.slf4j.Logger;
|
||||
@@ -203,6 +207,65 @@ public class UserVerificationService {
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the user in question, via both UserContext and AuthenticatedUser,
|
||||
* to see if TOTP has been disabled for this user, either directly or via
|
||||
* membership in a group that has had TOTP marked as disabled.
|
||||
*
|
||||
* @param context
|
||||
* The UserContext for the user being verified.
|
||||
*
|
||||
* @param authenticatedUser
|
||||
* The AuthenticatedUser for the user being verified.
|
||||
*
|
||||
* @return
|
||||
* True if TOTP access has been disabled for the user, otherwise
|
||||
* false.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the extension handling storage fails internally while attempting
|
||||
* to update the user.
|
||||
*/
|
||||
private boolean totpDisabled(UserContext context,
|
||||
AuthenticatedUser authenticatedUser)
|
||||
throws GuacamoleException {
|
||||
|
||||
// If TOTP is disabled for this user, return, allowing login to continue
|
||||
Map<String, String> myAttributes = context.self().getAttributes();
|
||||
if (myAttributes != null
|
||||
&& TOTPUser.TRUTH_VALUE.equals(myAttributes.get(TOTPUser.TOTP_KEY_DISABLED_ATTRIBUTE_NAME))) {
|
||||
|
||||
logger.warn("TOTP validation has been disabled for user \"{}\"",
|
||||
context.self().getIdentifier());
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// Check if any effective user groups have TOTP marked as disabled
|
||||
Set<String> userGroups = authenticatedUser.getEffectiveUserGroups();
|
||||
Directory<UserGroup> directoryGroups = context.getPrivileged().getUserGroupDirectory();
|
||||
for (String userGroup : userGroups) {
|
||||
UserGroup thisGroup = directoryGroups.get(userGroup);
|
||||
if (thisGroup == null)
|
||||
continue;
|
||||
|
||||
Map<String, String> grpAttributes = thisGroup.getAttributes();
|
||||
if (grpAttributes != null
|
||||
&& TOTPUserGroup.TRUTH_VALUE.equals(grpAttributes.get(TOTPUserGroup.TOTP_KEY_DISABLED_ATTRIBUTE_NAME))) {
|
||||
|
||||
logger.warn("TOTP validation will be bypassed for user \"{}\""
|
||||
+ " because it has been disabled for group \"{}\"",
|
||||
context.self().getIdentifier(), userGroup);
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// TOTP has not been disabled
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the identity of the given user using TOTP. If a authentication
|
||||
@@ -230,6 +293,10 @@ public class UserVerificationService {
|
||||
if (username.equals(AuthenticatedUser.ANONYMOUS_IDENTIFIER))
|
||||
return;
|
||||
|
||||
// Check if TOTP has been disabled for this user
|
||||
if (totpDisabled(context, authenticatedUser))
|
||||
return;
|
||||
|
||||
// Ignore users which do not have an associated key
|
||||
UserTOTPKey key = getKey(context, username);
|
||||
if (key == null)
|
||||
|
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.usergroup;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.guacamole.form.BooleanField;
|
||||
import org.apache.guacamole.form.Form;
|
||||
import org.apache.guacamole.net.auth.DelegatingUserGroup;
|
||||
import org.apache.guacamole.net.auth.UserGroup;
|
||||
|
||||
/**
|
||||
* A UserGroup that wraps another UserGroup implementation, decorating it with
|
||||
* attributes that control TOTP configuration for users that are members of that
|
||||
* group.
|
||||
*/
|
||||
public class TOTPUserGroup extends DelegatingUserGroup {
|
||||
|
||||
/**
|
||||
* The attribute associated with a group that disables the TOTP requirement
|
||||
* for any users that are a member of that group, or are members of any
|
||||
* groups that are members of this group.
|
||||
*/
|
||||
public static final String TOTP_KEY_DISABLED_ATTRIBUTE_NAME = "guac-totp-disabled";
|
||||
|
||||
/**
|
||||
* The string value used by TOTP user attributes to represent the boolean
|
||||
* value "true".
|
||||
*/
|
||||
public static final String TRUTH_VALUE = "true";
|
||||
|
||||
/**
|
||||
* The form that contains fields for configuring TOTP for members of this
|
||||
* group.
|
||||
*/
|
||||
public static final Form TOTP_USER_GROUP_CONFIG = new Form("totp-user-group-config",
|
||||
Arrays.asList(
|
||||
new BooleanField(TOTP_KEY_DISABLED_ATTRIBUTE_NAME, TRUTH_VALUE)
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Create a new instance of this user group implementation, wrapping the
|
||||
* provided UserGroup.
|
||||
*
|
||||
* @param userGroup
|
||||
* The UserGroup to be wrapped.
|
||||
*/
|
||||
public TOTPUserGroup(UserGroup userGroup) {
|
||||
super(userGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original UserGroup that this implementation is wrapping.
|
||||
*
|
||||
* @return
|
||||
* The original UserGroup that this implementation wraps.
|
||||
*/
|
||||
public UserGroup getUndecorated() {
|
||||
return getDelegateUserGroupGroup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not TOTP has been disabled for members of this group.
|
||||
*
|
||||
* @return
|
||||
* True if TOTP has been disabled for members of this group, otherwise
|
||||
* false.
|
||||
*/
|
||||
public boolean totpDisabled() {
|
||||
return (TRUTH_VALUE.equals(getAttributes().get(TOTP_KEY_DISABLED_ATTRIBUTE_NAME)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttributes() {
|
||||
|
||||
// Create a mutable copy of the attributes
|
||||
Map<String, String> attributes = new HashMap<>(super.getAttributes());
|
||||
|
||||
if (!attributes.containsKey(TOTP_KEY_DISABLED_ATTRIBUTE_NAME))
|
||||
attributes.put(TOTP_KEY_DISABLED_ATTRIBUTE_NAME, null);
|
||||
|
||||
return attributes;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -33,11 +33,20 @@
|
||||
|
||||
"USER_ATTRIBUTES" : {
|
||||
|
||||
"FIELD_HEADER_GUAC_TOTP_DISABLED" : "Disable TOTP:",
|
||||
"FIELD_HEADER_GUAC_TOTP_KEY_GENERATED" : "Secret key generated:",
|
||||
"FIELD_HEADER_GUAC_TOTP_KEY_CONFIRMED" : "Authentication device confirmed:",
|
||||
|
||||
"SECTION_HEADER_TOTP_ENROLLMENT_STATUS" : "TOTP Enrollment Status"
|
||||
|
||||
},
|
||||
|
||||
"USER_GROUP_ATTRIBUTES" : {
|
||||
|
||||
"FIELD_HEADER_GUAC_TOTP_DISABLED" : "Disable TOTP:",
|
||||
|
||||
"SECTION_HEADER_TOTP_USER_GROUP_CONFIG" : "TOTP Configuration"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user