LCOV - code coverage report
Current view: top level - httpd/auth/container - HttpAuthFilter.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 0 54 0.0 %
Date: 2022-11-19 15:00:39 Functions: 0 12 0.0 %

          Line data    Source code
       1             : // Copyright (C) 2009 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.httpd.auth.container;
      16             : 
      17             : import static com.google.common.base.MoreObjects.firstNonNull;
      18             : import static com.google.common.base.Strings.emptyToNull;
      19             : import static com.google.common.net.HttpHeaders.AUTHORIZATION;
      20             : import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GERRIT;
      21             : import static java.nio.charset.StandardCharsets.ISO_8859_1;
      22             : import static java.nio.charset.StandardCharsets.UTF_8;
      23             : 
      24             : import com.google.gerrit.common.Nullable;
      25             : import com.google.gerrit.extensions.registration.DynamicItem;
      26             : import com.google.gerrit.httpd.HtmlDomUtil;
      27             : import com.google.gerrit.httpd.RemoteUserUtil;
      28             : import com.google.gerrit.httpd.WebSession;
      29             : import com.google.gerrit.server.account.externalids.ExternalId;
      30             : import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
      31             : import com.google.gerrit.server.config.AuthConfig;
      32             : import com.google.gerrit.util.http.CacheHeaders;
      33             : import com.google.gerrit.util.http.RequestUtil;
      34             : import com.google.inject.Inject;
      35             : import com.google.inject.Singleton;
      36             : import java.io.FileNotFoundException;
      37             : import java.io.IOException;
      38             : import java.io.OutputStream;
      39             : import java.util.Locale;
      40             : import java.util.Optional;
      41             : import javax.servlet.Filter;
      42             : import javax.servlet.FilterChain;
      43             : import javax.servlet.FilterConfig;
      44             : import javax.servlet.ServletException;
      45             : import javax.servlet.ServletRequest;
      46             : import javax.servlet.ServletResponse;
      47             : import javax.servlet.http.HttpServletRequest;
      48             : import javax.servlet.http.HttpServletResponse;
      49             : 
      50             : /**
      51             :  * Watches request for the host page and requires login if not yet signed in.
      52             :  *
      53             :  * <p>If HTTP authentication has been enabled on this server this filter is bound in front of the
      54             :  * Gerrit and redirects users who are not yet signed in to visit {@code /login/}, so the web
      55             :  * container can force login. This redirect is performed with JavaScript, such that any existing
      56             :  * anchor token in the URL can be rewritten and preserved through the authentication process of any
      57             :  * enterprise single sign-on solutions.
      58             :  */
      59             : @Singleton
      60             : class HttpAuthFilter implements Filter {
      61             :   private final DynamicItem<WebSession> sessionProvider;
      62             :   private final byte[] signInRaw;
      63             :   private final byte[] signInGzip;
      64             :   private final String loginHeader;
      65             :   private final String displaynameHeader;
      66             :   private final String emailHeader;
      67             :   private final String externalIdHeader;
      68             :   private final boolean userNameToLowerCase;
      69             :   private final ExternalIdKeyFactory externalIdKeyFactory;
      70             : 
      71             :   @Inject
      72             :   HttpAuthFilter(
      73             :       DynamicItem<WebSession> webSession,
      74             :       AuthConfig authConfig,
      75             :       ExternalIdKeyFactory externalIdKeyFactory)
      76           0 :       throws IOException {
      77           0 :     this.sessionProvider = webSession;
      78           0 :     this.externalIdKeyFactory = externalIdKeyFactory;
      79             : 
      80           0 :     final String pageName = "LoginRedirect.html";
      81           0 :     final String doc = HtmlDomUtil.readFile(getClass(), pageName);
      82           0 :     if (doc == null) {
      83           0 :       throw new FileNotFoundException("No " + pageName + " in webapp");
      84             :     }
      85             : 
      86           0 :     signInRaw = doc.getBytes(HtmlDomUtil.ENC);
      87           0 :     signInGzip = HtmlDomUtil.compress(signInRaw);
      88           0 :     loginHeader = firstNonNull(emptyToNull(authConfig.getLoginHttpHeader()), AUTHORIZATION);
      89           0 :     displaynameHeader = emptyToNull(authConfig.getHttpDisplaynameHeader());
      90           0 :     emailHeader = emptyToNull(authConfig.getHttpEmailHeader());
      91           0 :     externalIdHeader = emptyToNull(authConfig.getHttpExternalIdHeader());
      92           0 :     userNameToLowerCase = authConfig.isUserNameToLowerCase();
      93           0 :   }
      94             : 
      95             :   @Override
      96             :   public void doFilter(final ServletRequest request, ServletResponse response, FilterChain chain)
      97             :       throws IOException, ServletException {
      98           0 :     if (isSessionValid((HttpServletRequest) request)) {
      99           0 :       chain.doFilter(request, response);
     100             :     } else {
     101             :       // Not signed in yet. Since the browser state might have an anchor
     102             :       // token which we want to capture and carry through the auth process
     103             :       // we send back JavaScript now to capture that, and do the real work
     104             :       // of redirecting to the authentication area.
     105             :       //
     106           0 :       final HttpServletRequest req = (HttpServletRequest) request;
     107           0 :       final HttpServletResponse rsp = (HttpServletResponse) response;
     108             :       final byte[] tosend;
     109           0 :       if (RequestUtil.acceptsGzipEncoding(req)) {
     110           0 :         rsp.setHeader("Content-Encoding", "gzip");
     111           0 :         tosend = signInGzip;
     112             :       } else {
     113           0 :         tosend = signInRaw;
     114             :       }
     115             : 
     116           0 :       CacheHeaders.setNotCacheable(rsp);
     117           0 :       rsp.setContentType("text/html");
     118           0 :       rsp.setCharacterEncoding(HtmlDomUtil.ENC.name());
     119           0 :       rsp.setContentLength(tosend.length);
     120           0 :       try (OutputStream out = rsp.getOutputStream()) {
     121           0 :         out.write(tosend);
     122             :       }
     123             :     }
     124           0 :   }
     125             : 
     126             :   private boolean isSessionValid(HttpServletRequest req) {
     127           0 :     WebSession session = sessionProvider.get();
     128           0 :     if (session.isSignedIn()) {
     129           0 :       String user = getRemoteUser(req);
     130           0 :       return user == null || correctUser(user, session);
     131             :     }
     132           0 :     return false;
     133             :   }
     134             : 
     135             :   private boolean correctUser(String user, WebSession session) {
     136           0 :     Optional<ExternalId.Key> id = session.getUser().getLastLoginExternalIdKey();
     137           0 :     return id.map(i -> i.equals(externalIdKeyFactory.create(SCHEME_GERRIT, user))).orElse(false);
     138             :   }
     139             : 
     140             :   String getRemoteUser(HttpServletRequest req) {
     141           0 :     String remoteUser = RemoteUserUtil.getRemoteUser(req, loginHeader);
     142           0 :     return (userNameToLowerCase && remoteUser != null)
     143           0 :         ? remoteUser.toLowerCase(Locale.US)
     144           0 :         : remoteUser;
     145             :   }
     146             : 
     147             :   @Nullable
     148             :   String getRemoteDisplayname(HttpServletRequest req) {
     149           0 :     if (displaynameHeader != null) {
     150           0 :       String raw = req.getHeader(displaynameHeader);
     151           0 :       return emptyToNull(new String(raw.getBytes(ISO_8859_1), UTF_8));
     152             :     }
     153           0 :     return null;
     154             :   }
     155             : 
     156             :   @Nullable
     157             :   String getRemoteEmail(HttpServletRequest req) {
     158           0 :     if (emailHeader != null) {
     159           0 :       return emptyToNull(req.getHeader(emailHeader));
     160             :     }
     161           0 :     return null;
     162             :   }
     163             : 
     164             :   @Nullable
     165             :   String getRemoteExternalIdToken(HttpServletRequest req) {
     166           0 :     if (externalIdHeader != null) {
     167           0 :       return emptyToNull(req.getHeader(externalIdHeader));
     168             :     }
     169           0 :     return null;
     170             :   }
     171             : 
     172             :   String getLoginHeader() {
     173           0 :     return loginHeader;
     174             :   }
     175             : 
     176             :   @Override
     177           0 :   public void init(FilterConfig filterConfig) {}
     178             : 
     179             :   @Override
     180           0 :   public void destroy() {}
     181             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750