diff --git a/README.md b/README.md index 79bbd85..606218c 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,33 @@ #Silverpeas-Spnego ###This project is a fork of the spnego.sourceforge.net project -* Fork of [https://github.com/joval/SPNEGO](https://github.com/joval/SPNEGO) which corresponds to stable spnego-r7.jar (2010-OCT-15) -* Install Guide, Reference and API Documentation can be found at: [http://spnego.sourceforge.net](http://spnego.sourceforge.net) -* Before getting started, the pre-flight doc is a must read [http://spnego.sourceforge.net/pre_flight.html](http://spnego.sourceforge.net/pre_flight.html) -* Need JDK 1.6 or higher and servlet-api.jar required to compile source -In order to perform user authentication in our Silverpeas product by SSO mechanism using SPNEGO and Kerberos, we were interested in the Sourceforge Spnego project. -Despite several successful integration tests, we identified some additional needs in order to manage more precisely in a JEE application, such as Silverpeas, the different possible errors that can happen during the SSO process for a user. -We then made ​​the necessary developments and have proposed them as a contribution to the project [https://github.com/joval/SPNEGO] (https://github.com/joval/SPNEGO). -As it has not been integrated, and after several months without any response, we decided to make our own fork of the project that includes our needs. +* Fork of [https://github.com/joval/SPNEGO](https://github.com/joval/SPNEGO) which corresponds to + stable spnego-r7.jar (2010-OCT-15) +* Install Guide, Reference and API Documentation can be found + at: [http://spnego.sourceforge.net](http://spnego.sourceforge.net) +* Before getting started, the pre-flight doc is a must + read [http://spnego.sourceforge.net/pre_flight.html](http://spnego.sourceforge.net/pre_flight.html) +* Require JDK 11 or higher and Jakarta EE 10 servlet && SOAP APIs to compile source -###The contributions of Silverpeas's version : -* adding apache maven building capabilities -* adding typed runtime exception that can be used to handle SSO errors in the JEE application (not activated by default, to activate it set the added filter parameter "spnego.throw.typedRuntimeException" to true) -* upgrading the SPNEGO HTTP Filter so that it can be used in several URL matching (filter mapping) -* modifying the extraction of remote user name (removing from the Kerberos Principal only the part of the Kerberos REALM) \ No newline at end of file +In order to perform user authentication in our Silverpeas product by SSO using the SPNEGO +mechanism within the Kerberos protocol, we were interested in the Sourceforge Spnego project. +Despite several successful integration tests, we identified some additional requirements in +order to manage more precisely, in a Jakarta EE application such as Silverpeas, the different +possible errors that can happen during the SSO negotiation process for a user. + +We then made the necessary developments and have proposed them as a contribution to the +project [https://github.com/joval/SPNEGO] (https://github.com/joval/SPNEGO). +As it has not been integrated, and after several months without any responses or feedbacks, we +decided to make our own fork of the project that includes our needs. + +###The contributions of Silverpeas's version: + +* Adding apache maven building capabilities +* Migrating to Jakarta EE +* Adding typed runtime exceptions that can be used to handle SSO errors in the Jakarta EE + application (not enabled by default, this feature can be enabled by setting the filter + parameter `spnego.throw.typedRuntimeException` to `true`) +* Updating the SPNEGO HTTP Filter so that it can be used in several URL matchers (filter mapping) +* Modifying the extraction of the remote user name (removing from the Kerberos Principal only the + part of the Kerberos REALM) \ No newline at end of file diff --git a/pom.xml b/pom.xml index cbcb952..f796f90 100644 --- a/pom.xml +++ b/pom.xml @@ -1,29 +1,52 @@ - + 4.0.0 org.silverpeas.spnego silverpeas-spnego - 1.1-SNAPSHOT + 2.0-SNAPSHOT jar Silverpeas SPNEGO - + + + 11 + + - javax.servlet - javax.servlet-api - 3.0.1 + jakarta.servlet + jakarta.servlet-api + 6.0.0 + provided + + + jakarta.xml.soap + jakarta.xml.soap-api + 3.0.0 provided + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + ${maven.compiler.release} + UTF-8 + true + true + + org.apache.maven.plugins maven-release-plugin - 2.4.1 + 3.0.1 true false @@ -35,6 +58,7 @@ org.apache.maven.plugins maven-source-plugin + 3.3.0 attach-sources @@ -68,7 +92,8 @@ scm:git:git@github.com:Silverpeas/Silverpeas-Spnego.git - scm:git:git@github.com:Silverpeas/Silverpeas-Spnego.git + scm:git:git@github.com:Silverpeas/Silverpeas-Spnego.git + https://gitub.com/Silverpeas/Silverpeas-Spnego.git HEAD diff --git a/src/main/java/org/silverpeas/spnego/Base64.java b/src/main/java/org/silverpeas/spnego/Base64.java deleted file mode 100644 index 2e7492b..0000000 --- a/src/main/java/org/silverpeas/spnego/Base64.java +++ /dev/null @@ -1,105 +0,0 @@ -/* Copyright (C) 2003 "Eric Glass" - * Encodes and decodes to and from Base64 notation. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -package org.silverpeas.spnego; - -public final class Base64 { - - private static final String ALPHABET = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - private Base64() { - // default private - } - - /** - * Base-64 encodes the supplied block of data. Line wrapping is not - * applied on output. - * @param bytes The block of data that is to be Base-64 encoded. - * @return A String containing the encoded data. - */ - public static String encode(final byte[] bytes) { - int length = bytes.length; - - if (length == 0) { - return ""; - } - - final StringBuilder buffer = new StringBuilder((int) Math.ceil(length / 3d) * 4); - final int remainder = length % 3; - length -= remainder; - int block; - int idx = 0; - while (idx < length) { - block = ((bytes[idx++] & 0xff) << 16) | ((bytes[idx++] & 0xff) << 8) | (bytes[idx++] & 0xff); - buffer.append(ALPHABET.charAt(block >>> 18)); - buffer.append(ALPHABET.charAt((block >>> 12) & 0x3f)); - buffer.append(ALPHABET.charAt((block >>> 6) & 0x3f)); - buffer.append(ALPHABET.charAt(block & 0x3f)); - } - if (remainder == 0) { - return buffer.toString(); - } - if (remainder == 1) { - block = (bytes[idx] & 0xff) << 4; - buffer.append(ALPHABET.charAt(block >>> 6)); - buffer.append(ALPHABET.charAt(block & 0x3f)); - buffer.append("=="); - return buffer.toString(); - } - block = (((bytes[idx++] & 0xff) << 8) | ((bytes[idx]) & 0xff)) << 2; - buffer.append(ALPHABET.charAt(block >>> 12)); - buffer.append(ALPHABET.charAt((block >>> 6) & 0x3f)); - buffer.append(ALPHABET.charAt(block & 0x3f)); - buffer.append("="); - return buffer.toString(); - } - - /** - * Decodes the supplied Base-64 encoded string. - * @param string The Base-64 encoded string that is to be decoded. - * @return A byte[] containing the decoded data block. - */ - public static byte[] decode(final String string) { - final int length = string.length(); - if (length == 0) { - return new byte[0]; - } - - final int pad = - (string.charAt(length - 2) == '=') ? 2 : (string.charAt(length - 1) == '=') ? 1 : 0; - final int size = length * 3 / 4 - pad; - byte[] buffer = new byte[size]; - int block; - int idx = 0; - int index = 0; - while (idx < length) { - block = (ALPHABET.indexOf(string.charAt(idx++)) & 0xff) << 18 | - (ALPHABET.indexOf(string.charAt(idx++)) & 0xff) << 12 | - (ALPHABET.indexOf(string.charAt(idx++)) & 0xff) << 6 | - (ALPHABET.indexOf(string.charAt(idx++)) & 0xff); - buffer[index++] = (byte) (block >>> 16); - if (index < size) { - buffer[index++] = (byte) ((block >>> 8) & 0xff); - } - if (index < size) { - buffer[index++] = (byte) (block & 0xff); - } - } - return buffer; - } -} diff --git a/src/main/java/org/silverpeas/spnego/DelegateServletRequest.java b/src/main/java/org/silverpeas/spnego/DelegateServletRequest.java index 4f84725..89045b6 100644 --- a/src/main/java/org/silverpeas/spnego/DelegateServletRequest.java +++ b/src/main/java/org/silverpeas/spnego/DelegateServletRequest.java @@ -1,4 +1,5 @@ -/** +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" * * This library is free software; you can redistribute it and/or @@ -20,7 +21,7 @@ import org.ietf.jgss.GSSCredential; -import javax.servlet.ServletRequest; +import jakarta.servlet.ServletRequest; /** * The default installation of Internet Explorer and Active Directory @@ -82,5 +83,6 @@ public interface DelegateServletRequest extends ServletRequest { *

