LCOV - code coverage report
Current view: top level - httpd/auth/become - BecomeAnyAccountLoginServlet.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 32 97 33.0 %
Date: 2022-11-19 15:00:39 Functions: 6 11 54.5 %

          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.become;
      16             : 
      17             : import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
      18             : import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_UUID;
      19             : 
      20             : import com.google.gerrit.common.Nullable;
      21             : import com.google.gerrit.common.PageLinks;
      22             : import com.google.gerrit.entities.Account;
      23             : import com.google.gerrit.extensions.registration.DynamicItem;
      24             : import com.google.gerrit.httpd.HtmlDomUtil;
      25             : import com.google.gerrit.httpd.LoginUrlToken;
      26             : import com.google.gerrit.httpd.WebSession;
      27             : import com.google.gerrit.httpd.template.SiteHeaderFooter;
      28             : import com.google.gerrit.server.account.AccountCache;
      29             : import com.google.gerrit.server.account.AccountException;
      30             : import com.google.gerrit.server.account.AccountManager;
      31             : import com.google.gerrit.server.account.AccountState;
      32             : import com.google.gerrit.server.account.Accounts;
      33             : import com.google.gerrit.server.account.AuthRequest;
      34             : import com.google.gerrit.server.account.AuthResult;
      35             : import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
      36             : import com.google.gerrit.server.query.account.InternalAccountQuery;
      37             : import com.google.gerrit.util.http.CacheHeaders;
      38             : import com.google.inject.Inject;
      39             : import com.google.inject.Provider;
      40             : import com.google.inject.Singleton;
      41             : import java.io.FileNotFoundException;
      42             : import java.io.IOException;
      43             : import java.io.OutputStream;
      44             : import java.io.Writer;
      45             : import java.util.List;
      46             : import java.util.Optional;
      47             : import java.util.UUID;
      48             : import javax.servlet.ServletException;
      49             : import javax.servlet.http.HttpServlet;
      50             : import javax.servlet.http.HttpServletRequest;
      51             : import javax.servlet.http.HttpServletResponse;
      52             : import org.eclipse.jgit.errors.ConfigInvalidException;
      53             : import org.w3c.dom.Document;
      54             : import org.w3c.dom.Element;
      55             : 
      56             : @Singleton
      57             : class BecomeAnyAccountLoginServlet extends HttpServlet {
      58             :   private static final long serialVersionUID = 1L;
      59             : 
      60             :   private final DynamicItem<WebSession> webSession;
      61             :   private final Accounts accounts;
      62             :   private final AccountCache accountCache;
      63             :   private final AccountManager accountManager;
      64             :   private final SiteHeaderFooter headers;
      65             :   private final Provider<InternalAccountQuery> queryProvider;
      66             :   private final ExternalIdKeyFactory externalIdKeyFactory;
      67             :   private final AuthRequest.Factory authRequestFactory;
      68             : 
      69             :   @Inject
      70             :   BecomeAnyAccountLoginServlet(
      71             :       DynamicItem<WebSession> ws,
      72             :       Accounts a,
      73             :       AccountCache ac,
      74             :       AccountManager am,
      75             :       SiteHeaderFooter shf,
      76             :       Provider<InternalAccountQuery> qp,
      77             :       ExternalIdKeyFactory eikf,
      78           2 :       AuthRequest.Factory arf) {
      79           2 :     webSession = ws;
      80           2 :     accounts = a;
      81           2 :     accountCache = ac;
      82           2 :     accountManager = am;
      83           2 :     headers = shf;
      84           2 :     queryProvider = qp;
      85           2 :     externalIdKeyFactory = eikf;
      86           2 :     authRequestFactory = arf;
      87           2 :   }
      88             : 
      89             :   @Override
      90             :   protected void doGet(HttpServletRequest req, HttpServletResponse rsp)
      91             :       throws IOException, ServletException {
      92           1 :     doPost(req, rsp);
      93           1 :   }
      94             : 
      95             :   @Override
      96             :   protected void doPost(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
      97           1 :     CacheHeaders.setNotCacheable(rsp);
      98             : 
      99             :     final AuthResult res;
     100           1 :     if ("create_account".equals(req.getParameter("action"))) {
     101           0 :       res = create();
     102             : 
     103           1 :     } else if (req.getParameter("user_name") != null) {
     104           0 :       res = byUserName(req.getParameter("user_name"));
     105             : 
     106           1 :     } else if (req.getParameter("preferred_email") != null) {
     107           0 :       res = byPreferredEmail(req.getParameter("preferred_email")).orElse(null);
     108             : 
     109           1 :     } else if (req.getParameter("account_id") != null) {
     110           1 :       res = byAccountId(req.getParameter("account_id")).orElse(null);
     111             : 
     112             :     } else {
     113             :       byte[] raw;
     114           0 :       raw = prepareHtmlOutput();
     115           0 :       rsp.setContentType("text/html");
     116           0 :       rsp.setCharacterEncoding(HtmlDomUtil.ENC.name());
     117           0 :       rsp.setContentLength(raw.length);
     118           0 :       try (OutputStream out = rsp.getOutputStream()) {
     119           0 :         out.write(raw);
     120             :       }
     121           0 :       return;
     122             :     }
     123             : 
     124           1 :     if (res != null) {
     125           1 :       webSession.get().login(res, false);
     126           1 :       final StringBuilder rdr = new StringBuilder();
     127           1 :       rdr.append(req.getContextPath());
     128           1 :       rdr.append("/");
     129             : 
     130           1 :       if (res.isNew()) {
     131           0 :         rdr.append('#' + PageLinks.REGISTER);
     132             :       } else {
     133           1 :         rdr.append(LoginUrlToken.getToken(req));
     134             :       }
     135           1 :       rsp.sendRedirect(rdr.toString());
     136             : 
     137           1 :     } else {
     138           0 :       rsp.setContentType("text/html");
     139           0 :       rsp.setCharacterEncoding(HtmlDomUtil.ENC.name());
     140           0 :       try (Writer out = rsp.getWriter()) {
     141           0 :         out.write("<html>");
     142           0 :         out.write("<body>");
     143           0 :         out.write("<h1>Account Not Found</h1>");
     144           0 :         out.write("</body>");
     145           0 :         out.write("</html>");
     146             :       }
     147             :     }
     148           1 :   }
     149             : 
     150             :   private byte[] prepareHtmlOutput() throws IOException {
     151           0 :     final String pageName = "BecomeAnyAccount.html";
     152           0 :     Document doc = headers.parse(getClass(), pageName);
     153           0 :     if (doc == null) {
     154           0 :       throw new FileNotFoundException("No " + pageName + " in webapp");
     155             :     }
     156             : 
     157           0 :     Element userlistElement = HtmlDomUtil.find(doc, "userlist");
     158           0 :     for (Account.Id accountId : accounts.firstNIds(100)) {
     159           0 :       Optional<AccountState> accountState = accountCache.get(accountId);
     160           0 :       if (!accountState.isPresent()) {
     161           0 :         continue;
     162             :       }
     163           0 :       Account account = accountState.get().account();
     164             :       String displayName;
     165           0 :       if (accountState.get().userName().isPresent()) {
     166           0 :         displayName = accountState.get().userName().get();
     167           0 :       } else if (account.fullName() != null && !account.fullName().isEmpty()) {
     168           0 :         displayName = account.fullName();
     169           0 :       } else if (account.preferredEmail() != null) {
     170           0 :         displayName = account.preferredEmail();
     171             :       } else {
     172           0 :         displayName = accountId.toString();
     173             :       }
     174             : 
     175           0 :       Element linkElement = doc.createElement("a");
     176           0 :       linkElement.setAttribute("href", "?account_id=" + account.id().toString());
     177           0 :       linkElement.setTextContent(displayName);
     178           0 :       userlistElement.appendChild(linkElement);
     179           0 :       userlistElement.appendChild(doc.createElement("br"));
     180           0 :     }
     181             : 
     182           0 :     return HtmlDomUtil.toUTF8(doc);
     183             :   }
     184             : 
     185             :   private Optional<AuthResult> auth(Optional<AccountState> account) {
     186           1 :     return account.map(a -> new AuthResult(a.account().id(), null, false));
     187             :   }
     188             : 
     189             :   @Nullable
     190             :   private AuthResult auth(Account.Id account) {
     191           0 :     if (account != null) {
     192           0 :       return new AuthResult(account, null, false);
     193             :     }
     194           0 :     return null;
     195             :   }
     196             : 
     197             :   @Nullable
     198             :   private AuthResult byUserName(String userName) {
     199           0 :     List<AccountState> accountStates = queryProvider.get().byExternalId(SCHEME_USERNAME, userName);
     200           0 :     if (accountStates.isEmpty()) {
     201           0 :       getServletContext().log("No accounts with username " + userName + " found");
     202           0 :       return null;
     203             :     }
     204           0 :     if (accountStates.size() > 1) {
     205           0 :       getServletContext().log("Multiple accounts with username " + userName + " found");
     206           0 :       return null;
     207             :     }
     208           0 :     return auth(accountStates.get(0).account().id());
     209             :   }
     210             : 
     211             :   private Optional<AuthResult> byPreferredEmail(String email) {
     212           0 :     return auth(queryProvider.get().byPreferredEmail(email).stream().findFirst());
     213             :   }
     214             : 
     215             :   private Optional<AuthResult> byAccountId(String idStr) {
     216           1 :     Optional<Account.Id> id = Account.Id.tryParse(idStr);
     217           1 :     if (!id.isPresent()) {
     218           0 :       return Optional.empty();
     219             :     }
     220             : 
     221             :     try {
     222           1 :       return auth(accounts.get(id.get()));
     223           0 :     } catch (IOException | ConfigInvalidException e) {
     224           0 :       getServletContext().log("cannot query database", e);
     225           0 :       return Optional.empty();
     226             :     }
     227             :   }
     228             : 
     229             :   @Nullable
     230             :   private AuthResult create() throws IOException {
     231             :     try {
     232           0 :       return accountManager.authenticate(
     233           0 :           authRequestFactory.create(
     234           0 :               externalIdKeyFactory.create(SCHEME_UUID, UUID.randomUUID().toString())));
     235           0 :     } catch (AccountException e) {
     236           0 :       getServletContext().log("cannot create new account", e);
     237           0 :       return null;
     238             :     }
     239             :   }
     240             : }

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