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 : }