* @return delegated credential or null */ + @SuppressWarnings("unused") GSSCredential getDelegatedCredential(); } diff --git a/src/main/java/org/silverpeas/spnego/SpnegoAuthScheme.java b/src/main/java/org/silverpeas/spnego/SpnegoAuthScheme.java index 8b5a4af..5e8a32d 100644 --- a/src/main/java/org/silverpeas/spnego/SpnegoAuthScheme.java +++ b/src/main/java/org/silverpeas/spnego/SpnegoAuthScheme.java @@ -1,4 +1,5 @@ -/** +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" * * This library is free software; you can redistribute it and/or @@ -20,6 +21,8 @@ import org.silverpeas.spnego.SpnegoHttpFilter.Constants; +import java.util.Base64; + /** * Example schemes are "Negotiate" and "Basic". *

@@ -33,7 +36,7 @@ final class SpnegoAuthScheme { /** * Zero length byte array. */ - private static final transient byte[] EMPTY_BYTE_ARRAY = new byte[0]; + private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; /** * HTTP (Request) "Authorization" Header scheme. @@ -61,8 +64,9 @@ final class SpnegoAuthScheme { private final transient boolean ntlm; /** - * @param authScheme - * @param authToken + * Constructs a Spnego authentication scheme. + * @param authScheme the authentication scheme used in the authentication parameter. + * @param authToken the authentication token carried with the authentication parameter. */ public SpnegoAuthScheme(final String authScheme, final String authToken) { this.scheme = authScheme; @@ -115,6 +119,6 @@ public String getScheme() { * @return copy of token */ public byte[] getToken() { - return (null == this.token) ? EMPTY_BYTE_ARRAY : Base64.decode(this.token); + return (null == this.token) ? EMPTY_BYTE_ARRAY : Base64.getDecoder().decode(this.token); } } diff --git a/src/main/java/org/silverpeas/spnego/SpnegoAuthenticator.java b/src/main/java/org/silverpeas/spnego/SpnegoAuthenticator.java index fc7785d..ac483a7 100644 --- a/src/main/java/org/silverpeas/spnego/SpnegoAuthenticator.java +++ b/src/main/java/org/silverpeas/spnego/SpnegoAuthenticator.java @@ -1,17 +1,17 @@ -/** - * Copyright (C) 2014 Silverpeas +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -28,14 +28,17 @@ import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; -import javax.servlet.FilterConfig; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; + +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import java.io.FileNotFoundException; import java.io.IOException; import java.net.URISyntaxException; import java.security.PrivilegedActionException; +import java.util.Base64; import java.util.Collections; import java.util.Enumeration; import java.util.Map; @@ -46,8 +49,7 @@ /** * Handles SPNEGO or Basic - * authentication. + * href="http://en.wikipedia.org/wiki/Basic_access_authentication">Basic authentication. *

*

* Package scope is deliberate; this Class MUST NOT be used/referenced directly @@ -55,37 +57,35 @@ *

*

*

- * Basic Authentication must be enabled through the filter configuration. See - * an example web.xml configuration in the installing on tomcat documentation or the - * {@link SpnegoHttpFilter} javadoc. + * Basic Authentication must be enabled through the filter configuration. See an example web.xml + * configuration in the installing on tomcat documentation or the {@link SpnegoHttpFilter} javadoc. *

*

*

- * Localhost is supported but must be enabled through the filter configuration. Allowing - * requests to come from the DNS http://localhost will obviate the requirement that a - * service must have an SPN. Note that Kerberos authentication (if localhost) does - * not occur but instead simply returns the System.getProperty("user.name") - * or the Server's pre-authentication username. + * Localhost is supported but must be enabled through the filter configuration. Allowing requests to + * come from the DNS http://localhost will obviate the requirement that a service must have an SPN. + * Note that Kerberos authentication (if localhost) does not occur but instead simply returns + * the + * System.getProperty("user.name") or the Server's pre-authentication username. *

*

*

- * NTLM tokens are NOT supported. However it is still possible to avoid an error - * being returned by downgrading the authentication from Negotiate NTLM to Basic Auth. + * NTLM tokens are NOT supported. However it is still possible to avoid an error being returned by + * downgrading the authentication from Negotiate NTLM to Basic Auth. *

*

*

- * See the reference docs on how to configure the web.xml to prompt - * when if a request is being made using NTLM. + * See the reference + * docs on how to configure the web.xml to prompt when if a request is being made using NTLM. *

*

*

- * Finally, to see a working example and instructions on how to use a keytab, take - * a look at the creating a server keytab example. + * Finally, to see a working example and instructions on how to use a keytab, take a look at the creating a server + * keytab example. *

+ * * @author Darwin V. Felix */ public final class SpnegoAuthenticator { @@ -154,12 +154,15 @@ public final class SpnegoAuthenticator { /** * Create an authenticator for SPNEGO and/or BASIC authentication. + * * @param config servlet filter initialization parameters - * @throws javax.security.auth.login.LoginException - * @throws org.ietf.jgss.GSSException - * @throws java.security.PrivilegedActionException + * @throws LoginException if a caller-specified name does not appear in the Configuration and + * there is no Configuration entry for "other", or if the auth.login.defaultCallbackHandler + * security property was set, but the implementation class could not be loaded. + * @throws GSSException if a GSS-API error occurs. + * @throws PrivilegedActionException if the execution of some privileged actions aren't allowed */ - public SpnegoAuthenticator(final SpnegoFilterConfig config) + SpnegoAuthenticator(final SpnegoFilterConfig config) throws LoginException, GSSException, PrivilegedActionException { LOGGER.fine("config=" + config); @@ -189,36 +192,36 @@ public SpnegoAuthenticator(final SpnegoFilterConfig config) } /** - * Create an authenticator for SPNEGO and/or BASIC authentication. For third-party - * code/frameworks that want to authenticate via their own filter/valve/code/etc. + * Create an authenticator for SPNEGO and/or BASIC authentication. For third-party code/frameworks + * that want to authenticate via their own filter/valve/code/etc. *

*

- * The ExampleSpnegoAuthenticatorValve.java demonstrates a working example of - * how to use this constructor. + * The ExampleSpnegoAuthenticatorValve.java demonstrates a working example of how to use this + * constructor. *

*

*

* Example of some Map keys and values:
* *

- * Map map = new HashMap(); - * map.put("spnego.krb5.conf", "krb5.conf"); - * map.put("spnego.allow.basic", "true"); - * map.put("spnego.preauth.username", "dfelix"); - * map.put("spnego.preauth.password", "myp@s5"); - * ... + * Map map = new HashMap(); map.put("spnego.krb5.conf", "krb5.conf"); + * map.put("spnego.allow.basic", "true"); map.put("spnego.preauth.username", "dfelix"); + * map.put("spnego.preauth.password", "myp@s5"); ... *

- * SpnegoAuthenticator authenticator = new SpnegoAuthenticator(map); - * ... + * SpnegoAuthenticator authenticator = new SpnegoAuthenticator(map); ... * *

- * @param config - * @throws javax.security.auth.login.LoginException - * @throws org.ietf.jgss.GSSException - * @throws java.security.PrivilegedActionException - * @throws java.io.FileNotFoundException - * @throws java.net.URISyntaxException + * + * @param config a map of key-values configuration properties. + * @throws LoginException if a caller-specified name does not appear in the Configuration and + * there is no Configuration entry for "other", or if the auth.login.defaultCallbackHandler + * security property was set, but the implementation class could not be loaded. + * @throws GSSException if a GSS-API error occurs. + * @throws PrivilegedActionException if the execution of some privileged actions aren't allowed + * @throws FileNotFoundException if there is no login configuration file found + * @throws URISyntaxException if the syntax of the path of the login configuration file is wrong */ + @SuppressWarnings("unused") public SpnegoAuthenticator(final Map config) throws LoginException, GSSException, PrivilegedActionException, FileNotFoundException, URISyntaxException { @@ -240,9 +243,8 @@ public String getInitParameter(final String param) { return map.get(param); } - @SuppressWarnings("rawtypes") @Override - public Enumeration getInitParameterNames() { + public Enumeration getInitParameterNames() { throw new UnsupportedOperationException(); } @@ -261,14 +263,16 @@ public ServletContext getServletContext() { *

*

*

- * Method will throw UnsupportedOperationException if client authz - * request is NOT "Negotiate" or "Basic". + * Method will throw UnsupportedOperationException if the client authentication request is NOT + * "Negotiate" nor "Basic". *

+ * * @param req servlet request * @param resp servlet response * @return null if auth not complete else SpnegoPrincipal of client - * @throws org.ietf.jgss.GSSException - * @throws java.io.IOException + * @throws GSSException if a GSS-API error occurs. + * @throws IOException if an error occurs with the remote server during the authentication + * process. */ public SpnegoPrincipal authenticate(final HttpServletRequest req, final SpnegoHttpServletResponse resp) throws GSSException, IOException { @@ -319,12 +323,12 @@ public SpnegoPrincipal authenticate(final HttpServletRequest req, } /** - * Logout. Since server uses LoginContext to login/pre-authenticate, we must - * also logout when we are done using this object. + * Logout. Since server uses LoginContext to login/pre-authenticate, we must also logout when we + * are done using this object. *

*

- * Generally, instantiators of this class should be the only to call - * dispose() as it indicates that this class will no longer be used. + * Generally, codes using instances of this class have to call dispose() as it indicates + * that those instances will no longer be used. *

*/ public void dispose() { @@ -348,9 +352,10 @@ public void dispose() { * Performs authentication using the BASIC Auth mechanism. *

*

- * Returns null if authentication failed or if the provided - * the auth scheme did not contain BASIC Auth data/token. + * Returns null if authentication failed or if the provided the auth scheme did not contain BASIC + * Auth data/token. *

+ * * @return SpnegoPrincipal for the given auth scheme. */ private SpnegoPrincipal doBasicAuth(final SpnegoAuthScheme scheme, @@ -381,7 +386,7 @@ private SpnegoPrincipal doBasicAuth(final SpnegoAuthScheme scheme, try { // assert - if (null == username || username.isEmpty()) { + if (username.isEmpty()) { throw new LoginException("Username is required."); } @@ -426,9 +431,10 @@ private SpnegoPrincipal doLocalhost() { * Performs authentication using the SPNEGO mechanism. *

*

- * Returns null if authentication failed or if the provided - * the auth scheme did not contain the SPNEGO/GSS token. + * Returns null if authentication failed or if the provided the auth scheme did not contain the + * SPNEGO/GSS token. *

+ * * @return SpnegoPrincipal for the given auth scheme. */ private SpnegoPrincipal doSpnegoAuth(final SpnegoAuthScheme scheme, @@ -446,7 +452,7 @@ private SpnegoPrincipal doSpnegoAuth(final SpnegoAuthScheme scheme, GSSCredential delegCred = null; try { - byte[] token = null; + byte[] token; SpnegoAuthenticator.LOCK.lock(); try { @@ -462,7 +468,7 @@ private SpnegoPrincipal doSpnegoAuth(final SpnegoAuthScheme scheme, } resp.setHeader(Constants.AUTHN_HEADER, - Constants.NEGOTIATE_HEADER + ' ' + Base64.encode(token)); + Constants.NEGOTIATE_HEADER + ' ' + Base64.getEncoder().encodeToString(token)); if (!context.isEstablished()) { LOGGER.fine("context not established"); @@ -492,6 +498,7 @@ private SpnegoPrincipal doSpnegoAuth(final SpnegoAuthScheme scheme, /** * Returns true if HTTP request is from the same host (localhost). + * * @param req servlet request * @return true if HTTP request is from the same host (localhost) */ @@ -502,6 +509,7 @@ private boolean isLocalhost(final HttpServletRequest req) { /** * Returns true if typed runtime exceptions have to be thrown. + * * @return true if typed runtime exceptions have to be thrown */ public boolean isTypedRuntimeExceptionThrown() { diff --git a/src/main/java/org/silverpeas/spnego/SpnegoFilterConfig.java b/src/main/java/org/silverpeas/spnego/SpnegoFilterConfig.java index 06982fc..036a09a 100644 --- a/src/main/java/org/silverpeas/spnego/SpnegoFilterConfig.java +++ b/src/main/java/org/silverpeas/spnego/SpnegoFilterConfig.java @@ -1,17 +1,17 @@ -/** - * Copyright (C) 2014 Silverpeas +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -23,12 +23,15 @@ import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.Configuration; -import javax.servlet.FilterConfig; + +import jakarta.servlet.FilterConfig; + import java.io.File; import java.io.FileNotFoundException; import java.net.URI; import java.net.URISyntaxException; import java.util.Map; +import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; @@ -42,9 +45,9 @@ * and if all of the LoginModule options have been set.

*

*

- * To see a working example and instructions on how to use a keytab, take - * a look at the creating a server keytab example. + * To see a working example and instructions on how to use a keytab, take a look at the creating a server + * keytab example. *

*

*

The class should be used as a Singleton:
@@ -54,9 +57,10 @@ *

*

*

See an example web.xml configuration in the - * installing on tomcat documentation. + * installing on + * tomcat documentation. *

+ * * @author Darwin V. Felix */ final class SpnegoFilterConfig { // NOPMD @@ -66,21 +70,17 @@ final class SpnegoFilterConfig { // NOPMD private static final String MISSING_PROPERTY = "Servlet Filter init param(s) in web.xml missing: "; - private static transient SpnegoFilterConfig instance = null; + private static SpnegoFilterConfig instance = null; /** - * true if typed runtime exception have to be thrown and handled behind by the server - * application. - * For example : - * + * true if typed runtime exception have to be thrown and handled behind by the server application. + * For example : * org.silverpeas.spnego.SpnegoGSSException * /technicalError.jsp - * - * + * * org.silverpeas.spnego.SpnegoUnsupportedOperationException * /unsupportedOperationError.jsp - * - * + * * org.silverpeas.spnego.SpnegoUnauthenticatedException * /unauthenticatedError.jsp * @@ -231,15 +231,14 @@ private void doClientModule(final String moduleName) { if (!option.getKey().startsWith("jboss")) { throw new UnsupportedOperationException( "Login Module for client must not " + "specify any options: " + opt.size() + - "; moduleName=" + moduleName + "; options=" + opt.toString()); + "; moduleName=" + moduleName + "; options=" + opt); } } } } /** - * Set the canUseKeyTab flag by determining if all LoginModule options - * have been set. + * Set the canUseKeyTab flag by determining if all LoginModule options have been set. *

*

    * my-spnego-login-module {
@@ -251,7 +250,8 @@ private void doClientModule(final String moduleName) {
    *      principal="my_preauth_account";
    * };
    * 
- * @param moduleName + * + * @param moduleName the name of the login module */ private void doServerModule(final String moduleName) { @@ -280,16 +280,14 @@ private void doServerModule(final String moduleName) { "Login Module for server does " + "not have the storeKey option defined in login file."); } - if (opt.containsKey("useKeyTab") && opt.containsKey("principal") && opt.containsKey("keyTab")) { - this.canUseKeyTab = true; - } else { - this.canUseKeyTab = false; - } + this.canUseKeyTab = + opt.containsKey("useKeyTab") && opt.containsKey("principal") && opt.containsKey("keyTab"); } /** - * Returns true if a client sends an NTLM token and the - * filter should ask client for a Basic Auth token instead. + * Returns true if a client sends an NTLM token and the filter should ask client for a Basic Auth + * token instead. + * * @return true if client should be prompted for Basic Auth */ boolean downgradeNtlm() { @@ -297,8 +295,8 @@ boolean downgradeNtlm() { } /** - * Return the value defined in the servlet's init params - * in the web.xml file. + * Return the value defined in the servlet's init params in the web.xml file. + * * @return the name of the login module for the client */ String getClientLoginModule() { @@ -307,6 +305,7 @@ String getClientLoginModule() { /** * Return the password to the pre-authentication domain account. + * * @return password of pre-auth domain account */ String getPreauthPassword() { @@ -315,6 +314,7 @@ String getPreauthPassword() { /** * Return the name of the pre-authentication domain account. + * * @return name of pre-auth domain account */ String getPreauthUsername() { @@ -322,8 +322,8 @@ String getPreauthUsername() { } /** - * Return the value defined in the servlet's init params - * in the web.xml file. + * Return the value defined in the servlet's init params in the web.xml file. + * * @return the name of the login module for the server */ String getServerLoginModule() { @@ -332,6 +332,7 @@ String getServerLoginModule() { /** * Returns the instance of the servlet's config parameters. + * * @param config FilterConfi from servlet's init method * @return the instance of that represent the init params * @throws java.io.FileNotFoundException if login conf file not found @@ -352,6 +353,7 @@ static SpnegoFilterConfig getInstance(final FilterConfig config) /** * Returns true if typed runtime exceptions have to be thrown. + * * @return true if typed runtime exceptions have to be thrown */ boolean isTypedRuntimeExceptionThrown() { @@ -360,6 +362,7 @@ boolean isTypedRuntimeExceptionThrown() { /** * Returns true if Basic Authentication is allowed. + * * @return true if Basic Auth is allowed */ boolean isBasicAllowed() { @@ -368,6 +371,7 @@ boolean isBasicAllowed() { /** * Returns true if the server should support credential delegation requests. + * * @return true if server supports credential delegation */ boolean isDelegationAllowed() { @@ -376,6 +380,7 @@ boolean isDelegationAllowed() { /** * Returns true if requests from localhost are allowed. + * * @return true if requests from localhost are allowed */ boolean isLocalhostAllowed() { @@ -384,6 +389,7 @@ boolean isLocalhostAllowed() { /** * Returns true if SSL/TLS is required. + * * @return true if SSL/TLS is required */ boolean isUnsecureAllowed() { @@ -409,23 +415,7 @@ private boolean loginConfExists(final String loginconf) private boolean moduleExists(final String side, final String moduleName) { // confirm that runtime loaded the login file - final Configuration config = Configuration.getConfiguration(); - - // we only expect one entry - final AppConfigurationEntry[] entry = config.getAppConfigurationEntry(moduleName); - - // confirm that the module name exists in the file - if (null == entry) { - throw new IllegalArgumentException( - "The " + side + " module name " + "was not found in the login file: " + moduleName); - } - - // confirm that the login module class was defined - if (0 == entry.length) { - throw new IllegalArgumentException( - "The " + side + " module name " + "exists but login module class not defined: " + - moduleName); - } + final AppConfigurationEntry[] entry = getAppConfigurationEntries(side, moduleName); // confirm that only one login module class specified if (entry.length > 1) { @@ -449,9 +439,31 @@ private boolean moduleExists(final String side, final String moduleName) { return true; } + private static AppConfigurationEntry[] getAppConfigurationEntries(String side, + String moduleName) { + final Configuration config = Configuration.getConfiguration(); + + // we only expect one entry + final AppConfigurationEntry[] entry = config.getAppConfigurationEntry(moduleName); + + // confirm that the module name exists in the file + if (null == entry) { + throw new IllegalArgumentException( + "The " + side + " module name " + "was not found in the login file: " + moduleName); + } + + // confirm that the login module class was defined + if (0 == entry.length) { + throw new IllegalArgumentException( + "The " + side + " module name " + "exists but login module class not defined: " + + moduleName); + } + return entry; + } + /** - * Specify if Basic authentication is allowed and if un-secure/non-ssl - * Basic should be allowed. + * Specify if Basic authentication is allowed and if un-secure/non-ssl Basic should be allowed. + * * @param basic true if basic is allowed * @param unsecure true if un-secure basic is allowed */ @@ -472,6 +484,7 @@ private void setBasicSupport(final String basic, final String unsecure) { /** * Specify the logging level. + * * @param level logging level */ private void setLogLevel(final String level) { @@ -503,9 +516,9 @@ private void setLogLevel(final String level) { } /** - * If request contains NTLM token, specify if a 401 should - * be sent back to client with Basic Auth as the only - * available authentication scheme. + * If request contains NTLM token, specify if a 401 should be sent back to client with Basic Auth + * as the only available authentication scheme. + * * @param ntlm true/false */ private void setNtlmSupport(final String ntlm) { @@ -526,24 +539,15 @@ private void setNtlmSupport(final String ntlm) { /** * Set the username and password if specified in web.xml's init params. + * * @param usr domain account * @param psswrd the password to the domain account * @throws IllegalArgumentException if user/pass AND keyTab set */ private void setUsernamePassword(final String usr, final String psswrd) { boolean mustUseKtab = false; - - if (null == usr) { - this.username = ""; - } else { - this.username = usr; - } - - if (null == psswrd) { - this.password = ""; - } else { - this.password = psswrd; - } + this.username = Objects.requireNonNullElse(usr, ""); + this.password = Objects.requireNonNullElse(psswrd, ""); if (this.username.isEmpty() || this.password.isEmpty()) { mustUseKtab = true; @@ -556,6 +560,7 @@ private void setUsernamePassword(final String usr, final String psswrd) { /** * Returns true if LoginContext should use keyTab. + * * @return true if LoginContext should use keyTab. */ boolean useKeyTab() { @@ -565,12 +570,8 @@ boolean useKeyTab() { @Override public String toString() { - final StringBuilder buff = new StringBuilder(); - - buff.append("allowBasic=" + this.allowBasic + "; allowUnsecure=" + this.allowUnsecure + + return "allowBasic=" + this.allowBasic + "; allowUnsecure=" + this.allowUnsecure + "; canUseKeyTab=" + this.canUseKeyTab + "; clientLoginModule=" + this.clientLoginModule + - "; serverLoginModule=" + this.serverLoginModule); - - return buff.toString(); + "; serverLoginModule=" + this.serverLoginModule; } } diff --git a/src/main/java/org/silverpeas/spnego/SpnegoGSSException.java b/src/main/java/org/silverpeas/spnego/SpnegoGSSException.java index 38c2675..495558e 100644 --- a/src/main/java/org/silverpeas/spnego/SpnegoGSSException.java +++ b/src/main/java/org/silverpeas/spnego/SpnegoGSSException.java @@ -1,5 +1,5 @@ -/** - * Copyright (C) 2014 Silverpeas +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" * * This library is free software; you can redistribute it and/or diff --git a/src/main/java/org/silverpeas/spnego/SpnegoHttpFilter.java b/src/main/java/org/silverpeas/spnego/SpnegoHttpFilter.java index ff001a8..aef74a1 100644 --- a/src/main/java/org/silverpeas/spnego/SpnegoHttpFilter.java +++ b/src/main/java/org/silverpeas/spnego/SpnegoHttpFilter.java @@ -1,5 +1,5 @@ -/** - * Copyright (C) 2014 Silverpeas +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" * * This library is free software; you can redistribute it and/or @@ -22,14 +22,14 @@ import org.ietf.jgss.GSSException; import javax.security.auth.login.LoginException; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URISyntaxException; @@ -195,16 +195,9 @@ public void init(final FilterConfig filterConfig) throws ServletException { // pre-authenticate this.authenticator = new SpnegoAuthenticator(config); - } catch (final LoginException le) { + } catch (final LoginException | URISyntaxException | FileNotFoundException | + PrivilegedActionException | GSSException le) { throw new ServletException(le); - } catch (final GSSException gsse) { - throw new ServletException(gsse); - } catch (final PrivilegedActionException pae) { - throw new ServletException(pae); - } catch (final FileNotFoundException fnfe) { - throw new ServletException(fnfe); - } catch (final URISyntaxException uri) { - throw new ServletException(uri); } } @@ -328,6 +321,7 @@ private Constants() { *

This feature helps to obviate the requirement of * creating an SPN for developer machines.

*/ + @SuppressWarnings("JavadocLinkAsPlainText") public static final String ALLOW_LOCALHOST = "spnego.allow.localhost"; /** diff --git a/src/main/java/org/silverpeas/spnego/SpnegoHttpServletRequest.java b/src/main/java/org/silverpeas/spnego/SpnegoHttpServletRequest.java index 87fed4d..9e7338f 100644 --- a/src/main/java/org/silverpeas/spnego/SpnegoHttpServletRequest.java +++ b/src/main/java/org/silverpeas/spnego/SpnegoHttpServletRequest.java @@ -1,4 +1,5 @@ -/** +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" * * This library is free software; you can redistribute it and/or @@ -21,8 +22,8 @@ import org.ietf.jgss.GSSCredential; import org.silverpeas.spnego.SpnegoHttpFilter.Constants; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; import java.security.Principal; /** @@ -45,9 +46,9 @@ final class SpnegoHttpServletRequest extends HttpServletRequestWrapper private final transient SpnegoPrincipal principal; /** - * Creates Servlet Request specifying KerberosPrincipal of user. - * @param request - * @param spnegoPrincipal + * Creates a Servlet Request specifying KerberosPrincipal of user. + * @param request the incoming HTTP request + * @param spnegoPrincipal the principal behind the request. */ SpnegoHttpServletRequest(final HttpServletRequest request, final SpnegoPrincipal spnegoPrincipal) { @@ -59,7 +60,7 @@ final class SpnegoHttpServletRequest extends HttpServletRequestWrapper /** * Returns "Negotiate" or "Basic" else default auth type. - * @see javax.servlet.http.HttpServletRequest#getAuthType() + * @see jakarta.servlet.http.HttpServletRequest#getAuthType() */ @Override public String getAuthType() { @@ -90,7 +91,7 @@ public GSSCredential getDelegatedCredential() { /** * Returns authenticated username (without domain/realm) else default username. - * @see javax.servlet.http.HttpServletRequest#getRemoteUser() + * @see jakarta.servlet.http.HttpServletRequest#getRemoteUser() */ @Override public String getRemoteUser() { @@ -110,7 +111,7 @@ public String getRemoteUser() { /** * Returns KerberosPrincipal of user. - * @see javax.servlet.http.HttpServletRequest#getUserPrincipal() + * @see jakarta.servlet.http.HttpServletRequest#getUserPrincipal() */ @Override public Principal getUserPrincipal() { diff --git a/src/main/java/org/silverpeas/spnego/SpnegoHttpServletResponse.java b/src/main/java/org/silverpeas/spnego/SpnegoHttpServletResponse.java index e9f58cf..f88abe2 100644 --- a/src/main/java/org/silverpeas/spnego/SpnegoHttpServletResponse.java +++ b/src/main/java/org/silverpeas/spnego/SpnegoHttpServletResponse.java @@ -1,4 +1,5 @@ -/** +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" * * This library is free software; you can redistribute it and/or @@ -18,8 +19,8 @@ package org.silverpeas.spnego; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpServletResponseWrapper; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; import java.io.IOException; /** @@ -38,7 +39,8 @@ public final class SpnegoHttpServletResponse extends HttpServletResponseWrapper private transient boolean statusSet = false; /** - * @param response + * Wraps the specified HTTP response. + * @param response the response to decorate. */ public SpnegoHttpServletResponse(final HttpServletResponse response) { super(response); @@ -63,7 +65,7 @@ public void setStatus(final int status) { * length to zero and flush the buffer. * @param status http status code * @param immediate set to true to set content len to zero and flush - * @throws java.io.IOException + * @throws IOException if an IO error occurs while setting the HTTP response status. * @see #setStatus(int) */ public void setStatus(final int status, final boolean immediate) throws IOException { diff --git a/src/main/java/org/silverpeas/spnego/SpnegoHttpURLConnection.java b/src/main/java/org/silverpeas/spnego/SpnegoHttpURLConnection.java index 3519d43..8d20226 100644 --- a/src/main/java/org/silverpeas/spnego/SpnegoHttpURLConnection.java +++ b/src/main/java/org/silverpeas/spnego/SpnegoHttpURLConnection.java @@ -1,4 +1,5 @@ -/** +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" * * This library is free software; you can redistribute it and/or @@ -34,11 +35,7 @@ import java.net.Proxy; import java.net.URL; import java.security.PrivilegedActionException; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; @@ -93,7 +90,7 @@ * public static void main(final String[] args) throws Exception { * final String creds = "dfelix:myp@s5"; * - * final String token = Base64.encode(creds.getBytes()); + * final String token = Base64.getEncoder().encodeToString(creds.getBytes()); * * URL url = new URL("http://medusa:8080/index.jsp"); * @@ -148,7 +145,7 @@ public final class SpnegoHttpURLConnection { * @see java.net.URLConnection#getRequestProperties() */ private final transient Map> requestProperties = - new LinkedHashMap>(); + new LinkedHashMap<>(); /** * Login Context for authenticating client. If username/password @@ -193,9 +190,9 @@ public final class SpnegoHttpURLConnection { /** * Creates an instance where the LoginContext relies on a keytab * file being specified by "java.security.auth.login.config" or - * where LoginContext relies on tgtsessionkey. - * @param loginModuleName - * @throws javax.security.auth.login.LoginException + * where LoginContext relies on the session key. + * @param loginModuleName the name of the login module + * @throws LoginException if the login context cannot be got. */ public SpnegoHttpURLConnection(final String loginModuleName) throws LoginException { @@ -209,6 +206,7 @@ public SpnegoHttpURLConnection(final String loginModuleName) throws LoginExcepti * and where the GSSCredential is automatically disposed after use. * @param creds credentials to use */ + @SuppressWarnings("unused") public SpnegoHttpURLConnection(final GSSCredential creds) { this(creds, true); } @@ -229,10 +227,10 @@ public SpnegoHttpURLConnection(final GSSCredential creds, final boolean dispose) * Creates an instance where the LoginContext does not require a keytab * file. However, the "java.security.auth.login.config" property must still * be set prior to instantiating this object. - * @param loginModuleName - * @param username - * @param password - * @throws javax.security.auth.login.LoginException + * @param loginModuleName the name of the login module. + * @param username the user login identifier + * @param password the user password. + * @throws LoginException if an error occurs while performing the login. */ public SpnegoHttpURLConnection(final String loginModuleName, final String username, final String password) throws LoginException { @@ -273,25 +271,28 @@ private void assertNotConnected() { * connect(URL, ByteArrayOutputStream) method but passing in a null * for the second argument. *

- * @param url + * @param url the URL of the resource to connect. * @return an HttpURLConnection object - * @throws org.ietf.jgss.GSSException - * @throws java.security.PrivilegedActionException - * @throws java.io.IOException - * @throws javax.security.auth.login.LoginException + * @throws GSSException if a GSS-API error occurs. + * @throws PrivilegedActionException if the connection fails to be established in a privileged + * way. + * @throws IOException if the connection with the remote resource fails. * @see java.net.URLConnection#connect() */ + @SuppressWarnings("unused") public HttpURLConnection connect(final URL url) throws GSSException, PrivilegedActionException, IOException { return this.connect(url, null, null); } + @SuppressWarnings("unused") public HttpURLConnection connect(final URL url, final Proxy proxy) throws GSSException, PrivilegedActionException, IOException { return this.connect(url, proxy, null); } + @SuppressWarnings("UnusedReturnValue") public HttpURLConnection connect(final URL url, final ByteArrayOutputStream dooutput) throws GSSException, PrivilegedActionException, IOException { return this.connect(url, null, dooutput); @@ -300,17 +301,17 @@ public HttpURLConnection connect(final URL url, final ByteArrayOutputStream doou /** * Opens a communications link to the resource referenced by * this URL, if such a connection has not already been established. - * @param url - * @param dooutput optional message/payload to send to server + * @param url the URL of the resource to connect. + * @param output optional message/payload to send to server * @return an HttpURLConnection object - * @throws org.ietf.jgss.GSSException - * @throws java.security.PrivilegedActionException - * @throws java.io.IOException - * @throws javax.security.auth.login.LoginException + * @throws GSSException if a GSS-API error occurs + * @throws PrivilegedActionException if the connection fails to be established in a privileged + * way. + * @throws IOException if the connection fails. * @see java.net.URLConnection#connect() */ public HttpURLConnection connect(final URL url, final Proxy proxy, - final ByteArrayOutputStream dooutput) + final ByteArrayOutputStream output) throws GSSException, PrivilegedActionException, IOException { assertNotConnected(); @@ -318,7 +319,7 @@ public HttpURLConnection connect(final URL url, final Proxy proxy, GSSContext context = null; try { - byte[] data = null; + byte[] data; SpnegoHttpURLConnection.LOCK.lock(); try { @@ -361,11 +362,11 @@ public HttpURLConnection connect(final URL url, final Proxy proxy, this.conn.setRequestMethod(this.requestMethod); this.conn.setRequestProperty(Constants.AUTHZ_HEADER, - Constants.NEGOTIATE_HEADER + ' ' + Base64.encode(data)); + Constants.NEGOTIATE_HEADER + ' ' + Base64.getEncoder().encodeToString(data)); - if (null != dooutput && dooutput.size() > 0) { + if (null != output && output.size() > 0) { this.conn.setDoOutput(true); - dooutput.writeTo(this.conn.getOutputStream()); + output.writeTo(this.conn.getOutputStream()); } this.conn.connect(); @@ -459,6 +460,7 @@ public void disconnect() { * Returns true if GSSContext has been established. * @return true if GSSContext has been established, false otherwise. */ + @SuppressWarnings("unused") public boolean isContextEstablished() { return this.cntxtEstablished; } @@ -504,15 +506,15 @@ public void setRequestProperty(final String key, final String value) { assertNotConnected(); assertKeyValue(key, value); - this.requestProperties.put(key, Arrays.asList(value)); + this.requestProperties.put(key, Collections.singletonList(value)); } /** - * Returns a GSSContextt for the given url with a default lifetime. + * Returns a GSSContext for the given url with a default lifetime. * @param url http address * @return GSSContext for the given url - * @throws org.ietf.jgss.GSSException - * @throws java.security.PrivilegedActionException + * @throws GSSException if a GSS-API error occurs + * @throws PrivilegedActionException if the getting of the GSS-API context isn't allowed. */ private GSSContext getGSSContext(final URL url) throws GSSException, PrivilegedActionException { @@ -531,7 +533,7 @@ private GSSContext getGSSContext(final URL url) throws GSSException, PrivilegedA /** * Returns an error stream that reads from this open connection. * @return error stream that reads from this open connection - * @throws java.io.IOException + * @throws IOException if an error occurs while getting the stream. * @see java.net.HttpURLConnection#getErrorStream() */ public InputStream getErrorStream() throws IOException { @@ -542,9 +544,10 @@ public InputStream getErrorStream() throws IOException { /** * Get header value at specified index. - * @param index + * @param index the index of the header field. * @return header value at specified index */ + @SuppressWarnings("unused") public String getHeaderField(final int index) { assertConnected(); @@ -557,6 +560,7 @@ public String getHeaderField(final int index) { * @return header value * @see java.net.HttpURLConnection#getHeaderField(String) */ + @SuppressWarnings("unused") public String getHeaderField(final String name) { assertConnected(); @@ -565,9 +569,10 @@ public String getHeaderField(final String name) { /** * Get header field key at specified index. - * @param index + * @param index the index of the header field key. * @return header field key at specified index */ + @SuppressWarnings("unused") public String getHeaderFieldKey(final int index) { assertConnected(); @@ -577,7 +582,7 @@ public String getHeaderFieldKey(final int index) { /** * Returns an input stream that reads from this open connection. * @return input stream that reads from this open connection - * @throws java.io.IOException + * @throws IOException if an error occurs while getting the input stream. * @see java.net.HttpURLConnection#getInputStream() */ public InputStream getInputStream() throws IOException { @@ -589,9 +594,10 @@ public InputStream getInputStream() throws IOException { /** * Returns an output stream that writes to this open connection. * @return output stream that writes to this connections - * @throws java.io.IOException + * @throws IOException if an error occurs while getting the output stream. * @see java.net.HttpURLConnection#getOutputStream() */ + @SuppressWarnings("unused") public OutputStream getOutputStream() throws IOException { assertConnected(); @@ -601,9 +607,10 @@ public OutputStream getOutputStream() throws IOException { /** * Returns HTTP Status code. * @return HTTP Status Code - * @throws java.io.IOException + * @throws IOException if an error occurs while fetching the response status code. * @see java.net.HttpURLConnection#getResponseCode() */ + @SuppressWarnings("unused") public int getResponseCode() throws IOException { assertConnected(); @@ -613,9 +620,10 @@ public int getResponseCode() throws IOException { /** * Returns HTTP Status message. * @return HTTP Status Message - * @throws java.io.IOException + * @throws IOException if an error occurs while fetching the response message. * @see java.net.HttpURLConnection#getResponseMessage() */ + @SuppressWarnings("unused") public String getResponseMessage() throws IOException { assertConnected(); @@ -626,6 +634,7 @@ public String getResponseMessage() throws IOException { * Request that this GSSCredential be allowed for delegation. * @param requestDelegation true to allow/request delegation */ + @SuppressWarnings("unused") public void requestCredDeleg(final boolean requestDelegation) { this.assertNotConnected(); @@ -634,9 +643,10 @@ public void requestCredDeleg(final boolean requestDelegation) { /** * May override the default GET method. - * @param method + * @param method the HTTP method to set. * @see java.net.HttpURLConnection#setRequestMethod(String) */ + @SuppressWarnings("unused") public void setRequestMethod(final String method) { assertNotConnected(); diff --git a/src/main/java/org/silverpeas/spnego/SpnegoPrincipal.java b/src/main/java/org/silverpeas/spnego/SpnegoPrincipal.java index e2baf59..0243cf4 100644 --- a/src/main/java/org/silverpeas/spnego/SpnegoPrincipal.java +++ b/src/main/java/org/silverpeas/spnego/SpnegoPrincipal.java @@ -1,4 +1,5 @@ -/** +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" * * This library is free software; you can redistribute it and/or @@ -45,6 +46,7 @@ public final class SpnegoPrincipal implements Principal { * Constructs a SpnegoPrincipal from the provided String input. * @param name the principal name */ + @SuppressWarnings("unused") public SpnegoPrincipal(final String name) { this.kerberosPrincipal = new KerberosPrincipal(name); this.delegatedCred = null; @@ -91,6 +93,7 @@ public String getName() { * Returns the name type of the KerberosPrincipal. * @return name type of the KerberosPrincipal */ + @SuppressWarnings("unused") public int getNameType() { return this.kerberosPrincipal.getNameType(); } @@ -99,6 +102,7 @@ public int getNameType() { * Returns the realm component of this Kerberos principal. * @return realm component of this Kerberos principal */ + @SuppressWarnings("unused") public String getRealm() { return this.kerberosPrincipal.getRealm(); } diff --git a/src/main/java/org/silverpeas/spnego/SpnegoProvider.java b/src/main/java/org/silverpeas/spnego/SpnegoProvider.java index e4b67fa..c9a0362 100644 --- a/src/main/java/org/silverpeas/spnego/SpnegoProvider.java +++ b/src/main/java/org/silverpeas/spnego/SpnegoProvider.java @@ -1,16 +1,17 @@ -/** +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -18,21 +19,15 @@ package org.silverpeas.spnego; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.ietf.jgss.*; import org.silverpeas.spnego.SpnegoHttpFilter.Constants; -import org.ietf.jgss.GSSContext; -import org.ietf.jgss.GSSCredential; -import org.ietf.jgss.GSSException; -import org.ietf.jgss.GSSManager; -import org.ietf.jgss.GSSName; -import org.ietf.jgss.Oid; import javax.security.auth.Subject; -import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URL; import java.security.PrivilegedActionException; @@ -41,23 +36,22 @@ import java.util.logging.Logger; /** - * This is a Utility Class that can be used for finer grained control - * over message integrity, confidentiality and mutual authentication. + * This is a Utility Class that can be used for finer grained control over message integrity, + * confidentiality and mutual authentication. *

*

- * This Class is exposed for developers who want to implement a custom - * HTTP client. + * This Class is exposed for developers who want to implement a custom HTTP client. *

*

*

- * Take a look at the {@link SpnegoHttpURLConnection} class and the - * {@link SpnegoHttpFilter} class before attempting to implement your - * own HTTP client. + * Take a look at the {@link SpnegoHttpURLConnection} class and the {@link SpnegoHttpFilter} class + * before attempting to implement your own HTTP client. *

*

*

For more example usage, see the documentation at * http://spnego.sourceforge.net *

+ * * @author Darwin V. Felix */ public final class SpnegoProvider { @@ -85,24 +79,22 @@ private SpnegoProvider() { } /** - * Returns the {@link SpnegoAuthScheme} mechanism used to authenticate - * the request. + * Returns the {@link SpnegoAuthScheme} mechanism used to authenticate the request. *

*

- * This method may return null in which case you must check the HTTP - * Status Code to determine if additional processing is required. - *
- * For example, if req. did not contain the SpnegoConstants.AUTHZ_HEADER, - * the HTTP Status Code SC_UNAUTHORIZED will be set and the client must - * send authentication information on the next request. + * This method may return null in which case you must check the HTTP Status Code to determine if + * additional processing is required.
For example, if req. did not contain the + * SpnegoConstants.AUTHZ_HEADER, the HTTP Status Code SC_UNAUTHORIZED will be set and the client + * must send authentication information on the next request. *

+ * * @param req servlet request * @param resp servlet response * @param basicSupported pass true to offer/allow BASIC Authentication * @param promptIfNtlm pass true ntlm request should be downgraded * @param realm should be the realm the server used to pre-authenticate * @return null if negotiation needs to continue or failed - * @throws java.io.IOException + * @throws IOException if an IO error occurs during the negotiation */ static SpnegoAuthScheme negotiate(final HttpServletRequest req, final SpnegoHttpServletResponse resp, final boolean basicSupported, @@ -154,41 +146,39 @@ static SpnegoAuthScheme negotiate(final HttpServletRequest req, } /** - * Returns the GSS-API interface for creating a security context. + * Gets from the specified subject the specific GSS-API user credentials used to authenticate and + * identified the user behind the subject. + * * @param subject the person to be authenticated * @return GSSCredential to be used for creating a security context. - * @throws java.security.PrivilegedActionException + * @throws PrivilegedActionException if the creation of the GSS-API client credentials isn't + * allowed. */ public static GSSCredential getClientCredential(final Subject subject) throws PrivilegedActionException { final PrivilegedExceptionAction action = - new PrivilegedExceptionAction() { - public GSSCredential run() throws GSSException { - return MANAGER - .createCredential(null, GSSCredential.DEFAULT_LIFETIME, SpnegoProvider.SPNEGO_OID, - GSSCredential.INITIATE_ONLY); - } - }; + () -> MANAGER + .createCredential(null, GSSCredential.DEFAULT_LIFETIME, SpnegoProvider.SPNEGO_OID, + GSSCredential.INITIATE_ONLY); return Subject.doAs(subject, action); } /** - * Returns a GSSContext to be used by custom clients to set - * data integrity requirements, confidentiality and if mutual - * authentication is required. - * @param creds credentials of the person to be authenticated + * Gets a GSSContext to be used by custom clients to set data integrity requirements, + * confidentiality and if mutual authentication is required. + * + * @param credentials credentials of the person to be authenticated * @param url HTTP address of server (used for constructing a {@link org.ietf.jgss.GSSName}). * @return GSSContext - * @throws org.ietf.jgss.GSSException - * @throws java.security.PrivilegedActionException + * @throws GSSException if a GSS-API error occurs */ - public static GSSContext getGSSContext(final GSSCredential creds, final URL url) + public static GSSContext getGSSContext(final GSSCredential credentials, final URL url) throws GSSException { return MANAGER - .createContext(SpnegoProvider.getServerName(url), SpnegoProvider.SPNEGO_OID, creds, + .createContext(SpnegoProvider.getServerName(url), SpnegoProvider.SPNEGO_OID, credentials, GSSContext.DEFAULT_LIFETIME); } @@ -196,13 +186,13 @@ public static GSSContext getGSSContext(final GSSCredential creds, final URL url) * Returns the {@link SpnegoAuthScheme} or null if header is missing. *

*

- * Throws UnsupportedOperationException if header is NOT Negotiate - * or Basic. + * Throws UnsupportedOperationException if header is NOT Negotiate or Basic. *

+ * * @param header ex. Negotiate or Basic * @return null if header missing/null else the auth scheme */ - public static SpnegoAuthScheme getAuthScheme(final String header) { + static SpnegoAuthScheme getAuthScheme(final String header) { if (null == header || header.isEmpty()) { LOGGER.finer("authorization header was missing/null"); @@ -222,8 +212,8 @@ public static SpnegoAuthScheme getAuthScheme(final String header) { } /** - * Returns the Universal Object Identifier representation of - * the SPNEGO mechanism. + * Returns the Universal Object Identifier representation of the SPNEGO mechanism. + * * @return Object Identifier of the GSS-API mechanism */ private static Oid getOid() { @@ -238,28 +228,25 @@ private static Oid getOid() { /** * Returns the {@link org.ietf.jgss.GSSCredential} the server uses for pre-authentication. + * * @param subject account server uses for pre-authentication * @return credential that allows server to authenticate clients - * @throws java.security.PrivilegedActionException + * @throws PrivilegedActionException if the getting of the credentials is not allowed. */ static GSSCredential getServerCredential(final Subject subject) throws PrivilegedActionException { final PrivilegedExceptionAction action = - new PrivilegedExceptionAction() { - public GSSCredential run() throws GSSException { - return MANAGER.createCredential(null, GSSCredential.INDEFINITE_LIFETIME, - SpnegoProvider.SPNEGO_OID, GSSCredential.ACCEPT_ONLY); - } - }; + () -> MANAGER.createCredential(null, GSSCredential.INDEFINITE_LIFETIME, + SpnegoProvider.SPNEGO_OID, GSSCredential.ACCEPT_ONLY); return Subject.doAs(subject, action); } /** - * Returns the {@link org.ietf.jgss.GSSName} constructed out of the passed-in - * URL object. + * Returns the {@link org.ietf.jgss.GSSName} constructed out of the passed-in URL object. + * * @param url HTTP address of server * @return GSSName of URL. - * @throws org.ietf.jgss.GSSException + * @throws GSSException if a GSS-API error occurs */ static GSSName getServerName(final URL url) throws GSSException { return MANAGER.createName("HTTP@" + url.getHost(), GSSName.NT_HOSTBASED_SERVICE, @@ -267,8 +254,9 @@ static GSSName getServerName(final URL url) throws GSSException { } /** - * Used by the BASIC Auth mechanism for establishing a LoginContext - * to authenticate a client/caller/request. + * Used by the BASIC Auth mechanism for establishing a LoginContext to authenticate a + * client/caller/request. + * * @param username client username * @param password client password * @return CallbackHandler to be used for establishing a LoginContext @@ -278,23 +266,19 @@ public static CallbackHandler getUsernamePasswordHandler(final String username, LOGGER.fine("username=" + username + "; password=" + password.hashCode()); - final CallbackHandler handler = new CallbackHandler() { - public void handle(final Callback[] callback) { - for (int i = 0; i < callback.length; i++) { - if (callback[i] instanceof NameCallback) { - final NameCallback nameCallback = (NameCallback) callback[i]; - nameCallback.setName(username); - } else if (callback[i] instanceof PasswordCallback) { - final PasswordCallback passCallback = (PasswordCallback) callback[i]; - passCallback.setPassword(password.toCharArray()); - } else { - LOGGER.warning( - "Unsupported Callback i=" + i + "; class=" + callback[i].getClass().getName()); - } + return callback -> { + for (int i = 0; i < callback.length; i++) { + if (callback[i] instanceof NameCallback) { + final NameCallback nameCallback = (NameCallback) callback[i]; + nameCallback.setName(username); + } else if (callback[i] instanceof PasswordCallback) { + final PasswordCallback passCallback = (PasswordCallback) callback[i]; + passCallback.setPassword(password.toCharArray()); + } else { + LOGGER.warning( + "Unsupported Callback i=" + i + "; class=" + callback[i].getClass().getName()); } } }; - - return handler; } } diff --git a/src/main/java/org/silverpeas/spnego/SpnegoSOAPConnection.java b/src/main/java/org/silverpeas/spnego/SpnegoSOAPConnection.java index 5844c15..243a1d5 100644 --- a/src/main/java/org/silverpeas/spnego/SpnegoSOAPConnection.java +++ b/src/main/java/org/silverpeas/spnego/SpnegoSOAPConnection.java @@ -1,4 +1,5 @@ -/** +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" * * This library is free software; you can redistribute it and/or @@ -18,19 +19,13 @@ package org.silverpeas.spnego; +import jakarta.xml.soap.*; import org.ietf.jgss.GSSCredential; import org.ietf.jgss.GSSException; import javax.security.auth.login.LoginException; -import javax.xml.soap.MessageFactory; -import javax.xml.soap.MimeHeaders; -import javax.xml.soap.SOAPConnection; -import javax.xml.soap.SOAPConstants; -import javax.xml.soap.SOAPException; -import javax.xml.soap.SOAPMessage; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.net.MalformedURLException; import java.net.URL; import java.security.PrivilegedActionException; @@ -106,9 +101,9 @@ public class SpnegoSOAPConnection extends SOAPConnection { /** * Creates an instance where the LoginContext relies on a keytab * file being specified by "java.security.auth.login.config" or - * where LoginContext relies on tgtsessionkey. - * @param loginModuleName - * @throws javax.security.auth.login.LoginException + * where LoginContext relies on the session key. + * @param loginModuleName the name of the login module + * @throws LoginException if the login context cannot be got. */ @SuppressWarnings("UnusedDeclaration") public SpnegoSOAPConnection(final String loginModuleName) throws LoginException { @@ -142,10 +137,10 @@ public SpnegoSOAPConnection(final GSSCredential creds, final boolean dispose) { * Creates an instance where the LoginContext does not require a keytab * file. However, the "java.security.auth.login.config" property must still * be set prior to instantiating this object. - * @param loginModuleName - * @param username - * @param password - * @throws javax.security.auth.login.LoginException + * @param loginModuleName the name of the login module + * @param username the user login + * @param password the user password + * @throws LoginException if the login context cannot be got. */ @SuppressWarnings("UnusedDeclaration") public SpnegoSOAPConnection(final String loginModuleName, final String username, @@ -159,7 +154,7 @@ public SpnegoSOAPConnection(final String loginModuleName, final String username, public final SOAPMessage call(final SOAPMessage request, final Object endpoint) throws SOAPException { - SOAPMessage message = null; + SOAPMessage message; final ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { @@ -208,13 +203,7 @@ public final SOAPMessage call(final SOAPMessage request, final Object endpoint) message = factory.createMessage(null, this.conn.getErrorStream()); } - } catch (MalformedURLException e) { - throw new SOAPException(e); - } catch (IOException e) { - throw new SOAPException(e); - } catch (GSSException e) { - throw new SOAPException(e); - } catch (PrivilegedActionException e) { + } catch (PrivilegedActionException | GSSException | IOException e) { throw new SOAPException(e); } finally { try { diff --git a/src/main/java/org/silverpeas/spnego/SpnegoUnauthenticatedException.java b/src/main/java/org/silverpeas/spnego/SpnegoUnauthenticatedException.java index 59df9bc..82669af 100644 --- a/src/main/java/org/silverpeas/spnego/SpnegoUnauthenticatedException.java +++ b/src/main/java/org/silverpeas/spnego/SpnegoUnauthenticatedException.java @@ -1,5 +1,5 @@ -/** - * Copyright (C) 2014 Silverpeas +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" * * This library is free software; you can redistribute it and/or diff --git a/src/main/java/org/silverpeas/spnego/SpnegoUnsupportedOperationException.java b/src/main/java/org/silverpeas/spnego/SpnegoUnsupportedOperationException.java index 71cedcd..12b97dc 100644 --- a/src/main/java/org/silverpeas/spnego/SpnegoUnsupportedOperationException.java +++ b/src/main/java/org/silverpeas/spnego/SpnegoUnsupportedOperationException.java @@ -1,5 +1,5 @@ -/** - * Copyright (C) 2014 Silverpeas +/* + * Copyright (C) 2014-2023 Silverpeas * Copyright (C) 2009 "Darwin V. Felix" * * This library is free software; you can redistribute it and/or