Line data Source code
1 : // Copyright (C) 2019 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.server; 16 : 17 : import static com.google.common.base.Preconditions.checkState; 18 : import static java.util.Objects.requireNonNull; 19 : 20 : import com.google.auto.value.AutoValue; 21 : import com.google.auto.value.extension.memoized.Memoized; 22 : import com.google.common.base.Splitter; 23 : import com.google.gerrit.common.UsedAt; 24 : import com.google.gerrit.entities.Project; 25 : import com.google.gerrit.server.logging.TraceContext; 26 : import java.util.Optional; 27 : 28 : /** Information about a request that was received from a user. */ 29 : @AutoValue 30 105 : public abstract class RequestInfo { 31 : /** Channel through which a user request was received. */ 32 105 : public enum RequestType { 33 : /** request type for git push */ 34 105 : GIT_RECEIVE, 35 : 36 : /** request type for git fetch */ 37 105 : GIT_UPLOAD, 38 : 39 : /** request type for call to REST API */ 40 105 : REST, 41 : 42 : /** request type for call to SSH API */ 43 105 : SSH 44 : } 45 : 46 : /** 47 : * Type of the request, telling through which channel the request was coming in. 48 : * 49 : * <p>See {@link RequestType} for the types that are used by Gerrit core. Other request types are 50 : * possible, e.g. if a plugin supports receiving requests through another channel. 51 : */ 52 : public abstract String requestType(); 53 : 54 : /** 55 : * Request URI. 56 : * 57 : * <p>Only set if request type is {@link RequestType#REST}. 58 : * 59 : * <p>Never includes the "/a" prefix. 60 : */ 61 : public abstract Optional<String> requestUri(); 62 : 63 : /** 64 : * Redacted request URI. 65 : * 66 : * <p>Request URI where resource IDs are replaced by '*'. 67 : */ 68 : @Memoized 69 : public Optional<String> redactedRequestUri() { 70 105 : return requestUri().map(RequestInfo::redactRequestUri); 71 : } 72 : 73 : /** The user that has sent the request. */ 74 : public abstract CurrentUser callingUser(); 75 : 76 : /** The trace context of the request. */ 77 : public abstract TraceContext traceContext(); 78 : 79 : /** 80 : * The name of the project for which the request is being done. Only available if the request is 81 : * tied to a project or change. If a project is available it's not guaranteed that it actually 82 : * exists (e.g. if a user made a request for a project that doesn't exist). 83 : */ 84 : public abstract Optional<Project.NameKey> project(); 85 : 86 : @Memoized 87 : public String formatForLogging() { 88 105 : StringBuilder sb = new StringBuilder(); 89 105 : sb.append(requestType()); 90 105 : redactedRequestUri().ifPresent(redactedRequestUri -> sb.append(' ').append(redactedRequestUri)); 91 105 : return sb.toString(); 92 : } 93 : 94 : /** 95 : * Redacts resource IDs from the given request URI. 96 : * 97 : * <p>resource IDs in the request URI are replaced with '*'. 98 : * 99 : * @param requestUri a REST URI that has path segments that alternate between view name and 100 : * resource IDs (e.g. "/<view>", "/<view>/<id>", "/<view>/<id>/<view>", 101 : * "/<view>/<id>/<view>/<id>", "/<view>/<id>/<view>/<id>/<view>" etc.), must be given without 102 : * the '/a' prefix 103 : * @return the redacted request URI 104 : */ 105 : static String redactRequestUri(String requestUri) { 106 29 : requireNonNull(requestUri, "requestUri"); 107 29 : checkState( 108 29 : !requestUri.startsWith("/a/"), "request URI must not start with '/a/': %s", requestUri); 109 : 110 29 : StringBuilder redactedRequestUri = new StringBuilder(); 111 : 112 29 : boolean hasLeadingSlash = false; 113 29 : boolean hasTrailingSlash = false; 114 29 : if (requestUri.startsWith("/")) { 115 29 : hasLeadingSlash = true; 116 29 : requestUri = requestUri.substring(1); 117 : } 118 29 : if (requestUri.endsWith("/")) { 119 12 : hasTrailingSlash = true; 120 12 : requestUri = requestUri.substring(0, requestUri.length() - 1); 121 : } 122 : 123 29 : boolean idPathSegment = false; 124 29 : for (String pathSegment : Splitter.on('/').split(requestUri)) { 125 29 : if (!idPathSegment) { 126 29 : redactedRequestUri.append("/" + pathSegment); 127 29 : idPathSegment = true; 128 : } else { 129 28 : redactedRequestUri.append("/"); 130 28 : if (!pathSegment.isEmpty()) { 131 28 : redactedRequestUri.append("*"); 132 : } 133 28 : idPathSegment = false; 134 : } 135 29 : } 136 : 137 29 : if (!hasLeadingSlash) { 138 1 : redactedRequestUri.deleteCharAt(0); 139 : } 140 29 : if (hasTrailingSlash) { 141 12 : redactedRequestUri.append('/'); 142 : } 143 : 144 29 : return redactedRequestUri.toString(); 145 : } 146 : 147 : public static RequestInfo.Builder builder( 148 : RequestType requestType, CurrentUser callingUser, TraceContext traceContext) { 149 105 : return builder().requestType(requestType).callingUser(callingUser).traceContext(traceContext); 150 : } 151 : 152 : @UsedAt(UsedAt.Project.GOOGLE) 153 : public static RequestInfo.Builder builder() { 154 105 : return new AutoValue_RequestInfo.Builder(); 155 : } 156 : 157 : @AutoValue.Builder 158 105 : public abstract static class Builder { 159 : public abstract Builder requestType(String requestType); 160 : 161 : public Builder requestType(RequestType requestType) { 162 105 : return requestType(requestType.name()); 163 : } 164 : 165 : public abstract Builder requestUri(String requestUri); 166 : 167 : public abstract Builder callingUser(CurrentUser callingUser); 168 : 169 : public abstract Builder traceContext(TraceContext traceContext); 170 : 171 : public abstract Builder project(Project.NameKey projectName); 172 : 173 : public abstract RequestInfo build(); 174 : } 175 : }