mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
Merge 1.6.0 changes back to patch.
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,67 @@
|
|||||||
|
/*
|
||||||
|
* 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.calendar.RestrictionType;
|
||||||
|
import org.apache.guacamole.net.auth.Attributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface which defines methods that apply to items that can have
|
||||||
|
* restrictions applied to them.
|
||||||
|
*/
|
||||||
|
public interface Restrictable extends Attributes {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the restriction state for this restrictable object at the
|
||||||
|
* current date and time. By default returns an implicit denial.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The restriction status for the current date and time.
|
||||||
|
*/
|
||||||
|
default public RestrictionType getCurrentTimeRestriction() {
|
||||||
|
return RestrictionType.IMPLICIT_DENY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the restriction state for this restrictable object for the host
|
||||||
|
* from which the current user is logged in. By default returns an implicit
|
||||||
|
* denial.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The restriction status for the host from which the current user is
|
||||||
|
* logged in.
|
||||||
|
*/
|
||||||
|
default public RestrictionType getCurrentHostRestriction() {
|
||||||
|
return RestrictionType.IMPLICIT_DENY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the current item is available based on the restrictions
|
||||||
|
* for the given implementation of this interface, or false if the item is
|
||||||
|
* not currently available. The default implementation checks current time
|
||||||
|
* and host restrictions, allowing if both those restrictions allow access.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* true if the item is available, otherwise false.
|
||||||
|
*/
|
||||||
|
default public boolean isAvailable() {
|
||||||
|
return (getCurrentTimeRestriction().isAllowed() && getCurrentHostRestriction().isAllowed());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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.RestrictedUserContext;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
String remoteAddress = credentials.getRemoteAddress();
|
||||||
|
|
||||||
|
// Verify identity of user
|
||||||
|
RestrictionVerificationService.verifyLoginRestrictions(context,
|
||||||
|
authenticatedUser.getEffectiveUserGroups(), remoteAddress);
|
||||||
|
|
||||||
|
// User has been verified, and authentication should be allowed to
|
||||||
|
// continue
|
||||||
|
return new RestrictedUserContext(context, remoteAddress,
|
||||||
|
authenticatedUser.getEffectiveUserGroups());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,538 @@
|
|||||||
|
/*
|
||||||
|
* 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.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.auth.restrict.connection.RestrictedConnection;
|
||||||
|
import org.apache.guacamole.auth.restrict.user.RestrictedUser;
|
||||||
|
import org.apache.guacamole.auth.restrict.usergroup.RestrictedUserGroup;
|
||||||
|
import org.apache.guacamole.calendar.DailyRestriction;
|
||||||
|
import org.apache.guacamole.calendar.RestrictionType;
|
||||||
|
import org.apache.guacamole.calendar.TimeRestrictionParser;
|
||||||
|
import org.apache.guacamole.host.HostRestrictionParser;
|
||||||
|
import org.apache.guacamole.language.TranslatableGuacamoleSecurityException;
|
||||||
|
import org.apache.guacamole.net.auth.User;
|
||||||
|
import org.apache.guacamole.net.auth.UserContext;
|
||||||
|
import org.apache.guacamole.net.auth.UserGroup;
|
||||||
|
import org.apache.guacamole.net.auth.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, and returning the appropriate restriction type.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
* A RestrictionType based on the provided allowed and denied strings.
|
||||||
|
*/
|
||||||
|
public static RestrictionType allowedByTimeRestrictions(String allowedTimeString,
|
||||||
|
String deniedTimeString) {
|
||||||
|
|
||||||
|
// Check for denied entries, first, returning the explicit deny 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 RestrictionType.EXPLICIT_DENY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no allowed entries are present, return the implicit allow, allowing
|
||||||
|
// the login or connection to continue.
|
||||||
|
if (allowedTimeString == null || allowedTimeString.isEmpty())
|
||||||
|
return RestrictionType.IMPLICIT_ALLOW;
|
||||||
|
|
||||||
|
// Pull the list of allowed times.
|
||||||
|
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 the explicit allow.
|
||||||
|
if (restriction.appliesNow())
|
||||||
|
return RestrictionType.EXPLICIT_ALLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have allowed entries, but login hasn't matched, so implicitly deny it.
|
||||||
|
return RestrictionType.IMPLICIT_DENY;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the strings of allowed and denied hosts, verify that the login or
|
||||||
|
* connection should be allowed from the given remote address, returning
|
||||||
|
* the RestrictionType that matches the provided allowed and denied strings.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
* A RestrictionType that matches the provided allow and deny strings.
|
||||||
|
*/
|
||||||
|
public static RestrictionType allowedByHostRestrictions(String allowedHostsString,
|
||||||
|
String deniedHostsString, String remoteAddress) {
|
||||||
|
|
||||||
|
// Convert the string to a HostName
|
||||||
|
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 RestrictionType.IMPLICIT_ALLOW;
|
||||||
|
|
||||||
|
// 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 RestrictionType.IMPLICIT_DENY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 RestrictionType.EXPLICIT_DENY;
|
||||||
|
|
||||||
|
else
|
||||||
|
for (IPAddress currAddr : hostName.toAllAddresses())
|
||||||
|
if (currAddr.matches(remoteHostName.asAddressString()))
|
||||||
|
return RestrictionType.EXPLICIT_DENY;
|
||||||
|
}
|
||||||
|
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 RestrictionType.IMPLICIT_DENY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 RestrictionType.IMPLICIT_ALLOW;
|
||||||
|
|
||||||
|
// 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 RestrictionType.EXPLICIT_ALLOW;
|
||||||
|
|
||||||
|
// Entry is a hostname, so resolve to IPs and check each one
|
||||||
|
for (IPAddress currAddr : hostName.toAllAddresses())
|
||||||
|
if (currAddr.matches(remoteHostName.asAddressString()))
|
||||||
|
return RestrictionType.EXPLICIT_ALLOW;
|
||||||
|
|
||||||
|
}
|
||||||
|
// 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 RestrictionType.IMPLICIT_DENY;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the host restrictions for the user associated with the given
|
||||||
|
* UserContext, throwing an exception if any of the restrictions result
|
||||||
|
* in the user not being allowed to be logged in to Guacamole from this
|
||||||
|
* host.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* The UserContext associated with the user who is being verified.
|
||||||
|
*
|
||||||
|
* @param effectiveUserGroups
|
||||||
|
* The set of identifiers of groups of which the user who is being
|
||||||
|
* verified is a member.
|
||||||
|
*
|
||||||
|
* @param remoteAddress
|
||||||
|
* The remote address of the client from which the current user is
|
||||||
|
* logged in.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the restrictions on the user should prevent the user from
|
||||||
|
* logging in from the current client, or if an error occurs attempting
|
||||||
|
* to retrieve permissions.
|
||||||
|
*/
|
||||||
|
public static void verifyHostRestrictions(UserContext context,
|
||||||
|
Set<String> effectiveUserGroups, String remoteAddress)
|
||||||
|
throws GuacamoleException {
|
||||||
|
|
||||||
|
// Get the current user
|
||||||
|
User currentUser = context.self();
|
||||||
|
|
||||||
|
// Admins always have access.
|
||||||
|
if (currentUser.getEffectivePermissions().getSystemPermissions().hasPermission(SystemPermission.Type.ADMINISTER)) {
|
||||||
|
LOGGER.warn("User \"{}\" has System Administration permissions; additional restrictions will be bypassed.",
|
||||||
|
currentUser.getIdentifier());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user's attributes
|
||||||
|
Map<String, String> userAttributes = currentUser.getAttributes();
|
||||||
|
|
||||||
|
// Verify host-based restrictions specific to the user
|
||||||
|
String allowedHostString = userAttributes.get(RestrictedUser.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
|
||||||
|
String deniedHostString = userAttributes.get(RestrictedUser.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
|
||||||
|
RestrictionType hostRestrictionResult = allowedByHostRestrictions(allowedHostString, deniedHostString, remoteAddress);
|
||||||
|
|
||||||
|
switch (hostRestrictionResult) {
|
||||||
|
// User-level explicit deny overrides everything
|
||||||
|
case EXPLICIT_DENY:
|
||||||
|
throw new TranslatableInvalidHostLoginException("User \""
|
||||||
|
+ currentUser.getIdentifier()
|
||||||
|
+"\" is not allowed to log in from \""
|
||||||
|
+ remoteAddress + "\"",
|
||||||
|
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST"
|
||||||
|
);
|
||||||
|
|
||||||
|
// User-level explicit allow means the user is allowed.
|
||||||
|
case EXPLICIT_ALLOW:
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather user's effective groups.
|
||||||
|
Collection<UserGroup> userGroups = context
|
||||||
|
.getPrivileged()
|
||||||
|
.getUserGroupDirectory()
|
||||||
|
.getAll(effectiveUserGroups);
|
||||||
|
|
||||||
|
// Loop user's effective groups and verify restrictions
|
||||||
|
for (UserGroup userGroup : userGroups) {
|
||||||
|
|
||||||
|
// Get group's attributes
|
||||||
|
Map<String, String> grpAttributes = userGroup.getAttributes();
|
||||||
|
|
||||||
|
// Pull host-based restrictions for this group and verify
|
||||||
|
String grpAllowedHostString = grpAttributes.get(RestrictedUserGroup.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
|
||||||
|
String grpDeniedHostString = grpAttributes.get(RestrictedUserGroup.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
|
||||||
|
RestrictionType grpRestrictionResult = allowedByHostRestrictions(grpAllowedHostString, grpDeniedHostString, remoteAddress);
|
||||||
|
|
||||||
|
// Any explicit denials are thrown immediately
|
||||||
|
if (grpRestrictionResult == RestrictionType.EXPLICIT_DENY)
|
||||||
|
throw new TranslatableInvalidHostLoginException("User \""
|
||||||
|
+ currentUser.getIdentifier()
|
||||||
|
+ "\" is not allowed to log in from host \""
|
||||||
|
+ remoteAddress
|
||||||
|
+ "\" due to restrictions on group \""
|
||||||
|
+ userGroup.getIdentifier() + "\".",
|
||||||
|
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compare the two, returning the highest-priority restriction so far.
|
||||||
|
hostRestrictionResult = RestrictionType.getHigherPriority(hostRestrictionResult, grpRestrictionResult);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the result and log allowed
|
||||||
|
switch (hostRestrictionResult) {
|
||||||
|
// Explicit allow was the highest result, so we log it and return, allowing the user to be logged in.
|
||||||
|
case EXPLICIT_ALLOW:
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Implicit allow was the highest result, so we log it and return, allowing the user to be logged in.
|
||||||
|
case IMPLICIT_ALLOW:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach, here, we've reached an implict deny, so we throw an exception.
|
||||||
|
throw new TranslatableInvalidHostLoginException("User \""
|
||||||
|
+ currentUser.getIdentifier()
|
||||||
|
+ "\" is implicitly denied at this time.",
|
||||||
|
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_FROM_HOST"
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the host-based restrictions of the Connection, throwing an
|
||||||
|
* exception if the Connection should be allowed from the host from which
|
||||||
|
* the user is logged in.
|
||||||
|
*
|
||||||
|
* @param restrictable
|
||||||
|
* The Restrictable object that should be verified against host restrictions.
|
||||||
|
*
|
||||||
|
* @param remoteAddress
|
||||||
|
* The remote address of the client from which the current user is
|
||||||
|
* logged in.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the connection should not be allowed from the remote host from
|
||||||
|
* which the user is logged in.
|
||||||
|
*/
|
||||||
|
public static void verifyHostRestrictions(Restrictable restrictable,
|
||||||
|
String remoteAddress) throws GuacamoleException {
|
||||||
|
|
||||||
|
// Verify time-based restrictions specific to this connection.
|
||||||
|
String allowedHostsString = restrictable.getAttributes().get(RestrictedConnection.RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
|
||||||
|
String deniedHostsString = restrictable.getAttributes().get(RestrictedConnection.RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
|
||||||
|
RestrictionType hostRestrictionResult = allowedByHostRestrictions(allowedHostsString, deniedHostsString, remoteAddress);
|
||||||
|
|
||||||
|
// If the host is not allowed
|
||||||
|
if (!hostRestrictionResult.isAllowed())
|
||||||
|
throw new TranslatableGuacamoleSecurityException(
|
||||||
|
"Use of this connection is not allowed from this remote host: \"" + remoteAddress + "\".",
|
||||||
|
"RESTRICT.ERROR_CONNECTION_NOT_ALLOWED_NOW"
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the time restrictions for this extension and whether or not the
|
||||||
|
* account should be allowed to be logged in to Guacamole at the current
|
||||||
|
* day and time, throwing an exception if any of the restrictions result
|
||||||
|
* in a violation of the time constraints of the account.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* The UserContext of the user whose access to Guacamole is being
|
||||||
|
* checked.
|
||||||
|
*
|
||||||
|
* @param effectiveUserGroups
|
||||||
|
* The set of identifiers of groups of which the user who is being
|
||||||
|
* verified is a member.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If any of the time constraints configured for the user result in the
|
||||||
|
* user not being allowed to be logged in to Guacamole, or if errors
|
||||||
|
* occur trying to retrieve permissions or attributes.
|
||||||
|
*/
|
||||||
|
public static void verifyTimeRestrictions(UserContext context,
|
||||||
|
Set<String> effectiveUserGroups) throws GuacamoleException {
|
||||||
|
|
||||||
|
// Retrieve the current User object associated with the UserContext
|
||||||
|
User currentUser = context.self();
|
||||||
|
|
||||||
|
// Admins always have access.
|
||||||
|
if (currentUser.getEffectivePermissions().getSystemPermissions().hasPermission(SystemPermission.Type.ADMINISTER)) {
|
||||||
|
LOGGER.warn("User \"{}\" has System Administration permissions; additional restrictions will be bypassed.",
|
||||||
|
currentUser.getIdentifier());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user's attributes
|
||||||
|
Map<String, String> userAttributes = currentUser.getAttributes();
|
||||||
|
|
||||||
|
// Verify time-based restrictions specific to the user
|
||||||
|
String allowedTimeString = userAttributes.get(RestrictedUser.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
|
||||||
|
String deniedTimeString = userAttributes.get(RestrictedUser.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
|
||||||
|
RestrictionType timeRestrictionResult = allowedByTimeRestrictions(allowedTimeString, deniedTimeString);
|
||||||
|
|
||||||
|
// Check the time restriction for explicit results.
|
||||||
|
switch (timeRestrictionResult) {
|
||||||
|
// User-level explicit deny overrides everything
|
||||||
|
case EXPLICIT_DENY:
|
||||||
|
throw new TranslatableInvalidTimeLoginException("User \""
|
||||||
|
+ currentUser.getIdentifier()
|
||||||
|
+ "\" is not allowed to log in at this time.",
|
||||||
|
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW"
|
||||||
|
);
|
||||||
|
|
||||||
|
// User-level explicit allow means the user is allowed.
|
||||||
|
case EXPLICIT_ALLOW:
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather user's effective groups.
|
||||||
|
Collection<UserGroup> userGroups = context
|
||||||
|
.getPrivileged()
|
||||||
|
.getUserGroupDirectory()
|
||||||
|
.getAll(effectiveUserGroups);
|
||||||
|
|
||||||
|
// Loop user's effective groups and verify restrictions
|
||||||
|
for (UserGroup userGroup : userGroups) {
|
||||||
|
|
||||||
|
// Get group's attributes
|
||||||
|
Map<String, String> grpAttributes = userGroup.getAttributes();
|
||||||
|
|
||||||
|
// Pull time-based restrictions for this group and verify
|
||||||
|
String grpAllowedTimeString = grpAttributes.get(RestrictedUserGroup.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
|
||||||
|
String grpDeniedTimeString = grpAttributes.get(RestrictedUserGroup.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
|
||||||
|
RestrictionType grpRestrictionResult = allowedByTimeRestrictions(grpAllowedTimeString, grpDeniedTimeString);
|
||||||
|
|
||||||
|
// An explicit deny results in immediate denial of the login.
|
||||||
|
if (grpRestrictionResult == RestrictionType.EXPLICIT_DENY)
|
||||||
|
throw new TranslatableInvalidTimeLoginException("User \""
|
||||||
|
+ currentUser.getIdentifier()
|
||||||
|
+"\" is not allowed to log in at this time due to restrictions on group \""
|
||||||
|
+ userGroup + "\".",
|
||||||
|
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compare the two, returning the highest-priority restriction so far.
|
||||||
|
timeRestrictionResult = RestrictionType.getHigherPriority(timeRestrictionResult, grpRestrictionResult);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (timeRestrictionResult) {
|
||||||
|
// Explicit allow was the highest result, so we log it and return, allowing the user to be logged in.
|
||||||
|
case EXPLICIT_ALLOW:
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Implicit allow was the highest result, so we log it and return, allowing the user to be logged in.
|
||||||
|
case IMPLICIT_ALLOW:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach, here, we've reached an implict deny, so we throw an exception.
|
||||||
|
throw new TranslatableInvalidTimeLoginException("User \""
|
||||||
|
+ currentUser.getIdentifier()
|
||||||
|
+ "\" is implicitly denied at this time.",
|
||||||
|
"RESTRICT.ERROR_USER_LOGIN_NOT_ALLOWED_NOW"
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the time restrictions for the given Connection object, throwing
|
||||||
|
* an exception if the connection should not be allowed, or silently
|
||||||
|
* returning if the connection should be allowed.
|
||||||
|
*
|
||||||
|
* @param restrictable
|
||||||
|
* The item that supports restrictions that is to be verified against
|
||||||
|
* the current time.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If the connection should not be allowed at the current time.
|
||||||
|
*/
|
||||||
|
public static void verifyTimeRestrictions(Restrictable restrictable) throws GuacamoleException {
|
||||||
|
|
||||||
|
// Verify time-based restrictions specific to this connection.
|
||||||
|
String allowedTimeString = restrictable.getAttributes().get(RestrictedConnection.RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
|
||||||
|
String deniedTimeString = restrictable.getAttributes().get(RestrictedConnection.RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
|
||||||
|
RestrictionType timeRestriction = allowedByTimeRestrictions(allowedTimeString, deniedTimeString);
|
||||||
|
if (!timeRestriction.isAllowed())
|
||||||
|
throw new TranslatableGuacamoleSecurityException(
|
||||||
|
"Use of this connection or connection group is not allowed at this time.",
|
||||||
|
"RESTRICT.ERROR_CONNECTION_NOT_ALLOWED_NOW"
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 effectiveUserGroups
|
||||||
|
* The identifiers of the UserGroups of which the user who is logging
|
||||||
|
* in is a member.
|
||||||
|
*
|
||||||
|
* @param remoteAddress
|
||||||
|
* The remote address of the client from which the current user is
|
||||||
|
* logged in.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If any of the restrictions should prevent the user from logging in.
|
||||||
|
*/
|
||||||
|
public static void verifyLoginRestrictions(UserContext context,
|
||||||
|
Set<String> effectiveUserGroups, String remoteAddress)
|
||||||
|
throws GuacamoleException {
|
||||||
|
|
||||||
|
verifyTimeRestrictions(context, effectiveUserGroups);
|
||||||
|
verifyHostRestrictions(context, effectiveUserGroups, remoteAddress);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 restrictable
|
||||||
|
* The object that supports restrictions that is to be verified to be
|
||||||
|
* usable within the current restrictions.
|
||||||
|
*
|
||||||
|
* @param remoteAddress
|
||||||
|
* The remote address of the client from which the current user is
|
||||||
|
* logged in.
|
||||||
|
*
|
||||||
|
* @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(Restrictable restrictable,
|
||||||
|
String remoteAddress) throws GuacamoleException {
|
||||||
|
verifyTimeRestrictions(restrictable);
|
||||||
|
verifyHostRestrictions(restrictable, remoteAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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,200 @@
|
|||||||
|
/*
|
||||||
|
* 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.Restrictable;
|
||||||
|
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.calendar.RestrictionType;
|
||||||
|
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 RestrictedConnection extends DelegatingConnection implements Restrictable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The remote address of the client from which the user logged in.
|
||||||
|
*/
|
||||||
|
private final String remoteAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 of the client from which the current user logged
|
||||||
|
* in.
|
||||||
|
*/
|
||||||
|
public RestrictedConnection(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(this, remoteAddress);
|
||||||
|
|
||||||
|
// Connect
|
||||||
|
return super.connect(info, tokens);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestrictionType getCurrentTimeRestriction() {
|
||||||
|
String allowedTimeString = getAttributes().get(RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
|
||||||
|
String deniedTimeString = getAttributes().get(RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
|
||||||
|
return RestrictionVerificationService.allowedByTimeRestrictions(allowedTimeString, deniedTimeString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestrictionType getCurrentHostRestriction() {
|
||||||
|
String allowedHostString = getAttributes().get(RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
|
||||||
|
String deniedHostString = getAttributes().get(RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
|
||||||
|
return RestrictionVerificationService.allowedByHostRestrictions(allowedHostString, deniedHostString, remoteAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* 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.Restrictable;
|
||||||
|
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.calendar.RestrictionType;
|
||||||
|
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 RestrictedConnectionGroup extends DelegatingConnectionGroup implements Restrictable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The remote address of the client from which the current user logged in.
|
||||||
|
*/
|
||||||
|
private final String remoteAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 of the client from which the current user logged
|
||||||
|
* in.
|
||||||
|
*/
|
||||||
|
public RestrictedConnectionGroup(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(this, remoteAddress);
|
||||||
|
|
||||||
|
// Connect
|
||||||
|
return super.connect(info, tokens);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestrictionType getCurrentTimeRestriction() {
|
||||||
|
String allowedTimeString = getAttributes().get(RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
|
||||||
|
String deniedTimeString = getAttributes().get(RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
|
||||||
|
return RestrictionVerificationService.allowedByTimeRestrictions(allowedTimeString, deniedTimeString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestrictionType getCurrentHostRestriction() {
|
||||||
|
String allowedHostString = getAttributes().get(RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
|
||||||
|
String deniedHostString = getAttributes().get(RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
|
||||||
|
return RestrictionVerificationService.allowedByHostRestrictions(allowedHostString, deniedHostString, remoteAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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,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 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,199 @@
|
|||||||
|
/*
|
||||||
|
* 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.Restrictable;
|
||||||
|
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.calendar.RestrictionType;
|
||||||
|
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 RestrictedUser extends DelegatingUser implements Restrictable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The remote address of the client from which the current user is logged in.
|
||||||
|
*/
|
||||||
|
private final String remoteAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* true if the user logged in to Guacamole has administrative privileges
|
||||||
|
* for this user object, otherwise false.
|
||||||
|
*/
|
||||||
|
private final boolean hasAdmin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param remoteAddress
|
||||||
|
* The remote address of the client from which the current user is logged
|
||||||
|
* in.
|
||||||
|
*/
|
||||||
|
public RestrictedUser(User user, String remoteAddress, boolean hasAdmin) {
|
||||||
|
super(user);
|
||||||
|
this.remoteAddress = remoteAddress;
|
||||||
|
this.hasAdmin = hasAdmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
|
||||||
|
/* If the user lacks admin access, don't set restriction attributes. */
|
||||||
|
if (!hasAdmin) {
|
||||||
|
attributes.remove(attribute);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Replace empty values with null values. */
|
||||||
|
String value = attributes.get(attribute);
|
||||||
|
if (value != null && value.isEmpty())
|
||||||
|
attributes.put(attribute, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setAttributes(attributes);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestrictionType getCurrentTimeRestriction() {
|
||||||
|
String allowedTimeString = getAttributes().get(RESTRICT_TIME_ALLOWED_ATTRIBUTE_NAME);
|
||||||
|
String deniedTimeString = getAttributes().get(RESTRICT_TIME_DENIED_ATTRIBUTE_NAME);
|
||||||
|
return RestrictionVerificationService.allowedByTimeRestrictions(allowedTimeString, deniedTimeString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestrictionType getCurrentHostRestriction() {
|
||||||
|
String allowedHostString = getAttributes().get(RESTRICT_HOSTS_ALLOWED_ATTRIBUTE_NAME);
|
||||||
|
String deniedHostString = getAttributes().get(RESTRICT_HOSTS_DENIED_ATTRIBUTE_NAME);
|
||||||
|
return RestrictionVerificationService.allowedByHostRestrictions(allowedHostString, deniedHostString, remoteAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* 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 java.util.Set;
|
||||||
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.auth.restrict.RestrictionVerificationService;
|
||||||
|
import org.apache.guacamole.auth.restrict.connection.RestrictedConnection;
|
||||||
|
import org.apache.guacamole.auth.restrict.connectiongroup.RestrictedConnectionGroup;
|
||||||
|
import org.apache.guacamole.auth.restrict.usergroup.RestrictedUserGroup;
|
||||||
|
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.Permissions;
|
||||||
|
import org.apache.guacamole.net.auth.User;
|
||||||
|
import org.apache.guacamole.net.auth.UserContext;
|
||||||
|
import org.apache.guacamole.net.auth.UserGroup;
|
||||||
|
import org.apache.guacamole.net.auth.permission.ObjectPermission;
|
||||||
|
import org.apache.guacamole.net.auth.permission.SystemPermission;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A UserContext implementation for additional login and connection restrictions
|
||||||
|
* which wraps the UserContext of some other extension.
|
||||||
|
*/
|
||||||
|
public class RestrictedUserContext extends DelegatingUserContext {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The logger for this class.
|
||||||
|
*/
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(RestrictedUserContext.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The remote address from which this user logged in.
|
||||||
|
*/
|
||||||
|
private final String remoteAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identifiers effective groups of the user associated with this context.
|
||||||
|
*/
|
||||||
|
private final Set<String> effectiveUserGroups;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new RestrictedUserContext 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.
|
||||||
|
*
|
||||||
|
* @param effectiveUserGroups
|
||||||
|
* The identifiers of the groups this user is associated with.
|
||||||
|
*/
|
||||||
|
public RestrictedUserContext(UserContext userContext, String remoteAddress,
|
||||||
|
Set<String> effectiveUserGroups) {
|
||||||
|
super(userContext);
|
||||||
|
this.remoteAddress = remoteAddress;
|
||||||
|
this.effectiveUserGroups = effectiveUserGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Directory<Connection> getConnectionDirectory() throws GuacamoleException {
|
||||||
|
return new DecoratingDirectory<Connection>(super.getConnectionDirectory()) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Connection decorate(Connection object) throws GuacamoleException {
|
||||||
|
return new RestrictedConnection(object, remoteAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Connection undecorate(Connection object) {
|
||||||
|
assert(object instanceof RestrictedConnection);
|
||||||
|
return ((RestrictedConnection) object).getUndecorated();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Form> getConnectionAttributes() {
|
||||||
|
Collection<Form> connectionAttrs = new HashSet<>(super.getConnectionAttributes());
|
||||||
|
connectionAttrs.add(RestrictedConnection.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) throws GuacamoleException {
|
||||||
|
return new RestrictedConnectionGroup(object, remoteAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConnectionGroup undecorate(ConnectionGroup object) {
|
||||||
|
assert(object instanceof RestrictedConnectionGroup);
|
||||||
|
return ((RestrictedConnectionGroup) object).getUndecorated();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Form> getConnectionGroupAttributes() {
|
||||||
|
Collection<Form> connectionGroupAttrs = new HashSet<>(super.getConnectionGroupAttributes());
|
||||||
|
connectionGroupAttrs.add(RestrictedConnectionGroup.RESTRICT_CONNECTIONGROUP_FORM);
|
||||||
|
return Collections.unmodifiableCollection(connectionGroupAttrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Directory<User> getUserDirectory() throws GuacamoleException {
|
||||||
|
|
||||||
|
// Pull permissions of the current logged-in user.
|
||||||
|
Permissions currentPermissions = self().getEffectivePermissions();
|
||||||
|
boolean isAdmin = currentPermissions.getSystemPermissions().hasPermission(
|
||||||
|
SystemPermission.Type.ADMINISTER
|
||||||
|
);
|
||||||
|
Collection<String> adminIdentifiers =
|
||||||
|
currentPermissions.getUserPermissions().getAccessibleObjects(
|
||||||
|
Collections.singletonList(ObjectPermission.Type.ADMINISTER), super.getUserDirectory().getIdentifiers());
|
||||||
|
|
||||||
|
return new DecoratingDirectory<User>(super.getUserDirectory()) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected User decorate(User object) throws GuacamoleException {
|
||||||
|
|
||||||
|
// Check and see if the logged in user has admin privileges -
|
||||||
|
// either system-level or for that particular object.
|
||||||
|
boolean hasAdmin = isAdmin || adminIdentifiers.contains(object.getIdentifier());
|
||||||
|
return new RestrictedUser(object, remoteAddress, hasAdmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected User undecorate(User object) {
|
||||||
|
assert(object instanceof RestrictedUser);
|
||||||
|
return ((RestrictedUser) object).getUndecorated();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Form> getUserAttributes() {
|
||||||
|
Collection<Form> userAttrs = new HashSet<>(super.getUserAttributes());
|
||||||
|
userAttrs.add(RestrictedUser.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 RestrictedUserGroup(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected UserGroup undecorate(UserGroup object) {
|
||||||
|
assert(object instanceof RestrictedUserGroup);
|
||||||
|
return ((RestrictedUserGroup) object).getUndecorated();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Form> getUserGroupAttributes() {
|
||||||
|
Collection<Form> userGroupAttrs = new HashSet<>(super.getUserGroupAttributes());
|
||||||
|
userGroupAttrs.add(RestrictedUserGroup.RESTRICT_LOGIN_FORM);
|
||||||
|
return Collections.unmodifiableCollection(userGroupAttrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid() {
|
||||||
|
try {
|
||||||
|
// Verify whether or not time restrictions still apply.
|
||||||
|
RestrictionVerificationService.verifyTimeRestrictions(this, effectiveUserGroups);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (GuacamoleException e) {
|
||||||
|
LOGGER.debug("User account is now restricted and is no longer valid", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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 RestrictedUserGroup 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 RestrictedUserGroup(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,140 @@
|
|||||||
|
/*
|
||||||
|
* 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(ZoneId.of("UTC")).getDayOfWeek();
|
||||||
|
LocalTime currentTime = LocalTime.now(ZoneId.of("UTC"));
|
||||||
|
|
||||||
|
// If end time is less than the start time, we check the remainder of this
|
||||||
|
// day and the beginning of the next day.
|
||||||
|
if (endTime.isBefore(startTime)) {
|
||||||
|
if (weekDays.contains(currentDay) && currentTime.isAfter(startTime) && currentTime.isBefore(LocalTime.MAX))
|
||||||
|
return true;
|
||||||
|
return (weekDays.contains(currentDay.plus(1)) && currentTime.isAfter(LocalTime.MIDNIGHT) && currentTime.isBefore(endTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we are in the specified time restriction
|
||||||
|
return (weekDays.contains(currentDay) && currentTime.isAfter(startTime) && currentTime.isBefore(endTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A data type that represents various values of what type of restriction applies
|
||||||
|
* at a given time.
|
||||||
|
*/
|
||||||
|
public enum RestrictionType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access is explicitly allowed.
|
||||||
|
*/
|
||||||
|
EXPLICIT_ALLOW(1, true),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access is explicitly denied.
|
||||||
|
*/
|
||||||
|
EXPLICIT_DENY(0, false),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access has not been explicitly allowed or denied, therefore it is
|
||||||
|
* implicitly allowed.
|
||||||
|
*/
|
||||||
|
IMPLICIT_ALLOW(3, true),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access has not been explicitly allowed or denied, therefore it is
|
||||||
|
* implicitly denied.
|
||||||
|
*/
|
||||||
|
IMPLICIT_DENY(2, false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The overall priority of the restriction, with zero being the highest
|
||||||
|
* priority and the priority decreasing as numbers increase from zero.
|
||||||
|
*/
|
||||||
|
final private int priority;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* true if the restriction allows access, otherwise false.
|
||||||
|
*/
|
||||||
|
final private boolean allowed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the new instance of this RestrictionType, with the given
|
||||||
|
* priority value for the instance.
|
||||||
|
*
|
||||||
|
* @param priority
|
||||||
|
* The priority of the restriction type, where zero is the highest
|
||||||
|
* priority.
|
||||||
|
*
|
||||||
|
* @param allowed
|
||||||
|
* true if the restriction allows access, otherwise false.
|
||||||
|
*/
|
||||||
|
RestrictionType(int priority, boolean allowed) {
|
||||||
|
this.priority = priority;
|
||||||
|
this.allowed = allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates two restrictions, returning the higher priority of the two.
|
||||||
|
*
|
||||||
|
* @param restriction1
|
||||||
|
* The first restriction to compare.
|
||||||
|
*
|
||||||
|
* @param restriction2
|
||||||
|
* The second restriction to compare.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Return which of the two restrictions is the higher-priority.
|
||||||
|
*/
|
||||||
|
public static RestrictionType getHigherPriority(RestrictionType restriction1, RestrictionType restriction2) {
|
||||||
|
|
||||||
|
// If the second is higher than the first, return the second.
|
||||||
|
if (restriction1.priority > restriction2.priority)
|
||||||
|
return restriction2;
|
||||||
|
|
||||||
|
// Return the first.
|
||||||
|
return restriction1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this restriction allows access, otherwise false.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* true if this restriction allows access, otherwise false.
|
||||||
|
*/
|
||||||
|
public boolean isAllowed() {
|
||||||
|
return this.allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* 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>*:0900-1700 - Every day, 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("(?:^|;)+([0-7*])(?::((?:[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 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;
|
||||||
|
|
||||||
|
// A specific day of the week.
|
||||||
|
default:
|
||||||
|
int dayInt = Integer.parseInt(dayString);
|
||||||
|
|
||||||
|
// While JavaScript sees Sunday as "0" and "7", DayOfWeek
|
||||||
|
// does not, so we'll convert it to "7" in order to process it.
|
||||||
|
if (dayInt == 0)
|
||||||
|
dayInt = 7;
|
||||||
|
|
||||||
|
restrictions.add(new DailyRestriction(DayOfWeek.of(dayInt), 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,317 @@
|
|||||||
|
/*
|
||||||
|
* 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. Note that Sunday occurs
|
||||||
|
* twice - once for the 0-index and once for the 7 index.
|
||||||
|
*/
|
||||||
|
$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' }
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
|
||||||
|
// Array to store the restrictions
|
||||||
|
var restrictions = [];
|
||||||
|
|
||||||
|
// Grab the current date so that we can accurately parse DST later
|
||||||
|
var templateDate = new Date();
|
||||||
|
|
||||||
|
// 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('^([0-7*])(?::((?:[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);
|
||||||
|
let entry = new TimeRestrictionEntry();
|
||||||
|
entry.startTime = new Date(Date.UTC(templateDate.getFullYear(),
|
||||||
|
templateDate.getMonth(),
|
||||||
|
templateDate.getDate(),
|
||||||
|
parseInt(currArray[2].slice(0,2)),
|
||||||
|
parseInt(currArray[2].slice(2))));
|
||||||
|
entry.endTime = new Date(Date.UTC(templateDate.getFullYear(),
|
||||||
|
templateDate.getMonth(),
|
||||||
|
templateDate.getDate(),
|
||||||
|
parseInt(currArray[3].slice(0,2)),
|
||||||
|
parseInt(currArray[3].slice(2))));
|
||||||
|
var origDay = currArray[1];
|
||||||
|
|
||||||
|
if (currArray[1] === '*')
|
||||||
|
entry.weekDay = '' + currArray[1];
|
||||||
|
|
||||||
|
else {
|
||||||
|
// If UTC day is greater than local day, we subtract a day,
|
||||||
|
// wrapping as required.
|
||||||
|
if (entry.startTime.getDay() < entry.startTime.getUTCDay()) {
|
||||||
|
if (origDay <= 0)
|
||||||
|
entry.weekDay = '' + 6;
|
||||||
|
else
|
||||||
|
entry.weekDay = '' + (--origDay);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If UTC day is less than local day, we add a day,
|
||||||
|
// wrapping as required.
|
||||||
|
else if (entry.startTime.getDay() > entry.startTime.getUTCDay()) {
|
||||||
|
if (origDay >= 6)
|
||||||
|
entry.weekDay = '' + 0;
|
||||||
|
else
|
||||||
|
entry.weekDay = '' + (++origDay);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local day and UTC day are the same, adjust the display day
|
||||||
|
else
|
||||||
|
entry.weekDay = '' + origDay;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
restrictions.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return restrictions;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since new Time fields in HTML get a default year of 1970, we need to
|
||||||
|
* merge the hours and minutes from the time field into the current Date,
|
||||||
|
* primarily so that Daylight Savings Time offsets are correct.
|
||||||
|
*
|
||||||
|
* @param {Date} justTime
|
||||||
|
* The Date object produced by an HTML field that contains the hours
|
||||||
|
* and minutes we need.
|
||||||
|
*
|
||||||
|
* @returns {Date}
|
||||||
|
* The Date object that merges the current calendar date with the
|
||||||
|
* hours and minutes from the HTML field.
|
||||||
|
*/
|
||||||
|
const timeToCurrentDate = function timeToCurrentDate(justTime) {
|
||||||
|
let dateAndTime = new Date();
|
||||||
|
dateAndTime.setHours(justTime.getHours());
|
||||||
|
dateAndTime.setMinutes(justTime.getMinutes());
|
||||||
|
|
||||||
|
return dateAndTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 '';
|
||||||
|
|
||||||
|
let 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
|
||||||
|
|| restrictions[i].weekDay === ''
|
||||||
|
|| !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 += ';';
|
||||||
|
|
||||||
|
// When these fields first gets a value, the default year is 1970
|
||||||
|
// In order to avoid issues with Daylight Savings Time, we have to
|
||||||
|
// work around this.
|
||||||
|
if (restrictions[i].startTime instanceof Date && restrictions[i].startTime.getFullYear() === 1970)
|
||||||
|
restrictions[i].startTime = timeToCurrentDate(restrictions[i].startTime);
|
||||||
|
|
||||||
|
if (restrictions[i].endTime instanceof Date && restrictions[i].endTime.getFullYear() === 1970)
|
||||||
|
restrictions[i].endTime = timeToCurrentDate(restrictions[i].endTime);
|
||||||
|
|
||||||
|
// Process the start day, factoring in wrapping for local time to
|
||||||
|
// UTC adjustments.
|
||||||
|
let weekDay = restrictions[i].weekDay;
|
||||||
|
const startDay = restrictions[i].startTime.getDay();
|
||||||
|
const utcStartDay = restrictions[i].startTime.getUTCDay();
|
||||||
|
|
||||||
|
// Local day is less than UTC day, so we add a day for storing,
|
||||||
|
// wrapping around as required.
|
||||||
|
if (weekDay !== '*' && startDay < utcStartDay) {
|
||||||
|
if (weekDay >= 6)
|
||||||
|
weekDay = 0;
|
||||||
|
else
|
||||||
|
weekDay++;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (weekDay !== '*' && startDay > utcStartDay) {
|
||||||
|
if (weekDay <= 0)
|
||||||
|
weekDay = 6;
|
||||||
|
else
|
||||||
|
weekDay--;
|
||||||
|
}
|
||||||
|
|
||||||
|
let currString = '' + weekDay.toString();
|
||||||
|
currString += ':';
|
||||||
|
|
||||||
|
// Retrieve startTime hours component and add it, adding leading zero if required.
|
||||||
|
let startHours = restrictions[i].startTime.getUTCHours();
|
||||||
|
if (startHours !== null && startHours < 10)
|
||||||
|
currString += '0';
|
||||||
|
currString += startHours.toString();
|
||||||
|
|
||||||
|
// Retrieve startTime minutes component and add it, adding leading zero if required.
|
||||||
|
let startMins = restrictions[i].startTime.getUTCMinutes();
|
||||||
|
if (startMins !== null && startMins < 10)
|
||||||
|
currString += '0';
|
||||||
|
currString += startMins.toString();
|
||||||
|
|
||||||
|
currString += '-';
|
||||||
|
|
||||||
|
// Retrieve endTime hours component and add it, adding leading zero if required.
|
||||||
|
let endHours = restrictions[i].endTime.getUTCHours();
|
||||||
|
if (endHours !== null && endHours < 10)
|
||||||
|
currString += '0';
|
||||||
|
currString += endHours.toString();
|
||||||
|
|
||||||
|
// Retrieve endTime minutes component and add it, adding leading zero if required.
|
||||||
|
let endMins = restrictions[i].endTime.getUTCMinutes();
|
||||||
|
if (endMins !== null && endMins < 10)
|
||||||
|
currString += '0';
|
||||||
|
currString += endMins.toString();
|
||||||
|
|
||||||
|
// 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 {string}
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
}]);
|
@@ -28,7 +28,7 @@
|
|||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<version>1.6.0</version>
|
<version>1.6.0</version>
|
||||||
<name>guacamole-auth-totp</name>
|
<name>guacamole-auth-totp</name>
|
||||||
<url>http://guacamole.incubator.apache.org/</url>
|
<url>http://guacamole.apache.org/</url>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.guacamole</groupId>
|
<groupId>org.apache.guacamole</groupId>
|
||||||
|
@@ -47,6 +47,7 @@
|
|||||||
<module>guacamole-auth-json</module>
|
<module>guacamole-auth-json</module>
|
||||||
<module>guacamole-auth-ldap</module>
|
<module>guacamole-auth-ldap</module>
|
||||||
<module>guacamole-auth-quickconnect</module>
|
<module>guacamole-auth-quickconnect</module>
|
||||||
|
<module>guacamole-auth-restrict</module>
|
||||||
<module>guacamole-auth-sso</module>
|
<module>guacamole-auth-sso</module>
|
||||||
<module>guacamole-auth-totp</module>
|
<module>guacamole-auth-totp</module>
|
||||||
|
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
##
|
##
|
||||||
## @fn 010-build-and-install-guacamole.sh
|
## @fn 000-build-and-install-guacamole.sh
|
||||||
##
|
##
|
||||||
## Builds the Guacamole web application and all main extensions, installing the
|
## Builds the Guacamole web application and all main extensions, installing the
|
||||||
## resulting binaries to standard locations within the Docker image. After the
|
## resulting binaries to standard locations within the Docker image. After the
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
##
|
##
|
||||||
## @fn 020-map-guacamole-extensions.sh
|
## @fn 010-map-guacamole-extensions.sh
|
||||||
##
|
##
|
||||||
## Maps all installed Guacamole extensions (built in a previous step) to their
|
## Maps all installed Guacamole extensions (built in a previous step) to their
|
||||||
## corresponding environment variable prefixes, adding symbolic links so that
|
## corresponding environment variable prefixes, adding symbolic links so that
|
||||||
@@ -106,6 +106,7 @@ map_extensions <<'EOF'
|
|||||||
guacamole-auth-ldap.........................LDAP_
|
guacamole-auth-ldap.........................LDAP_
|
||||||
guacamole-auth-quickconnect.................QUICKCONNECT_
|
guacamole-auth-quickconnect.................QUICKCONNECT_
|
||||||
guacamole-auth-radius.......................RADIUS_
|
guacamole-auth-radius.......................RADIUS_
|
||||||
|
guacamole-auth-restrict.....................RESTRICT_
|
||||||
guacamole-auth-sso/cas......................CAS_
|
guacamole-auth-sso/cas......................CAS_
|
||||||
guacamole-auth-sso/openid...................OPENID_
|
guacamole-auth-sso/openid...................OPENID_
|
||||||
guacamole-auth-sso/saml.....................SAML_
|
guacamole-auth-sso/saml.....................SAML_
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
##
|
##
|
||||||
## @fn 030-download-drivers.sh
|
## @fn 020-download-drivers.sh
|
||||||
##
|
##
|
||||||
## Downloads all JDBC drivers required by the various supported databases. Each
|
## Downloads all JDBC drivers required by the various supported databases. Each
|
||||||
## downloaded driver is stored beneath /opt/guacamole/drivers, with symbolic
|
## downloaded driver is stored beneath /opt/guacamole/drivers, with symbolic
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
##
|
##
|
||||||
## @fn 800-configure-features.sh
|
## @fn 700-configure-features.sh
|
||||||
##
|
##
|
||||||
## Automatically checks all environment variables currently set and performs
|
## Automatically checks all environment variables currently set and performs
|
||||||
## configuration tasks related to those variabels, including installing any
|
## configuration tasks related to those variabels, including installing any
|
||||||
|
Reference in New Issue
Block a user