mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-1020: Implement extension with enhanced login and connection restrictions.
This commit is contained in:
3
extensions/guacamole-auth-restrict/.gitignore
vendored
Normal file
3
extensions/guacamole-auth-restrict/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
src/main/resources/generated/
|
||||
target/
|
||||
*~
|
0
extensions/guacamole-auth-restrict/.ratignore
Normal file
0
extensions/guacamole-auth-restrict/.ratignore
Normal file
179
extensions/guacamole-auth-restrict/pom.xml
Normal file
179
extensions/guacamole-auth-restrict/pom.xml
Normal file
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>guacamole-auth-restrict</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.6.0</version>
|
||||
<name>guacamole-auth-restrict</name>
|
||||
<url>http://guacamole.apache.org/</url>
|
||||
|
||||
<parent>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>extensions</artifactId>
|
||||
<version>1.6.0</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<!-- Pre-cache Angular templates with maven-angular-plugin -->
|
||||
<plugin>
|
||||
<groupId>com.keithbranton.mojo</groupId>
|
||||
<artifactId>angular-maven-plugin</artifactId>
|
||||
<version>0.3.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>html2js</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<sourceDir>${basedir}/src/main/resources</sourceDir>
|
||||
<include>**/*.html</include>
|
||||
<target>${basedir}/src/main/resources/generated/templates-main/templates.js</target>
|
||||
<prefix>app/ext/restrict</prefix>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- JS/CSS Minification Plugin -->
|
||||
<plugin>
|
||||
<groupId>com.github.buckelieg</groupId>
|
||||
<artifactId>minify-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-cli</id>
|
||||
<configuration>
|
||||
<charset>UTF-8</charset>
|
||||
|
||||
<webappSourceDir>${basedir}/src/main/resources</webappSourceDir>
|
||||
<webappTargetDir>${project.build.directory}/classes</webappTargetDir>
|
||||
|
||||
<cssSourceDir>/</cssSourceDir>
|
||||
<cssTargetDir>/</cssTargetDir>
|
||||
<cssFinalFile>restrict.css</cssFinalFile>
|
||||
|
||||
<cssSourceFiles>
|
||||
<cssSourceFile>license.txt</cssSourceFile>
|
||||
</cssSourceFiles>
|
||||
|
||||
<cssSourceIncludes>
|
||||
<cssSourceInclude>**/*.css</cssSourceInclude>
|
||||
</cssSourceIncludes>
|
||||
|
||||
<jsSourceDir>/</jsSourceDir>
|
||||
<jsTargetDir>/</jsTargetDir>
|
||||
<jsFinalFile>restrict.js</jsFinalFile>
|
||||
|
||||
<jsSourceFiles>
|
||||
<jsSourceFile>license.txt</jsSourceFile>
|
||||
</jsSourceFiles>
|
||||
|
||||
<jsSourceIncludes>
|
||||
<jsSourceInclude>**/*.js</jsSourceInclude>
|
||||
</jsSourceIncludes>
|
||||
|
||||
<!-- Do not minify and include tests -->
|
||||
<jsSourceExcludes>
|
||||
<jsSourceExclude>**/*.test.js</jsSourceExclude>
|
||||
</jsSourceExcludes>
|
||||
<jsEngine>CLOSURE</jsEngine>
|
||||
|
||||
<!-- Disable warnings for JSDoc annotations -->
|
||||
<closureWarningLevels>
|
||||
<misplacedTypeAnnotation>OFF</misplacedTypeAnnotation>
|
||||
<nonStandardJsDocs>OFF</nonStandardJsDocs>
|
||||
</closureWarningLevels>
|
||||
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>minify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Guacamole Extension API -->
|
||||
<dependency>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>guacamole-ext</artifactId>
|
||||
<version>1.6.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Guava - Utility Library -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Guice -->
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Java servlet API -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>2.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JUnit -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Guacamole depends on an implementation of JAX-WS -->
|
||||
<dependency>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
<artifactId>javax.ws.rs-api</artifactId>
|
||||
<version>2.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Library for unified IPv4/6 parsing and validation -->
|
||||
<dependency>
|
||||
<groupId>com.github.seancfoley</groupId>
|
||||
<artifactId>ipaddress</artifactId>
|
||||
<version>5.5.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<assembly
|
||||
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
|
||||
|
||||
<id>dist</id>
|
||||
<baseDirectory>${project.artifactId}-${project.version}</baseDirectory>
|
||||
|
||||
<!-- Output tar.gz -->
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
|
||||
<!-- Include licenses and extension .jar -->
|
||||
<fileSets>
|
||||
|
||||
<!-- Include licenses -->
|
||||
<fileSet>
|
||||
<outputDirectory></outputDirectory>
|
||||
<directory>target/licenses</directory>
|
||||
</fileSet>
|
||||
|
||||
<!-- Include extension .jar -->
|
||||
<fileSet>
|
||||
<directory>target</directory>
|
||||
<outputDirectory></outputDirectory>
|
||||
<includes>
|
||||
<include>*.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
|
||||
</fileSets>
|
||||
|
||||
</assembly>
|
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.restrict;
|
||||
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.restrict.user.RestrictUserContext;
|
||||
import org.apache.guacamole.net.auth.AbstractAuthenticationProvider;
|
||||
import org.apache.guacamole.net.auth.AuthenticatedUser;
|
||||
import org.apache.guacamole.net.auth.Credentials;
|
||||
import org.apache.guacamole.net.auth.UserContext;
|
||||
|
||||
/**
|
||||
* AuthenticationProvider implementation which provides additional restrictions
|
||||
* for users, groups of users, connections, and connection groups, allowing
|
||||
* administrators to further control access to Guacamole resources.
|
||||
*/
|
||||
public class RestrictionAuthenticationProvider extends AbstractAuthenticationProvider {
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return "restrict";
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserContext decorate(UserContext context,
|
||||
AuthenticatedUser authenticatedUser, Credentials credentials)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Verify identity of user
|
||||
RestrictionVerificationService.verifyLoginRestrictions(context, authenticatedUser);
|
||||
|
||||
// User has been verified, and authentication should be allowed to
|
||||
// continue
|
||||
return new RestrictUserContext(context, credentials.getRemoteAddress());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserContext redecorate(UserContext decorated, UserContext context,
|
||||
AuthenticatedUser authenticatedUser, Credentials credentials)
|
||||
throws GuacamoleException {
|
||||
return new RestrictUserContext(context, credentials.getRemoteAddress());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* 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.restrict;
|
||||
|
||||
import inet.ipaddr.HostName;
|
||||
import inet.ipaddr.HostNameException;
|
||||
import inet.ipaddr.IPAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.restrict.connection.RestrictConnection;
|
||||
import org.apache.guacamole.auth.restrict.user.RestrictUser;
|
||||
import org.apache.guacamole.auth.restrict.usergroup.RestrictUserGroup;
|
||||
import org.apache.guacamole.calendar.DailyRestriction;
|
||||
import org.apache.guacamole.calendar.TimeRestrictionParser;
|
||||
import org.apache.guacamole.host.HostRestrictionParser;
|
||||
import org.apache.guacamole.language.TranslatableGuacamoleSecurityException;
|
||||
import org.apache.guacamole.net.auth.AuthenticatedUser;
|
||||
import org.apache.guacamole.net.auth.Directory;
|
||||
import org.apache.guacamole.net.auth.UserContext;
|
||||
import org.apache.guacamole.net.auth.UserGroup;
|
||||
import org.apache.guacamole.net.auth.permission.SystemPermission;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Service for verifying additional user login restrictions against a given
|
||||
* login attempt.
|
||||
*/
|
||||
public class RestrictionVerificationService {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RestrictionVerificationService.class);
|
||||
|
||||
/**
|
||||
* Parse out the provided strings of allowed and denied times, verifying
|
||||
* whether or not a login or connection should be allowed at the current
|
||||
* day and time. A boolean true will be returned if the action should be
|
||||
* allowed, otherwise false will be returned.
|
||||
*
|
||||
* @param allowedTimeString
|
||||
* The string containing the times that should be parsed to determine if
|
||||
* the login or connection should be allowed at the current time, or
|
||||
* null or an empty string if there are no specific allowed times defined.
|
||||
*
|
||||
* @param deniedTimeString
|
||||
* The string containing the times that should be parsed to determine if
|
||||
* the login or connection should be denied at the current time, or null
|
||||
* or an empty string if there are no specific times during which a
|
||||
* action should be denied.
|
||||
*
|
||||
* @return
|
||||
* True if the login or connection should be allowed, otherwise false.
|
||||
*/
|
||||
private static boolean allowedByTimeRestrictions(String allowedTimeString,
|
||||
String deniedTimeString) {
|
||||
|
||||
// Check for denied entries, first, returning false if the login or
|
||||
// connection should not be allowed.
|
||||
if (deniedTimeString != null && !deniedTimeString.isEmpty()) {
|
||||
List<DailyRestriction> deniedTimes =
|
||||
TimeRestrictionParser.parseString(deniedTimeString);
|
||||
|
||||
for (DailyRestriction restriction : deniedTimes) {
|
||||
if (restriction.appliesNow())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If no allowed entries are present, return true, allowing the login
|
||||
// or connection to continue.
|
||||
if (allowedTimeString == null || allowedTimeString.isEmpty())
|
||||
return true;
|
||||
|
||||
List<DailyRestriction> allowedTimes =
|
||||
TimeRestrictionParser.parseString(allowedTimeString);
|
||||
|
||||
// Allowed entries are present, loop through them and check for a valid time.
|
||||
for (DailyRestriction restriction : allowedTimes) {
|
||||
// If this time allows the login or connection return true.
|
||||
if (restriction.appliesNow())
|
||||
return true;
|
||||
}
|
||||
|
||||
// We have allowed entries, but login hasn't matched, so deny it.
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the strings of allowed and denied hosts, verify that the login or
|
||||
* connection should be allowed from the given remote address. If the action
|
||||
* should not be allowed, return false - otherwise, return true.
|
||||
*
|
||||
* @param allowedHostsString
|
||||
* The string containing a semicolon-separated list of hosts from
|
||||
* which the login or connection should be allowed, or null or an empty
|
||||
* string if no specific set of allowed hosts is defined.
|
||||
*
|
||||
* @param deniedHostsString
|
||||
* The string containing a semicolon-separated list of hosts from
|
||||
* which the login or connection should be denied, or null or an empty
|
||||
* string if no specific set of denied hosts is defined.
|
||||
*
|
||||
* @param remoteAddress
|
||||
* The IP address from which the user is logging in or has logged in
|
||||
* and is attempting to connect from, if it is known. If it is unknown
|
||||
* and restrictions are defined, the login or connection will be denied.
|
||||
*
|
||||
* @return
|
||||
* True if the login or connection should be allowed by the host-based
|
||||
* restrictions, otherwise false.
|
||||
*/
|
||||
private static boolean allowedByHostRestrictions(String allowedHostsString,
|
||||
String deniedHostsString, String remoteAddress) {
|
||||
|
||||
HostName remoteHostName = new HostName(remoteAddress);
|
||||
|
||||
// If attributes do not exist or are empty then the action is allowed.
|
||||
if ((allowedHostsString == null || allowedHostsString.isEmpty())
|
||||
&& (deniedHostsString == null || deniedHostsString.isEmpty()))
|
||||
return true;
|
||||
|
||||
// If the remote address cannot be determined, and restrictions are
|
||||
// in effect, log an error and deny the action.
|
||||
if (remoteAddress == null || remoteAddress.isEmpty()) {
|
||||
LOGGER.warn("Host-based restrictions are present, but the remote "
|
||||
+ "address is invalid or could not be resolved. "
|
||||
+ "The action will not be allowed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Split denied hosts attribute and process each entry, checking them
|
||||
// against the current remote address, and returning false if a match is
|
||||
// found.
|
||||
List<HostName> deniedHosts = HostRestrictionParser.parseHostList(deniedHostsString);
|
||||
for (HostName hostName : deniedHosts) {
|
||||
try {
|
||||
if (hostName.isAddress() && hostName.toAddress().contains(remoteHostName.asAddress()))
|
||||
return false;
|
||||
|
||||
else
|
||||
for (IPAddress currAddr : hostName.toAllAddresses())
|
||||
if (currAddr.matches(remoteHostName.asAddressString()))
|
||||
return false;
|
||||
}
|
||||
catch (UnknownHostException | HostNameException e) {
|
||||
LOGGER.warn("Unknown or invalid host in denied hosts list: \"{}\"", hostName);
|
||||
LOGGER.debug("Exception while trying to resolve host: \"{}\"", hostName, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If denied hosts have been checked and allowed hosts are empty, we're
|
||||
// good, and can allow the action.
|
||||
if (allowedHostsString == null || allowedHostsString.isEmpty())
|
||||
return true;
|
||||
|
||||
// Run through allowed hosts, if there are any, and return, allowing the
|
||||
// action if there are any matches.
|
||||
List<HostName> allowedHosts = HostRestrictionParser.parseHostList(allowedHostsString);
|
||||
for (HostName hostName : allowedHosts) {
|
||||
try {
|
||||
// If the entry is an IP or Subnet, check the remote address against it directly
|
||||
if (hostName.isAddress() && hostName.toAddress().contains(remoteHostName.asAddress()))
|
||||
return true;
|
||||
|
||||
// Entry is a hostname, so resolve to IPs and check each one
|
||||
for (IPAddress currAddr : hostName.toAllAddresses())
|
||||
if (currAddr.matches(remoteHostName.asAddressString()))
|
||||
return true;
|
||||
|
||||
}
|
||||
// If an entry cannot be resolved we will log a warning.
|
||||
catch (UnknownHostException | HostNameException e) {
|
||||
LOGGER.warn("Unknown host encountered in allowed host string: {}", hostName);
|
||||
LOGGER.debug("Exception received trying to resolve host: {}", hostName, e);
|
||||
}
|
||||
}
|
||||
|
||||
// If we've made it here, the allowed hosts do not contain the remote
|
||||
// address, and the action should not be allowed;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the login restrictions supported by this extension for the user
|
||||
* who is attempting to log in, throwing an exception if any of the
|
||||
* restrictions result in the user not being allowed to log in.
|
||||
*
|
||||
* @param context
|
||||
* The context of the user who is attempting to log in.
|
||||
*
|
||||
* @param authenticatedUser
|
||||
* The AuthenticatedUser object associated with the user who is
|
||||
* attempting to log in.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If any of the restrictions should prevent the user from logging in.
|
||||
*/
|
||||
public static void verifyLoginRestrictions(UserContext context,
|
||||
AuthenticatedUser authenticatedUser) throws GuacamoleException {
|
||||
|
||||
// Get user's attributes
|
||||
Map<String, String> userAttributes = context.self().getAttributes();
|
||||
String remoteAddress = authenticatedUser.getCredentials().getRemoteAddress();
|
||||
|
||||
if (context.self().getEffectivePermissions().getSystemPermissions().hasPermission(SystemPermission.Type.ADMINISTER)) {
|
||||
LOGGER.warn("User \"{}\" has System Administration permissions; additional restrictions will be bypassed.",
|
||||
authenticatedUser.getIdentifier());
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify time-based restrictions specific to the user
|
||||
String allowedTimeString = userAttributes.get(RestrictUser.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
|
||||
String deniedTimeString = userAttributes.get(RestrictUser.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
|
||||
if (!allowedByTimeRestrictions(allowedTimeString, deniedTimeString))
|
||||
throw new TranslatableInvalidTimeLoginException("User \""
|
||||
+ authenticatedUser.getIdentifier()
|
||||
+ "\" is not allowed to log in at this time.",
|
||||
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW");
|
||||
|
||||
// Verify host-based restrictions specific to the user
|
||||
String allowedHostString = userAttributes.get(RestrictUser.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
|
||||
String deniedHostString = userAttributes.get(RestrictUser.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
|
||||
if (!allowedByHostRestrictions(allowedHostString, deniedHostString, remoteAddress))
|
||||
throw new TranslatableInvalidHostLoginException("User \""
|
||||
+ authenticatedUser.getIdentifier()
|
||||
+"\" is not allowed to log in from \""
|
||||
+ remoteAddress + "\"",
|
||||
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST");
|
||||
|
||||
// Gather user's effective groups.
|
||||
Set<String> userGroups = authenticatedUser.getEffectiveUserGroups();
|
||||
Directory<UserGroup> directoryGroups = context.getPrivileged().getUserGroupDirectory();
|
||||
|
||||
// Loop user's effective groups and verify restrictions
|
||||
for (String userGroup : userGroups) {
|
||||
UserGroup thisGroup = directoryGroups.get(userGroup);
|
||||
if (thisGroup == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get group's attributes
|
||||
Map<String, String> grpAttributes = thisGroup.getAttributes();
|
||||
|
||||
// Pull time-based restrictions for this group and verify
|
||||
String grpAllowedTimeString = grpAttributes.get(RestrictUserGroup.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
|
||||
String grpDeniedTimeString = grpAttributes.get(RestrictUserGroup.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
|
||||
if (!allowedByTimeRestrictions(grpAllowedTimeString, grpDeniedTimeString))
|
||||
throw new TranslatableInvalidTimeLoginException("User \""
|
||||
+ authenticatedUser.getIdentifier()
|
||||
+"\" is not allowed to log in at this time due to restrictions on group \""
|
||||
+ userGroup + "\".",
|
||||
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW");
|
||||
|
||||
// Pull host-based restrictions for this group and verify
|
||||
String grpAllowedHostString = grpAttributes.get(RestrictUserGroup.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
|
||||
String grpDeniedHostString = grpAttributes.get(RestrictUserGroup.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
|
||||
if (!allowedByHostRestrictions(grpAllowedHostString, grpDeniedHostString, remoteAddress))
|
||||
throw new TranslatableInvalidHostLoginException("User \""
|
||||
+ authenticatedUser.getIdentifier()
|
||||
+ "\" is not allowed to log in from this host due to restrictions on group \""
|
||||
+ userGroup + "\".",
|
||||
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the connection restrictions supported by this extension for the
|
||||
* connection the user is attempting to access, throwing an exception if
|
||||
* any of the restrictions result in the connection being unavailable.
|
||||
*
|
||||
* @param connectionAttributes
|
||||
* The attributes of the connection that may contain any additional
|
||||
* restrictions on use of the connection.
|
||||
*
|
||||
* @param remoteAddress
|
||||
* The remote IP address of the user trying to access the connection.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If any of the restrictions should prevent the connection from being
|
||||
* used by the user at the current time.
|
||||
*/
|
||||
public static void verifyConnectionRestrictions(
|
||||
Map<String, String> connectionAttributes, String remoteAddress)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Verify time-based restrictions specific to this connection.
|
||||
String allowedTimeString = connectionAttributes.get(RestrictConnection.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
|
||||
String deniedTimeString = connectionAttributes.get(RestrictConnection.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
|
||||
if (!allowedByTimeRestrictions(allowedTimeString, deniedTimeString))
|
||||
throw new TranslatableGuacamoleSecurityException(
|
||||
"Use of this connection is not allowed at this time.",
|
||||
"RESTRICT.ERROR_CONNECTION_NOT_ALLOWED_NOW"
|
||||
);
|
||||
|
||||
// Verify host-based restrictions specific to this connection.
|
||||
String allowedHostString = connectionAttributes.get(RestrictConnection.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
|
||||
String deniedHostString = connectionAttributes.get(RestrictConnection.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
|
||||
if (!allowedByHostRestrictions(allowedHostString, deniedHostString, remoteAddress))
|
||||
throw new TranslatableGuacamoleSecurityException(
|
||||
"Use of this connection is not allowed from this remote host: \"" + remoteAddress + "\".",
|
||||
"RESTRICT.ERROR_CONNECTION_NOT_ALLOWED_NOW"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.restrict;
|
||||
|
||||
import org.apache.guacamole.language.TranslatableGuacamoleSecurityException;
|
||||
import org.apache.guacamole.language.TranslatableMessage;
|
||||
|
||||
/**
|
||||
* An exception that represents an invalid login or connection due to
|
||||
* restrictions based on the host from which the action should be allowed.
|
||||
*/
|
||||
public class TranslatableInvalidHostConnectionException
|
||||
extends TranslatableGuacamoleSecurityException {
|
||||
|
||||
/**
|
||||
* The serial version ID of this class.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Create a new host-based connection exception with the given message and
|
||||
* translation string that can be processed by Guacamole's translation
|
||||
* service.
|
||||
*
|
||||
* @param message
|
||||
* The non-translatable, human-readable message containing details
|
||||
* of the exception.
|
||||
*
|
||||
* @param translatableMessage
|
||||
* A translatable, human-readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public TranslatableInvalidHostConnectionException(String message,
|
||||
TranslatableMessage translatableMessage) {
|
||||
super(message, translatableMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new host-based connection exception with the given message and
|
||||
* translation string that can be processed by Guacamole's translation
|
||||
* service.
|
||||
*
|
||||
* @param message
|
||||
* The non-translatable, human-readable message containing details
|
||||
* of the exception.
|
||||
*
|
||||
* @param translationKey
|
||||
* The arbitrary key which can be used to look up the message to be
|
||||
* displayed in the user's native language.
|
||||
*/
|
||||
public TranslatableInvalidHostConnectionException(String message,
|
||||
String translationKey) {
|
||||
super(message, new TranslatableMessage(translationKey));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.restrict;
|
||||
|
||||
import org.apache.guacamole.language.TranslatableGuacamoleClientException;
|
||||
import org.apache.guacamole.language.TranslatableMessage;
|
||||
|
||||
/**
|
||||
* An exception that represents an invalid login or connection due to
|
||||
* restrictions based on the host from which the action should be allowed.
|
||||
*/
|
||||
public class TranslatableInvalidHostLoginException
|
||||
extends TranslatableGuacamoleClientException {
|
||||
|
||||
/**
|
||||
* The serial version ID of this class.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Create a new host-based login exception with the given message and
|
||||
* translation string that can be processed by Guacamole's translation
|
||||
* service.
|
||||
*
|
||||
* @param message
|
||||
* The non-translatable, human-readable message containing details
|
||||
* of the exception.
|
||||
*
|
||||
* @param translatableMessage
|
||||
* A translatable, human-readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public TranslatableInvalidHostLoginException(String message,
|
||||
TranslatableMessage translatableMessage) {
|
||||
super(message, translatableMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new host-based login exception with the given message and
|
||||
* translation string that can be processed by Guacamole's translation
|
||||
* service.
|
||||
*
|
||||
* @param message
|
||||
* The non-translatable, human-readable message containing details
|
||||
* of the exception.
|
||||
*
|
||||
* @param translationKey
|
||||
* The arbitrary key which can be used to look up the message to be
|
||||
* displayed in the user's native language.
|
||||
*/
|
||||
public TranslatableInvalidHostLoginException(String message, String translationKey) {
|
||||
super(message, new TranslatableMessage(translationKey));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.restrict;
|
||||
|
||||
import org.apache.guacamole.language.TranslatableGuacamoleSecurityException;
|
||||
import org.apache.guacamole.language.TranslatableMessage;
|
||||
|
||||
/**
|
||||
* An exception that represents an invalid login due to restrictions based
|
||||
* on the time of day and day of week the user is allowed to log in.
|
||||
*/
|
||||
public class TranslatableInvalidTimeConnectionException
|
||||
extends TranslatableGuacamoleSecurityException {
|
||||
|
||||
/**
|
||||
* The serial version ID of this class.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Create a new time-based login exception with the given message and
|
||||
* translation string that can be processed by Guacamole's translation
|
||||
* service.
|
||||
*
|
||||
* @param message
|
||||
* The non-translatable, human-readable message containing details
|
||||
* of the exception.
|
||||
*
|
||||
* @param translatableMessage
|
||||
* A translatable, human-readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public TranslatableInvalidTimeConnectionException(String message,
|
||||
TranslatableMessage translatableMessage) {
|
||||
super(message, translatableMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new time-based login exception with the given message and
|
||||
* translation string that can be processed by Guacamole's translation
|
||||
* service.
|
||||
*
|
||||
* @param message
|
||||
* The non-translatable, human-readable message containing details
|
||||
* of the exception.
|
||||
*
|
||||
* @param translationKey
|
||||
* The arbitrary key which can be used to look up the message to be
|
||||
* displayed in the user's native language.
|
||||
*/
|
||||
public TranslatableInvalidTimeConnectionException(String message,
|
||||
String translationKey) {
|
||||
super(message, new TranslatableMessage(translationKey));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.restrict;
|
||||
|
||||
import org.apache.guacamole.language.TranslatableGuacamoleClientException;
|
||||
import org.apache.guacamole.language.TranslatableMessage;
|
||||
|
||||
/**
|
||||
* An exception that represents an invalid login due to restrictions based
|
||||
* on the time of day and day of week the user is allowed to log in.
|
||||
*/
|
||||
public class TranslatableInvalidTimeLoginException
|
||||
extends TranslatableGuacamoleClientException {
|
||||
|
||||
/**
|
||||
* The serial version ID of this class.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Create a new time-based login exception with the given message and
|
||||
* translation string that can be processed by Guacamole's translation
|
||||
* service.
|
||||
*
|
||||
* @param message
|
||||
* The non-translatable, human-readable message containing details
|
||||
* of the exception.
|
||||
*
|
||||
* @param translatableMessage
|
||||
* A translatable, human-readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public TranslatableInvalidTimeLoginException(String message,
|
||||
TranslatableMessage translatableMessage) {
|
||||
super(message, translatableMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new time-based login exception with the given message and
|
||||
* translation string that can be processed by Guacamole's translation
|
||||
* service.
|
||||
*
|
||||
* @param message
|
||||
* The non-translatable, human-readable message containing details
|
||||
* of the exception.
|
||||
*
|
||||
* @param translationKey
|
||||
* The arbitrary key which can be used to look up the message to be
|
||||
* displayed in the user's native language.
|
||||
*/
|
||||
public TranslatableInvalidTimeLoginException(String message,
|
||||
String translationKey) {
|
||||
super(message, new TranslatableMessage(translationKey));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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.restrict.connection;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.restrict.RestrictionVerificationService;
|
||||
import org.apache.guacamole.auth.restrict.form.HostRestrictionField;
|
||||
import org.apache.guacamole.auth.restrict.form.TimeRestrictionField;
|
||||
import org.apache.guacamole.form.Form;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.apache.guacamole.net.auth.Connection;
|
||||
import org.apache.guacamole.net.auth.DelegatingConnection;
|
||||
import org.apache.guacamole.protocol.GuacamoleClientInformation;
|
||||
|
||||
/**
|
||||
* A Connection implementation that wraps another connection, providing additional
|
||||
* ability to control access to the connection.
|
||||
*/
|
||||
public class RestrictConnection extends DelegatingConnection {
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of weekdays and times (UTC)
|
||||
* that this connection can be accessed. The presence of values within this
|
||||
* attribute will automatically restrict use of the connections at any
|
||||
* times that are not specified.
|
||||
*/
|
||||
public static final String RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME = "guac-restrict-time-allowed";
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of weekdays and times (UTC)
|
||||
* that this connection cannot be accessed. Denied times will always take
|
||||
* precedence over allowed times. The presence of this attribute without
|
||||
* guac-restrict-time-allowed will deny access only during the times listed
|
||||
* in this attribute, allowing access at all other times. The presence of
|
||||
* this attribute along with the guac-restrict-time-allowed attribute will
|
||||
* deny access at any times that overlap with the allowed times.
|
||||
*/
|
||||
public static final String RESTRICT_TIME_DENIED_ATTRIBUTE_NAME = "guac-restrict-time-denied";
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of hosts from which a user
|
||||
* may access this connection. The presence of this attribute will restrict
|
||||
* access to only users accessing Guacamole from the list of hosts contained
|
||||
* in the attribute, subject to further restriction by the
|
||||
* guac-restrict-hosts-denied attribute.
|
||||
*/
|
||||
public static final String RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME = "guac-restrict-hosts-allowed";
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of hosts from which
|
||||
* a user may not access this connection. The presence of this attribute,
|
||||
* absent the guac-restrict-hosts-allowed attribute, will allow access from
|
||||
* all hosts except the ones listed in this attribute. The presence of this
|
||||
* attribute coupled with the guac-restrict-hosts-allowed attribute will
|
||||
* block access from any IPs in this list, overriding any that may be
|
||||
* allowed.
|
||||
*/
|
||||
public static final String RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME = "guac-restrict-hosts-denied";
|
||||
|
||||
/**
|
||||
* The list of all connection attributes provided by this Connection implementation.
|
||||
*/
|
||||
public static final List<String> RESTRICT_CONNECTION_ATTRIBUTES = Arrays.asList(
|
||||
RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME,
|
||||
RESTRICT_TIME_DENIED_ATTRIBUTE_NAME,
|
||||
RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME,
|
||||
RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME
|
||||
);
|
||||
|
||||
/**
|
||||
* The form containing the list of fields for the attributes provided
|
||||
* by this module.
|
||||
*/
|
||||
public static final Form RESTRICT_CONNECTION_FORM = new Form("restrict-login-form",
|
||||
Arrays.asList(
|
||||
new TimeRestrictionField(RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME),
|
||||
new TimeRestrictionField(RESTRICT_TIME_DENIED_ATTRIBUTE_NAME),
|
||||
new HostRestrictionField(RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME),
|
||||
new HostRestrictionField(RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME)
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* The remote address from which the user attempting to access this
|
||||
* connection logged in.
|
||||
*/
|
||||
private final String remoteAddress;
|
||||
|
||||
/**
|
||||
* Wraps the given Connection object, providing capability of further
|
||||
* restricting connection access beyond the default access control provided
|
||||
* by other modules.
|
||||
*
|
||||
* @param connection
|
||||
* The Connection object to wrap.
|
||||
*
|
||||
* @param remoteAddress
|
||||
* The remote address from which the user attempting to access this
|
||||
* connection logged in.
|
||||
*/
|
||||
public RestrictConnection(Connection connection, String remoteAddress) {
|
||||
super(connection);
|
||||
this.remoteAddress = remoteAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original Connection object wrapped by this RestrictConnection.
|
||||
*
|
||||
* @return
|
||||
* The wrapped Connection object.
|
||||
*/
|
||||
public Connection getUndecorated() {
|
||||
return getDelegateConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttributes() {
|
||||
|
||||
// Create independent, mutable copy of attributes
|
||||
Map<String, String> attributes = new HashMap<>(super.getAttributes());
|
||||
|
||||
// Loop through extension-specific attributes and add them where no
|
||||
// values exist, so that they show up in the web UI.
|
||||
for (String attribute : RESTRICT_CONNECTION_ATTRIBUTES) {
|
||||
String value = attributes.get(attribute);
|
||||
if (value == null || value.isEmpty())
|
||||
attributes.put(attribute, null);
|
||||
}
|
||||
|
||||
return attributes;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
|
||||
// Create independent, mutable copy of attributes
|
||||
attributes = new HashMap<>(attributes);
|
||||
|
||||
// Loop through extension-specific attributes, only sending ones
|
||||
// that are non-null and non-empty to the underlying storage mechanism.
|
||||
for (String attribute : RESTRICT_CONNECTION_ATTRIBUTES) {
|
||||
String value = attributes.get(attribute);
|
||||
if (value != null && value.isEmpty())
|
||||
attributes.put(attribute, null);
|
||||
}
|
||||
|
||||
super.setAttributes(attributes);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleTunnel connect(GuacamoleClientInformation info,
|
||||
Map<String, String> tokens) throws GuacamoleException {
|
||||
|
||||
// Verify the restrictions for this connection.
|
||||
RestrictionVerificationService.verifyConnectionRestrictions(getAttributes(), remoteAddress);
|
||||
|
||||
// Connect
|
||||
return super.connect(info, tokens);
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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.restrict.connectiongroup;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.restrict.RestrictionVerificationService;
|
||||
import org.apache.guacamole.auth.restrict.form.HostRestrictionField;
|
||||
import org.apache.guacamole.auth.restrict.form.TimeRestrictionField;
|
||||
import org.apache.guacamole.form.Form;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.apache.guacamole.net.auth.ConnectionGroup;
|
||||
import org.apache.guacamole.net.auth.DelegatingConnectionGroup;
|
||||
import org.apache.guacamole.protocol.GuacamoleClientInformation;
|
||||
|
||||
/**
|
||||
* A ConnectionGroup implementation that wraps an existing ConnectionGroup,
|
||||
* providing additional ability to control access to the ConnectionGroup.
|
||||
*/
|
||||
public class RestrictConnectionGroup extends DelegatingConnectionGroup {
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of weekdays and times (UTC)
|
||||
* that this connection group can be accessed. The presence of values within
|
||||
* this attribute will automatically restrict use of the connection group
|
||||
* at any times that are not specified.
|
||||
*/
|
||||
public static final String RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME = "guac-restrict-time-allowed";
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of weekdays and times (UTC)
|
||||
* that this connection group cannot be accessed. Denied times will always
|
||||
* take precedence over allowed times. The presence of this attribute without
|
||||
* guac-restrict-time-allowed will deny access only during the times listed
|
||||
* in this attribute, allowing access at all other times. The presence of
|
||||
* this attribute along with the guac-restrict-time-allowed attribute will
|
||||
* deny access at any times that overlap with the allowed times.
|
||||
*/
|
||||
public static final String RESTRICT_TIME_DENIED_ATTRIBUTE_NAME = "guac-restrict-time-denied";
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of hosts from which a user
|
||||
* may access this connection group. The presence of this attribute will
|
||||
* restrict access to only users accessing Guacamole from the list of hosts
|
||||
* contained in the attribute, subject to further restriction by the
|
||||
* guac-restrict-hosts-denied attribute.
|
||||
*/
|
||||
public static final String RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME = "guac-restrict-hosts-allowed";
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of hosts from which
|
||||
* a user may not access this connection group. The presence of this
|
||||
* attribute, absent the guac-restrict-hosts-allowed attribute, will allow
|
||||
* access from all hosts except the ones listed in this attribute. The
|
||||
* presence of this attribute coupled with the guac-restrict-hosts-allowed
|
||||
* attribute will block access from any hosts in this list, overriding any
|
||||
* that may be allowed.
|
||||
*/
|
||||
public static final String RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME = "guac-restrict-hosts-denied";
|
||||
|
||||
/**
|
||||
* The list of all connection group attributes provided by this
|
||||
* ConnectionGroup implementation.
|
||||
*/
|
||||
public static final List<String> RESTRICT_CONNECTIONGROUP_ATTRIBUTES = Arrays.asList(
|
||||
RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME,
|
||||
RESTRICT_TIME_DENIED_ATTRIBUTE_NAME,
|
||||
RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME,
|
||||
RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME
|
||||
);
|
||||
|
||||
/**
|
||||
* The form containing the list of fields for the attributes provided
|
||||
* by this ConnectionGroup implementation.
|
||||
*/
|
||||
public static final Form RESTRICT_CONNECTIONGROUP_FORM = new Form("restrict-login-form",
|
||||
Arrays.asList(
|
||||
new TimeRestrictionField(RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME),
|
||||
new TimeRestrictionField(RESTRICT_TIME_DENIED_ATTRIBUTE_NAME),
|
||||
new HostRestrictionField(RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME),
|
||||
new HostRestrictionField(RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME)
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* The remote address from which the user accessing this connection group
|
||||
* logged in.
|
||||
*/
|
||||
private final String remoteAddress;
|
||||
|
||||
/**
|
||||
* Wraps the given ConnectionGroup object, providing capability of further
|
||||
* restricting connection group access beyond the default access control
|
||||
* provided by other modules.
|
||||
*
|
||||
* @param connectionGroup
|
||||
* The ConnectionGroup object to wrap.
|
||||
*
|
||||
* @param remoteAddress
|
||||
* The remote address from which the user accessing this connection
|
||||
* logged in.
|
||||
*/
|
||||
public RestrictConnectionGroup(ConnectionGroup connectionGroup, String remoteAddress) {
|
||||
super(connectionGroup);
|
||||
this.remoteAddress = remoteAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original ConnectionGroup object wrapped by this
|
||||
* RestrictConnectionGroup.
|
||||
*
|
||||
* @return
|
||||
* The wrapped ConnectionGroup object.
|
||||
*/
|
||||
public ConnectionGroup getUndecorated() {
|
||||
return getDelegateConnectionGroup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttributes() {
|
||||
|
||||
// Create independent, mutable copy of attributes
|
||||
Map<String, String> attributes = new HashMap<>(super.getAttributes());
|
||||
|
||||
// Loop through extension-specific attributes and add them where no
|
||||
// values exist, so that they show up in the web UI.
|
||||
for (String attribute : RESTRICT_CONNECTIONGROUP_ATTRIBUTES) {
|
||||
String value = attributes.get(attribute);
|
||||
if (value == null || value.isEmpty())
|
||||
attributes.put(attribute, null);
|
||||
}
|
||||
|
||||
return attributes;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
|
||||
// Create independent, mutable copy of attributes
|
||||
attributes = new HashMap<>(attributes);
|
||||
|
||||
// Loop through extension-specific attributes, only sending ones
|
||||
// that are non-null and non-empty to the underlying storage mechanism.
|
||||
for (String attribute : RESTRICT_CONNECTIONGROUP_ATTRIBUTES) {
|
||||
String value = attributes.get(attribute);
|
||||
if (value != null && value.isEmpty())
|
||||
attributes.put(attribute, null);
|
||||
}
|
||||
|
||||
super.setAttributes(attributes);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleTunnel connect(GuacamoleClientInformation info,
|
||||
Map<String, String> tokens) throws GuacamoleException {
|
||||
|
||||
// Verify restrictions for this connection group.
|
||||
RestrictionVerificationService.verifyConnectionRestrictions(getAttributes(), remoteAddress);
|
||||
|
||||
// Connect
|
||||
return super.connect(info, tokens);
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.restrict.form;
|
||||
|
||||
import org.apache.guacamole.form.Field;
|
||||
|
||||
/**
|
||||
* A field that parses out a string of semi-colon separated hosts into
|
||||
* individual entries that can be managed more easily in a web interface.
|
||||
*/
|
||||
public class HostRestrictionField extends Field {
|
||||
|
||||
/**
|
||||
* The field type.
|
||||
*/
|
||||
public static final String FIELD_TYPE = "GUAC_HOST_RESTRICTION";
|
||||
|
||||
/**
|
||||
* Create a new field that tracks host restrictions.
|
||||
*
|
||||
* @param name
|
||||
* The name of the parameter that will be used to pass this field
|
||||
* between the REST API and the web front-end.
|
||||
*
|
||||
*/
|
||||
public HostRestrictionField(String name) {
|
||||
super(name, FIELD_TYPE);
|
||||
}
|
||||
|
||||
}
|
@@ -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.restrict.form;
|
||||
|
||||
import org.apache.guacamole.form.Field;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A field that parses a string containing time restrictions into its individual
|
||||
* components for user-friendly display on the web interface.
|
||||
*/
|
||||
public class TimeRestrictionField extends Field {
|
||||
|
||||
/**
|
||||
* The field type.
|
||||
*/
|
||||
public static final String FIELD_TYPE = "GUAC_TIME_RESTRICTION";
|
||||
|
||||
/**
|
||||
* Create a new field that tracks time restrictions.
|
||||
*
|
||||
* @param name
|
||||
* The name of the parameter that will be used to pass this field
|
||||
* between the REST API and the web front-end.
|
||||
*
|
||||
*/
|
||||
public TimeRestrictionField(String name) {
|
||||
super(name, FIELD_TYPE);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.restrict.user;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.guacamole.auth.restrict.form.HostRestrictionField;
|
||||
import org.apache.guacamole.auth.restrict.form.TimeRestrictionField;
|
||||
import org.apache.guacamole.form.Form;
|
||||
import org.apache.guacamole.net.auth.DelegatingUser;
|
||||
import org.apache.guacamole.net.auth.User;
|
||||
|
||||
/**
|
||||
* User implementation which wraps a User from another extension and enforces
|
||||
* additional restrictions.
|
||||
*/
|
||||
public class RestrictUser extends DelegatingUser {
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of weekdays and times (UTC)
|
||||
* that a user is allowed to log in. The presence of this attribute will
|
||||
* restrict the user to logins only during the times that are contained
|
||||
* within the attribute, subject to further restriction by the
|
||||
* guac-restrict-time-denied attribute.
|
||||
*/
|
||||
public static final String RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME = "guac-restrict-time-allowed";
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of weekdays and times (UTC)
|
||||
* that a user is not allowed to log in. Denied times will always take
|
||||
* precedence over allowed times. The presence of this attribute without
|
||||
* guac-restrict-time-allowed will deny logins only during the times listed
|
||||
* in this attribute, allowing logins at all other times. The presence of
|
||||
* this attribute along with the guac-restrict-time-allowed attribute will
|
||||
* deny logins at any times that overlap with the allowed times.
|
||||
*/
|
||||
public static final String RESTRICT_TIME_DENIED_ATTRIBUTE_NAME = "guac-restrict-time-denied";
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of IP addresses from which
|
||||
* a user is allowed to log in. The presence of this attribute will restrict
|
||||
* users to only the list of IP addresses contained in the attribute, subject
|
||||
* to further restriction by the guac-restrict-hosts-denied attribute.
|
||||
*/
|
||||
public static final String RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME = "guac-restrict-hosts-allowed";
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of IP addresses from which
|
||||
* a user is not allowed to log in. The presence of this attribute, absent
|
||||
* the guac-restrict-hosts-allowed attribute, will allow logins from all
|
||||
* hosts except the ones listed in this attribute. The presence of this
|
||||
* attribute coupled with the guac-restrict-hosts-allowed attribute will
|
||||
* block access from any IPs in this list, overriding any that may be
|
||||
* allowed.
|
||||
*/
|
||||
public static final String RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME = "guac-restrict-hosts-denied";
|
||||
|
||||
/**
|
||||
* The list of all user attributes provided by this User implementation.
|
||||
*/
|
||||
public static final List<String> RESTRICT_USER_ATTRIBUTES = Arrays.asList(
|
||||
RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME,
|
||||
RESTRICT_TIME_DENIED_ATTRIBUTE_NAME,
|
||||
RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME,
|
||||
RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME
|
||||
);
|
||||
|
||||
/**
|
||||
* The form containing the list of fields for the attributes provided
|
||||
* by this module.
|
||||
*/
|
||||
public static final Form RESTRICT_LOGIN_FORM = new Form("restrict-login-form",
|
||||
Arrays.asList(
|
||||
new TimeRestrictionField(RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME),
|
||||
new TimeRestrictionField(RESTRICT_TIME_DENIED_ATTRIBUTE_NAME),
|
||||
new HostRestrictionField(RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME),
|
||||
new HostRestrictionField(RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Wraps the given User object, providing capability of further restricting
|
||||
* logins beyond the default restrictions provided by default modules.
|
||||
*
|
||||
* @param user
|
||||
* The User object to wrap.
|
||||
*/
|
||||
public RestrictUser(User user) {
|
||||
super(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the User object wrapped by this RestrictUser.
|
||||
*
|
||||
* @return
|
||||
* The wrapped User object.
|
||||
*/
|
||||
public User getUndecorated() {
|
||||
return getDelegateUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttributes() {
|
||||
|
||||
// Create independent, mutable copy of attributes
|
||||
Map<String, String> attributes = new HashMap<>(super.getAttributes());
|
||||
|
||||
// Loop through extension-specific attributes, adding ones that are
|
||||
// empty so that they are displayed in the web UI.
|
||||
for (String attribute : RESTRICT_USER_ATTRIBUTES) {
|
||||
String value = attributes.get(attribute);
|
||||
if (value == null || value.isEmpty())
|
||||
attributes.put(attribute, null);
|
||||
}
|
||||
|
||||
return attributes;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
|
||||
// Create independent, mutable copy of attributes
|
||||
attributes = new HashMap<>(attributes);
|
||||
|
||||
// Loop through extension-specific attributes, only sending ones
|
||||
// that are non-null and non-empty to the underlying storage mechanism.
|
||||
for (String attribute : RESTRICT_USER_ATTRIBUTES) {
|
||||
String value = attributes.get(attribute);
|
||||
if (value != null && value.isEmpty())
|
||||
attributes.put(attribute, null);
|
||||
}
|
||||
|
||||
super.setAttributes(attributes);
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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.restrict.user;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.restrict.connection.RestrictConnection;
|
||||
import org.apache.guacamole.auth.restrict.connectiongroup.RestrictConnectionGroup;
|
||||
import org.apache.guacamole.auth.restrict.usergroup.RestrictUserGroup;
|
||||
import org.apache.guacamole.form.Form;
|
||||
import org.apache.guacamole.net.auth.Connection;
|
||||
import org.apache.guacamole.net.auth.ConnectionGroup;
|
||||
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;
|
||||
|
||||
/**
|
||||
* A UserContext implementation for additional login and connection restrictions
|
||||
* which wraps the UserContext of some other extension.
|
||||
*/
|
||||
public class RestrictUserContext extends DelegatingUserContext {
|
||||
|
||||
/**
|
||||
* The remote address from which this user logged in.
|
||||
*/
|
||||
private final String remoteAddress;
|
||||
|
||||
/**
|
||||
* Creates a new RestrictUserContext which wraps the given UserContext,
|
||||
* providing additional control for user logins and connections.
|
||||
*
|
||||
* @param userContext
|
||||
* The UserContext to wrap.
|
||||
*
|
||||
* @param remoteAddress
|
||||
* The address the user is logging in from, if known.
|
||||
*/
|
||||
public RestrictUserContext(UserContext userContext, String remoteAddress) {
|
||||
super(userContext);
|
||||
this.remoteAddress = remoteAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Directory<Connection> getConnectionDirectory() throws GuacamoleException {
|
||||
return new DecoratingDirectory<Connection>(super.getConnectionDirectory()) {
|
||||
|
||||
@Override
|
||||
protected Connection decorate(Connection object) {
|
||||
return new RestrictConnection(object, remoteAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Connection undecorate(Connection object) {
|
||||
assert(object instanceof RestrictConnection);
|
||||
return ((RestrictConnection) object).getUndecorated();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Form> getConnectionAttributes() {
|
||||
Collection<Form> connectionAttrs = new HashSet<>(super.getConnectionAttributes());
|
||||
connectionAttrs.add(RestrictConnection.RESTRICT_CONNECTION_FORM);
|
||||
return Collections.unmodifiableCollection(connectionAttrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Directory<ConnectionGroup> getConnectionGroupDirectory() throws GuacamoleException {
|
||||
return new DecoratingDirectory<ConnectionGroup>(super.getConnectionGroupDirectory()) {
|
||||
|
||||
@Override
|
||||
protected ConnectionGroup decorate(ConnectionGroup object) {
|
||||
return new RestrictConnectionGroup(object, remoteAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectionGroup undecorate(ConnectionGroup object) {
|
||||
assert(object instanceof RestrictConnectionGroup);
|
||||
return ((RestrictConnectionGroup) object).getUndecorated();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Form> getConnectionGroupAttributes() {
|
||||
Collection<Form> connectionGroupAttrs = new HashSet<>(super.getConnectionGroupAttributes());
|
||||
connectionGroupAttrs.add(RestrictConnectionGroup.RESTRICT_CONNECTIONGROUP_FORM);
|
||||
return Collections.unmodifiableCollection(connectionGroupAttrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Directory<User> getUserDirectory() throws GuacamoleException {
|
||||
return new DecoratingDirectory<User>(super.getUserDirectory()) {
|
||||
|
||||
@Override
|
||||
protected User decorate(User object) {
|
||||
return new RestrictUser(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected User undecorate(User object) {
|
||||
assert(object instanceof RestrictUser);
|
||||
return ((RestrictUser) object).getUndecorated();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Form> getUserAttributes() {
|
||||
Collection<Form> userAttrs = new HashSet<>(super.getUserAttributes());
|
||||
userAttrs.add(RestrictUser.RESTRICT_LOGIN_FORM);
|
||||
return Collections.unmodifiableCollection(userAttrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Directory<UserGroup> getUserGroupDirectory() throws GuacamoleException {
|
||||
return new DecoratingDirectory<UserGroup>(super.getUserGroupDirectory()) {
|
||||
|
||||
@Override
|
||||
protected UserGroup decorate(UserGroup object) {
|
||||
return new RestrictUserGroup(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserGroup undecorate(UserGroup object) {
|
||||
assert(object instanceof RestrictUserGroup);
|
||||
return ((RestrictUserGroup) object).getUndecorated();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Form> getUserGroupAttributes() {
|
||||
Collection<Form> userGroupAttrs = new HashSet<>(super.getUserGroupAttributes());
|
||||
userGroupAttrs.add(RestrictUserGroup.RESTRICT_LOGIN_FORM);
|
||||
return Collections.unmodifiableCollection(userGroupAttrs);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.restrict.usergroup;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.guacamole.auth.restrict.form.HostRestrictionField;
|
||||
import org.apache.guacamole.auth.restrict.form.TimeRestrictionField;
|
||||
import org.apache.guacamole.form.Form;
|
||||
import org.apache.guacamole.net.auth.DelegatingUserGroup;
|
||||
import org.apache.guacamole.net.auth.UserGroup;
|
||||
|
||||
/**
|
||||
* UserGroup implementation which wraps a UserGroup from another extension and
|
||||
* enforces additional restrictions for members of that group.
|
||||
*/
|
||||
public class RestrictUserGroup extends DelegatingUserGroup {
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of weekdays and times (UTC)
|
||||
* that members of a group are allowed to log in. The presence of this
|
||||
* attribute will restrict any users who are members of the group to logins
|
||||
* only during the times that are contained within the attribute,
|
||||
* subject to further restriction by the guac-restrict-time-denied attribute.
|
||||
*/
|
||||
public static final String RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME = "guac-restrict-time-allowed";
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of weekdays and times (UTC)
|
||||
* that members of a group are not allowed to log in. Denied times will
|
||||
* always take precedence over allowed times. The presence of this attribute
|
||||
* without guac-restrict-time-allowed will deny logins only during the times
|
||||
* listed in this attribute, allowing logins at all other times. The
|
||||
* presence of this attribute along with the guac-restrict-time-allowed
|
||||
* attribute will deny logins at any times that overlap with the allowed
|
||||
* times.
|
||||
*/
|
||||
public static final String RESTRICT_TIME_DENIED_ATTRIBUTE_NAME = "guac-restrict-time-denied";
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of IP addresses from which
|
||||
* members of a group are allowed to log in. The presence of this attribute
|
||||
* will restrict users to only the list of IP addresses contained in the
|
||||
* attribute, subject to further restriction by the
|
||||
* guac-restrict-hosts-denied attribute.
|
||||
*/
|
||||
public static final String RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME = "guac-restrict-hosts-allowed";
|
||||
|
||||
/**
|
||||
* The name of the attribute that contains a list of IP addresses from which
|
||||
* members of a group are not allowed to log in. The presence of this
|
||||
* attribute, absent the guac-restrict-hosts-allowed attribute, will allow
|
||||
* logins from all hosts except the ones listed in this attribute. The
|
||||
* presence of this attribute coupled with the guac-restrict-hosts-allowed
|
||||
* attribute will block access from any IPs in this list, overriding any
|
||||
* that may be allowed.
|
||||
*/
|
||||
public static final String RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME = "guac-restrict-hosts-denied";
|
||||
|
||||
/**
|
||||
* The list of all user attributes provided by this UserGroup implementation.
|
||||
*/
|
||||
public static final List<String> RESTRICT_USERGROUP_ATTRIBUTES = Arrays.asList(
|
||||
RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME,
|
||||
RESTRICT_TIME_DENIED_ATTRIBUTE_NAME,
|
||||
RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME,
|
||||
RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME
|
||||
);
|
||||
|
||||
/**
|
||||
* The form containing the list of fields for the attributes provided
|
||||
* by this module.
|
||||
*/
|
||||
public static final Form RESTRICT_LOGIN_FORM = new Form("restrict-login-form",
|
||||
Arrays.asList(
|
||||
new TimeRestrictionField(RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME),
|
||||
new TimeRestrictionField(RESTRICT_TIME_DENIED_ATTRIBUTE_NAME),
|
||||
new HostRestrictionField(RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME),
|
||||
new HostRestrictionField(RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Wraps the given UserGroup object, providing capability of further restricting
|
||||
* logins beyond the default restrictions provided by default modules.
|
||||
*
|
||||
* @param userGroup
|
||||
* The UserGroup object to wrap.
|
||||
*/
|
||||
public RestrictUserGroup(UserGroup userGroup) {
|
||||
super(userGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the UserGroup object wrapped by this RestrictUserGroup.
|
||||
*
|
||||
* @return
|
||||
* The wrapped UserGroup object.
|
||||
*/
|
||||
public UserGroup getUndecorated() {
|
||||
return getDelegateUserGroupGroup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttributes() {
|
||||
|
||||
// Create independent, mutable copy of attributes
|
||||
Map<String, String> attributes = new HashMap<>(super.getAttributes());
|
||||
|
||||
// Loop through extension-specific attributes, adding ones that are
|
||||
// empty so that they are displayed in the web UI.
|
||||
for (String attribute : RESTRICT_USERGROUP_ATTRIBUTES) {
|
||||
String value = attributes.get(attribute);
|
||||
if (value == null || value.isEmpty())
|
||||
attributes.put(attribute, null);
|
||||
}
|
||||
|
||||
return attributes;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
|
||||
// Create independent, mutable copy of attributes
|
||||
attributes = new HashMap<>(attributes);
|
||||
|
||||
// Loop through extension-specific attributes, only sending ones
|
||||
// that are non-null and non-empty to the underlying storage mechanism.
|
||||
for (String attribute : RESTRICT_USERGROUP_ATTRIBUTES) {
|
||||
String value = attributes.get(attribute);
|
||||
if (value != null && value.isEmpty())
|
||||
attributes.put(attribute, null);
|
||||
}
|
||||
|
||||
super.setAttributes(attributes);
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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.calendar;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class that stores a daily time restriction that can be used to determine
|
||||
* whether or not a user can log in on a certain day of the week and during
|
||||
* a certain time window.
|
||||
*/
|
||||
public class DailyRestriction {
|
||||
|
||||
/**
|
||||
* The days of the week that this restriction applies to.
|
||||
*/
|
||||
private final List<DayOfWeek> weekDays;
|
||||
|
||||
/**
|
||||
* The time that the restriction starts.
|
||||
*/
|
||||
private final LocalTime startTime;
|
||||
|
||||
/**
|
||||
* The time that the restriction ends.
|
||||
*/
|
||||
private final LocalTime endTime;
|
||||
|
||||
/**
|
||||
* Create a new daily restriction with the specified day of the week, start
|
||||
* time, and end time.
|
||||
*
|
||||
* @param weekDay
|
||||
* The day of the week that this restriction should apply to.
|
||||
*
|
||||
* @param startTime
|
||||
* The start time of the restriction.
|
||||
*
|
||||
* @param endTime
|
||||
* The end time of the restriction.
|
||||
*/
|
||||
public DailyRestriction(DayOfWeek weekDay,
|
||||
LocalTime startTime, LocalTime endTime) {
|
||||
this.weekDays = Collections.singletonList(weekDay);
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new daily restriction with the specified days of the week, start
|
||||
* time, and end time.
|
||||
*
|
||||
* @param weekDays
|
||||
* The days of the week that this restriction should apply to.
|
||||
*
|
||||
* @param startTime
|
||||
* The start time of the restriction.
|
||||
*
|
||||
* @param endTime
|
||||
* The end time of the restriction.
|
||||
*/
|
||||
public DailyRestriction(List<DayOfWeek> weekDays,
|
||||
LocalTime startTime, LocalTime endTime) {
|
||||
this.weekDays = weekDays;
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new daily restriction for an entire day, settings the start
|
||||
* time at midnight and the end time at the end of the day (235959).
|
||||
*
|
||||
* @param weekDay
|
||||
* The day of the week that this restriction should apply to.
|
||||
*/
|
||||
public DailyRestriction(DayOfWeek weekDay) {
|
||||
this.weekDays = Collections.singletonList(weekDay);
|
||||
this.startTime = LocalTime.of(0, 0, 0);
|
||||
this.endTime = LocalTime.of(23, 59, 59);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new daily restriction for entire days, settings the start
|
||||
* time at midnight and the end time at the end of the day (235959).
|
||||
*
|
||||
* @param weekDays
|
||||
* The days of the week that this restriction should apply to.
|
||||
*/
|
||||
public DailyRestriction(List<DayOfWeek> weekDays) {
|
||||
this.weekDays = weekDays;
|
||||
this.startTime = LocalTime.of(0, 0, 0);
|
||||
this.endTime = LocalTime.of(23, 59, 59);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this restriction applies now, otherwise false.
|
||||
*
|
||||
* @return
|
||||
* true if the current time of day falls within this restriction,
|
||||
* otherwise false.
|
||||
*/
|
||||
public boolean appliesNow() {
|
||||
DayOfWeek currentDay = LocalDate.now().getDayOfWeek();
|
||||
LocalTime currentTime = LocalTime.now(ZoneId.of("UTC"));
|
||||
|
||||
// Check that we are in the specified time restriction
|
||||
return (weekDays.contains(currentDay) && currentTime.isAfter(startTime) && currentTime.isBefore(endTime));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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.calendar;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A class for parsing time-based restrictions stored in a String into other
|
||||
* formats that can be used by Guacamole.
|
||||
*/
|
||||
public class TimeRestrictionParser {
|
||||
|
||||
/**
|
||||
* The compiled regular expression that matches one or more instances of
|
||||
* a restriction string, which specifies at least one day and time range
|
||||
* that the restriction applies to.
|
||||
*
|
||||
* <p>Examples of valid restrictions are as follows:
|
||||
* <ul>
|
||||
* <li>1:0700-1700 - Monday from 07:00 to 17:00
|
||||
* <li>7:0000-2359 - Sunday, all day (00:00 to 23:59)
|
||||
* <li>wd:0900-1700 - Monday through Friday, 09:00 to 17:00
|
||||
* <li>we:0900-1700 - Saturday and Sunday, 09:00 to 17:00
|
||||
* <li>6:0900-1600;7:1200-1300 - Saturday, 09:00 to 16:00, and Sunday,
|
||||
* 12:00 - 13:00
|
||||
* </ul>
|
||||
*/
|
||||
private static final Pattern RESTRICTION_REGEX =
|
||||
Pattern.compile("(?:^|;)+([1-7*]|(?:[w][ed]))(?::((?:[01][0-9]|2[0-3])[0-5][0-9])\\-((?:[01][0-9]|2[0-3])[0-5][0-9]))+");
|
||||
|
||||
/**
|
||||
* The RegEx group that contains the start day-of-week of the restriction.
|
||||
*/
|
||||
private static final int RESTRICTION_DAY_GROUP = 1;
|
||||
|
||||
/**
|
||||
* The RegEx group that contains the start time of the restriction.
|
||||
*/
|
||||
private static final int RESTRICTION_TIME_START_GROUP = 2;
|
||||
|
||||
/**
|
||||
* The RegEx group that contains the end time of the restriction.
|
||||
*/
|
||||
private static final int RESTRICTION_TIME_END_GROUP = 3;
|
||||
|
||||
/**
|
||||
* A list of DayOfWeek items that make up weekdays.
|
||||
*/
|
||||
private static final List<DayOfWeek> RESTRICTION_WEEKDAYS = Arrays.asList(
|
||||
DayOfWeek.MONDAY,
|
||||
DayOfWeek.TUESDAY,
|
||||
DayOfWeek.WEDNESDAY,
|
||||
DayOfWeek.THURSDAY,
|
||||
DayOfWeek.FRIDAY
|
||||
);
|
||||
|
||||
/**
|
||||
* A list of DayOfWeek items that make up weekends.
|
||||
*/
|
||||
private static final List<DayOfWeek> RESTRICTION_WEEKEND = Arrays.asList(
|
||||
DayOfWeek.SATURDAY,
|
||||
DayOfWeek.SUNDAY
|
||||
);
|
||||
|
||||
/**
|
||||
* A list of DayOfWeek items that make up all days of the week.
|
||||
*/
|
||||
private static final List<DayOfWeek> RESTRICTION_ALL_DAYS = Arrays.asList(
|
||||
DayOfWeek.MONDAY,
|
||||
DayOfWeek.TUESDAY,
|
||||
DayOfWeek.WEDNESDAY,
|
||||
DayOfWeek.THURSDAY,
|
||||
DayOfWeek.FRIDAY,
|
||||
DayOfWeek.SATURDAY,
|
||||
DayOfWeek.SUNDAY
|
||||
);
|
||||
|
||||
/**
|
||||
* Parse the provided string containing one or more restrictions into
|
||||
* a list of objects.
|
||||
*
|
||||
* @param restrictionString
|
||||
* The string that should contain one or more semicolon-separated
|
||||
* restriction periods.
|
||||
*
|
||||
* @return
|
||||
* A list of objects parsed from the string.
|
||||
*/
|
||||
public static List<DailyRestriction> parseString(String restrictionString) {
|
||||
|
||||
List<DailyRestriction> restrictions = new ArrayList<>();
|
||||
Matcher restrictionMatcher = RESTRICTION_REGEX.matcher(restrictionString);
|
||||
|
||||
// Loop through RegEx matches
|
||||
while (restrictionMatcher.find()) {
|
||||
|
||||
// Pull the day string, start time, and end time
|
||||
String dayString = restrictionMatcher.group(RESTRICTION_DAY_GROUP);
|
||||
String startTimeString = restrictionMatcher.group(RESTRICTION_TIME_START_GROUP);
|
||||
String endTimeString = restrictionMatcher.group(RESTRICTION_TIME_END_GROUP);
|
||||
LocalTime startTime, endTime;
|
||||
|
||||
// We must always have a value for the day.
|
||||
if (dayString == null || dayString.isEmpty())
|
||||
continue;
|
||||
|
||||
// Convert the start and end time strings to LocalTime values.
|
||||
DateTimeFormatter hourFormat = DateTimeFormatter.ofPattern("HHmm");
|
||||
|
||||
// If start time is empty, assume the start of the day.
|
||||
if (startTimeString == null || startTimeString.isEmpty())
|
||||
startTime = LocalTime.of(0, 0, 0);
|
||||
|
||||
// Otherwise, parse out the start time.
|
||||
else
|
||||
startTime = LocalTime.parse(startTimeString, hourFormat);
|
||||
|
||||
// If end time is empty, assume the end of the day.
|
||||
if (endTimeString == null || endTimeString.isEmpty())
|
||||
endTime = LocalTime.of(23, 59, 59);
|
||||
|
||||
// Otherwise, parse out the end time.
|
||||
else
|
||||
endTime = LocalTime.parse(endTimeString, hourFormat);
|
||||
|
||||
// Based on value of day string, add the appropriate entry.
|
||||
switch(dayString) {
|
||||
// All days of the week.
|
||||
case "*":
|
||||
restrictions.add(new DailyRestriction(RESTRICTION_ALL_DAYS, startTime, endTime));
|
||||
break;
|
||||
|
||||
// Weekdays only.
|
||||
case "wd":
|
||||
restrictions.add(new DailyRestriction(RESTRICTION_WEEKDAYS, startTime, endTime));
|
||||
break;
|
||||
|
||||
// Weekend days only.
|
||||
case "we":
|
||||
restrictions.add(new DailyRestriction(RESTRICTION_WEEKEND, startTime, endTime));
|
||||
break;
|
||||
|
||||
// A specific day of the week.
|
||||
default:
|
||||
restrictions.add(new DailyRestriction(DayOfWeek.of(Integer.parseInt(dayString)), startTime, endTime));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Return the list of restrictions
|
||||
return restrictions;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.host;
|
||||
|
||||
import inet.ipaddr.HostName;
|
||||
import inet.ipaddr.HostNameException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A utility class that parses a string for a set of IPv4 or IPv6 addresses,
|
||||
* or hostnames, splitting the string into a list of components.
|
||||
*/
|
||||
public class HostRestrictionParser {
|
||||
|
||||
/**
|
||||
* The logger for this class.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HostRestrictionParser.class);
|
||||
|
||||
/**
|
||||
* Parse the provided string into a List of HostName objects, validating
|
||||
* that each item is an IP address, subnet, and/or DNS name.
|
||||
*
|
||||
* @param hostString
|
||||
* The string that contains a semi-colon-separated list of items to
|
||||
* parse.
|
||||
*
|
||||
* @return
|
||||
* A List of HostName objects parsed from the provided string.
|
||||
*/
|
||||
public static List<HostName> parseHostList(String hostString) {
|
||||
|
||||
List<HostName> addressList = new ArrayList<>();
|
||||
|
||||
if (hostString == null || hostString.isEmpty())
|
||||
return addressList;
|
||||
|
||||
// First split the string by semicolons and process each entry
|
||||
for (String host : hostString.split(";")) {
|
||||
|
||||
HostName hostName = new HostName(host);
|
||||
try {
|
||||
hostName.validate();
|
||||
addressList.add(hostName);
|
||||
}
|
||||
catch (HostNameException e) {
|
||||
LOGGER.warn("Invalid host name or IP: {}", host);
|
||||
LOGGER.debug("HostNameException.", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return addressList;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Config block which registers restrict-specific field types.
|
||||
*/
|
||||
angular.module('guacRestrict').config(['formServiceProvider',
|
||||
function guacRestrictConfig(formServiceProvider) {
|
||||
|
||||
// Define the time restriction field
|
||||
formServiceProvider.registerFieldType('GUAC_TIME_RESTRICTION', {
|
||||
module : 'guacRestrict',
|
||||
controller : 'timeRestrictionFieldController',
|
||||
templateUrl : 'app/ext/restrict/templates/timeRestrictionField.html'
|
||||
});
|
||||
|
||||
// Define the host restriction field
|
||||
formServiceProvider.registerFieldType('GUAC_HOST_RESTRICTION', {
|
||||
module : 'guacRestrict',
|
||||
controller : 'hostRestrictionFieldController',
|
||||
templateUrl : 'app/ext/restrict/templates/hostRestrictionField.html'
|
||||
});
|
||||
|
||||
}]);
|
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Controller for host restriction fields, which are used to configure a
|
||||
* hostname, IP address, or CIDR range, that this restriction applies to.
|
||||
*/
|
||||
angular.module('guacRestrict').controller('hostRestrictionFieldController', ['$scope', '$injector',
|
||||
function hostRestrictionFieldController($scope, $injector) {
|
||||
|
||||
// Required types
|
||||
const HostRestrictionEntry = $injector.get('HostRestrictionEntry');
|
||||
|
||||
/**
|
||||
* Options which dictate the behavior of the input field model, as defined
|
||||
* by https://docs.angularjs.org/api/ng/directive/ngModelOptions
|
||||
*
|
||||
* @type Object.<String, String>
|
||||
*/
|
||||
$scope.modelOptions = {
|
||||
|
||||
/**
|
||||
* Space-delimited list of events on which the model will be updated.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
updateOn : 'blur',
|
||||
|
||||
/**
|
||||
* The time zone to use when reading/writing the Date object of the
|
||||
* model.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
timezone : 'UTC'
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The restrictions, as objects, that are used by the HTML template to
|
||||
* present the restrictions to the user via the web interface.
|
||||
*
|
||||
* @type HostRestrictionEntry[]
|
||||
*/
|
||||
$scope.restrictions = [];
|
||||
|
||||
/**
|
||||
* Remove the current entry from the list.
|
||||
*
|
||||
* @param {HostRestrictionEntry} entry
|
||||
* A restriction entry.
|
||||
*/
|
||||
$scope.removeEntry = function removeEntry(entry) {
|
||||
if (entry === null || entry.$$hashKey === '') {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < $scope.restrictions.length; i++) {
|
||||
if ($scope.restrictions[i].$$hashKey === entry.$$hashKey) {
|
||||
$scope.restrictions.splice(i,1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an empty entry to the restriction list.
|
||||
*/
|
||||
$scope.addEntry = function addEntry() {
|
||||
$scope.restrictions.push(new HostRestrictionEntry());
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the provided string into an array containing the objects that
|
||||
* represent each of entries that can then be displayed as a more
|
||||
* user-friendly field.
|
||||
*
|
||||
* @param {String} restrString
|
||||
* The string that contains the restrictions, un-parsed and as stored
|
||||
* in the underlying field.
|
||||
*
|
||||
* @returns {HostRestrictionEntry[]}
|
||||
* An array of objects that represents each of the entries as parsed
|
||||
* out of the string field, and which can be interpreted by the
|
||||
* AngularJS field for display.
|
||||
*/
|
||||
const parseRestrictions = function parseRestrictions(restrString) {
|
||||
|
||||
var restrictions = [];
|
||||
|
||||
// If the string is null or empty, just return an empty array
|
||||
if (restrString === null || restrString === "")
|
||||
return restrictions;
|
||||
|
||||
// Set up the RegEx and split the string using the separator.
|
||||
var restrArray = restrString.split(";");
|
||||
|
||||
// Loop through split string and process each item
|
||||
for (let i = 0; i < restrArray.length; i++) {
|
||||
var entry = new HostRestrictionEntry();
|
||||
entry.host = restrArray[i];
|
||||
restrictions.push(entry);
|
||||
}
|
||||
|
||||
return restrictions;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the restrictions in the field into a string that can be stored
|
||||
* in an underlying module.
|
||||
*
|
||||
* @param {HostRestrictionEntry[]} restrictions
|
||||
* The array of restrictions that will be converted to a string.
|
||||
*
|
||||
* @returns {String}
|
||||
* The string containing the restriction data that can be stored in e.g.
|
||||
* a database.
|
||||
*/
|
||||
const storeRestrictions = function storeRestrictions(restrictions) {
|
||||
// If there are no members of the array, just return an empty string.
|
||||
if (restrictions === null || restrictions.length < 1)
|
||||
return '';
|
||||
|
||||
var restrString = '';
|
||||
for (let i = 0; i < restrictions.length; i++) {
|
||||
// If any of the properties are not defined, skip this one.
|
||||
if (!Object.hasOwn(restrictions[i], 'host')
|
||||
|| restrictions[i].host === null)
|
||||
continue;
|
||||
|
||||
// If this is not the first item, then add a semi-colon separator
|
||||
if (restrString.length > 0)
|
||||
restrString += ';';
|
||||
|
||||
// Add the current host to the list
|
||||
restrString += restrictions[i].host;
|
||||
}
|
||||
|
||||
return restrString;
|
||||
|
||||
};
|
||||
|
||||
// Update the field when the model changes.
|
||||
$scope.$watch('model', function modelChanged(model) {
|
||||
$scope.restrictions = parseRestrictions(model);
|
||||
});
|
||||
|
||||
// Update string value in model when web form is changed
|
||||
$scope.$watch('restrictions', function restrictionsChanged(restrictions) {
|
||||
$scope.model = storeRestrictions(restrictions);
|
||||
}, true);
|
||||
|
||||
}]);
|
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Controller for time restriction fields, which are used to select weekday and
|
||||
* time restrictions that apply to user logins and connections.
|
||||
*/
|
||||
angular.module('guacRestrict').controller('timeRestrictionFieldController', ['$scope', '$injector',
|
||||
function timeRestrictionFieldController($scope, $injector) {
|
||||
|
||||
// Required types
|
||||
const TimeRestrictionEntry = $injector.get('TimeRestrictionEntry');
|
||||
|
||||
/**
|
||||
* Options which dictate the behavior of the input field model, as defined
|
||||
* by https://docs.angularjs.org/api/ng/directive/ngModelOptions
|
||||
*
|
||||
* @type Object.<String, String>
|
||||
*/
|
||||
$scope.modelOptions = {
|
||||
|
||||
/**
|
||||
* Space-delimited list of events on which the model will be updated.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
updateOn : 'blur'
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The restrictions, as objects, that are used by the HTML template to
|
||||
* present the restrictions to the user via the web interface.
|
||||
*
|
||||
* @type TimeRestrictionEntry[]
|
||||
*/
|
||||
$scope.restrictions = [];
|
||||
|
||||
/**
|
||||
* Map of weekday identifier to display name.
|
||||
*/
|
||||
$scope.weekDays = [
|
||||
{ id : '1', day : 'Monday' },
|
||||
{ id : '2', day : 'Tuesday' },
|
||||
{ id : '3', day : 'Wednesday' },
|
||||
{ id : '4', day : 'Thursday' },
|
||||
{ id : '5', day : 'Friday' },
|
||||
{ id : '6', day : 'Saturday' },
|
||||
{ id : '7', day : 'Sunday' },
|
||||
{ id : '*', day : 'All days' },
|
||||
{ id : 'wd', day: 'Week days' },
|
||||
{ id : 'we', day: 'Week end' }
|
||||
];
|
||||
|
||||
/**
|
||||
* Remove the current entry from the list.
|
||||
*
|
||||
* @param {TimeRestrictionEntry} entry
|
||||
* A restriction entry.
|
||||
*/
|
||||
$scope.removeEntry = function removeEntry(entry) {
|
||||
if (entry === null || entry.$$hashKey === '') {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < $scope.restrictions.length; i++) {
|
||||
if ($scope.restrictions[i].$$hashKey === entry.$$hashKey) {
|
||||
$scope.restrictions.splice(i,1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an empty entry to the restriction list.
|
||||
*/
|
||||
$scope.addEntry = function addEntry() {
|
||||
$scope.restrictions.push(new TimeRestrictionEntry());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parse the provided string into an array containing the objects that
|
||||
* represent each of entries that can then be displayed as a more
|
||||
* user-friendly field.
|
||||
*
|
||||
* @param {String} restrString
|
||||
* The string that contains the restrictions, un-parsed and as stored
|
||||
* in the underlying field.
|
||||
*
|
||||
* @returns {TimeRestrictionEntry[]}
|
||||
* An array of objects that represents each of the entries as parsed
|
||||
* out of the string field, and which can be interpreted by the
|
||||
* AngularJS field for display.
|
||||
*/
|
||||
const parseRestrictions = function parseRestrictions(restrString) {
|
||||
|
||||
var restrictions = [];
|
||||
|
||||
// If the string is null or empty, just return an empty array
|
||||
if (restrString === null || restrString === "")
|
||||
return restrictions;
|
||||
|
||||
// Set up the RegEx and split the string using the separator.
|
||||
const restrictionRegex = new RegExp('^([1-7*]|(?:[w][ed]))(?::((?:[01][0-9]|2[0-3])[0-5][0-9])\-((?:[01][0-9]|2[0-3])[0-5][0-9]))$');
|
||||
var restrArray = restrString.split(";");
|
||||
|
||||
// Loop through split string and process each item
|
||||
for (let i = 0; i < restrArray.length; i++) {
|
||||
|
||||
// Test if our regex matches
|
||||
if (restrictionRegex.test(restrArray[i])) {
|
||||
var currArray = restrArray[i].match(restrictionRegex);
|
||||
var entry = new TimeRestrictionEntry();
|
||||
entry.weekDay = '' + currArray[1];
|
||||
entry.startTime = new Date(Date.UTC(1970, 1, 1, parseInt(currArray[2].slice(0,2)), parseInt(currArray[2].slice(2)), 0, 0));
|
||||
entry.endTime = new Date(Date.UTC(1970, 1, 1, parseInt(currArray[3].slice(0,2)), parseInt(currArray[3].slice(2)), 0, 0));
|
||||
restrictions.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return restrictions;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the restrictions in the field into a string that can be stored
|
||||
* in an underlying module.
|
||||
*
|
||||
* @param {TimeRestrictionEntry[]} restrictions
|
||||
* The array of restrictions that will be converted to a string.
|
||||
*
|
||||
* @returns {String}
|
||||
* The string containing the restriction data that can be stored in e.g.
|
||||
* a database.
|
||||
*/
|
||||
const storeRestrictions = function storeRestrictions(restrictions) {
|
||||
// If there are no members of the array, just return an empty string.
|
||||
if (restrictions === null || restrictions.length < 1)
|
||||
return '';
|
||||
|
||||
var restrString = '';
|
||||
for (let i = 0; i < restrictions.length; i++) {
|
||||
// If any of the properties are not defined, skip this one.
|
||||
if (!Object.hasOwn(restrictions[i], 'weekDay')
|
||||
|| restrictions[i].weekDay === null
|
||||
|| !Object.hasOwn(restrictions[i], 'startTime')
|
||||
|| restrictions[i].startTime === null
|
||||
|| !(restrictions[i].startTime instanceof Date)
|
||||
|| !Object.hasOwn(restrictions[i], 'endTime')
|
||||
|| restrictions[i].endTime === null
|
||||
|| !(restrictions[i].endTime instanceof Date))
|
||||
continue;
|
||||
|
||||
// If this is not the first item, then add a semi-colon separator
|
||||
if (restrString.length > 0)
|
||||
restrString += ';';
|
||||
|
||||
// Add the weekday component of the restriction, insuring it is a string.
|
||||
var currString = '' + restrictions[i].weekDay;
|
||||
currString += ':';
|
||||
|
||||
// Retrieve startTime hours component and add it, adding leading zero if required.
|
||||
startHours = restrictions[i].startTime.getUTCHours();
|
||||
if (startHours !== null && startHours < 10)
|
||||
startHours = '0' + startHours;
|
||||
currString += startHours;
|
||||
|
||||
// Retrieve startTime minutes component and add it, adding leading zero if required.
|
||||
startMins = restrictions[i].startTime.getUTCMinutes();
|
||||
if (startMins !== null && startMins < 10)
|
||||
startMins = '0' + startMins;
|
||||
currString += startMins;
|
||||
|
||||
currString += '-';
|
||||
|
||||
// Retrieve endTime hours component and add it, adding leading zero if required.
|
||||
endHours = restrictions[i].endTime.getUTCHours();
|
||||
if (endHours !== null && endHours < 10)
|
||||
endHours = '0' + endHours;
|
||||
currString += endHours;
|
||||
|
||||
// Retrieve endTime minutes component and add it, adding leading zero if required.
|
||||
endMins = restrictions[i].endTime.getUTCMinutes();
|
||||
if (endMins < 10)
|
||||
endMins = '0' + endMins;
|
||||
currString += endMins;
|
||||
|
||||
// Add the newly-created string to the overall restriction string.
|
||||
restrString += currString;
|
||||
}
|
||||
|
||||
return restrString;
|
||||
|
||||
};
|
||||
|
||||
// Update the field when the model changes.
|
||||
$scope.$watch('model', function modelChanged(model) {
|
||||
$scope.restrictions = parseRestrictions(model);
|
||||
});
|
||||
|
||||
// Update string value in model when web form is changed
|
||||
$scope.$watch('restrictions', function restrictionsChanged(restrictions) {
|
||||
$scope.model = storeRestrictions(restrictions);
|
||||
}, true);
|
||||
|
||||
}]);
|
@@ -0,0 +1,29 @@
|
||||
{
|
||||
|
||||
"guacamoleVersion" : "1.6.0",
|
||||
|
||||
"name" : "Restriction Authentication Backend",
|
||||
"namespace" : "restrict",
|
||||
|
||||
"authProviders" : [
|
||||
"org.apache.guacamole.auth.restrict.RestrictionAuthenticationProvider"
|
||||
],
|
||||
|
||||
"translations" : [
|
||||
"translations/en.json"
|
||||
],
|
||||
|
||||
"js" : [
|
||||
"restrict.min.js"
|
||||
],
|
||||
|
||||
"css" : [
|
||||
"restrict.min.css"
|
||||
],
|
||||
|
||||
"resources" : {
|
||||
"templates/hostRestrictionField.html" : "text/html",
|
||||
"templates/timeRestrictionField.html" : "text/html"
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module which provides handling for additional login and connection
|
||||
* restrictions.
|
||||
*/
|
||||
angular.module('guacRestrict', [
|
||||
'form'
|
||||
]);
|
||||
|
||||
// Ensure the guacRestrict module is loaded along with the rest of the app
|
||||
angular.module('index').requires.push('guacRestrict');
|
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.restrictionList {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
button.restrictionListButton {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
img.restrictionListHeader {
|
||||
width: 0.75em;
|
||||
height: 0.75em;
|
||||
}
|
||||
|
||||
img.restrictionListItem {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
<div class="hostField">
|
||||
<table class="restrictionList" ng-show="restrictions !== null && restrictions.length > 0">
|
||||
<tr>
|
||||
<th>{{ 'RESTRICT.TABLE_HEADER_HOST' | translate }}</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
<tr class="restrictionListItem" ng-repeat="entry in restrictions">
|
||||
<td>
|
||||
<input type="text" ng-model="entry.host" ng-model-options="modelOptions">
|
||||
</td>
|
||||
<td>
|
||||
<img class="restrictionListItem"
|
||||
src="images/x-red.svg"
|
||||
alt="Remove entry"
|
||||
ng-click="removeEntry(entry)">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button ng-click="addEntry()"
|
||||
class="restrictionListButton">
|
||||
{{ 'RESTRICT.ACTION_ADD_ENTRY' | translate }}
|
||||
</button>
|
||||
</div>
|
@@ -0,0 +1,38 @@
|
||||
<div class="timeRestrictionField">
|
||||
<table class="restrictionList" ng-show="restrictions !== null && restrictions.length > 0">
|
||||
<tr>
|
||||
<th>{{ 'RESTRICT.TABLE_HEADER_DAY' | translate }}</th>
|
||||
<th>{{ 'RESTRICT.TABLE_HEADER_START_TIME' | translate }}</th>
|
||||
<th>{{ 'RESTRICT.TABLE_HEADER_END_TIME' | translate }}</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
<tr class="restrictionListItem" ng-repeat="entry in restrictions">
|
||||
<td>
|
||||
<select ng-model="entry.weekDay"
|
||||
ng-options="weekDay.id as weekDay.day for weekDay in weekDays"
|
||||
ng-model-options="modelOptions">
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<input type="time"
|
||||
ng-model="entry.startTime"
|
||||
ng-model-options="modelOptions">
|
||||
</td>
|
||||
<td>
|
||||
<input type="time"
|
||||
ng-model="entry.endTime"
|
||||
ng-model-options="modelOptions">
|
||||
</td>
|
||||
<td>
|
||||
<img class="restrictionListItem"
|
||||
src="images/x-red.svg"
|
||||
alt="Remove entry"
|
||||
ng-click="removeEntry(entry)">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button ng-click="addEntry()"
|
||||
class="restrictionListButton">
|
||||
{{ 'RESTRICT.ACTION_ADD_ENTRY' | translate }}
|
||||
</button>
|
||||
</div>
|
@@ -0,0 +1,67 @@
|
||||
{
|
||||
|
||||
"DATA_SOURCE_LOGIN_RESTRICTIONS" : {
|
||||
"NAME" : "Additional Restrictions"
|
||||
},
|
||||
|
||||
"CONNECTION_ATTRIBUTES" : {
|
||||
|
||||
"FIELD_HEADER_GUAC_RESTRICT_HOSTS_ALLOWED" : "Hosts from which connection may be accessed:",
|
||||
"FIELD_HEADER_GUAC_RESTRICT_HOSTS_DENIED" : "Hosts from which connection may not be accessed:",
|
||||
"FIELD_HEADER_GUAC_RESTRICT_TIME_ALLOWED" : "Times connection is allowed to be used:",
|
||||
"FIELD_HEADER_GUAC_RESTRICT_TIME_DENIED" : "Times connection may not be used:",
|
||||
|
||||
"SECTION_HEADER_RESTRICT_LOGIN_FORM" : "Additional Connection Restrictions"
|
||||
|
||||
},
|
||||
|
||||
"CONNECTION_GROUP_ATTRIBUTES" : {
|
||||
|
||||
"FIELD_HEADER_GUAC_RESTRICT_HOSTS_ALLOWED" : "Hosts from which connection group may be accessed:",
|
||||
"FIELD_HEADER_GUAC_RESTRICT_HOSTS_DENIED" : "Hosts from which connection group may not be accessed:",
|
||||
"FIELD_HEADER_GUAC_RESTRICT_TIME_ALLOWED" : "Times connection group is allowed to be used:",
|
||||
"FIELD_HEADER_GUAC_RESTRICT_TIME_DENIED" : "Times connection group may not be used:",
|
||||
|
||||
"SECTION_HEADER_RESTRICT_LOGIN_FORM" : "Additional Connection Restrictions"
|
||||
|
||||
},
|
||||
|
||||
"RESTRICT" : {
|
||||
|
||||
"ACTION_ADD_ENTRY" : "Add Entry",
|
||||
|
||||
"ERROR_CONNECTION_NOT_ALLOWED_NOW" : "The connection is not available at this time.",
|
||||
"ERROR_CONNECTION_NOT_ALLOWED_FROM_HOST" : "The connection is not allowed from this host.",
|
||||
"ERROR_USER_LOGIN_NOT_ALLOWED_NOW" : "The login for this user is not allowed at this time.",
|
||||
"ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST" : "The login for this user is not allowed from this host.",
|
||||
|
||||
"TABLE_HEADER_DAY" : "Day",
|
||||
"TABLE_HEADER_END_TIME" : "End Time",
|
||||
"TABLE_HEADER_HOST" : "Host",
|
||||
"TABLE_HEADER_START_TIME" : "Start Time"
|
||||
|
||||
},
|
||||
|
||||
"USER_ATTRIBUTES" : {
|
||||
|
||||
"FIELD_HEADER_GUAC_RESTRICT_HOSTS_ALLOWED" : "Hosts from which user can log in:",
|
||||
"FIELD_HEADER_GUAC_RESTRICT_HOSTS_DENIED" : "Hosts from which user may not log in:",
|
||||
"FIELD_HEADER_GUAC_RESTRICT_TIME_ALLOWED" : "Times user is allowed to log in:",
|
||||
"FIELD_HEADER_GUAC_RESTRICT_TIME_DENIED" : "Times user is denied from log in:",
|
||||
|
||||
"SECTION_HEADER_RESTRICT_LOGIN_FORM" : "Additional Login Restrictions"
|
||||
|
||||
},
|
||||
|
||||
"USER_GROUP_ATTRIBUTES" : {
|
||||
|
||||
"FIELD_HEADER_GUAC_RESTRICT_HOSTS_ALLOWED" : "Hosts from which members may log in:",
|
||||
"FIELD_HEADER_GUAC_RESTRICT_HOSTS_DENIED" : "Hosts from which members may not log in:",
|
||||
"FIELD_HEADER_GUAC_RESTRICT_TIME_ALLOWED" : "Times members are allowed to log in:",
|
||||
"FIELD_HEADER_GUAC_RESTRICT_TIME_DENIED" : "Times members are denied from log in:",
|
||||
|
||||
"SECTION_HEADER_RESTRICT_LOGIN_FORM" : "Additional Login Restrictions"
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides the HostRestrictionEntry class definition.
|
||||
*/
|
||||
angular.module('guacRestrict').factory('HostRestrictionEntry', [
|
||||
function defineHostRestrictionEntry() {
|
||||
|
||||
/**
|
||||
* Creates a new HostRestrictionEntry, initializing the properties of that
|
||||
* HostRestrictionEntry with the corresponding properties of the given
|
||||
* template.
|
||||
*
|
||||
* @constructor
|
||||
* @param {HostRestrictionEntry|Object} [template={}]
|
||||
* The object whose properties should be copied within the new
|
||||
* HostRestrictionEntry.
|
||||
*/
|
||||
var HostRestrictionEntry = function HostRestrictionEntry(template) {
|
||||
|
||||
// Use empty object by default
|
||||
template = template || {};
|
||||
|
||||
/**
|
||||
* The IP address, CIDR notation range, or DNS hostname of the host(s)
|
||||
* specified by this restriction.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.host = template.host || '';
|
||||
|
||||
};
|
||||
|
||||
return HostRestrictionEntry;
|
||||
|
||||
}]);
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides the TimeRestrictionEntry class definition.
|
||||
*/
|
||||
angular.module('guacRestrict').factory('TimeRestrictionEntry', [
|
||||
function defineTimeRestrictionEntry() {
|
||||
|
||||
/**
|
||||
* Creates a new TimeRestrictionEntry, initializing the properties of that
|
||||
* TimeRestrictionEntry with the corresponding properties of the given
|
||||
* template.
|
||||
*
|
||||
* @constructor
|
||||
* @param {TimeRestrictionEntry|Object} [template={}]
|
||||
* The object whose properties should be copied within the new
|
||||
* TimeRestrictionEntry.
|
||||
*/
|
||||
var TimeRestrictionEntry = function TimeRestrictionEntry(template) {
|
||||
|
||||
// Use empty object by default
|
||||
template = template || {};
|
||||
|
||||
/**
|
||||
* The numerical representation of the day of the week this restriction
|
||||
* applies to.
|
||||
*
|
||||
* @type Number
|
||||
*/
|
||||
this.weekDay = template.weekDay;
|
||||
|
||||
/**
|
||||
* The hour and minute that this restriction starts, in 24-hour time,
|
||||
* and with no separator between the hour and minute.
|
||||
*
|
||||
* @type Date
|
||||
*/
|
||||
this.startTime = template.startTime;
|
||||
|
||||
/**
|
||||
* The hour and minute that this restriction ends, in 24-hour time, and
|
||||
* with no separator between the hour and minute.
|
||||
*
|
||||
* @type Date
|
||||
*/
|
||||
this.endTime = template.endTime;
|
||||
|
||||
};
|
||||
|
||||
return TimeRestrictionEntry;
|
||||
|
||||
}]);
|
Reference in New Issue
Block a user