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; 16 : 17 : import static java.util.concurrent.TimeUnit.HOURS; 18 : 19 : import com.google.common.annotations.VisibleForTesting; 20 : import com.google.common.base.Strings; 21 : import com.google.gerrit.common.Nullable; 22 : import com.google.gerrit.common.UsedAt; 23 : import com.google.gerrit.entities.Account; 24 : import com.google.gerrit.extensions.restapi.BadRequestException; 25 : import com.google.gerrit.httpd.restapi.ParameterParser; 26 : import com.google.gerrit.server.AccessPath; 27 : import com.google.gerrit.server.AnonymousUser; 28 : import com.google.gerrit.server.CurrentUser; 29 : import com.google.gerrit.server.IdentifiedUser; 30 : import com.google.gerrit.server.PropertyMap; 31 : import com.google.gerrit.server.account.AccountCache; 32 : import com.google.gerrit.server.account.AuthResult; 33 : import com.google.gerrit.server.account.externalids.ExternalId; 34 : import com.google.gerrit.server.config.AuthConfig; 35 : import com.google.inject.Provider; 36 : import java.util.EnumSet; 37 : import javax.servlet.http.Cookie; 38 : import javax.servlet.http.HttpServletRequest; 39 : import javax.servlet.http.HttpServletResponse; 40 : import org.eclipse.jgit.http.server.GitSmartHttpTools; 41 : 42 : public abstract class CacheBasedWebSession extends WebSession { 43 : @VisibleForTesting public static final String ACCOUNT_COOKIE = "GerritAccount"; 44 : 45 : @UsedAt(UsedAt.Project.PLUGIN_WEBSESSION_FLATFILE) 46 100 : public static final long MAX_AGE_MINUTES = HOURS.toMinutes(12); 47 : 48 : private final HttpServletRequest request; 49 : private final HttpServletResponse response; 50 : private final WebSessionManager manager; 51 : private final AuthConfig authConfig; 52 : private final Provider<AnonymousUser> anonymousProvider; 53 : private final IdentifiedUser.RequestFactory identified; 54 39 : private final EnumSet<AccessPath> okPaths = EnumSet.of(AccessPath.UNKNOWN); 55 : private final AccountCache byIdCache; 56 : private Cookie outCookie; 57 : 58 : private WebSessionManager.Key key; 59 : private WebSessionManager.Val val; 60 : private CurrentUser user; 61 : 62 : protected CacheBasedWebSession( 63 : HttpServletRequest request, 64 : HttpServletResponse response, 65 : WebSessionManager manager, 66 : AuthConfig authConfig, 67 : Provider<AnonymousUser> anonymousProvider, 68 : IdentifiedUser.RequestFactory identified, 69 39 : AccountCache byIdCache) { 70 39 : this.request = request; 71 39 : this.response = response; 72 39 : this.manager = manager; 73 39 : this.authConfig = authConfig; 74 39 : this.anonymousProvider = anonymousProvider; 75 39 : this.identified = identified; 76 39 : this.byIdCache = byIdCache; 77 : 78 39 : String cookie = readCookie(request); 79 39 : if (cookie != null) { 80 2 : authFromCookie(cookie); 81 39 : } else if (request.getRequestURI() == null || !GitSmartHttpTools.isGitClient(request)) { 82 : String token; 83 : try { 84 31 : token = ParameterParser.getQueryParams(request).accessToken(); 85 0 : } catch (BadRequestException e) { 86 0 : token = null; 87 31 : } 88 31 : if (token != null) { 89 0 : authFromQueryParameter(token); 90 : } 91 : } 92 39 : if (val != null && !checkAccountStatus(val.getAccountId())) { 93 1 : val = null; 94 1 : okPaths.clear(); 95 : } 96 39 : if (val != null && val.needsCookieRefresh()) { 97 : // Session is more than half old; update cache entry with new expiration date. 98 0 : val = manager.createVal(key, val); 99 : } 100 39 : } 101 : 102 : private void authFromCookie(String cookie) { 103 2 : key = new WebSessionManager.Key(cookie); 104 2 : val = manager.get(key); 105 2 : String token = request.getHeader(XsrfConstants.XSRF_HEADER_NAME); 106 2 : if (val != null && token != null && token.equals(val.getAuth())) { 107 0 : okPaths.add(AccessPath.REST_API); 108 : } 109 2 : } 110 : 111 : private void authFromQueryParameter(String accessToken) { 112 0 : key = new WebSessionManager.Key(accessToken); 113 0 : val = manager.get(key); 114 0 : if (val != null) { 115 0 : okPaths.add(AccessPath.REST_API); 116 : } 117 0 : } 118 : 119 : @Nullable 120 : private static String readCookie(HttpServletRequest request) { 121 39 : Cookie[] all = request.getCookies(); 122 39 : if (all != null) { 123 2 : for (Cookie c : all) { 124 2 : if (ACCOUNT_COOKIE.equals(c.getName())) { 125 2 : return Strings.emptyToNull(c.getValue()); 126 : } 127 : } 128 : } 129 39 : return null; 130 : } 131 : 132 : @Override 133 : public boolean isSignedIn() { 134 39 : return val != null; 135 : } 136 : 137 : @Override 138 : @Nullable 139 : public String getXGerritAuth() { 140 0 : return isSignedIn() ? val.getAuth() : null; 141 : } 142 : 143 : @Override 144 : public boolean isValidXGerritAuth(String keyIn) { 145 0 : return keyIn.equals(getXGerritAuth()); 146 : } 147 : 148 : @Override 149 : public boolean isAccessPathOk(AccessPath path) { 150 19 : return okPaths.contains(path); 151 : } 152 : 153 : @Override 154 : public void setAccessPathOk(AccessPath path, boolean ok) { 155 38 : if (ok) { 156 38 : okPaths.add(path); 157 : } else { 158 0 : okPaths.remove(path); 159 : } 160 38 : } 161 : 162 : @Override 163 : public CurrentUser getUser() { 164 38 : if (user == null) { 165 34 : if (isSignedIn()) { 166 : 167 1 : user = identified.create(val.getAccountId(), getUserProperties(val)); 168 : } else { 169 34 : user = anonymousProvider.get(); 170 : } 171 : } 172 38 : return user; 173 : } 174 : 175 : private static PropertyMap getUserProperties(@Nullable WebSessionManager.Val val) { 176 2 : if (val == null || val.getExternalId() == null) { 177 2 : return PropertyMap.EMPTY; 178 : } 179 0 : return PropertyMap.builder() 180 0 : .put(CurrentUser.LAST_LOGIN_EXTERNAL_ID_PROPERTY_KEY, val.getExternalId()) 181 0 : .build(); 182 : } 183 : 184 : @Override 185 : public void login(AuthResult res, boolean rememberMe) { 186 2 : Account.Id id = res.getAccountId(); 187 2 : ExternalId.Key identity = res.getExternalId(); 188 : 189 2 : if (val != null) { 190 1 : manager.destroy(key); 191 : } 192 : 193 2 : if (!checkAccountStatus(id)) { 194 1 : val = null; 195 1 : return; 196 : } 197 : 198 2 : key = manager.createKey(id); 199 2 : val = manager.createVal(key, id, rememberMe, identity, null, null); 200 2 : saveCookie(); 201 2 : user = identified.create(val.getAccountId(), getUserProperties(val)); 202 2 : } 203 : 204 : /** Set the user account for this current request only. */ 205 : @Override 206 : public void setUserAccountId(Account.Id id) { 207 38 : key = new WebSessionManager.Key("id:" + id); 208 38 : val = new WebSessionManager.Val(id, 0, false, null, 0, null, null); 209 38 : user = identified.runAs(id, user, PropertyMap.EMPTY); 210 38 : } 211 : 212 : @Override 213 : public void logout() { 214 1 : if (val != null) { 215 0 : manager.destroy(key); 216 0 : key = null; 217 0 : val = null; 218 0 : saveCookie(); 219 0 : user = anonymousProvider.get(); 220 : } 221 1 : } 222 : 223 : @Nullable 224 : @Override 225 : public String getSessionId() { 226 37 : return val != null ? val.getSessionId() : null; 227 : } 228 : 229 : private boolean checkAccountStatus(Account.Id id) { 230 2 : return byIdCache.get(id).filter(as -> as.account().isActive()).isPresent(); 231 : } 232 : 233 : private void saveCookie() { 234 2 : if (response == null) { 235 0 : return; 236 : } 237 : 238 : final String token; 239 : final int ageSeconds; 240 : 241 2 : if (key == null) { 242 0 : token = ""; 243 0 : ageSeconds = 0 /* erase at client */; 244 : } else { 245 2 : token = key.getToken(); 246 2 : ageSeconds = manager.getCookieAge(val); 247 : } 248 : 249 2 : String path = authConfig.getCookiePath(); 250 2 : if (Strings.isNullOrEmpty(path)) { 251 2 : path = request.getContextPath(); 252 2 : if (Strings.isNullOrEmpty(path)) { 253 2 : path = "/"; 254 : } 255 : } 256 : 257 2 : if (outCookie != null) { 258 0 : throw new IllegalStateException("Cookie " + ACCOUNT_COOKIE + " was set"); 259 : } 260 : 261 2 : outCookie = new Cookie(ACCOUNT_COOKIE, token); 262 : 263 2 : String domain = authConfig.getCookieDomain(); 264 2 : if (!Strings.isNullOrEmpty(domain)) { 265 0 : outCookie.setDomain(domain); 266 : } 267 : 268 2 : outCookie.setSecure(isSecure(request)); 269 2 : outCookie.setPath(path); 270 2 : outCookie.setMaxAge(ageSeconds); 271 2 : outCookie.setSecure(authConfig.getCookieSecure()); 272 2 : response.addCookie(outCookie); 273 2 : } 274 : 275 : private static boolean isSecure(HttpServletRequest req) { 276 2 : return req.isSecure() || "https".equals(req.getScheme()); 277 : } 278 : }