# WebAuthHandler.java

    /*
    ** Module   : WebAuthHandler.java
    ** Abstract : Handler responsible for authenticating and authorizing a request.
    **
    ** Copyright (c) 2022-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ------------------------------------Description-------------------------------------------
    ** 001 CA  20220404 Created the first version.
    ** 002 OM  20250206 Dropped unneeded null checks.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program 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 Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.main;

    import javax.servlet.http.*;
    import org.eclipse.jetty.http.*;
    import org.eclipse.jetty.server.*;
    import org.eclipse.jetty.server.handler.*;
    import com.goldencode.p2j.util.LegacyService;

    /**
     * Instances of this class are created when the authentication is enabled at a certain web service type.
     * <p>
     * If the {@link #loginPath} is set, then that must be used to get an authentication token, which will then
     * be passed to any other request.  The context will be destroyed when {@link #logoutPath logout} is performed,
     * or on the configured timeout.
     */
    public class WebAuthHandler
    extends AbstractHandler
    {
       /** The authentication and authorization implementation. */
       private final WebServiceAuth auth;

       /**
        * The explicit login path (relative to basepath plus any address configured at the {@link LegacyService}).
        */
       private final String loginPath;

       /**
        * The explicit logout path (relative to basepath plus any address configured at the {@link LegacyService}).
        */
       private final String logoutPath;

       /**
        * Initialize this instance.
        *
        * @param    type
        *           The web service type (REST, SOAP, WEBHANDLER).
        * @param    authType
        *           The authentication type (only "basic" is supported at this time).
        * @param    loginPath
        *           The login path.
        * @param    logoutPath
        *           The logout path.
        * @param    timeout
        *           The context timeout.
        */
       public WebAuthHandler(String type, String authType, String loginPath, String logoutPath, int timeout)
       {
          this.loginPath  = preparePath(loginPath);
          this.logoutPath = preparePath(logoutPath);

          switch (authType.toLowerCase())
          {
             case "basic":
                this.auth = new BasicAuth(type, loginPath != null, timeout);
                break;
             case "oauth1":
                // TODO: add OAuth1
             default:
                throw new IllegalArgumentException("Authentication mode " + authType + " is unknown. " +
                                                   "Only 'basic' authentication type is supported at this time.");
          }
       }

       /**
        * Check if this is a login or logout API call.  Otherwise, use {@link WebServiceAuth#authenticate} to
        * check if the request either has full authentication details, or a valid authentication token.
        * <p>
        * No authorization is performed at this time.  {@link #authorize} will be called by the web service
        * implementation after the web service is resolved from the request.
        *
        * @param    target
        *           The target path.
        * @param    baseRequest
        *           The HTTP request, Jetty style.
        * @param    request
        *           The HTTP request.
        * @param    response
        *           The HTTP response.
        */
       @Override
       public void handle(String              target,
                          Request             baseRequest,
                          HttpServletRequest  request,
                          HttpServletResponse response)
       {
          boolean isGet = request.getMethod().equals(HttpMethod.GET.asString());

          if (isGet && target.equals(loginPath))
          {
             baseRequest.setHandled(true);
             String token = auth.login(request);
             if (token != null)
             {
                auth.setAuthorizationToken(token, response);
                response.setStatus(HttpStatus.OK_200);
             }
             else
             {
                response.setStatus(HttpStatus.UNAUTHORIZED_401);
             }

             return;
          }

          if (isGet && target.equals(logoutPath))
          {
             baseRequest.setHandled(true);
             String token = auth.getAuthorizationToken(request);
             boolean logout = auth.logout(token);
             response.setStatus(logout ? HttpStatus.OK_200 : HttpStatus.FORBIDDEN_403);

             return;
          }

          // authenticate, but don't create the context yet - that will be created on authorization
          if (!auth.authenticate(request, response))
          {
             // the authentication failed
             baseRequest.setHandled(true);
          }
       }

       /**
        * Perform any post-handle logic.  This includes logging out the request, if there is no explicit
        * {@link #logoutPath logout path} enforced.
        *
        * @param    token
        *           The authentication token.
        */
       public void postHandle(String token)
       {
          if (logoutPath == null)
          {
             // must perform a logout
             auth.logout(token);
          }
       }

       /**
        * Authorize this request (it is known to have passed authentication).
        *
        * @param    target
        *           The target path.
        * @param    request
        *           The HTTP request.
        * @param    response
        *           The HTTP response.
        *
        * @return   <code>null</code> if the request couldn't be authorized.  Otherwise, the authentication token.
        */
       public String authorize(String target, HttpServletRequest  request, HttpServletResponse response)
       {
          return auth.authorize(target, request, response);
       }

       /**
        * Prepare this path by ensuring it will always start with a '/' but not end with a '/'.
        *
        * @param    path
        *           The path to prepare.
        *
        * @return   See above.
        */
       private String preparePath(String path)
       {
          if (path == null)
          {
             return null;
          }

          while (path.startsWith("/"))
          {
             path = path.substring(1);
          }
          while (path.endsWith("/"))
          {
             path = path.substring(0, path.length() - 1);
          }

          return "/" + path;
       }
    }