Skip to content

Commit

Permalink
GUACAMOLE-793: Merge add support for retrieving effective groups from…
Browse files Browse the repository at this point in the history
… CAS.
  • Loading branch information
necouchman authored Dec 14, 2020
2 parents ecd385b + a5acb5a commit ae9c55c
Show file tree
Hide file tree
Showing 13 changed files with 731 additions and 57 deletions.
20 changes: 20 additions & 0 deletions extensions/guacamole-auth-cas/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,26 @@
<scope>provided</scope>
</dependency>

<!-- JUnit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@
package org.apache.guacamole.auth.cas;

import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Arrays;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.guacamole.form.Field;
import org.apache.guacamole.GuacamoleException;
Expand Down Expand Up @@ -53,12 +51,6 @@ public class AuthenticationProviderService {
@Inject
private TicketValidationService ticketService;

/**
* Provider for AuthenticatedUser objects.
*/
@Inject
private Provider<CASAuthenticatedUser> authenticatedUserProvider;

/**
* Returns an AuthenticatedUser representing the user authenticated by the
* given credentials.
Expand All @@ -82,13 +74,7 @@ public CASAuthenticatedUser authenticateUser(Credentials credentials)
if (request != null) {
String ticket = request.getParameter(CASTicketField.PARAMETER_NAME);
if (ticket != null) {
Map<String, String> tokens = ticketService.validateTicket(ticket, credentials);
String username = credentials.getUsername();
if (username != null) {
CASAuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
authenticatedUser.init(username, credentials, tokens);
return authenticatedUser;
}
return ticketService.validateTicket(ticket, credentials);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@

package org.apache.guacamole.auth.cas.conf;

import org.apache.guacamole.auth.cas.group.GroupFormat;
import org.apache.guacamole.properties.EnumGuacamoleProperty;
import org.apache.guacamole.properties.URIGuacamoleProperty;
import org.apache.guacamole.properties.StringGuacamoleProperty;

/**
* Provides properties required for use of the CAS authentication provider.
Expand Down Expand Up @@ -68,5 +71,51 @@ private CASGuacamoleProperties() {}
public String getName() { return "cas-clearpass-key"; }

};

/**
* The name of the CAS attribute used for group membership, such as
* "memberOf". This attribute is case sensitive.
*/
public static final StringGuacamoleProperty CAS_GROUP_ATTRIBUTE =
new StringGuacamoleProperty() {

@Override
public String getName() { return "cas-group-attribute"; }

};

/**
* The format used by CAS to represent group names. Possible formats are
* "plain" (simple text names) or "ldap" (fully-qualified LDAP DNs).
*/
public static final EnumGuacamoleProperty<GroupFormat> CAS_GROUP_FORMAT =
new EnumGuacamoleProperty<GroupFormat>(GroupFormat.class) {

@Override
public String getName() { return "cas-group-format"; }

};

/**
* The LDAP base DN to require for all CAS groups.
*/
public static final LdapNameGuacamoleProperty CAS_GROUP_LDAP_BASE_DN =
new LdapNameGuacamoleProperty() {

@Override
public String getName() { return "cas-group-ldap-base-dn"; }

};

/**
* The LDAP attribute to require for the names of CAS groups.
*/
public static final StringGuacamoleProperty CAS_GROUP_LDAP_ATTRIBUTE =
new StringGuacamoleProperty() {

@Override
public String getName() { return "cas-group-ldap-attribute"; }

};

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@
import com.google.inject.Inject;
import java.net.URI;
import java.security.PrivateKey;
import javax.naming.ldap.LdapName;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.auth.cas.group.GroupFormat;
import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.auth.cas.group.GroupParser;
import org.apache.guacamole.auth.cas.group.LDAPGroupParser;
import org.apache.guacamole.auth.cas.group.PlainGroupParser;

/**
* Service for retrieving configuration information regarding the CAS service.
Expand Down Expand Up @@ -85,4 +91,102 @@ public PrivateKey getClearpassKey() throws GuacamoleException {
return environment.getProperty(CASGuacamoleProperties.CAS_CLEARPASS_KEY);
}

/**
* Returns the CAS attribute that should be used to determine group
* memberships in CAS, such as "memberOf". If no attribute has been
* specified, null is returned.
*
* @return
* The attribute name used to determine group memberships in CAS,
* null if not defined.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed.
*/
public String getGroupAttribute() throws GuacamoleException {
return environment.getProperty(CASGuacamoleProperties.CAS_GROUP_ATTRIBUTE);
}

/**
* Returns the format that CAS is expected to use for its group names, such
* as {@link GroupFormat#PLAIN} (simple plain-text names) or
* {@link GroupFormat#LDAP} (fully-qualified LDAP DNs). If not specified,
* PLAIN is used by default.
*
* @return
* The format that CAS is expected to use for its group names.
*
* @throws GuacamoleException
* If the format specified within guacamole.properties is not valid.
*/
public GroupFormat getGroupFormat() throws GuacamoleException {
return environment.getProperty(CASGuacamoleProperties.CAS_GROUP_FORMAT, GroupFormat.PLAIN);
}

/**
* Returns the base DN that all LDAP-formatted CAS groups must reside
* beneath. Any groups that are not beneath this base DN should be ignored.
* If no such base DN is provided, the tree structure of the ancestors of
* LDAP-formatted CAS groups should not be considered.
*
* @return
* The base DN that all LDAP-formatted CAS groups must reside beneath,
* or null if the tree structure of the ancestors of LDAP-formatted
* CAS groups should not be considered.
*
* @throws GuacamoleException
* If the provided base DN is not a valid LDAP DN.
*/
public LdapName getGroupLDAPBaseDN() throws GuacamoleException {
return environment.getProperty(CASGuacamoleProperties.CAS_GROUP_LDAP_BASE_DN);
}

/**
* Returns the LDAP attribute that should be required for all LDAP-formatted
* CAS groups. Any groups that do not use this attribute as the last
* (leftmost) attribute of their DN should be ignored. If no such LDAP
* attribute is provided, the last (leftmost) attribute should still be
* used to determine the group name, but the specific attribute involved
* should not be considered.
*
* @return
* The LDAP attribute that should be required for all LDAP-formatted
* CAS groups, or null if any attribute should be allowed.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed.
*/
public String getGroupLDAPAttribute() throws GuacamoleException {
return environment.getProperty(CASGuacamoleProperties.CAS_GROUP_LDAP_ATTRIBUTE);
}

/**
* Returns a GroupParser instance that can be used to parse CAS group
* names. The parser returned will take into account the configured CAS
* group format, as well as any configured LDAP-specific restrictions.
*
* @return
* A GroupParser instance that can be used to parse CAS group names as
* configured in guacamole.properties.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed.
*/
public GroupParser getGroupParser() throws GuacamoleException {
switch (getGroupFormat()) {

// Simple, plain-text groups
case PLAIN:
return new PlainGroupParser();

// LDAP DNs
case LDAP:
return new LDAPGroupParser(getGroupLDAPAttribute(), getGroupLDAPBaseDN());

default:
throw new GuacamoleServerException("Unsupported CAS group format: " + getGroupFormat());

}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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.cas.conf;

import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import org.apache.guacamole.properties.GuacamoleProperty;
import org.apache.guacamole.GuacamoleServerException;

/**
* A GuacamoleProperty whose value is an LDAP DN.
*/
public abstract class LdapNameGuacamoleProperty implements GuacamoleProperty<LdapName> {

@Override
public LdapName parseValue(String value) throws GuacamoleServerException {

// Consider null/empty values to be empty
if (value == null || value.isEmpty())
return null;

// Parse provided value as an LDAP DN
try {
return new LdapName(value);
}
catch (InvalidNameException e) {
throw new GuacamoleServerException("Invalid LDAP distinguished name.", e);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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.cas.group;

import org.apache.guacamole.properties.EnumGuacamoleProperty.PropertyValue;

/**
* Possible formats of group names received from CAS.
*/
public enum GroupFormat {

/**
* Simple, plain-text group names.
*/
@PropertyValue("plain")
PLAIN,

/**
* Group names formatted as LDAP DNs.
*/
@PropertyValue("ldap")
LDAP

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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.cas.group;

/**
* Parser which converts the group names returned by CAS into names usable by
* Guacamole. The format of a CAS group name may vary by the underlying
* authentication backend. For example, a CAS deployment backed by LDAP may
* provide group names as LDAP DNs, which must be transformed into normal group
* names to be usable within Guacamole.
*
* @see LDAPGroupParser
*/
public interface GroupParser {

/**
* Parses the given CAS group name into a group name usable by Guacamole.
*
* @param casGroup
* The group name retrieved from CAS.
*
* @return
* A group name usable by Guacamole, or null if the group is not valid.
*/
String parse(String casGroup);

}
Loading

0 comments on commit ae9c55c

Please sign in to comment.