You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by jm...@apache.org on 2016/12/06 06:27:29 UTC
[01/12] incubator-guacamole-client git commit: GUACAMOLE-136: Add
Apache RAT plugin to build. Add missing license header to pom.xml.
Repository: incubator-guacamole-client
Updated Branches:
refs/heads/master 718e4dab0 -> cf6a2b84a
GUACAMOLE-136: Add Apache RAT plugin to build. Add missing license header to pom.xml.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/3457bcfc
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/3457bcfc
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/3457bcfc
Branch: refs/heads/master
Commit: 3457bcfc842b71162492ffe7127831b1372b5bd6
Parents: bf4446a
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Dec 5 20:26:54 2016 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Dec 5 22:19:45 2016 -0800
----------------------------------------------------------------------
extensions/guacamole-auth-duo/pom.xml | 48 ++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/3457bcfc/extensions/guacamole-auth-duo/pom.xml
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/pom.xml b/extensions/guacamole-auth-duo/pom.xml
index a55e54f..b129591 100644
--- a/extensions/guacamole-auth-duo/pom.xml
+++ b/extensions/guacamole-auth-duo/pom.xml
@@ -1,3 +1,22 @@
+<?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
@@ -134,6 +153,35 @@
</executions>
</plugin>
+ <!-- Verify format using Apache RAT -->
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <version>0.12</version>
+
+ <configuration>
+ <excludes>
+ <exclude>**/*.json</exclude>
+ <exclude>src/licenses/**/*</exclude>
+ <exclude>src/main/resources/templates/*.html</exclude>
+ <exclude>src/main/resources/lib/DuoWeb/**/*</exclude>
+ <exclude>src/main/java/com/duosecurity/duoweb/**/*</exclude>
+ </excludes>
+ </configuration>
+
+ <!-- Bind RAT to validate phase -->
+ <executions>
+ <execution>
+ <id>validate</id>
+ <phase>validate</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+
+ </plugin>
+
</plugins>
</build>
[12/12] incubator-guacamole-client git commit: GUACAMOLE-136: Merge
new Duo two factor authentication extension.
Posted by jm...@apache.org.
GUACAMOLE-136: Merge new Duo two factor authentication extension.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/cf6a2b84
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/cf6a2b84
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/cf6a2b84
Branch: refs/heads/master
Commit: cf6a2b84ab33e10d30be435af742a580276b8fcd
Parents: 718e4da a0f9bf5
Author: James Muehlner <ja...@guac-dev.org>
Authored: Mon Dec 5 22:23:34 2016 -0800
Committer: James Muehlner <ja...@guac-dev.org>
Committed: Mon Dec 5 22:23:34 2016 -0800
----------------------------------------------------------------------
LICENSE | 37 +-
extensions/guacamole-auth-duo/.gitignore | 3 +
extensions/guacamole-auth-duo/pom.xml | 250 +++
.../guacamole-auth-duo/src/licenses/DISCLAIMER | 7 +
.../guacamole-auth-duo/src/licenses/LICENSE | 274 ++++
.../guacamole-auth-duo/src/licenses/NOTICE | 5 +
.../src/licenses/bundled/README | 4 +
.../licenses/bundled/aopalliance-1.0/LICENSE | 4 +
.../licenses/bundled/duo-java-56570d1/LICENSE | 25 +
.../src/licenses/bundled/guice-3.0/COPYING | 202 +++
.../bundled/javax.inject-1/LICENSE-2.0.txt | 202 +++
.../src/main/assembly/dist.xml | 53 +
.../java/com/duosecurity/duoweb/Base64.java | 1500 ++++++++++++++++++
.../java/com/duosecurity/duoweb/DuoWeb.java | 138 ++
.../com/duosecurity/duoweb/DuoWebException.java | 8 +
.../main/java/com/duosecurity/duoweb/Util.java | 26 +
.../auth/duo/DuoAuthenticationProvider.java | 100 ++
.../duo/DuoAuthenticationProviderModule.java | 81 +
.../guacamole/auth/duo/DuoWebService.java | 212 +++
.../auth/duo/UserVerificationService.java | 108 ++
.../auth/duo/conf/ConfigurationService.java | 160 ++
.../auth/duo/form/DuoSignedResponseField.java | 100 ++
.../src/main/resources/config/duoConfig.js | 33 +
.../controllers/duoSignedResponseController.js | 86 +
.../src/main/resources/duoModule.js | 28 +
.../src/main/resources/guac-manifest.json | 28 +
.../src/main/resources/lib/DuoWeb/Duo-Web-v2.js | 366 +++++
.../src/main/resources/lib/DuoWeb/LICENSE.js | 27 +
.../src/main/resources/license.txt | 18 +
.../src/main/resources/styles/duo.css | 62 +
.../templates/duoSignedResponseField.html | 6 +
.../src/main/resources/translations/en.json | 13 +
pom.xml | 1 +
33 files changed, 4166 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
[07/12] incubator-guacamole-client git commit: GUACAMOLE-136: Use
hidden submit button for submitting Duo response (form.submit() inserts query
parameters and causes browser navigation,
despite ngSubmit preventing default).
Posted by jm...@apache.org.
GUACAMOLE-136: Use hidden submit button for submitting Duo response (form.submit() inserts query parameters and causes browser navigation, despite ngSubmit preventing default).
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/548966ae
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/548966ae
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/548966ae
Branch: refs/heads/master
Commit: 548966ae5a9bfc8ba574d977bbccad482d036702
Parents: 48af3ef
Author: Michael Jumper <mj...@apache.org>
Authored: Thu Dec 1 21:33:28 2016 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Dec 5 22:19:45 2016 -0800
----------------------------------------------------------------------
.../main/resources/controllers/duoSignedResponseController.js | 2 +-
extensions/guacamole-auth-duo/src/main/resources/styles/duo.css | 4 ++++
.../src/main/resources/templates/duoSignedResponseField.html | 1 +
3 files changed, 6 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/548966ae/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js b/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js
index 0d10f8e..f683698 100644
--- a/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js
+++ b/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js
@@ -56,7 +56,7 @@ angular.module('guacDuo').controller('duoSignedResponseController', ['$scope',
});
// Submit updated credentials
- $(iframe).parents('form').submit();
+ $('.duo-signature-response-field input[type="submit"]').click();
};
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/548966ae/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css b/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css
index 36d6031..db49ab3 100644
--- a/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css
+++ b/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css
@@ -17,6 +17,10 @@
* under the License.
*/
+.duo-signature-response-field input[type="submit"] {
+ display: none !important;
+}
+
.duo-signature-response-field iframe {
width: 100%;
max-width: 620px;
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/548966ae/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html b/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html
index 4658ed0..622bd42 100644
--- a/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html
+++ b/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html
@@ -1,3 +1,4 @@
<div class="duo-signature-response-field" ng-class="{ loading : !duoInterfaceLoaded }">
<iframe></iframe>
+ <input type="submit">
</div>
[04/12] incubator-guacamole-client git commit: GUACAMOLE-136:
Implement basic support for verifying user identity using Duo.
Posted by jm...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/conf/ConfigurationService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/conf/ConfigurationService.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/conf/ConfigurationService.java
new file mode 100644
index 0000000..40ccde9
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/conf/ConfigurationService.java
@@ -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.duo.conf;
+
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.environment.Environment;
+import org.apache.guacamole.properties.StringGuacamoleProperty;
+
+/**
+ * Service for retrieving configuration information regarding the Duo
+ * authentication extension.
+ */
+public class ConfigurationService {
+
+ /**
+ * The Guacamole server environment.
+ */
+ @Inject
+ private Environment environment;
+
+ /**
+ * The property within guacamole.properties which defines the hostname
+ * of the Duo API endpoint to be used to verify user identities. This will
+ * usually be in the form "api-XXXXXXXX.duosecurity.com", where "XXXXXXXX"
+ * is some arbitrary alphanumeric value assigned by Duo and specific to
+ * your organization.
+ */
+ private static final StringGuacamoleProperty DUO_API_HOSTNAME =
+ new StringGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "duo-api-hostname"; }
+
+ };
+
+ /**
+ * The property within guacamole.properties which defines the integration
+ * key received from Duo for verifying Guacamole users. This value MUST be
+ * exactly 20 characters.
+ */
+ private static final StringGuacamoleProperty DUO_INTEGRATION_KEY =
+ new StringGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "duo-integration-key"; }
+
+ };
+
+ /**
+ * The property within guacamole.properties which defines the secret key
+ * received from Duo for verifying Guacamole users. This value MUST be
+ * exactly 40 characters.
+ */
+ private static final StringGuacamoleProperty DUO_SECRET_KEY =
+ new StringGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "duo-secret-key"; }
+
+ };
+
+ /**
+ * The property within guacamole.properties which defines the arbitrary
+ * random key which was generated for Guacamole. Note that this value is not
+ * provided by Duo, but is expected to be generated by the administrator of
+ * the system hosting Guacamole. This value MUST be at least 40 characters.
+ */
+ private static final StringGuacamoleProperty DUO_APPLICATION_KEY =
+ new StringGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "duo-application-key"; }
+
+ };
+
+ /**
+ * Returns the hostname of the Duo API endpoint to be used to verify user
+ * identities, as defined in guacamole.properties by the "duo-api-hostname"
+ * property. This will usually be in the form
+ * "api-XXXXXXXX.duosecurity.com", where "XXXXXXXX" is some arbitrary
+ * alphanumeric value assigned by Duo and specific to your organization.
+ *
+ * @return
+ * The hostname of the Duo API endpoint to be used to verify user
+ * identities.
+ *
+ * @throws GuacamoleException
+ * If the associated property within guacamole.properties is missing.
+ */
+ public String getAPIHostname() throws GuacamoleException {
+ return environment.getRequiredProperty(DUO_API_HOSTNAME);
+ }
+
+ /**
+ * Returns the integration key received from Duo for verifying Guacamole
+ * users, as defined in guacamole.properties by the "duo-integration-key"
+ * property. This value MUST be exactly 20 characters.
+ *
+ * @return
+ * The integration key received from Duo for verifying Guacamole
+ * users.
+ *
+ * @throws GuacamoleException
+ * If the associated property within guacamole.properties is missing.
+ */
+ public String getIntegrationKey() throws GuacamoleException {
+ return environment.getRequiredProperty(DUO_INTEGRATION_KEY);
+ }
+
+ /**
+ * Returns the secret key received from Duo for verifying Guacamole users,
+ * as defined in guacamole.properties by the "duo-secret-key" property. This
+ * value MUST be exactly 20 characters.
+ *
+ * @return
+ * The secret key received from Duo for verifying Guacamole users.
+ *
+ * @throws GuacamoleException
+ * If the associated property within guacamole.properties is missing.
+ */
+ public String getSecretKey() throws GuacamoleException {
+ return environment.getRequiredProperty(DUO_SECRET_KEY);
+ }
+
+ /**
+ * Returns the arbitrary random key which was generated for Guacamole, as
+ * defined in guacamole.properties by the "duo-application-key" property.
+ * Note that this value is not provided by Duo, but is expected to be
+ * generated by the administrator of the system hosting Guacamole. This
+ * value MUST be at least 40 characters.
+ *
+ * @return
+ * The arbitrary random key which was generated for Guacamole.
+ *
+ * @throws GuacamoleException
+ * If the associated property within guacamole.properties is missing.
+ */
+ public String getApplicationKey() throws GuacamoleException {
+ return environment.getRequiredProperty(DUO_APPLICATION_KEY);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/form/DuoSignedResponseField.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/form/DuoSignedResponseField.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/form/DuoSignedResponseField.java
new file mode 100644
index 0000000..192cbf8
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/form/DuoSignedResponseField.java
@@ -0,0 +1,100 @@
+/*
+ * 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.duo.form;
+
+import org.apache.guacamole.form.Field;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * A custom field type which uses the DuoWeb API to produce a signed response
+ * for a particular user. The signed response serves as an additional
+ * authentication factor, as it cryptographically verifies possession of the
+ * physical device associated with that user's Duo account.
+ */
+public class DuoSignedResponseField extends Field {
+
+ /**
+ * The name of the HTTP parameter which an instance of this field will
+ * populate within a user's credentials.
+ */
+ public static final String PARAMETER_NAME = "guac-duo-signed-response";
+
+ /**
+ * The unique name associated with this field type.
+ */
+ private static final String FIELD_TYPE_NAME = "GUAC_DUO_SIGNED_RESPONSE";
+
+ /**
+ * The hostname of the DuoWeb API endpoint.
+ */
+ private final String apiHost;
+
+ /**
+ * The signed request generated by a call to DuoWeb.signRequest().
+ */
+ private final String signedRequest;
+
+ /**
+ * Creates a new field which uses the DuoWeb API to prompt the user for
+ * additional credentials. The provided credentials, if valid, will
+ * ultimately be verified by Duo's service, resulting in a signed response
+ * which can be cryptographically verified.
+ *
+ * @param apiHost
+ * The hostname of the DuoWeb API endpoint.
+ *
+ * @param signedRequest
+ * A signed request generated for the user in question by a call to
+ * DuoWeb.signRequest().
+ */
+ public DuoSignedResponseField(String apiHost, String signedRequest) {
+
+ // Init base field type properties
+ super(PARAMETER_NAME, FIELD_TYPE_NAME);
+
+ // Init Duo-specific properties
+ this.apiHost = apiHost;
+ this.signedRequest = signedRequest;
+
+ }
+
+ /**
+ * Returns the hostname of the DuoWeb API endpoint.
+ *
+ * @return
+ * The hostname of the DuoWeb API endpoint.
+ */
+ @JsonProperty("apiHost")
+ public String getAPIHost() {
+ return apiHost;
+ }
+
+ /**
+ * Returns the signed request string, which must have been generated by a
+ * call to DuoWeb.signRequest().
+ *
+ * @return
+ * The signed request generated by a call to DuoWeb.signRequest().
+ */
+ public String getSignedRequest() {
+ return signedRequest;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/resources/config/duoConfig.js
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/config/duoConfig.js b/extensions/guacamole-auth-duo/src/main/resources/config/duoConfig.js
new file mode 100644
index 0000000..43c37dc
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/resources/config/duoConfig.js
@@ -0,0 +1,33 @@
+/*
+ * 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 Duo-specific field types.
+ */
+angular.module('guacDuo').config(['formServiceProvider',
+ function guacDuoConfig(formServiceProvider) {
+
+ // Define field for the signed response from the Duo service
+ formServiceProvider.registerFieldType('GUAC_DUO_SIGNED_RESPONSE', {
+ module : 'guacDuo',
+ controller : 'duoSignedResponseController',
+ templateUrl : 'app/ext/duo/templates/duoSignedResponseField.html'
+ });
+
+}]);
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js b/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js
new file mode 100644
index 0000000..0d10f8e
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js
@@ -0,0 +1,78 @@
+/*
+ * 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 the "GUAC_DUO_SIGNED_RESPONSE" field which uses the DuoWeb
+ * API to prompt the user for additional credentials, ultimately receiving a
+ * signed response from the Duo service.
+ */
+angular.module('guacDuo').controller('duoSignedResponseController', ['$scope',
+ function duoSignedResponseController($scope) {
+
+ /**
+ * The iframe which contains the Duo authentication interface.
+ *
+ * @type HTMLIFrameElement
+ */
+ var iframe = $('.duo-signature-response-field iframe')[0];
+
+ /**
+ * Whether the Duo interface has finished loading within the iframe.
+ *
+ * @type Boolean
+ */
+ $scope.duoInterfaceLoaded = false;
+
+ /**
+ * Submits the signed response from Duo once the user has authenticated.
+ * This is a callback invoked by the DuoWeb API after the user has been
+ * verified and the signed response has been received.
+ *
+ * @param {HTMLFormElement} form
+ * The form element provided by the DuoWeb API containing the signed
+ * response as the value of an input field named "sig_response".
+ */
+ var submitSignedResponse = function submitSignedResponse(form) {
+
+ // Update model to match received response
+ $scope.$apply(function updateModel() {
+ $scope.model = form.elements['sig_response'].value;
+ });
+
+ // Submit updated credentials
+ $(iframe).parents('form').submit();
+
+ };
+
+ // Update Duo loaded state when iframe finishes loading
+ iframe.onload = function duoLoaded() {
+ $scope.$apply(function updateLoadedState() {
+ $scope.duoInterfaceLoaded = true;
+ });
+ };
+
+ // Initialize Duo interface within iframe
+ Duo.init({
+ iframe : iframe,
+ host : $scope.field.apiHost,
+ sig_request : $scope.field.signedRequest,
+ submit_callback : submitSignedResponse
+ });
+
+}]);
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/resources/duoModule.js
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/duoModule.js b/extensions/guacamole-auth-duo/src/main/resources/duoModule.js
new file mode 100644
index 0000000..49a342f
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/resources/duoModule.js
@@ -0,0 +1,28 @@
+/*
+ * 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 Duo multi-factor authentication.
+ */
+angular.module('guacDuo', [
+ 'form'
+]);
+
+// Ensure the guacDuo module is loaded along with the rest of the app
+angular.module('index').requires.push('guacDuo');
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json
new file mode 100644
index 0000000..ff8fab2
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json
@@ -0,0 +1,35 @@
+{
+
+ "guacamoleVersion" : "0.9.10-incubating",
+
+ "name" : "Duo TFA Authentication Backend",
+ "namespace" : "duo",
+
+ "authProviders" : [
+ "org.apache.guacamole.auth.duo.DuoAuthenticationProvider"
+ ],
+
+ "translations" : [
+ "translations/en.json"
+ ],
+
+ "js" : [
+
+ "duoModule.js",
+ "controllers/duoSignedResponseController.js",
+ "config/duoConfig.js",
+
+ "lib/DuoWeb/LICENSE.js",
+ "lib/DuoWeb/Duo-Web-v2.js"
+
+ ],
+
+ "css" : [
+ "styles/duo.css"
+ ],
+
+ "resources" : {
+ "templates/duoSignedResponseField.html" : "text/html"
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/resources/lib/DuoWeb/Duo-Web-v2.js
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/lib/DuoWeb/Duo-Web-v2.js b/extensions/guacamole-auth-duo/src/main/resources/lib/DuoWeb/Duo-Web-v2.js
new file mode 100644
index 0000000..a02a957
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/resources/lib/DuoWeb/Duo-Web-v2.js
@@ -0,0 +1,366 @@
+/**
+ * Duo Web SDK v2
+ * Copyright 2015, Duo Security
+ */
+window.Duo = (function(document, window) {
+ var DUO_MESSAGE_FORMAT = /^(?:AUTH|ENROLL)+\|[A-Za-z0-9\+\/=]+\|[A-Za-z0-9\+\/=]+$/;
+ var DUO_ERROR_FORMAT = /^ERR\|[\w\s\.\(\)]+$/;
+
+ var iframeId = 'duo_iframe',
+ postAction = '',
+ postArgument = 'sig_response',
+ host,
+ sigRequest,
+ duoSig,
+ appSig,
+ iframe,
+ submitCallback;
+
+ function throwError(message, url) {
+ throw new Error(
+ 'Duo Web SDK error: ' + message +
+ (url ? ('\n' + 'See ' + url + ' for more information') : '')
+ );
+ }
+
+ function hyphenize(str) {
+ return str.replace(/([a-z])([A-Z])/, '$1-$2').toLowerCase();
+ }
+
+ // cross-browser data attributes
+ function getDataAttribute(element, name) {
+ if ('dataset' in element) {
+ return element.dataset[name];
+ } else {
+ return element.getAttribute('data-' + hyphenize(name));
+ }
+ }
+
+ // cross-browser event binding/unbinding
+ function on(context, event, fallbackEvent, callback) {
+ if ('addEventListener' in window) {
+ context.addEventListener(event, callback, false);
+ } else {
+ context.attachEvent(fallbackEvent, callback);
+ }
+ }
+
+ function off(context, event, fallbackEvent, callback) {
+ if ('removeEventListener' in window) {
+ context.removeEventListener(event, callback, false);
+ } else {
+ context.detachEvent(fallbackEvent, callback);
+ }
+ }
+
+ function onReady(callback) {
+ on(document, 'DOMContentLoaded', 'onreadystatechange', callback);
+ }
+
+ function offReady(callback) {
+ off(document, 'DOMContentLoaded', 'onreadystatechange', callback);
+ }
+
+ function onMessage(callback) {
+ on(window, 'message', 'onmessage', callback);
+ }
+
+ function offMessage(callback) {
+ off(window, 'message', 'onmessage', callback);
+ }
+
+ /**
+ * Parse the sig_request parameter, throwing errors if the token contains
+ * a server error or if the token is invalid.
+ *
+ * @param {String} sig Request token
+ */
+ function parseSigRequest(sig) {
+ if (!sig) {
+ // nothing to do
+ return;
+ }
+
+ // see if the token contains an error, throwing it if it does
+ if (sig.indexOf('ERR|') === 0) {
+ throwError(sig.split('|')[1]);
+ }
+
+ // validate the token
+ if (sig.indexOf(':') === -1 || sig.split(':').length !== 2) {
+ throwError(
+ 'Duo was given a bad token. This might indicate a configuration ' +
+ 'problem with one of Duo\'s client libraries.',
+ 'https://www.duosecurity.com/docs/duoweb#first-steps'
+ );
+ }
+
+ var sigParts = sig.split(':');
+
+ // hang on to the token, and the parsed duo and app sigs
+ sigRequest = sig;
+ duoSig = sigParts[0];
+ appSig = sigParts[1];
+
+ return {
+ sigRequest: sig,
+ duoSig: sigParts[0],
+ appSig: sigParts[1]
+ };
+ }
+
+ /**
+ * This function is set up to run when the DOM is ready, if the iframe was
+ * not available during `init`.
+ */
+ function onDOMReady() {
+ iframe = document.getElementById(iframeId);
+
+ if (!iframe) {
+ throw new Error(
+ 'This page does not contain an iframe for Duo to use.' +
+ 'Add an element like <iframe id="duo_iframe"></iframe> ' +
+ 'to this page. ' +
+ 'See https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe ' +
+ 'for more information.'
+ );
+ }
+
+ // we've got an iframe, away we go!
+ ready();
+
+ // always clean up after yourself
+ offReady(onDOMReady);
+ }
+
+ /**
+ * Validate that a MessageEvent came from the Duo service, and that it
+ * is a properly formatted payload.
+ *
+ * The Google Chrome sign-in page injects some JS into pages that also
+ * make use of postMessage, so we need to do additional validation above
+ * and beyond the origin.
+ *
+ * @param {MessageEvent} event Message received via postMessage
+ */
+ function isDuoMessage(event) {
+ return Boolean(
+ event.origin === ('https://' + host) &&
+ typeof event.data === 'string' &&
+ (
+ event.data.match(DUO_MESSAGE_FORMAT) ||
+ event.data.match(DUO_ERROR_FORMAT)
+ )
+ );
+ }
+
+ /**
+ * Validate the request token and prepare for the iframe to become ready.
+ *
+ * All options below can be passed into an options hash to `Duo.init`, or
+ * specified on the iframe using `data-` attributes.
+ *
+ * Options specified using the options hash will take precedence over
+ * `data-` attributes.
+ *
+ * Example using options hash:
+ * ```javascript
+ * Duo.init({
+ * iframe: "some_other_id",
+ * host: "api-main.duo.test",
+ * sig_request: "...",
+ * post_action: "/auth",
+ * post_argument: "resp"
+ * });
+ * ```
+ *
+ * Example using `data-` attributes:
+ * ```
+ * <iframe id="duo_iframe"
+ * data-host="api-main.duo.test"
+ * data-sig-request="..."
+ * data-post-action="/auth"
+ * data-post-argument="resp"
+ * >
+ * </iframe>
+ * ```
+ *
+ * @param {Object} options
+ * @param {String} options.iframe The iframe, or id of an iframe to set up
+ * @param {String} options.host Hostname
+ * @param {String} options.sig_request Request token
+ * @param {String} [options.post_action=''] URL to POST back to after successful auth
+ * @param {String} [options.post_argument='sig_response'] Parameter name to use for response token
+ * @param {Function} [options.submit_callback] If provided, duo will not submit the form instead execute
+ * the callback function with reference to the "duo_form" form object
+ * submit_callback can be used to prevent the webpage from reloading.
+ */
+ function init(options) {
+ if (options) {
+ if (options.host) {
+ host = options.host;
+ }
+
+ if (options.sig_request) {
+ parseSigRequest(options.sig_request);
+ }
+
+ if (options.post_action) {
+ postAction = options.post_action;
+ }
+
+ if (options.post_argument) {
+ postArgument = options.post_argument;
+ }
+
+ if (options.iframe) {
+ if ('tagName' in options.iframe) {
+ iframe = options.iframe;
+ } else if (typeof options.iframe === 'string') {
+ iframeId = options.iframe;
+ }
+ }
+
+ if (typeof options.submit_callback === 'function') {
+ submitCallback = options.submit_callback;
+ }
+ }
+
+ // if we were given an iframe, no need to wait for the rest of the DOM
+ if (iframe) {
+ ready();
+ } else {
+ // try to find the iframe in the DOM
+ iframe = document.getElementById(iframeId);
+
+ // iframe is in the DOM, away we go!
+ if (iframe) {
+ ready();
+ } else {
+ // wait until the DOM is ready, then try again
+ onReady(onDOMReady);
+ }
+ }
+
+ // always clean up after yourself!
+ offReady(init);
+ }
+
+ /**
+ * This function is called when a message was received from another domain
+ * using the `postMessage` API. Check that the event came from the Duo
+ * service domain, and that the message is a properly formatted payload,
+ * then perform the post back to the primary service.
+ *
+ * @param event Event object (contains origin and data)
+ */
+ function onReceivedMessage(event) {
+ if (isDuoMessage(event)) {
+ // the event came from duo, do the post back
+ doPostBack(event.data);
+
+ // always clean up after yourself!
+ offMessage(onReceivedMessage);
+ }
+ }
+
+ /**
+ * Point the iframe at Duo, then wait for it to postMessage back to us.
+ */
+ function ready() {
+ if (!host) {
+ host = getDataAttribute(iframe, 'host');
+
+ if (!host) {
+ throwError(
+ 'No API hostname is given for Duo to use. Be sure to pass ' +
+ 'a `host` parameter to Duo.init, or through the `data-host` ' +
+ 'attribute on the iframe element.',
+ 'https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe'
+ );
+ }
+ }
+
+ if (!duoSig || !appSig) {
+ parseSigRequest(getDataAttribute(iframe, 'sigRequest'));
+
+ if (!duoSig || !appSig) {
+ throwError(
+ 'No valid signed request is given. Be sure to give the ' +
+ '`sig_request` parameter to Duo.init, or use the ' +
+ '`data-sig-request` attribute on the iframe element.',
+ 'https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe'
+ );
+ }
+ }
+
+ // if postAction/Argument are defaults, see if they are specified
+ // as data attributes on the iframe
+ if (postAction === '') {
+ postAction = getDataAttribute(iframe, 'postAction') || postAction;
+ }
+
+ if (postArgument === 'sig_response') {
+ postArgument = getDataAttribute(iframe, 'postArgument') || postArgument;
+ }
+
+ // point the iframe at Duo
+ iframe.src = [
+ 'https://', host, '/frame/web/v1/auth?tx=', duoSig,
+ '&parent=', encodeURIComponent(document.location.href),
+ '&v=2.3'
+ ].join('');
+
+ // listen for the 'message' event
+ onMessage(onReceivedMessage);
+ }
+
+ /**
+ * We received a postMessage from Duo. POST back to the primary service
+ * with the response token, and any additional user-supplied parameters
+ * given in form#duo_form.
+ */
+ function doPostBack(response) {
+ // create a hidden input to contain the response token
+ var input = document.createElement('input');
+ input.type = 'hidden';
+ input.name = postArgument;
+ input.value = response + ':' + appSig;
+
+ // user may supply their own form with additional inputs
+ var form = document.getElementById('duo_form');
+
+ // if the form doesn't exist, create one
+ if (!form) {
+ form = document.createElement('form');
+
+ // insert the new form after the iframe
+ iframe.parentElement.insertBefore(form, iframe.nextSibling);
+ }
+
+ // make sure we are actually posting to the right place
+ form.method = 'POST';
+ form.action = postAction;
+
+ // add the response token input to the form
+ form.appendChild(input);
+
+ // away we go!
+ if (typeof submitCallback === "function") {
+ submitCallback.call(null, form);
+ } else {
+ form.submit();
+ }
+ }
+
+ // when the DOM is ready, initialize
+ // note that this will get cleaned up if the user calls init directly!
+ onReady(init);
+
+ return {
+ init: init,
+ _parseSigRequest: parseSigRequest,
+ _isDuoMessage: isDuoMessage,
+ _doPostBack: doPostBack
+ };
+}(document, window));
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/resources/lib/DuoWeb/LICENSE.js
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/lib/DuoWeb/LICENSE.js b/extensions/guacamole-auth-duo/src/main/resources/lib/DuoWeb/LICENSE.js
new file mode 100644
index 0000000..58ead21
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/resources/lib/DuoWeb/LICENSE.js
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011, Duo Security, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css b/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css
new file mode 100644
index 0000000..36d6031
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+.duo-signature-response-field iframe {
+ width: 100%;
+ max-width: 620px;
+ height: 330px;
+ border: none;
+}
+
+.duo-signature-response-field iframe {
+ opacity: 1;
+ -webkit-transition: opacity 0.125s;
+ -moz-transition: opacity 0.125s;
+ -ms-transition: opacity 0.125s;
+ -o-transition: opacity 0.125s;
+ transition: opacity 0.125s;
+}
+
+.duo-signature-response-field.loading iframe {
+ opacity: 0;
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html b/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html
new file mode 100644
index 0000000..4658ed0
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html
@@ -0,0 +1,3 @@
+<div class="duo-signature-response-field" ng-class="{ loading : !duoInterfaceLoaded }">
+ <iframe></iframe>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/resources/translations/en.json
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/translations/en.json b/extensions/guacamole-auth-duo/src/main/resources/translations/en.json
new file mode 100644
index 0000000..8682cba
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/resources/translations/en.json
@@ -0,0 +1,13 @@
+{
+
+ "DATA_SOURCE_DUO" : {
+ "NAME" : "Duo TFA Backend"
+ },
+
+ "LOGIN" : {
+ "FIELD_HEADER_GUAC_DUO_SIGNED_RESPONSE" : "",
+ "INFO_DUO_VALIDATION_CODE_INCORRECT" : "Duo validation code incorrect.",
+ "INFO_DUO_AUTH_REQUIRED" : "Please authenticate with Duo to continue."
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 99aed8b..adad175 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,7 @@
<module>guacamole-common-js</module>
<!-- Authentication extensions -->
+ <module>extensions/guacamole-auth-duo</module>
<module>extensions/guacamole-auth-jdbc</module>
<module>extensions/guacamole-auth-ldap</module>
<module>extensions/guacamole-auth-noauth</module>
[11/12] incubator-guacamole-client git commit: GUACAMOLE-136: Use
$element to retrieve field elements rather than straight jQuery.
Posted by jm...@apache.org.
GUACAMOLE-136: Use $element to retrieve field elements rather than straight jQuery.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/a0f9bf54
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/a0f9bf54
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/a0f9bf54
Branch: refs/heads/master
Commit: a0f9bf541feee5cd65bc866723637bfb65555abb
Parents: e6f0b7d
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Dec 5 22:08:49 2016 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Dec 5 22:20:24 2016 -0800
----------------------------------------------------------------------
.../controllers/duoSignedResponseController.js | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/a0f9bf54/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js b/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js
index f683698..b4ca4f3 100644
--- a/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js
+++ b/extensions/guacamole-auth-duo/src/main/resources/controllers/duoSignedResponseController.js
@@ -22,15 +22,23 @@
* API to prompt the user for additional credentials, ultimately receiving a
* signed response from the Duo service.
*/
-angular.module('guacDuo').controller('duoSignedResponseController', ['$scope',
- function duoSignedResponseController($scope) {
+angular.module('guacDuo').controller('duoSignedResponseController', ['$scope', '$element',
+ function duoSignedResponseController($scope, $element) {
/**
* The iframe which contains the Duo authentication interface.
*
* @type HTMLIFrameElement
*/
- var iframe = $('.duo-signature-response-field iframe')[0];
+ var iframe = $element.find('iframe')[0];
+
+ /**
+ * The submit button which should be used to submit the login form once
+ * the Duo response has been received.
+ *
+ * @type HTMLInputElement
+ */
+ var submit = $element.find('input[type="submit"]')[0];
/**
* Whether the Duo interface has finished loading within the iframe.
@@ -56,7 +64,7 @@ angular.module('guacDuo').controller('duoSignedResponseController', ['$scope',
});
// Submit updated credentials
- $('.duo-signature-response-field input[type="submit"]').click();
+ submit.click();
};
[03/12] incubator-guacamole-client git commit: GUACAMOLE-136: Fix
Carlito LICENSE path.
Posted by jm...@apache.org.
GUACAMOLE-136: Fix Carlito LICENSE path.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/00a2539e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/00a2539e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/00a2539e
Branch: refs/heads/master
Commit: 00a2539e9c5a9948191fdb00611a656677466512
Parents: e9e8612
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Dec 5 19:57:59 2016 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Dec 5 22:19:45 2016 -0800
----------------------------------------------------------------------
LICENSE | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/00a2539e/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
index b01b683..82b98eb 100644
--- a/LICENSE
+++ b/LICENSE
@@ -217,5 +217,5 @@ Carlito (http://code.google.com/p/chromium/issues/detail?id=280557)
Version: N/A
From: 'tyPoland Lukasz Dziedzic' (http://www.lukaszdziedzic.eu/)
License(s):
- SIL Open Font (guacamole/src/dependencies/carlito/LICENSE)
+ SIL Open Font (guacamole/src/licenses/bundled/carlito/LICENSE)
[02/12] incubator-guacamole-client git commit: GUACAMOLE-136: Add
licenses for libraries bundled with guacamole-auth-duo.
Posted by jm...@apache.org.
GUACAMOLE-136: Add licenses for libraries bundled with guacamole-auth-duo.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/bf4446a1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/bf4446a1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/bf4446a1
Branch: refs/heads/master
Commit: bf4446a199c0fd1b55823ce12a610a5bdf06a772
Parents: 00a2539
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Dec 5 20:04:53 2016 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Dec 5 22:19:45 2016 -0800
----------------------------------------------------------------------
LICENSE | 35 +++
.../guacamole-auth-duo/src/licenses/DISCLAIMER | 7 +
.../guacamole-auth-duo/src/licenses/LICENSE | 274 +++++++++++++++++++
.../guacamole-auth-duo/src/licenses/NOTICE | 5 +
.../src/licenses/bundled/README | 4 +
.../licenses/bundled/aopalliance-1.0/LICENSE | 4 +
.../licenses/bundled/duo-java-56570d1/LICENSE | 25 ++
.../src/licenses/bundled/guice-3.0/COPYING | 202 ++++++++++++++
.../bundled/javax.inject-1/LICENSE-2.0.txt | 202 ++++++++++++++
9 files changed, 758 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bf4446a1/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
index 82b98eb..2c1ab31 100644
--- a/LICENSE
+++ b/LICENSE
@@ -219,3 +219,38 @@ Carlito (http://code.google.com/p/chromium/issues/detail?id=280557)
License(s):
SIL Open Font (guacamole/src/licenses/bundled/carlito/LICENSE)
+
+DuoWeb Java SDK (https://github.com/duosecurity/duo_java)
+---------------------------------------------------------
+
+ Verson: commit 56570d18a1f98642500e6c1158a3f41e448963ff
+ From: `Duo Security, Inc.' (https://duo.com/)
+ License(s):
+ BSD 3-clause (extensions/guacamole-auth-duo/src/licenses/bundled/duo-java-56570d1/LICENSE)
+
+Copyright (c) 2011, Duo Security, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bf4446a1/extensions/guacamole-auth-duo/src/licenses/DISCLAIMER
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/licenses/DISCLAIMER b/extensions/guacamole-auth-duo/src/licenses/DISCLAIMER
new file mode 100644
index 0000000..1a9c3be
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/licenses/DISCLAIMER
@@ -0,0 +1,7 @@
+Apache Guacamole is an effort undergoing incubation at The Apache Software
+Foundation (ASF). Incubation is required of all newly accepted projects until a
+further review indicates that the infrastructure, communications, and decision
+making process have stabilized in a manner consistent with other successful ASF
+projects. While incubation status is not necessarily a reflection of the
+completeness or stability of the code, it does indicate that the project has
+yet to be fully endorsed by the ASF.
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bf4446a1/extensions/guacamole-auth-duo/src/licenses/LICENSE
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/licenses/LICENSE b/extensions/guacamole-auth-duo/src/licenses/LICENSE
new file mode 100644
index 0000000..4ae92c4
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/licenses/LICENSE
@@ -0,0 +1,274 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
+
+
+==============================================================================
+
+APACHE GUACAMOLE SUBCOMPONENTS
+
+Apache Guacamole includes a number of subcomponents with separate copyright
+notices and license terms. Your use of these subcomponents is subject to the
+terms and conditions of the following licenses.
+
+
+AOP Alliance (http://aopalliance.sourceforge.net/)
+--------------------------------------------------
+
+ Version: 1.0
+ From: 'AOP Alliance' (http://aopalliance.sourceforge.net/members.html)
+ License(s):
+ Public Domain (bundled/aopalliance-1.0/LICENSE)
+
+
+DuoWeb Java SDK (https://github.com/duosecurity/duo_java)
+---------------------------------------------------------
+
+ Verson: commit 56570d18a1f98642500e6c1158a3f41e448963ff
+ From: `Duo Security, Inc.' (https://duo.com/)
+ License(s):
+ BSD 3-clause (bundled/duo-java-56570d1/LICENSE)
+
+Copyright (c) 2011, Duo Security, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+Google Guice (https://github.com/google/guice)
+----------------------------------------------
+
+ Version: 3.0
+ From: 'Google Inc.' (http://www.google.com/)
+ License(s):
+ Apache v2.0 (bundled/guice-3.0/COPYING)
+
+
+JSR-330 / Dependency Injection for Java (http://code.google.com/p/atinject/)
+----------------------------------------------------------------------------
+
+ Version: 1
+ From: 'JSR-330 Expert Group' (https://jcp.org/en/jsr/detail?id=330)
+ License(s):
+ Apache v2.0 (bundled/javax.inject-1/LICENSE-2.0.txt)
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bf4446a1/extensions/guacamole-auth-duo/src/licenses/NOTICE
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/licenses/NOTICE b/extensions/guacamole-auth-duo/src/licenses/NOTICE
new file mode 100644
index 0000000..2ef7e54
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/licenses/NOTICE
@@ -0,0 +1,5 @@
+Apache Guacamole
+Copyright 2016 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bf4446a1/extensions/guacamole-auth-duo/src/licenses/bundled/README
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/licenses/bundled/README b/extensions/guacamole-auth-duo/src/licenses/bundled/README
new file mode 100644
index 0000000..47ba19d
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/licenses/bundled/README
@@ -0,0 +1,4 @@
+Apache Guacamole includes a number of subcomponents with separate copyright
+notices and license terms. Your use of these subcomponents is subject to the
+terms and conditions of their respective licenses, included within this
+directory for reference.
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bf4446a1/extensions/guacamole-auth-duo/src/licenses/bundled/aopalliance-1.0/LICENSE
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/licenses/bundled/aopalliance-1.0/LICENSE b/extensions/guacamole-auth-duo/src/licenses/bundled/aopalliance-1.0/LICENSE
new file mode 100644
index 0000000..8e0e378
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/licenses/bundled/aopalliance-1.0/LICENSE
@@ -0,0 +1,4 @@
+From http://aopalliance.sourceforge.net/:
+
+ LICENCE: all the source code provided by AOP Alliance is Public Domain.
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bf4446a1/extensions/guacamole-auth-duo/src/licenses/bundled/duo-java-56570d1/LICENSE
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/licenses/bundled/duo-java-56570d1/LICENSE b/extensions/guacamole-auth-duo/src/licenses/bundled/duo-java-56570d1/LICENSE
new file mode 100644
index 0000000..66e1f74
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/licenses/bundled/duo-java-56570d1/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2011, Duo Security, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bf4446a1/extensions/guacamole-auth-duo/src/licenses/bundled/guice-3.0/COPYING
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/licenses/bundled/guice-3.0/COPYING b/extensions/guacamole-auth-duo/src/licenses/bundled/guice-3.0/COPYING
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/licenses/bundled/guice-3.0/COPYING
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bf4446a1/extensions/guacamole-auth-duo/src/licenses/bundled/javax.inject-1/LICENSE-2.0.txt
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/licenses/bundled/javax.inject-1/LICENSE-2.0.txt b/extensions/guacamole-auth-duo/src/licenses/bundled/javax.inject-1/LICENSE-2.0.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/licenses/bundled/javax.inject-1/LICENSE-2.0.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
[10/12] incubator-guacamole-client git commit: GUACAMOLE-136: Rename
overly-generic AuthenticationProviderService to UserVerificationService.
Posted by jm...@apache.org.
GUACAMOLE-136: Rename overly-generic AuthenticationProviderService to UserVerificationService.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/e6f0b7df
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/e6f0b7df
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/e6f0b7df
Branch: refs/heads/master
Commit: e6f0b7df179266ba2b023b5cbaa24951c7b5d6ac
Parents: c3c553a
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Dec 5 21:33:50 2016 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Dec 5 22:19:46 2016 -0800
----------------------------------------------------------------------
.../auth/duo/AuthenticationProviderService.java | 109 -------------------
.../auth/duo/DuoAuthenticationProvider.java | 6 +-
.../duo/DuoAuthenticationProviderModule.java | 2 +-
.../auth/duo/UserVerificationService.java | 108 ++++++++++++++++++
4 files changed, 112 insertions(+), 113 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/e6f0b7df/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/AuthenticationProviderService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/AuthenticationProviderService.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/AuthenticationProviderService.java
deleted file mode 100644
index c3dd8ee..0000000
--- a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/AuthenticationProviderService.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.duo;
-
-import com.google.inject.Inject;
-import java.util.Collections;
-import javax.servlet.http.HttpServletRequest;
-import org.apache.guacamole.GuacamoleClientException;
-import org.apache.guacamole.GuacamoleException;
-import org.apache.guacamole.auth.duo.conf.ConfigurationService;
-import org.apache.guacamole.auth.duo.form.DuoSignedResponseField;
-import org.apache.guacamole.form.Field;
-import org.apache.guacamole.net.auth.AuthenticatedUser;
-import org.apache.guacamole.net.auth.Credentials;
-import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
-import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
-
-/**
- * Service providing convenience functions for the Duo AuthenticationProvider
- * implementation.
- */
-public class AuthenticationProviderService {
-
- /**
- * Service for retrieving Duo configuration information.
- */
- @Inject
- private ConfigurationService confService;
-
- /**
- * Service for verifying users with the DuoWeb API.
- */
- @Inject
- private DuoWebService duoWebService;
-
- /**
- * Verifies the identity of the given user via the Duo multi-factor
- * authentication service. If a signed response from Duo has not already
- * been provided, a signed response from Duo is requested in the
- * form of additional expected credentials. Any provided signed response
- * is cryptographically verified. If no signed response is present, or the
- * signed response is invalid, an exception is thrown.
- *
- * @param authenticatedUser
- * The user whose identity should be verified against Duo.
- *
- * @throws GuacamoleException
- * If required Duo-specific configuration options are missing or
- * malformed, or if the user's identity cannot be verified.
- */
- public void verifyAuthenticatedUser(AuthenticatedUser authenticatedUser)
- throws GuacamoleException {
-
- // Pull the original HTTP request used to authenticate
- Credentials credentials = authenticatedUser.getCredentials();
- HttpServletRequest request = credentials.getRequest();
-
- // Ignore anonymous users
- if (authenticatedUser.getIdentifier().equals(AuthenticatedUser.ANONYMOUS_IDENTIFIER))
- return;
-
- // Retrieve signed Duo response from request
- String signedResponse = request.getParameter(DuoSignedResponseField.PARAMETER_NAME);
-
- // If no signed response, request one
- if (signedResponse == null) {
-
- // Create field which requests a signed response from Duo that
- // verifies the identity of the given user via the configured
- // Duo API endpoint
- Field signedResponseField = new DuoSignedResponseField(
- confService.getAPIHostname(),
- duoWebService.createSignedRequest(authenticatedUser));
-
- // Create an overall description of the additional credentials
- // required to verify identity
- CredentialsInfo expectedCredentials = new CredentialsInfo(
- Collections.singletonList(signedResponseField));
-
- // Request additional credentials
- throw new GuacamoleInsufficientCredentialsException(
- "LOGIN.INFO_DUO_AUTH_REQUIRED", expectedCredentials);
-
- }
-
- // If signed response does not verify this user's identity, abort auth
- if (!duoWebService.isValidSignedResponse(authenticatedUser, signedResponse))
- throw new GuacamoleClientException("LOGIN.INFO_DUO_VALIDATION_CODE_INCORRECT");
-
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/e6f0b7df/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java
index bcf8c83..ccb1c40 100644
--- a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java
+++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java
@@ -78,11 +78,11 @@ public class DuoAuthenticationProvider implements AuthenticationProvider {
public UserContext getUserContext(AuthenticatedUser authenticatedUser)
throws GuacamoleException {
- AuthenticationProviderService authProviderService =
- injector.getInstance(AuthenticationProviderService.class);
+ UserVerificationService verificationService =
+ injector.getInstance(UserVerificationService.class);
// Verify user against Duo service
- authProviderService.verifyAuthenticatedUser(authenticatedUser);
+ verificationService.verifyAuthenticatedUser(authenticatedUser);
// User has been verified, and authentication should be allowed to
// continue
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/e6f0b7df/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProviderModule.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProviderModule.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProviderModule.java
index c3c129c..2dfc4eb 100644
--- a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProviderModule.java
+++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProviderModule.java
@@ -72,9 +72,9 @@ public class DuoAuthenticationProviderModule extends AbstractModule {
bind(Environment.class).toInstance(environment);
// Bind Duo-specific services
- bind(AuthenticationProviderService.class);
bind(ConfigurationService.class);
bind(DuoWebService.class);
+ bind(UserVerificationService.class);
}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/e6f0b7df/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/UserVerificationService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/UserVerificationService.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/UserVerificationService.java
new file mode 100644
index 0000000..3209be2
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/UserVerificationService.java
@@ -0,0 +1,108 @@
+/*
+ * 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.duo;
+
+import com.google.inject.Inject;
+import java.util.Collections;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.guacamole.GuacamoleClientException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.auth.duo.conf.ConfigurationService;
+import org.apache.guacamole.auth.duo.form.DuoSignedResponseField;
+import org.apache.guacamole.form.Field;
+import org.apache.guacamole.net.auth.AuthenticatedUser;
+import org.apache.guacamole.net.auth.Credentials;
+import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
+import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
+
+/**
+ * Service for verifying the identity of a user against Duo.
+ */
+public class UserVerificationService {
+
+ /**
+ * Service for retrieving Duo configuration information.
+ */
+ @Inject
+ private ConfigurationService confService;
+
+ /**
+ * Service for verifying users with the DuoWeb API.
+ */
+ @Inject
+ private DuoWebService duoWebService;
+
+ /**
+ * Verifies the identity of the given user via the Duo multi-factor
+ * authentication service. If a signed response from Duo has not already
+ * been provided, a signed response from Duo is requested in the
+ * form of additional expected credentials. Any provided signed response
+ * is cryptographically verified. If no signed response is present, or the
+ * signed response is invalid, an exception is thrown.
+ *
+ * @param authenticatedUser
+ * The user whose identity should be verified against Duo.
+ *
+ * @throws GuacamoleException
+ * If required Duo-specific configuration options are missing or
+ * malformed, or if the user's identity cannot be verified.
+ */
+ public void verifyAuthenticatedUser(AuthenticatedUser authenticatedUser)
+ throws GuacamoleException {
+
+ // Pull the original HTTP request used to authenticate
+ Credentials credentials = authenticatedUser.getCredentials();
+ HttpServletRequest request = credentials.getRequest();
+
+ // Ignore anonymous users
+ if (authenticatedUser.getIdentifier().equals(AuthenticatedUser.ANONYMOUS_IDENTIFIER))
+ return;
+
+ // Retrieve signed Duo response from request
+ String signedResponse = request.getParameter(DuoSignedResponseField.PARAMETER_NAME);
+
+ // If no signed response, request one
+ if (signedResponse == null) {
+
+ // Create field which requests a signed response from Duo that
+ // verifies the identity of the given user via the configured
+ // Duo API endpoint
+ Field signedResponseField = new DuoSignedResponseField(
+ confService.getAPIHostname(),
+ duoWebService.createSignedRequest(authenticatedUser));
+
+ // Create an overall description of the additional credentials
+ // required to verify identity
+ CredentialsInfo expectedCredentials = new CredentialsInfo(
+ Collections.singletonList(signedResponseField));
+
+ // Request additional credentials
+ throw new GuacamoleInsufficientCredentialsException(
+ "LOGIN.INFO_DUO_AUTH_REQUIRED", expectedCredentials);
+
+ }
+
+ // If signed response does not verify this user's identity, abort auth
+ if (!duoWebService.isValidSignedResponse(authenticatedUser, signedResponse))
+ throw new GuacamoleClientException("LOGIN.INFO_DUO_VALIDATION_CODE_INCORRECT");
+
+ }
+
+}
[08/12] incubator-guacamole-client git commit: GUACAMOLE-136: Render
Duo field as modal dialog.
Posted by jm...@apache.org.
GUACAMOLE-136: Render Duo field as modal dialog.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/f44ee1c9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/f44ee1c9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/f44ee1c9
Branch: refs/heads/master
Commit: f44ee1c95b62da184e306a8957421c4b84b5263d
Parents: 548966a
Author: Michael Jumper <mj...@apache.org>
Authored: Fri Dec 2 00:49:21 2016 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Dec 5 22:19:45 2016 -0800
----------------------------------------------------------------------
.../src/main/resources/styles/duo.css | 20 ++++++++++++++++++++
.../templates/duoSignedResponseField.html | 8 +++++---
2 files changed, 25 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/f44ee1c9/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css b/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css
index db49ab3..6d01a85 100644
--- a/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css
+++ b/extensions/guacamole-auth-duo/src/main/resources/styles/duo.css
@@ -17,6 +17,23 @@
* under the License.
*/
+
+.duo-signature-response-field-container {
+ height: 100%;
+ width: 100%;
+ position: fixed;
+ left: 0;
+ top: 0;
+ display: table;
+ background: white;
+}
+
+.duo-signature-response-field {
+ width: 100%;
+ display: table-cell;
+ vertical-align: middle;
+}
+
.duo-signature-response-field input[type="submit"] {
display: none !important;
}
@@ -26,6 +43,9 @@
max-width: 620px;
height: 330px;
border: none;
+ box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.5);
+ display: block;
+ margin: 1.5em auto;
}
.duo-signature-response-field iframe {
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/f44ee1c9/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html b/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html
index 622bd42..e51e190 100644
--- a/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html
+++ b/extensions/guacamole-auth-duo/src/main/resources/templates/duoSignedResponseField.html
@@ -1,4 +1,6 @@
-<div class="duo-signature-response-field" ng-class="{ loading : !duoInterfaceLoaded }">
- <iframe></iframe>
- <input type="submit">
+<div class="duo-signature-response-field-container">
+ <div class="duo-signature-response-field" ng-class="{ loading : !duoInterfaceLoaded }">
+ <iframe></iframe>
+ <input type="submit">
+ </div>
</div>
[06/12] incubator-guacamole-client git commit: GUACAMOLE-136: Minify
JavaScript and CSS. Add templates to Angular's cache.
Posted by jm...@apache.org.
GUACAMOLE-136: Minify JavaScript and CSS. Add templates to Angular's cache.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/e9e86126
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/e9e86126
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/e9e86126
Branch: refs/heads/master
Commit: e9e8612611fe017862fc25957f904d8ae34baede
Parents: f44ee1c
Author: Michael Jumper <mj...@apache.org>
Authored: Fri Dec 2 16:02:47 2016 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Dec 5 22:19:45 2016 -0800
----------------------------------------------------------------------
extensions/guacamole-auth-duo/.gitignore | 1 +
extensions/guacamole-auth-duo/pom.xml | 80 ++++++++++++++++++++
.../src/main/resources/guac-manifest.json | 11 +--
.../src/main/resources/license.txt | 18 +++++
4 files changed, 101 insertions(+), 9 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/e9e86126/extensions/guacamole-auth-duo/.gitignore
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/.gitignore b/extensions/guacamole-auth-duo/.gitignore
index 42f4a1a..1de9633 100644
--- a/extensions/guacamole-auth-duo/.gitignore
+++ b/extensions/guacamole-auth-duo/.gitignore
@@ -1,2 +1,3 @@
+src/main/resources/generated/
target/
*~
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/e9e86126/extensions/guacamole-auth-duo/pom.xml
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/pom.xml b/extensions/guacamole-auth-duo/pom.xml
index 4c1fdf6..a55e54f 100644
--- a/extensions/guacamole-auth-duo/pom.xml
+++ b/extensions/guacamole-auth-duo/pom.xml
@@ -34,6 +34,86 @@
</configuration>
</plugin>
+ <!-- Pre-cache Angular templates with maven-angular-plugin -->
+ <plugin>
+ <groupId>com.keithbranton.mojo</groupId>
+ <artifactId>angular-maven-plugin</artifactId>
+ <version>0.3.2</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/duo</prefix>
+ </configuration>
+ </plugin>
+
+ <!-- JS/CSS Minification Plugin -->
+ <plugin>
+ <groupId>com.samaxes.maven</groupId>
+ <artifactId>minify-maven-plugin</artifactId>
+ <version>1.7.5</version>
+ <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>duo.css</cssFinalFile>
+
+ <cssSourceFiles>
+ <cssSourceFile>license.txt</cssSourceFile>
+ </cssSourceFiles>
+
+ <cssSourceIncludes>
+ <cssSourceInclude>**/*.css</cssSourceInclude>
+ </cssSourceIncludes>
+
+ <jsSourceDir>/</jsSourceDir>
+ <jsTargetDir>/</jsTargetDir>
+ <jsFinalFile>duo.js</jsFinalFile>
+
+ <jsSourceFiles>
+ <jsSourceFile>license.txt</jsSourceFile>
+ <jsSourceFile>lib/DuoWeb/LICENSE.js</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>
+
<!-- Copy dependencies prior to packaging -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/e9e86126/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json
index ff8fab2..27658a8 100644
--- a/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json
@@ -14,18 +14,11 @@
],
"js" : [
-
- "duoModule.js",
- "controllers/duoSignedResponseController.js",
- "config/duoConfig.js",
-
- "lib/DuoWeb/LICENSE.js",
- "lib/DuoWeb/Duo-Web-v2.js"
-
+ "duo.min.js"
],
"css" : [
- "styles/duo.css"
+ "duo.min.css"
],
"resources" : {
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/e9e86126/extensions/guacamole-auth-duo/src/main/resources/license.txt
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/resources/license.txt b/extensions/guacamole-auth-duo/src/main/resources/license.txt
new file mode 100644
index 0000000..042f3ce
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/resources/license.txt
@@ -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.
+ */
[05/12] incubator-guacamole-client git commit: GUACAMOLE-136:
Implement basic support for verifying user identity using Duo.
Posted by jm...@apache.org.
GUACAMOLE-136: Implement basic support for verifying user identity using Duo.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/48af3ef4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/48af3ef4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/48af3ef4
Branch: refs/heads/master
Commit: 48af3ef45dbdbb7e8e7a712b79e877471dafd475
Parents: 718e4da
Author: Michael Jumper <mj...@apache.org>
Authored: Thu Dec 1 21:36:26 2016 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Dec 5 22:19:45 2016 -0800
----------------------------------------------------------------------
extensions/guacamole-auth-duo/.gitignore | 2 +
extensions/guacamole-auth-duo/pom.xml | 100 ++
.../java/com/duosecurity/duoweb/Base64.java | 1500 ++++++++++++++++++
.../java/com/duosecurity/duoweb/DuoWeb.java | 138 ++
.../com/duosecurity/duoweb/DuoWebException.java | 8 +
.../main/java/com/duosecurity/duoweb/Util.java | 26 +
.../auth/duo/AuthenticationProviderService.java | 109 ++
.../auth/duo/DuoAuthenticationProvider.java | 100 ++
.../duo/DuoAuthenticationProviderModule.java | 81 +
.../guacamole/auth/duo/DuoWebService.java | 212 +++
.../auth/duo/conf/ConfigurationService.java | 160 ++
.../auth/duo/form/DuoSignedResponseField.java | 100 ++
.../src/main/resources/config/duoConfig.js | 33 +
.../controllers/duoSignedResponseController.js | 78 +
.../src/main/resources/duoModule.js | 28 +
.../src/main/resources/guac-manifest.json | 35 +
.../src/main/resources/lib/DuoWeb/Duo-Web-v2.js | 366 +++++
.../src/main/resources/lib/DuoWeb/LICENSE.js | 27 +
.../src/main/resources/styles/duo.css | 38 +
.../templates/duoSignedResponseField.html | 3 +
.../src/main/resources/translations/en.json | 13 +
pom.xml | 1 +
22 files changed, 3158 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/.gitignore
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/.gitignore b/extensions/guacamole-auth-duo/.gitignore
new file mode 100644
index 0000000..42f4a1a
--- /dev/null
+++ b/extensions/guacamole-auth-duo/.gitignore
@@ -0,0 +1,2 @@
+target/
+*~
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/pom.xml
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/pom.xml b/extensions/guacamole-auth-duo/pom.xml
new file mode 100644
index 0000000..4c1fdf6
--- /dev/null
+++ b/extensions/guacamole-auth-duo/pom.xml
@@ -0,0 +1,100 @@
+<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-duo</artifactId>
+ <packaging>jar</packaging>
+ <version>0.9.10-incubating</version>
+ <name>guacamole-auth-duo</name>
+ <url>http://guacamole.incubator.apache.org/</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <build>
+ <plugins>
+
+ <!-- Written for 1.6 -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.3</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ <compilerArgs>
+ <arg>-Xlint:all</arg>
+ <arg>-Werror</arg>
+ </compilerArgs>
+ <fork>true</fork>
+ </configuration>
+ </plugin>
+
+ <!-- Copy dependencies prior to packaging -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.10</version>
+ <executions>
+ <execution>
+ <id>unpack-dependencies</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>unpack-dependencies</goal>
+ </goals>
+ <configuration>
+ <includeScope>runtime</includeScope>
+ <outputDirectory>${project.build.directory}/classes</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+
+ <dependencies>
+
+ <!-- Guacamole Java API -->
+ <dependency>
+ <groupId>org.apache.guacamole</groupId>
+ <artifactId>guacamole-common</artifactId>
+ <version>0.9.10-incubating</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- Guacamole Extension API -->
+ <dependency>
+ <groupId>org.apache.guacamole</groupId>
+ <artifactId>guacamole-ext</artifactId>
+ <version>0.9.10-incubating</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- Guice -->
+ <dependency>
+ <groupId>com.google.inject</groupId>
+ <artifactId>guice</artifactId>
+ <version>3.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.inject.extensions</groupId>
+ <artifactId>guice-multibindings</artifactId>
+ <version>3.0</version>
+ </dependency>
+
+ <!-- Java servlet API -->
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>2.5</version>
+ <scope>provided</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/Base64.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/Base64.java b/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/Base64.java
new file mode 100644
index 0000000..8f25477
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/Base64.java
@@ -0,0 +1,1500 @@
+package com.duosecurity.duoweb;
+
+public class Base64 {
+
+ /* ******** P U B L I C F I E L D S ******** */
+
+ /** No options specified. Value is zero. */
+ public final static int NO_OPTIONS = 0;
+
+ /** Specify encoding in first bit. Value is one. */
+ public final static int ENCODE = 1;
+
+ /** Specify decoding in first bit. Value is zero. */
+ public final static int DECODE = 0;
+
+ /** Specify that data should be gzip-compressed in second bit. Value is two. */
+ public final static int GZIP = 2;
+
+ /**
+ * Specify that gzipped data should <em>not</em> be automatically gunzipped.
+ */
+ public final static int DONT_GUNZIP = 4;
+
+ /** Do break lines when encoding. Value is 8. */
+ public final static int DO_BREAK_LINES = 8;
+
+ /**
+ * Encode using Base64-like encoding that is URL- and Filename-safe as
+ * described in Section 4 of RFC3548: <a
+ * href="http://www.faqs.org/rfcs/rfc3548.html"
+ * >http://www.faqs.org/rfcs/rfc3548.html</a>. It is important to note that
+ * data encoded this way is <em>not</em> officially valid Base64, or at the
+ * very least should not be called Base64 without also specifying that is
+ * was encoded using the URL- and Filename-safe dialect.
+ */
+ public final static int URL_SAFE = 16;
+
+ /**
+ * Encode using the special "ordered" dialect of Base64 described here: <a
+ * href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-
+ * 1940.html</a>.
+ */
+ public final static int ORDERED = 32;
+
+ /* ******** P R I V A T E F I E L D S ******** */
+
+ /** Maximum line length (76) of Base64 output. */
+ private final static int MAX_LINE_LENGTH = 76;
+
+ /** The equals sign (=) as a byte. */
+ private final static byte EQUALS_SIGN = (byte) '=';
+
+ /** The new line character (\n) as a byte. */
+ private final static byte NEW_LINE = (byte) '\n';
+
+ /** Preferred encoding. */
+ private final static String PREFERRED_ENCODING = "US-ASCII";
+
+ private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in
+ // encoding
+ private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in
+ // encoding
+
+ /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */
+
+ /** The 64 valid Base64 values. */
+ /*
+ * Host platform me be something funny like EBCDIC, so we hardcode these
+ * values.
+ */
+ private final static byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B',
+ (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G',
+ (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
+ (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q',
+ (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V',
+ (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a',
+ (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
+ (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k',
+ (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
+ (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u',
+ (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z',
+ (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
+ (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
+ (byte) '+', (byte) '/' };
+
+ /**
+ * Translates a Base64 value to either its 6-bit reconstruction value or a
+ * negative number indicating some other meaning.
+ **/
+ private final static byte[] _STANDARD_DECODABET = { -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, // Decimal 0 - 8
+ -5, -5, // Whitespace: Tab and Linefeed
+ -9, -9, // Decimal 11 - 12
+ -5, // Whitespace: Carriage Return
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
+ // 26
+ -9, -9, -9, -9, -9, // Decimal 27 - 31
+ -5, // Whitespace: Space
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
+ 62, // Plus sign at decimal 43
+ -9, -9, -9, // Decimal 44 - 46
+ 63, // Slash at decimal 47
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
+ -9, -9, -9, // Decimal 58 - 60
+ -1, // Equals sign at decimal 61
+ -9, -9, -9, // Decimal 62 - 64
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through
+ // 'N'
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
+ // through 'Z'
+ -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
+ // through 'm'
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
+ // through 'z'
+ -9, -9, -9, -9, -9 // Decimal 123 - 127
+ , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 -
+ // 139
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 -
+ // 152
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 -
+ // 165
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 -
+ // 178
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 -
+ // 191
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 -
+ // 204
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 -
+ // 217
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 -
+ // 230
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 -
+ // 243
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
+ };
+
+ /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */
+
+ /**
+ * Used in the URL- and Filename-safe dialect described in Section 4 of
+ * RFC3548: <a
+ * href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org
+ * /rfcs/rfc3548.html</a>. Notice that the last two bytes become "hyphen"
+ * and "underscore" instead of "plus" and "slash."
+ */
+ private final static byte[] _URL_SAFE_ALPHABET = { (byte) 'A', (byte) 'B',
+ (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G',
+ (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
+ (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q',
+ (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V',
+ (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a',
+ (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
+ (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k',
+ (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
+ (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u',
+ (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z',
+ (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
+ (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
+ (byte) '-', (byte) '_' };
+
+ /**
+ * Used in decoding URL- and Filename-safe dialects of Base64.
+ */
+ private final static byte[] _URL_SAFE_DECODABET = { -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, // Decimal 0 - 8
+ -5, -5, // Whitespace: Tab and Linefeed
+ -9, -9, // Decimal 11 - 12
+ -5, // Whitespace: Carriage Return
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
+ // 26
+ -9, -9, -9, -9, -9, // Decimal 27 - 31
+ -5, // Whitespace: Space
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
+ -9, // Plus sign at decimal 43
+ -9, // Decimal 44
+ 62, // Minus sign at decimal 45
+ -9, // Decimal 46
+ -9, // Slash at decimal 47
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
+ -9, -9, -9, // Decimal 58 - 60
+ -1, // Equals sign at decimal 61
+ -9, -9, -9, // Decimal 62 - 64
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through
+ // 'N'
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
+ // through 'Z'
+ -9, -9, -9, -9, // Decimal 91 - 94
+ 63, // Underscore at decimal 95
+ -9, // Decimal 96
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
+ // through 'm'
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
+ // through 'z'
+ -9, -9, -9, -9, -9 // Decimal 123 - 127
+ , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 -
+ // 139
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 -
+ // 152
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 -
+ // 165
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 -
+ // 178
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 -
+ // 191
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 -
+ // 204
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 -
+ // 217
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 -
+ // 230
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 -
+ // 243
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
+ };
+
+ /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */
+
+ /**
+ * I don't get the point of this technique, but someone requested it, and it
+ * is described here: <a
+ * href="http://www.faqs.org/qa/rfcc-1940.html">http://
+ * www.faqs.org/qa/rfcc-1940.html</a>.
+ */
+ private final static byte[] _ORDERED_ALPHABET = { (byte) '-', (byte) '0',
+ (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
+ (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A',
+ (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
+ (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
+ (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
+ (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
+ (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
+ (byte) '_', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd',
+ (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i',
+ (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n',
+ (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's',
+ (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
+ (byte) 'y', (byte) 'z' };
+
+ /**
+ * Used in decoding the "ordered" dialect of Base64.
+ */
+ private final static byte[] _ORDERED_DECODABET = { -9, -9, -9, -9, -9, -9,
+ -9, -9, -9, // Decimal 0 - 8
+ -5, -5, // Whitespace: Tab and Linefeed
+ -9, -9, // Decimal 11 - 12
+ -5, // Whitespace: Carriage Return
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
+ // 26
+ -9, -9, -9, -9, -9, // Decimal 27 - 31
+ -5, // Whitespace: Space
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
+ -9, // Plus sign at decimal 43
+ -9, // Decimal 44
+ 0, // Minus sign at decimal 45
+ -9, // Decimal 46
+ -9, // Slash at decimal 47
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine
+ -9, -9, -9, // Decimal 58 - 60
+ -1, // Equals sign at decimal 61
+ -9, -9, -9, // Decimal 62 - 64
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A'
+ // through 'M'
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N'
+ // through 'Z'
+ -9, -9, -9, -9, // Decimal 91 - 94
+ 37, // Underscore at decimal 95
+ -9, // Decimal 96
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a'
+ // through 'm'
+ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n'
+ // through 'z'
+ -9, -9, -9, -9, -9 // Decimal 123 - 127
+ , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128
+ // - 139
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 -
+ // 152
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 -
+ // 165
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 -
+ // 178
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 -
+ // 191
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 -
+ // 204
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 -
+ // 217
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 -
+ // 230
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 -
+ // 243
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
+ };
+
+ /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */
+
+ /**
+ * Returns one of the _SOMETHING_ALPHABET byte arrays depending on the
+ * options specified. It's possible, though silly, to specify ORDERED
+ * <b>and</b> URLSAFE in which case one of them will be picked, though there
+ * is no guarantee as to which one will be picked.
+ */
+ private final static byte[] getAlphabet(int options) {
+ if ((options & URL_SAFE) == URL_SAFE) {
+ return _URL_SAFE_ALPHABET;
+ } else if ((options & ORDERED) == ORDERED) {
+ return _ORDERED_ALPHABET;
+ } else {
+ return _STANDARD_ALPHABET;
+ }
+ } // end getAlphabet
+
+ /**
+ * Returns one of the _SOMETHING_DECODABET byte arrays depending on the
+ * options specified. It's possible, though silly, to specify ORDERED and
+ * URL_SAFE in which case one of them will be picked, though there is no
+ * guarantee as to which one will be picked.
+ */
+ private final static byte[] getDecodabet(int options) {
+ if ((options & URL_SAFE) == URL_SAFE) {
+ return _URL_SAFE_DECODABET;
+ } else if ((options & ORDERED) == ORDERED) {
+ return _ORDERED_DECODABET;
+ } else {
+ return _STANDARD_DECODABET;
+ }
+ } // end getAlphabet
+
+ /** Defeats instantiation. */
+ private Base64() {
+ }
+
+ /* ******** E N C O D I N G M E T H O D S ******** */
+
+ /**
+ * Encodes up to the first three bytes of array <var>threeBytes</var> and
+ * returns a four-byte array in Base64 notation. The actual number of
+ * significant bytes in your array is given by <var>numSigBytes</var>. The
+ * array <var>threeBytes</var> needs only be as big as
+ * <var>numSigBytes</var>. Code can reuse a byte array by passing a
+ * four-byte array as <var>b4</var>.
+ *
+ * @param b4
+ * A reusable byte array to reduce array instantiation
+ * @param threeBytes
+ * the array to convert
+ * @param numSigBytes
+ * the number of significant bytes in your array
+ * @return four byte array in Base64 notation.
+ * @since 1.5.1
+ */
+ private static byte[] encode3to4(byte[] b4, byte[] threeBytes,
+ int numSigBytes, int options) {
+ encode3to4(threeBytes, 0, numSigBytes, b4, 0, options);
+ return b4;
+ } // end encode3to4
+
+ /**
+ * <p>
+ * Encodes up to three bytes of the array <var>source</var> and writes the
+ * resulting four Base64 bytes to <var>destination</var>. The source and
+ * destination arrays can be manipulated anywhere along their length by
+ * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
+ * does not check to make sure your arrays are large enough to accomodate
+ * <var>srcOffset</var> + 3 for the <var>source</var> array or
+ * <var>destOffset</var> + 4 for the <var>destination</var> array. The
+ * actual number of significant bytes in your array is given by
+ * <var>numSigBytes</var>.
+ * </p>
+ * <p>
+ * This is the lowest level of the encoding methods with all possible
+ * parameters.
+ * </p>
+ *
+ * @param source
+ * the array to convert
+ * @param srcOffset
+ * the index where conversion begins
+ * @param numSigBytes
+ * the number of significant bytes in your array
+ * @param destination
+ * the array to hold the conversion
+ * @param destOffset
+ * the index where output will be put
+ * @return the <var>destination</var> array
+ * @since 1.3
+ */
+ private static byte[] encode3to4(byte[] source, int srcOffset,
+ int numSigBytes, byte[] destination, int destOffset, int options) {
+
+ byte[] ALPHABET = getAlphabet(options);
+
+ // 1 2 3
+ // 01234567890123456789012345678901 Bit position
+ // --------000000001111111122222222 Array position from threeBytes
+ // --------| || || || | Six bit groups to index ALPHABET
+ // >>18 >>12 >> 6 >> 0 Right shift necessary
+ // 0x3f 0x3f 0x3f Additional AND
+
+ // Create buffer with zero-padding if there are only one or two
+ // significant bytes passed in the array.
+ // We have to shift left 24 in order to flush out the 1's that appear
+ // when Java treats a value as negative that is cast from a byte to an
+ // int.
+ int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
+ | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
+ | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
+
+ switch (numSigBytes) {
+ case 3:
+ destination[destOffset] = ALPHABET[(inBuff >>> 18)];
+ destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
+ destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
+ destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
+ return destination;
+
+ case 2:
+ destination[destOffset] = ALPHABET[(inBuff >>> 18)];
+ destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
+ destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
+ destination[destOffset + 3] = EQUALS_SIGN;
+ return destination;
+
+ case 1:
+ destination[destOffset] = ALPHABET[(inBuff >>> 18)];
+ destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
+ destination[destOffset + 2] = EQUALS_SIGN;
+ destination[destOffset + 3] = EQUALS_SIGN;
+ return destination;
+
+ default:
+ return destination;
+ } // end switch
+ } // end encode3to4
+
+ /**
+ * Performs Base64 encoding on the <code>raw</code> ByteBuffer, writing it
+ * to the <code>encoded</code> ByteBuffer. This is an experimental feature.
+ * Currently it does not pass along any options (such as
+ * {@link #DO_BREAK_LINES} or {@link #GZIP}.
+ *
+ * @param raw
+ * input buffer
+ * @param encoded
+ * output buffer
+ * @since 2.3
+ */
+ public static void encode(java.nio.ByteBuffer raw,
+ java.nio.ByteBuffer encoded) {
+ byte[] raw3 = new byte[3];
+ byte[] enc4 = new byte[4];
+
+ while (raw.hasRemaining()) {
+ int rem = Math.min(3, raw.remaining());
+ raw.get(raw3, 0, rem);
+ Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS);
+ encoded.put(enc4);
+ } // end input remaining
+ }
+
+ /**
+ * Performs Base64 encoding on the <code>raw</code> ByteBuffer, writing it
+ * to the <code>encoded</code> CharBuffer. This is an experimental feature.
+ * Currently it does not pass along any options (such as
+ * {@link #DO_BREAK_LINES} or {@link #GZIP}.
+ *
+ * @param raw
+ * input buffer
+ * @param encoded
+ * output buffer
+ * @since 2.3
+ */
+ public static void encode(java.nio.ByteBuffer raw,
+ java.nio.CharBuffer encoded) {
+ byte[] raw3 = new byte[3];
+ byte[] enc4 = new byte[4];
+
+ while (raw.hasRemaining()) {
+ int rem = Math.min(3, raw.remaining());
+ raw.get(raw3, 0, rem);
+ Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS);
+ for (int i = 0; i < 4; i++) {
+ encoded.put((char) (enc4[i] & 0xFF));
+ }
+ } // end input remaining
+ }
+
+ /**
+ * Serializes an object and returns the Base64-encoded version of that
+ * serialized object.
+ *
+ * <p>
+ * As of v 2.3, if the object cannot be serialized or there is another
+ * error, the method will throw an java.io.IOException. <b>This is new to
+ * v2.3!</b> In earlier versions, it just returned a null value, but in
+ * retrospect that's a pretty poor way to handle it.
+ * </p>
+ *
+ * The object is not GZip-compressed before being encoded.
+ *
+ * @param serializableObject
+ * The object to encode
+ * @return The Base64-encoded object
+ * @throws java.io.IOException
+ * if there is an error
+ * @throws NullPointerException
+ * if serializedObject is null
+ * @since 1.4
+ */
+ public static String encodeObject(java.io.Serializable serializableObject)
+ throws java.io.IOException {
+ return encodeObject(serializableObject, NO_OPTIONS);
+ } // end encodeObject
+
+ /**
+ * Serializes an object and returns the Base64-encoded version of that
+ * serialized object.
+ *
+ * <p>
+ * As of v 2.3, if the object cannot be serialized or there is another
+ * error, the method will throw an java.io.IOException. <b>This is new to
+ * v2.3!</b> In earlier versions, it just returned a null value, but in
+ * retrospect that's a pretty poor way to handle it.
+ * </p>
+ *
+ * The object is not GZip-compressed before being encoded.
+ * <p>
+ * Example options:
+ *
+ * <pre>
+ * GZIP: gzip-compresses object before encoding it.
+ * DO_BREAK_LINES: break lines at 76 characters
+ * </pre>
+ * <p>
+ * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
+ * <p>
+ * Example:
+ * <code>encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
+ *
+ * @param serializableObject
+ * The object to encode
+ * @param options
+ * Specified options
+ * @return The Base64-encoded object
+ * @see Base64#GZIP
+ * @see Base64#DO_BREAK_LINES
+ * @throws java.io.IOException
+ * if there is an error
+ * @since 2.0
+ */
+ public static String encodeObject(java.io.Serializable serializableObject,
+ int options) throws java.io.IOException {
+
+ if (serializableObject == null) {
+ throw new NullPointerException("Cannot serialize a null object.");
+ } // end if: null
+
+ // Streams
+ java.io.ByteArrayOutputStream baos = null;
+ java.io.OutputStream b64os = null;
+ java.util.zip.GZIPOutputStream gzos = null;
+ java.io.ObjectOutputStream oos = null;
+
+ try {
+ // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
+ baos = new java.io.ByteArrayOutputStream();
+ b64os = new Base64.OutputStream(baos, ENCODE | options);
+ if ((options & GZIP) != 0) {
+ // Gzip
+ gzos = new java.util.zip.GZIPOutputStream(b64os);
+ oos = new java.io.ObjectOutputStream(gzos);
+ } else {
+ // Not gzipped
+ oos = new java.io.ObjectOutputStream(b64os);
+ }
+ oos.writeObject(serializableObject);
+ } // end try
+ catch (java.io.IOException e) {
+ // Catch it and then throw it immediately so that
+ // the finally{} block is called for cleanup.
+ throw e;
+ } // end catch
+ finally {
+ try {
+ oos.close();
+ } catch (Exception e) {
+ }
+ try {
+ gzos.close();
+ } catch (Exception e) {
+ }
+ try {
+ b64os.close();
+ } catch (Exception e) {
+ }
+ try {
+ baos.close();
+ } catch (Exception e) {
+ }
+ } // end finally
+
+ // Return value according to relevant encoding.
+ try {
+ return new String(baos.toByteArray(), PREFERRED_ENCODING);
+ } // end try
+ catch (java.io.UnsupportedEncodingException uue) {
+ // Fall back to some Java default
+ return new String(baos.toByteArray());
+ } // end catch
+
+ } // end encode
+
+ /**
+ * Encodes a byte array into Base64 notation. Does not GZip-compress data.
+ *
+ * @param source
+ * The data to convert
+ * @return The data in Base64-encoded form
+ * @throws NullPointerException
+ * if source array is null
+ * @since 1.4
+ */
+ public static String encodeBytes(byte[] source) {
+ // Since we're not going to have the GZIP encoding turned on,
+ // we're not going to have an java.io.IOException thrown, so
+ // we should not force the user to have to catch it.
+ String encoded = null;
+ try {
+ encoded = encodeBytes(source, 0, source.length, NO_OPTIONS);
+ } catch (java.io.IOException ex) {
+ assert false : ex.getMessage();
+ } // end catch
+ assert encoded != null;
+ return encoded;
+ } // end encodeBytes
+
+ /**
+ * Encodes a byte array into Base64 notation.
+ * <p>
+ * Example options:
+ *
+ * <pre>
+ * GZIP: gzip-compresses object before encoding it.
+ * DO_BREAK_LINES: break lines at 76 characters
+ * <i>Note: Technically, this makes your encoding non-compliant.</i>
+ * </pre>
+ * <p>
+ * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+ * <p>
+ * Example:
+ * <code>encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
+ *
+ *
+ * <p>
+ * As of v 2.3, if there is an error with the GZIP stream, the method will
+ * throw an java.io.IOException. <b>This is new to v2.3!</b> In earlier
+ * versions, it just returned a null value, but in retrospect that's a
+ * pretty poor way to handle it.
+ * </p>
+ *
+ *
+ * @param source
+ * The data to convert
+ * @param options
+ * Specified options
+ * @return The Base64-encoded data as a String
+ * @see Base64#GZIP
+ * @see Base64#DO_BREAK_LINES
+ * @throws java.io.IOException
+ * if there is an error
+ * @throws NullPointerException
+ * if source array is null
+ * @since 2.0
+ */
+ public static String encodeBytes(byte[] source, int options)
+ throws java.io.IOException {
+ return encodeBytes(source, 0, source.length, options);
+ } // end encodeBytes
+
+ /**
+ * Encodes a byte array into Base64 notation. Does not GZip-compress data.
+ *
+ * <p>
+ * As of v 2.3, if there is an error, the method will throw an
+ * java.io.IOException. <b>This is new to v2.3!</b> In earlier versions, it
+ * just returned a null value, but in retrospect that's a pretty poor way to
+ * handle it.
+ * </p>
+ *
+ *
+ * @param source
+ * The data to convert
+ * @param off
+ * Offset in array where conversion should begin
+ * @param len
+ * Length of data to convert
+ * @return The Base64-encoded data as a String
+ * @throws NullPointerException
+ * if source array is null
+ * @throws IllegalArgumentException
+ * if source array, offset, or length are invalid
+ * @since 1.4
+ */
+ public static String encodeBytes(byte[] source, int off, int len) {
+ // Since we're not going to have the GZIP encoding turned on,
+ // we're not going to have an java.io.IOException thrown, so
+ // we should not force the user to have to catch it.
+ String encoded = null;
+ try {
+ encoded = encodeBytes(source, off, len, NO_OPTIONS);
+ } catch (java.io.IOException ex) {
+ assert false : ex.getMessage();
+ } // end catch
+ assert encoded != null;
+ return encoded;
+ } // end encodeBytes
+
+ /**
+ * Encodes a byte array into Base64 notation.
+ * <p>
+ * Example options:
+ *
+ * <pre>
+ * GZIP: gzip-compresses object before encoding it.
+ * DO_BREAK_LINES: break lines at 76 characters
+ * <i>Note: Technically, this makes your encoding non-compliant.</i>
+ * </pre>
+ * <p>
+ * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+ * <p>
+ * Example:
+ * <code>encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
+ *
+ *
+ * <p>
+ * As of v 2.3, if there is an error with the GZIP stream, the method will
+ * throw an java.io.IOException. <b>This is new to v2.3!</b> In earlier
+ * versions, it just returned a null value, but in retrospect that's a
+ * pretty poor way to handle it.
+ * </p>
+ *
+ *
+ * @param source
+ * The data to convert
+ * @param off
+ * Offset in array where conversion should begin
+ * @param len
+ * Length of data to convert
+ * @param options
+ * Specified options
+ * @return The Base64-encoded data as a String
+ * @see Base64#GZIP
+ * @see Base64#DO_BREAK_LINES
+ * @throws java.io.IOException
+ * if there is an error
+ * @throws NullPointerException
+ * if source array is null
+ * @throws IllegalArgumentException
+ * if source array, offset, or length are invalid
+ * @since 2.0
+ */
+ public static String encodeBytes(byte[] source, int off, int len,
+ int options) throws java.io.IOException {
+ byte[] encoded = encodeBytesToBytes(source, off, len, options);
+
+ // Return value according to relevant encoding.
+ try {
+ return new String(encoded, PREFERRED_ENCODING);
+ } // end try
+ catch (java.io.UnsupportedEncodingException uue) {
+ return new String(encoded);
+ } // end catch
+
+ } // end encodeBytes
+
+ /**
+ * Similar to {@link #encodeBytes(byte[])} but returns a byte array instead
+ * of instantiating a String. This is more efficient if you're working with
+ * I/O streams and have large data sets to encode.
+ *
+ *
+ * @param source
+ * The data to convert
+ * @return The Base64-encoded data as a byte[] (of ASCII characters)
+ * @throws NullPointerException
+ * if source array is null
+ * @since 2.3.1
+ */
+ public static byte[] encodeBytesToBytes(byte[] source) {
+ byte[] encoded = null;
+ try {
+ encoded = encodeBytesToBytes(source, 0, source.length,
+ Base64.NO_OPTIONS);
+ } catch (java.io.IOException ex) {
+ assert false : "IOExceptions only come from GZipping, which is turned off: "
+ + ex.getMessage();
+ }
+ return encoded;
+ }
+
+ /**
+ * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns a byte
+ * array instead of instantiating a String. This is more efficient if you're
+ * working with I/O streams and have large data sets to encode.
+ *
+ *
+ * @param source
+ * The data to convert
+ * @param off
+ * Offset in array where conversion should begin
+ * @param len
+ * Length of data to convert
+ * @param options
+ * Specified options
+ * @return The Base64-encoded data as a String
+ * @see Base64#GZIP
+ * @see Base64#DO_BREAK_LINES
+ * @throws java.io.IOException
+ * if there is an error
+ * @throws NullPointerException
+ * if source array is null
+ * @throws IllegalArgumentException
+ * if source array, offset, or length are invalid
+ * @since 2.3.1
+ */
+ public static byte[] encodeBytesToBytes(byte[] source, int off, int len,
+ int options) throws java.io.IOException {
+
+ if (source == null) {
+ throw new NullPointerException("Cannot serialize a null array.");
+ } // end if: null
+
+ if (off < 0) {
+ throw new IllegalArgumentException("Cannot have negative offset: "
+ + off);
+ } // end if: off < 0
+
+ if (len < 0) {
+ throw new IllegalArgumentException("Cannot have length offset: "
+ + len);
+ } // end if: len < 0
+
+ if (off + len > source.length) {
+ throw new IllegalArgumentException(
+ String
+ .format(
+ "Cannot have offset of %d and length of %d with array of length %d",
+ off, len, source.length));
+ } // end if: off < 0
+
+ // Compress?
+ if ((options & GZIP) != 0) {
+ java.io.ByteArrayOutputStream baos = null;
+ java.util.zip.GZIPOutputStream gzos = null;
+ Base64.OutputStream b64os = null;
+
+ try {
+ // GZip -> Base64 -> ByteArray
+ baos = new java.io.ByteArrayOutputStream();
+ b64os = new Base64.OutputStream(baos, ENCODE | options);
+ gzos = new java.util.zip.GZIPOutputStream(b64os);
+
+ gzos.write(source, off, len);
+ gzos.close();
+ } // end try
+ catch (java.io.IOException e) {
+ // Catch it and then throw it immediately so that
+ // the finally{} block is called for cleanup.
+ throw e;
+ } // end catch
+ finally {
+ try {
+ gzos.close();
+ } catch (Exception e) {
+ }
+ try {
+ b64os.close();
+ } catch (Exception e) {
+ }
+ try {
+ baos.close();
+ } catch (Exception e) {
+ }
+ } // end finally
+
+ return baos.toByteArray();
+ } // end if: compress
+
+ // Else, don't compress. Better not to use streams at all then.
+ else {
+ boolean breakLines = (options & DO_BREAK_LINES) != 0;
+
+ // int len43 = len * 4 / 3;
+ // byte[] outBuff = new byte[ ( len43 ) // Main 4:3
+ // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
+ // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
+ // Try to determine more precisely how big the array needs to be.
+ // If we get it right, we don't have to do an array copy, and
+ // we save a bunch of memory.
+ int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0); // Bytes needed
+ // for actual
+ // encoding
+ if (breakLines) {
+ encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline
+ // characters
+ }
+ byte[] outBuff = new byte[encLen];
+
+ int d = 0;
+ int e = 0;
+ int len2 = len - 2;
+ int lineLength = 0;
+ for (; d < len2; d += 3, e += 4) {
+ encode3to4(source, d + off, 3, outBuff, e, options);
+
+ lineLength += 4;
+ if (breakLines && lineLength >= MAX_LINE_LENGTH) {
+ outBuff[e + 4] = NEW_LINE;
+ e++;
+ lineLength = 0;
+ } // end if: end of line
+ } // en dfor: each piece of array
+
+ if (d < len) {
+ encode3to4(source, d + off, len - d, outBuff, e, options);
+ e += 4;
+ } // end if: some padding needed
+
+ // Only resize array if we didn't guess it right.
+ if (e <= outBuff.length - 1) {
+ // If breaking lines and the last byte falls right at
+ // the line length (76 bytes per line), there will be
+ // one extra byte, and the array will need to be resized.
+ // Not too bad of an estimate on array size, I'd say.
+ byte[] finalOut = new byte[e];
+ System.arraycopy(outBuff, 0, finalOut, 0, e);
+ // System.err.println("Having to resize array from " +
+ // outBuff.length + " to " + e );
+ return finalOut;
+ } else {
+ // System.err.println("No need to resize array.");
+ return outBuff;
+ }
+
+ } // end else: don't compress
+
+ } // end encodeBytesToBytes
+
+ /* ******** D E C O D I N G M E T H O D S ******** */
+
+ /**
+ * Decodes four bytes from array <var>source</var> and writes the resulting
+ * bytes (up to three of them) to <var>destination</var>. The source and
+ * destination arrays can be manipulated anywhere along their length by
+ * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
+ * does not check to make sure your arrays are large enough to accomodate
+ * <var>srcOffset</var> + 4 for the <var>source</var> array or
+ * <var>destOffset</var> + 3 for the <var>destination</var> array. This
+ * method returns the actual number of bytes that were converted from the
+ * Base64 encoding.
+ * <p>
+ * This is the lowest level of the decoding methods with all possible
+ * parameters.
+ * </p>
+ *
+ *
+ * @param source
+ * the array to convert
+ * @param srcOffset
+ * the index where conversion begins
+ * @param destination
+ * the array to hold the conversion
+ * @param destOffset
+ * the index where output will be put
+ * @param options
+ * alphabet type is pulled from this (standard, url-safe,
+ * ordered)
+ * @return the number of decoded bytes converted
+ * @throws NullPointerException
+ * if source or destination arrays are null
+ * @throws IllegalArgumentException
+ * if srcOffset or destOffset are invalid or there is not enough
+ * room in the array.
+ * @since 1.3
+ */
+ private static int decode4to3(byte[] source, int srcOffset,
+ byte[] destination, int destOffset, int options) {
+
+ // Lots of error checking and exception throwing
+ if (source == null) {
+ throw new NullPointerException("Source array was null.");
+ } // end if
+ if (destination == null) {
+ throw new NullPointerException("Destination array was null.");
+ } // end if
+ if (srcOffset < 0 || srcOffset + 3 >= source.length) {
+ throw new IllegalArgumentException(
+ String
+ .format(
+ "Source array with length %d cannot have offset of %d and still process four bytes.",
+ source.length, srcOffset));
+ } // end if
+ if (destOffset < 0 || destOffset + 2 >= destination.length) {
+ throw new IllegalArgumentException(
+ String
+ .format(
+ "Destination array with length %d cannot have offset of %d and still store three bytes.",
+ destination.length, destOffset));
+ } // end if
+
+ byte[] DECODABET = getDecodabet(options);
+
+ // Example: Dk==
+ if (source[srcOffset + 2] == EQUALS_SIGN) {
+ // Two ways to do the same thing. Don't know which way I like best.
+ // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
+ // )
+ // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
+ int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
+ | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
+
+ destination[destOffset] = (byte) (outBuff >>> 16);
+ return 1;
+ }
+
+ // Example: DkL=
+ else if (source[srcOffset + 3] == EQUALS_SIGN) {
+ // Two ways to do the same thing. Don't know which way I like best.
+ // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
+ // )
+ // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+ // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
+ int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
+ | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
+ | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
+
+ destination[destOffset] = (byte) (outBuff >>> 16);
+ destination[destOffset + 1] = (byte) (outBuff >>> 8);
+ return 2;
+ }
+
+ // Example: DkLE
+ else {
+ // Two ways to do the same thing. Don't know which way I like best.
+ // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
+ // )
+ // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+ // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
+ // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
+ int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
+ | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
+ | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
+ | ((DECODABET[source[srcOffset + 3]] & 0xFF));
+
+ destination[destOffset] = (byte) (outBuff >> 16);
+ destination[destOffset + 1] = (byte) (outBuff >> 8);
+ destination[destOffset + 2] = (byte) (outBuff);
+
+ return 3;
+ }
+ } // end decodeToBytes
+
+ /**
+ * Low-level access to decoding ASCII characters in the form of a byte
+ * array. <strong>Ignores GUNZIP option, if it's set.</strong> This is not
+ * generally a recommended method, although it is used internally as part of
+ * the decoding process. Special case: if len = 0, an empty array is
+ * returned. Still, if you need more speed and reduced memory footprint (and
+ * aren't gzipping), consider this method.
+ *
+ * @param source
+ * The Base64 encoded data
+ * @return decoded data
+ * @since 2.3.1
+ */
+ public static byte[] decode(byte[] source) throws java.io.IOException {
+ byte[] decoded = null;
+ // try {
+ decoded = decode(source, 0, source.length, Base64.NO_OPTIONS);
+ // } catch( java.io.IOException ex ) {
+ // assert false :
+ // "IOExceptions only come from GZipping, which is turned off: " +
+ // ex.getMessage();
+ // }
+ return decoded;
+ }
+
+ /**
+ * Low-level access to decoding ASCII characters in the form of a byte
+ * array. <strong>Ignores GUNZIP option, if it's set.</strong> This is not
+ * generally a recommended method, although it is used internally as part of
+ * the decoding process. Special case: if len = 0, an empty array is
+ * returned. Still, if you need more speed and reduced memory footprint (and
+ * aren't gzipping), consider this method.
+ *
+ * @param source
+ * The Base64 encoded data
+ * @param off
+ * The offset of where to begin decoding
+ * @param len
+ * The length of characters to decode
+ * @param options
+ * Can specify options such as alphabet type to use
+ * @return decoded data
+ * @throws java.io.IOException
+ * If bogus characters exist in source data
+ * @since 1.3
+ */
+ public static byte[] decode(byte[] source, int off, int len, int options)
+ throws java.io.IOException {
+
+ // Lots of error checking and exception throwing
+ if (source == null) {
+ throw new NullPointerException("Cannot decode null source array.");
+ } // end if
+ if (off < 0 || off + len > source.length) {
+ throw new IllegalArgumentException(
+ String
+ .format(
+ "Source array with length %d cannot have offset of %d and process %d bytes.",
+ source.length, off, len));
+ } // end if
+
+ if (len == 0) {
+ return new byte[0];
+ } else if (len < 4) {
+ throw new IllegalArgumentException(
+ "Base64-encoded string must have at least four characters, but length specified was "
+ + len);
+ } // end if
+
+ byte[] DECODABET = getDecodabet(options);
+
+ int len34 = len * 3 / 4; // Estimate on array size
+ byte[] outBuff = new byte[len34]; // Upper limit on size of output
+ int outBuffPosn = 0; // Keep track of where we're writing
+
+ byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating
+ // white space
+ int b4Posn = 0; // Keep track of four byte input buffer
+ int i = 0; // Source array counter
+ byte sbiDecode = 0; // Special value from DECODABET
+
+ for (i = off; i < off + len; i++) { // Loop through source
+
+ sbiDecode = DECODABET[source[i] & 0xFF];
+
+ // White space, Equals sign, or legit Base64 character
+ // Note the values such as -5 and -9 in the
+ // DECODABETs at the top of the file.
+ if (sbiDecode >= WHITE_SPACE_ENC) {
+ if (sbiDecode >= EQUALS_SIGN_ENC) {
+ b4[b4Posn++] = source[i]; // Save non-whitespace
+ if (b4Posn > 3) { // Time to decode?
+ outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn,
+ options);
+ b4Posn = 0;
+
+ // If that was the equals sign, break out of 'for' loop
+ if (source[i] == EQUALS_SIGN) {
+ break;
+ } // end if: equals sign
+ } // end if: quartet built
+ } // end if: equals sign or better
+ } // end if: white space, equals sign or better
+ else {
+ // There's a bad input character in the Base64 stream.
+ throw new java.io.IOException(
+ String
+ .format(
+ "Bad Base64 input character decimal %d in array position %d",
+ ((int) source[i]) & 0xFF, i));
+ } // end else:
+ } // each input character
+
+ byte[] out = new byte[outBuffPosn];
+ System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
+ return out;
+ } // end decode
+
+ /**
+ * Decodes data from Base64 notation, automatically detecting
+ * gzip-compressed data and decompressing it.
+ *
+ * @param s
+ * the string to decode
+ * @return the decoded data
+ * @throws java.io.IOException
+ * If there is a problem
+ * @since 1.4
+ */
+ public static byte[] decode(String s) throws java.io.IOException {
+ return decode(s, NO_OPTIONS);
+ }
+
+ /**
+ * Decodes data from Base64 notation, automatically detecting
+ * gzip-compressed data and decompressing it.
+ *
+ * @param s
+ * the string to decode
+ * @param options
+ * encode options such as URL_SAFE
+ * @return the decoded data
+ * @throws java.io.IOException
+ * if there is an error
+ * @throws NullPointerException
+ * if <tt>s</tt> is null
+ * @since 1.4
+ */
+ public static byte[] decode(String s, int options)
+ throws java.io.IOException {
+
+ if (s == null) {
+ throw new NullPointerException("Input string was null.");
+ } // end if
+
+ byte[] bytes;
+ try {
+ bytes = s.getBytes(PREFERRED_ENCODING);
+ } // end try
+ catch (java.io.UnsupportedEncodingException uee) {
+ bytes = s.getBytes();
+ } // end catch
+ // </change>
+
+ // Decode
+ bytes = decode(bytes, 0, bytes.length, options);
+
+ // Check to see if it's gzip-compressed
+ // GZIP Magic Two-Byte Number: 0x8b1f (35615)
+ boolean dontGunzip = (options & DONT_GUNZIP) != 0;
+ if ((bytes != null) && (bytes.length >= 4) && (!dontGunzip)) {
+
+ int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
+ if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
+ java.io.ByteArrayInputStream bais = null;
+ java.util.zip.GZIPInputStream gzis = null;
+ java.io.ByteArrayOutputStream baos = null;
+ byte[] buffer = new byte[2048];
+ int length = 0;
+
+ try {
+ baos = new java.io.ByteArrayOutputStream();
+ bais = new java.io.ByteArrayInputStream(bytes);
+ gzis = new java.util.zip.GZIPInputStream(bais);
+
+ while ((length = gzis.read(buffer)) >= 0) {
+ baos.write(buffer, 0, length);
+ } // end while: reading input
+
+ // No error? Get new bytes.
+ bytes = baos.toByteArray();
+
+ } // end try
+ catch (java.io.IOException e) {
+ e.printStackTrace();
+ // Just return originally-decoded bytes
+ } // end catch
+ finally {
+ try {
+ baos.close();
+ } catch (Exception e) {
+ }
+ try {
+ gzis.close();
+ } catch (Exception e) {
+ }
+ try {
+ bais.close();
+ } catch (Exception e) {
+ }
+ } // end finally
+
+ } // end if: gzipped
+ } // end if: bytes.length >= 2
+
+ return bytes;
+ } // end decode
+
+ /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
+
+ /**
+ * A {@link Base64.OutputStream} will write data to another
+ * <tt>java.io.OutputStream</tt>, given in the constructor, and
+ * encode/decode to/from Base64 notation on the fly.
+ *
+ * @see Base64
+ * @since 1.3
+ */
+ public static class OutputStream extends java.io.FilterOutputStream {
+
+ private boolean encode;
+ private int position;
+ private byte[] buffer;
+ private int bufferLength;
+ private int lineLength;
+ private boolean breakLines;
+ private byte[] b4; // Scratch used in a few places
+ private boolean suspendEncoding;
+ private int options; // Record for later
+ private byte[] decodabet; // Local copies to avoid extra method calls
+
+ /**
+ * Constructs a {@link Base64.OutputStream} in ENCODE mode.
+ *
+ * @param out
+ * the <tt>java.io.OutputStream</tt> to which data will be
+ * written.
+ * @since 1.3
+ */
+ public OutputStream(java.io.OutputStream out) {
+ this(out, ENCODE);
+ } // end constructor
+
+ /**
+ * Constructs a {@link Base64.OutputStream} in either ENCODE or DECODE
+ * mode.
+ * <p>
+ * Valid options:
+ *
+ * <pre>
+ * ENCODE or DECODE: Encode or Decode as data is read.
+ * DO_BREAK_LINES: don't break lines at 76 characters
+ * (only meaningful when encoding)</i>
+ * </pre>
+ * <p>
+ * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
+ *
+ * @param out
+ * the <tt>java.io.OutputStream</tt> to which data will be
+ * written.
+ * @param options
+ * Specified options.
+ * @see Base64#ENCODE
+ * @see Base64#DECODE
+ * @see Base64#DO_BREAK_LINES
+ * @since 1.3
+ */
+ public OutputStream(java.io.OutputStream out, int options) {
+ super(out);
+ this.breakLines = (options & DO_BREAK_LINES) != 0;
+ this.encode = (options & ENCODE) != 0;
+ this.bufferLength = encode ? 3 : 4;
+ this.buffer = new byte[bufferLength];
+ this.position = 0;
+ this.lineLength = 0;
+ this.suspendEncoding = false;
+ this.b4 = new byte[4];
+ this.options = options;
+ this.decodabet = getDecodabet(options);
+ } // end constructor
+
+ /**
+ * Writes the byte to the output stream after converting to/from Base64
+ * notation. When encoding, bytes are buffered three at a time before
+ * the output stream actually gets a write() call. When decoding, bytes
+ * are buffered four at a time.
+ *
+ * @param theByte
+ * the byte to write
+ * @since 1.3
+ */
+ @Override
+ public void write(int theByte) throws java.io.IOException {
+ // Encoding suspended?
+ if (suspendEncoding) {
+ this.out.write(theByte);
+ return;
+ } // end if: supsended
+
+ // Encode?
+ if (encode) {
+ buffer[position++] = (byte) theByte;
+ if (position >= bufferLength) { // Enough to encode.
+
+ this.out
+ .write(encode3to4(b4, buffer, bufferLength, options));
+
+ lineLength += 4;
+ if (breakLines && lineLength >= MAX_LINE_LENGTH) {
+ this.out.write(NEW_LINE);
+ lineLength = 0;
+ } // end if: end of line
+
+ position = 0;
+ } // end if: enough to output
+ } // end if: encoding
+
+ // Else, Decoding
+ else {
+ // Meaningful Base64 character?
+ if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) {
+ buffer[position++] = (byte) theByte;
+ if (position >= bufferLength) { // Enough to output.
+
+ int len = Base64.decode4to3(buffer, 0, b4, 0, options);
+ out.write(b4, 0, len);
+ position = 0;
+ } // end if: enough to output
+ } // end if: meaningful base64 character
+ else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) {
+ throw new java.io.IOException(
+ "Invalid character in Base64 data.");
+ } // end else: not white space either
+ } // end else: decoding
+ } // end write
+
+ /**
+ * Calls {@link #write(int)} repeatedly until <var>len</var> bytes are
+ * written.
+ *
+ * @param theBytes
+ * array from which to read bytes
+ * @param off
+ * offset for array
+ * @param len
+ * max number of bytes to read into array
+ * @since 1.3
+ */
+ @Override
+ public void write(byte[] theBytes, int off, int len)
+ throws java.io.IOException {
+ // Encoding suspended?
+ if (suspendEncoding) {
+ this.out.write(theBytes, off, len);
+ return;
+ } // end if: supsended
+
+ for (int i = 0; i < len; i++) {
+ write(theBytes[off + i]);
+ } // end for: each byte written
+
+ } // end write
+
+ /**
+ * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer
+ * without closing the stream.
+ *
+ * @throws java.io.IOException
+ * if there's an error.
+ */
+ public void flushBase64() throws java.io.IOException {
+ if (position > 0) {
+ if (encode) {
+ out.write(encode3to4(b4, buffer, position, options));
+ position = 0;
+ } // end if: encoding
+ else {
+ throw new java.io.IOException(
+ "Base64 input not properly padded.");
+ } // end else: decoding
+ } // end if: buffer partially full
+
+ } // end flush
+
+ /**
+ * Flushes and closes (I think, in the superclass) the stream.
+ *
+ * @since 1.3
+ */
+ @Override
+ public void close() throws java.io.IOException {
+ // 1. Ensure that pending characters are written
+ flushBase64();
+
+ // 2. Actually close the stream
+ // Base class both flushes and closes.
+ super.close();
+
+ buffer = null;
+ out = null;
+ } // end close
+
+ /**
+ * Suspends encoding of the stream. May be helpful if you need to embed
+ * a piece of base64-encoded data in a stream.
+ *
+ * @throws java.io.IOException
+ * if there's an error flushing
+ * @since 1.5.1
+ */
+ public void suspendEncoding() throws java.io.IOException {
+ flushBase64();
+ this.suspendEncoding = true;
+ } // end suspendEncoding
+
+ /**
+ * Resumes encoding of the stream. May be helpful if you need to embed a
+ * piece of base64-encoded data in a stream.
+ *
+ * @since 1.5.1
+ */
+ public void resumeEncoding() {
+ this.suspendEncoding = false;
+ } // end resumeEncoding
+
+ } // end inner class OutputStream
+
+} // end class Base64
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/DuoWeb.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/DuoWeb.java b/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/DuoWeb.java
new file mode 100644
index 0000000..223a110
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/DuoWeb.java
@@ -0,0 +1,138 @@
+package com.duosecurity.duoweb;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+public final class DuoWeb {
+ private static final String DUO_PREFIX = "TX";
+ private static final String APP_PREFIX = "APP";
+ private static final String AUTH_PREFIX = "AUTH";
+
+ private static final int DUO_EXPIRE = 300;
+ private static final int APP_EXPIRE = 3600;
+
+ private static final int IKEY_LEN = 20;
+ private static final int SKEY_LEN = 40;
+ private static final int AKEY_LEN = 40;
+
+ public static final String ERR_USER = "ERR|The username passed to sign_request() is invalid.";
+ public static final String ERR_IKEY = "ERR|The Duo integration key passed to sign_request() is invalid.";
+ public static final String ERR_SKEY = "ERR|The Duo secret key passed to sign_request() is invalid.";
+ public static final String ERR_AKEY = "ERR|The application secret key passed to sign_request() must be at least " + AKEY_LEN + " characters.";
+ public static final String ERR_UNKNOWN = "ERR|An unknown error has occurred.";
+
+ public static String signRequest(final String ikey, final String skey, final String akey, final String username) {
+ return signRequest(ikey, skey, akey, username, System.currentTimeMillis() / 1000);
+ }
+
+ public static String signRequest(final String ikey, final String skey, final String akey, final String username, final long time) {
+ final String duo_sig;
+ final String app_sig;
+
+ if (username.equals("")) {
+ return ERR_USER;
+ }
+ if (username.indexOf('|') != -1) {
+ return ERR_USER;
+ }
+ if (ikey.equals("") || ikey.length() != IKEY_LEN) {
+ return ERR_IKEY;
+ }
+ if (skey.equals("") || skey.length() != SKEY_LEN) {
+ return ERR_SKEY;
+ }
+ if (akey.equals("") || akey.length() < AKEY_LEN) {
+ return ERR_AKEY;
+ }
+
+ try {
+ duo_sig = signVals(skey, username, ikey, DUO_PREFIX, DUO_EXPIRE, time);
+ app_sig = signVals(akey, username, ikey, APP_PREFIX, APP_EXPIRE, time);
+ } catch (Exception e) {
+ return ERR_UNKNOWN;
+ }
+
+ return duo_sig + ":" + app_sig;
+ }
+
+ public static String verifyResponse(final String ikey, final String skey, final String akey, final String sig_response)
+ throws DuoWebException, NoSuchAlgorithmException, InvalidKeyException, IOException {
+ return verifyResponse(ikey, skey, akey, sig_response, System.currentTimeMillis() / 1000);
+ }
+
+ public static String verifyResponse(final String ikey, final String skey, final String akey, final String sig_response, final long time)
+ throws DuoWebException, NoSuchAlgorithmException, InvalidKeyException, IOException {
+ String auth_user = null;
+ String app_user = null;
+
+ final String[] sigs = sig_response.split(":");
+ final String auth_sig = sigs[0];
+ final String app_sig = sigs[1];
+
+ auth_user = parseVals(skey, auth_sig, AUTH_PREFIX, ikey, time);
+ app_user = parseVals(akey, app_sig, APP_PREFIX, ikey, time);
+
+ if (!auth_user.equals(app_user)) {
+ throw new DuoWebException("Authentication failed.");
+ }
+
+ return auth_user;
+ }
+
+ private static String signVals(final String key, final String username, final String ikey, final String prefix, final int expire, final long time)
+ throws InvalidKeyException, NoSuchAlgorithmException {
+ final long expire_ts = time + expire;
+ final String exp = Long.toString(expire_ts);
+
+ final String val = username + "|" + ikey + "|" + exp;
+ final String cookie = prefix + "|" + Base64.encodeBytes(val.getBytes());
+ final String sig = Util.hmacSign(key, cookie);
+
+ return cookie + "|" + sig;
+ }
+
+ private static String parseVals(final String key, final String val, final String prefix, final String ikey, final long time)
+ throws InvalidKeyException, NoSuchAlgorithmException, IOException, DuoWebException {
+
+ final String[] parts = val.split("\\|");
+ if (parts.length != 3) {
+ throw new DuoWebException("Invalid response");
+ }
+
+ final String u_prefix = parts[0];
+ final String u_b64 = parts[1];
+ final String u_sig = parts[2];
+
+ final String sig = Util.hmacSign(key, u_prefix + "|" + u_b64);
+ if (!Util.hmacSign(key, sig).equals(Util.hmacSign(key, u_sig))) {
+ throw new DuoWebException("Invalid response");
+ }
+
+ if (!u_prefix.equals(prefix)) {
+ throw new DuoWebException("Invalid response");
+ }
+
+ final byte[] decoded = Base64.decode(u_b64);
+ final String cookie = new String(decoded);
+
+ final String[] cookie_parts = cookie.split("\\|");
+ if (cookie_parts.length != 3) {
+ throw new DuoWebException("Invalid response");
+ }
+ final String username = cookie_parts[0];
+ final String u_ikey = cookie_parts[1];
+ final String expire = cookie_parts[2];
+
+ if (!u_ikey.equals(ikey)) {
+ throw new DuoWebException("Invalid response");
+ }
+
+ final long expire_ts = Long.parseLong(expire);
+ if (time >= expire_ts) {
+ throw new DuoWebException("Transaction has expired. Please check that the system time is correct.");
+ }
+
+ return username;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/DuoWebException.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/DuoWebException.java b/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/DuoWebException.java
new file mode 100644
index 0000000..f721df7
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/DuoWebException.java
@@ -0,0 +1,8 @@
+package com.duosecurity.duoweb;
+
+public class DuoWebException extends Exception {
+
+ public DuoWebException(String message) {
+ super(message);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/Util.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/Util.java b/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/Util.java
new file mode 100644
index 0000000..55d7712
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/java/com/duosecurity/duoweb/Util.java
@@ -0,0 +1,26 @@
+package com.duosecurity.duoweb;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+public class Util {
+ public static String hmacSign(String skey, String data)
+ throws NoSuchAlgorithmException, InvalidKeyException {
+ SecretKeySpec key = new SecretKeySpec(skey.getBytes(), "HmacSHA1");
+ Mac mac = Mac.getInstance("HmacSHA1");
+ mac.init(key);
+ byte[] raw = mac.doFinal(data.getBytes());
+ return bytesToHex(raw);
+ }
+
+ public static String bytesToHex(byte[] b) {
+ String result = "";
+ for (int i = 0; i < b.length; i++) {
+ result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
+ }
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/AuthenticationProviderService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/AuthenticationProviderService.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/AuthenticationProviderService.java
new file mode 100644
index 0000000..c3dd8ee
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/AuthenticationProviderService.java
@@ -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.auth.duo;
+
+import com.google.inject.Inject;
+import java.util.Collections;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.guacamole.GuacamoleClientException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.auth.duo.conf.ConfigurationService;
+import org.apache.guacamole.auth.duo.form.DuoSignedResponseField;
+import org.apache.guacamole.form.Field;
+import org.apache.guacamole.net.auth.AuthenticatedUser;
+import org.apache.guacamole.net.auth.Credentials;
+import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
+import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
+
+/**
+ * Service providing convenience functions for the Duo AuthenticationProvider
+ * implementation.
+ */
+public class AuthenticationProviderService {
+
+ /**
+ * Service for retrieving Duo configuration information.
+ */
+ @Inject
+ private ConfigurationService confService;
+
+ /**
+ * Service for verifying users with the DuoWeb API.
+ */
+ @Inject
+ private DuoWebService duoWebService;
+
+ /**
+ * Verifies the identity of the given user via the Duo multi-factor
+ * authentication service. If a signed response from Duo has not already
+ * been provided, a signed response from Duo is requested in the
+ * form of additional expected credentials. Any provided signed response
+ * is cryptographically verified. If no signed response is present, or the
+ * signed response is invalid, an exception is thrown.
+ *
+ * @param authenticatedUser
+ * The user whose identity should be verified against Duo.
+ *
+ * @throws GuacamoleException
+ * If required Duo-specific configuration options are missing or
+ * malformed, or if the user's identity cannot be verified.
+ */
+ public void verifyAuthenticatedUser(AuthenticatedUser authenticatedUser)
+ throws GuacamoleException {
+
+ // Pull the original HTTP request used to authenticate
+ Credentials credentials = authenticatedUser.getCredentials();
+ HttpServletRequest request = credentials.getRequest();
+
+ // Ignore anonymous users
+ if (authenticatedUser.getIdentifier().equals(AuthenticatedUser.ANONYMOUS_IDENTIFIER))
+ return;
+
+ // Retrieve signed Duo response from request
+ String signedResponse = request.getParameter(DuoSignedResponseField.PARAMETER_NAME);
+
+ // If no signed response, request one
+ if (signedResponse == null) {
+
+ // Create field which requests a signed response from Duo that
+ // verifies the identity of the given user via the configured
+ // Duo API endpoint
+ Field signedResponseField = new DuoSignedResponseField(
+ confService.getAPIHostname(),
+ duoWebService.createSignedRequest(authenticatedUser));
+
+ // Create an overall description of the additional credentials
+ // required to verify identity
+ CredentialsInfo expectedCredentials = new CredentialsInfo(
+ Collections.singletonList(signedResponseField));
+
+ // Request additional credentials
+ throw new GuacamoleInsufficientCredentialsException(
+ "LOGIN.INFO_DUO_AUTH_REQUIRED", expectedCredentials);
+
+ }
+
+ // If signed response does not verify this user's identity, abort auth
+ if (!duoWebService.isValidSignedResponse(authenticatedUser, signedResponse))
+ throw new GuacamoleClientException("LOGIN.INFO_DUO_VALIDATION_CODE_INCORRECT");
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java
new file mode 100644
index 0000000..bcf8c83
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java
@@ -0,0 +1,100 @@
+/*
+ * 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.duo;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.AuthenticatedUser;
+import org.apache.guacamole.net.auth.AuthenticationProvider;
+import org.apache.guacamole.net.auth.Credentials;
+import org.apache.guacamole.net.auth.UserContext;
+
+/**
+ * AuthenticationProvider implementation which uses Duo as an additional
+ * authentication factor for users which have already been authenticated by
+ * some other AuthenticationProvider.
+ */
+public class DuoAuthenticationProvider implements AuthenticationProvider {
+
+ /**
+ * Injector which will manage the object graph of this authentication
+ * provider.
+ */
+ private final Injector injector;
+
+ /**
+ * Creates a new DuoAuthenticationProvider that verifies users
+ * using the Duo authentication service
+ *
+ * @throws GuacamoleException
+ * If a required property is missing, or an error occurs while parsing
+ * a property.
+ */
+ public DuoAuthenticationProvider() throws GuacamoleException {
+
+ // Set up Guice injector.
+ injector = Guice.createInjector(
+ new DuoAuthenticationProviderModule(this)
+ );
+
+ }
+
+ @Override
+ public String getIdentifier() {
+ return "duo";
+ }
+
+ @Override
+ public AuthenticatedUser authenticateUser(Credentials credentials)
+ throws GuacamoleException {
+ return null;
+ }
+
+ @Override
+ public AuthenticatedUser updateAuthenticatedUser(AuthenticatedUser authenticatedUser,
+ Credentials credentials) throws GuacamoleException {
+ return authenticatedUser;
+ }
+
+ @Override
+ public UserContext getUserContext(AuthenticatedUser authenticatedUser)
+ throws GuacamoleException {
+
+ AuthenticationProviderService authProviderService =
+ injector.getInstance(AuthenticationProviderService.class);
+
+ // Verify user against Duo service
+ authProviderService.verifyAuthenticatedUser(authenticatedUser);
+
+ // User has been verified, and authentication should be allowed to
+ // continue
+ return null;
+
+ }
+
+ @Override
+ public UserContext updateUserContext(UserContext context,
+ AuthenticatedUser authenticatedUser, Credentials credentials)
+ throws GuacamoleException {
+ return context;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProviderModule.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProviderModule.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProviderModule.java
new file mode 100644
index 0000000..c3c129c
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProviderModule.java
@@ -0,0 +1,81 @@
+/*
+ * 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.duo;
+
+import com.google.inject.AbstractModule;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.auth.duo.conf.ConfigurationService;
+import org.apache.guacamole.environment.Environment;
+import org.apache.guacamole.environment.LocalEnvironment;
+import org.apache.guacamole.net.auth.AuthenticationProvider;
+
+/**
+ * Guice module which configures Duo-specific injections.
+ */
+public class DuoAuthenticationProviderModule extends AbstractModule {
+
+ /**
+ * Guacamole server environment.
+ */
+ private final Environment environment;
+
+ /**
+ * A reference to the DuoAuthenticationProvider on behalf of which this
+ * module has configured injection.
+ */
+ private final AuthenticationProvider authProvider;
+
+ /**
+ * Creates a new Duo authentication provider module which configures
+ * injection for the DuoAuthenticationProvider.
+ *
+ * @param authProvider
+ * The AuthenticationProvider for which injection is being configured.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the Guacamole server
+ * environment.
+ */
+ public DuoAuthenticationProviderModule(AuthenticationProvider authProvider)
+ throws GuacamoleException {
+
+ // Get local environment
+ this.environment = new LocalEnvironment();
+
+ // Store associated auth provider
+ this.authProvider = authProvider;
+
+ }
+
+ @Override
+ protected void configure() {
+
+ // Bind core implementations of guacamole-ext classes
+ bind(AuthenticationProvider.class).toInstance(authProvider);
+ bind(Environment.class).toInstance(environment);
+
+ // Bind Duo-specific services
+ bind(AuthenticationProviderService.class);
+ bind(ConfigurationService.class);
+ bind(DuoWebService.class);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/48af3ef4/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoWebService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoWebService.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoWebService.java
new file mode 100644
index 0000000..3cdfdde
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoWebService.java
@@ -0,0 +1,212 @@
+/*
+ * 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.duo;
+
+import com.duosecurity.duoweb.DuoWeb;
+import com.duosecurity.duoweb.DuoWebException;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.guacamole.GuacamoleClientException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.auth.duo.conf.ConfigurationService;
+import org.apache.guacamole.net.auth.AuthenticatedUser;
+
+/**
+ * Service which wraps the DuoWeb Java API, providing predictable behavior and
+ * error handling.
+ */
+public class DuoWebService {
+
+ /**
+ * A regular expression which matches a valid signature part of a Duo
+ * signed response. A signature part may not contain pipe symbols (which
+ * act as delimiters between parts) nor colons (which act as delimiters
+ * between signatures).
+ */
+ private final String SIGNATURE_PART = "[^:|]*";
+
+ /**
+ * A regular expression which matches a valid signature within a Duo
+ * signed response. Each signature is made up of three distinct parts,
+ * separated by pipe symbols.
+ */
+ private final String SIGNATURE = SIGNATURE_PART + "\\|" + SIGNATURE_PART + "\\|" + SIGNATURE_PART;
+
+ /**
+ * A regular expression which matches a valid Duo signed response. Each
+ * response is made up of two signatures, separated by a colon.
+ */
+ private final String RESPONSE = SIGNATURE + ":" + SIGNATURE;
+
+ /**
+ * A Pattern which matches valid Duo signed responses. Strings which will
+ * be passed to DuoWeb.verifyResponse() MUST be matched against this
+ * Pattern. Strings which do not match this Pattern may cause
+ * DuoWeb.verifyResponse() to throw unchecked exceptions.
+ */
+ private final Pattern RESPONSE_PATTERN = Pattern.compile(RESPONSE);
+
+ /**
+ * Service for retrieving Duo configuration information.
+ */
+ @Inject
+ private ConfigurationService confService;
+
+ /**
+ * Creates and signs a new request to verify the identity of the given
+ * user. This request may ultimately be sent to Duo, resulting in a signed
+ * response from Duo if that verification succeeds.
+ *
+ * @param authenticatedUser
+ * The user whose identity should be verified.
+ *
+ * @return
+ * A signed user verification request which can be sent to Duo.
+ *
+ * @throws GuacamoleException
+ * If required Duo-specific configuration options are missing or
+ * invalid, or if an error occurs within the DuoWeb API which prevents
+ * generation of the signed request.
+ */
+ public String createSignedRequest(AuthenticatedUser authenticatedUser)
+ throws GuacamoleException {
+
+ // Retrieve username from externally-authenticated user
+ String username = authenticatedUser.getIdentifier();
+
+ // Retrieve Duo-specific keys from configuration
+ String ikey = confService.getIntegrationKey();
+ String skey = confService.getSecretKey();
+ String akey = confService.getApplicationKey();
+
+ // Create signed request for the provided user
+ String signedRequest = DuoWeb.signRequest(ikey, skey, akey, username);
+
+ if (DuoWeb.ERR_AKEY.equals(signedRequest))
+ throw new GuacamoleServerException("The Duo application key "
+ + "must is not valid. Duo application keys must be at "
+ + "least 40 characters long.");
+
+ if (DuoWeb.ERR_IKEY.equals(signedRequest))
+ throw new GuacamoleServerException("The provided Duo integration "
+ + "key is not valid. Integration keys must be exactly 20 "
+ + "characters long.");
+
+ if (DuoWeb.ERR_SKEY.equals(signedRequest))
+ throw new GuacamoleServerException("The provided Duo secret key "
+ + "is not valid. Secret keys must be exactly 40 "
+ + "characters long.");
+
+ if (DuoWeb.ERR_USER.equals(signedRequest))
+ throw new GuacamoleServerException("The provided username is "
+ + "not valid. Duo usernames may not be blank, nor may "
+ + "they contain pipe symbols (\"|\").");
+
+ if (DuoWeb.ERR_UNKNOWN.equals(signedRequest))
+ throw new GuacamoleServerException("An unknown error within the "
+ + "DuoWeb API prevented the signed request from being "
+ + "generated.");
+
+ // Return signed request if no error is indicated
+ return signedRequest;
+
+ }
+
+ /**
+ * Returns whether the given signed response is a valid response from Duo
+ * which verifies the identity of the given user. If the given response is
+ * invalid or does not verify the identity of the given user (including if
+ * it is a valid response which verifies the identity of a DIFFERENT user),
+ * false is returned.
+ *
+ * @param authenticatedUser
+ * The user that the given signed response should verify.
+ *
+ * @param signedResponse
+ * The signed response received from Duo in response to a signed
+ * request.
+ *
+ * @return
+ * true if the signed response is a valid response from Duo AND verifies
+ * the identity of the given user, false otherwise.
+ *
+ * @throws GuacamoleException
+ * If required Duo-specific configuration options are missing or
+ * invalid, or if an error occurs within the DuoWeb API which prevents
+ * validation of the signed response.
+ */
+ public boolean isValidSignedResponse(AuthenticatedUser authenticatedUser,
+ String signedResponse) throws GuacamoleException {
+
+ // Verify signature response format will not cause
+ // DuoWeb.verifyResponse() to fail with unchecked exceptions
+ Matcher responseMatcher = RESPONSE_PATTERN.matcher(signedResponse);
+ if (!responseMatcher.matches())
+ throw new GuacamoleClientException("Invalid Duo response format.");
+
+ // Retrieve username from externally-authenticated user
+ String username = authenticatedUser.getIdentifier();
+
+ // Retrieve Duo-specific keys from configuration
+ String ikey = confService.getIntegrationKey();
+ String skey = confService.getSecretKey();
+ String akey = confService.getApplicationKey();
+
+ // Verify validity of signed response
+ String verifiedUsername;
+ try {
+ verifiedUsername = DuoWeb.verifyResponse(ikey, skey, akey,
+ signedResponse);
+ }
+
+ // Rethrow any errors as appropriate GuacamoleExceptions
+ catch (IOException e) {
+ throw new GuacamoleClientException("Decoding of Duo response "
+ + "failed: Invalid base64 content.", e);
+ }
+ catch (NumberFormatException e) {
+ throw new GuacamoleClientException("Decoding of Duo response "
+ + "failed: Invalid expiry timestamp.", e);
+ }
+ catch (InvalidKeyException e) {
+ throw new GuacamoleServerException("Unable to produce HMAC "
+ + "signature: " + e.getMessage(), e);
+ }
+ catch (NoSuchAlgorithmException e) {
+ throw new GuacamoleServerException("Environment is missing "
+ + "support for producing HMAC-SHA1 signatures.", e);
+ }
+ catch (DuoWebException e) {
+ throw new GuacamoleClientException("Duo response verification "
+ + "failed: " + e.getMessage(), e);
+ }
+
+ // Signed response is valid iff the associated username matches the
+ // user's username
+ return username.equals(verifiedUsername);
+
+ }
+
+}
[09/12] incubator-guacamole-client git commit: GUACAMOLE-136: Bundle
guacamole-auth-duo .jar into .tar.gz, along with licenses.
Posted by jm...@apache.org.
GUACAMOLE-136: Bundle guacamole-auth-duo .jar into .tar.gz, along with licenses.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/c3c553a8
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/c3c553a8
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/c3c553a8
Branch: refs/heads/master
Commit: c3c553a8999ae9ca212ce2c44d60806795e7db96
Parents: 3457bcf
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Dec 5 20:31:03 2016 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Dec 5 22:19:46 2016 -0800
----------------------------------------------------------------------
extensions/guacamole-auth-duo/pom.xml | 22 ++++++++
.../src/main/assembly/dist.xml | 53 ++++++++++++++++++++
2 files changed, 75 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/c3c553a8/extensions/guacamole-auth-duo/pom.xml
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/pom.xml b/extensions/guacamole-auth-duo/pom.xml
index b129591..22ca020 100644
--- a/extensions/guacamole-auth-duo/pom.xml
+++ b/extensions/guacamole-auth-duo/pom.xml
@@ -133,6 +133,28 @@
</executions>
</plugin>
+ <!-- Assembly plugin - for easy distribution -->
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.5.3</version>
+ <configuration>
+ <finalName>${project.artifactId}-${project.version}</finalName>
+ <appendAssemblyId>false</appendAssemblyId>
+ <descriptors>
+ <descriptor>src/main/assembly/dist.xml</descriptor>
+ </descriptors>
+ </configuration>
+ <executions>
+ <execution>
+ <id>make-dist-archive</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
<!-- Copy dependencies prior to packaging -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/c3c553a8/extensions/guacamole-auth-duo/src/main/assembly/dist.xml
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-duo/src/main/assembly/dist.xml b/extensions/guacamole-auth-duo/src/main/assembly/dist.xml
new file mode 100644
index 0000000..b89fd53
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/assembly/dist.xml
@@ -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>src/licenses</directory>
+ </fileSet>
+
+ <!-- Include extension .jar -->
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory></outputDirectory>
+ <includes>
+ <include>*.jar</include>
+ </includes>
+ </fileSet>
+
+ </fileSets>
+
+</assembly>