Line data Source code
1 : // Copyright (C) 2008 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.entities; 16 : 17 : import static com.google.gerrit.entities.RefNames.REFS_DRAFT_COMMENTS; 18 : import static com.google.gerrit.entities.RefNames.REFS_STARRED_CHANGES; 19 : import static com.google.gerrit.entities.RefNames.REFS_USERS; 20 : 21 : import com.google.auto.value.AutoValue; 22 : import com.google.common.primitives.Ints; 23 : import com.google.gerrit.common.Nullable; 24 : import com.google.gerrit.extensions.client.DiffPreferencesInfo; 25 : import java.time.Instant; 26 : import java.util.Optional; 27 : 28 : /** 29 : * Information about a single user. 30 : * 31 : * <p>A user may have multiple identities they can use to login to Gerrit (see ExternalId), but in 32 : * such cases they always map back to a single Account entity. 33 : * 34 : * <p>Entities "owned" by an Account (that is, their primary key contains the {@link Account.Id} key 35 : * as part of their key structure): 36 : * 37 : * <ul> 38 : * <li>ExternalId: OpenID identities and email addresses known to be registered to this user. 39 : * Multiple records can exist when the user has more than one public identity, such as a work 40 : * and a personal email address. 41 : * <li>AccountSshKey: user's public SSH keys, for authentication through the internal SSH daemon. 42 : * One record per SSH key uploaded by the user, keys are checked in random order until a match 43 : * is found. 44 : * <li>{@link DiffPreferencesInfo}: user's preferences for rendering side-to-side and unified diff 45 : * </ul> 46 : */ 47 : @AutoValue 48 153 : public abstract class Account { 49 : 50 : /** Placeholder for indicating an account-id that does not correspond to any local account */ 51 161 : public static final Id UNKNOWN_ACCOUNT_ID = id(0); 52 : 53 : public static Id id(int id) { 54 161 : return new AutoValue_Account_Id(id); 55 : } 56 : 57 : /** Key local to Gerrit to identify a user. */ 58 : @AutoValue 59 161 : public abstract static class Id implements Comparable<Id> { 60 : /** Parse an Account.Id out of a string representation. */ 61 : public static Optional<Id> tryParse(String str) { 62 94 : return Optional.ofNullable(Ints.tryParse(str)).map(Account::id); 63 : } 64 : 65 : @Nullable 66 : public static Id fromRef(String name) { 67 152 : if (name == null) { 68 1 : return null; 69 : } 70 152 : if (name.startsWith(REFS_USERS)) { 71 152 : return fromRefPart(name.substring(REFS_USERS.length())); 72 152 : } else if (name.startsWith(REFS_DRAFT_COMMENTS)) { 73 4 : return parseAfterShardedRefPart(name.substring(REFS_DRAFT_COMMENTS.length())); 74 152 : } else if (name.startsWith(REFS_STARRED_CHANGES)) { 75 11 : return parseAfterShardedRefPart(name.substring(REFS_STARRED_CHANGES.length())); 76 : } 77 152 : return null; 78 : } 79 : 80 : /** 81 : * Parse an Account.Id out of a part of a ref-name. 82 : * 83 : * @param name a ref name with the following syntax: {@code "34/1234..."}. We assume that the 84 : * caller has trimmed any prefix. 85 : */ 86 : @Nullable 87 : public static Id fromRefPart(String name) { 88 152 : Integer id = RefNames.parseShardedRefPart(name); 89 152 : return id != null ? Account.id(id) : null; 90 : } 91 : 92 : @Nullable 93 : public static Id parseAfterShardedRefPart(String name) { 94 12 : Integer id = RefNames.parseAfterShardedRefPart(name); 95 12 : return id != null ? Account.id(id) : null; 96 : } 97 : 98 : /** 99 : * Parse an Account.Id out of the last part of a ref name. 100 : * 101 : * <p>The input is a ref name of the form {@code ".../1234"}, where the suffix is a non-sharded 102 : * account ID. Ref names using a sharded ID should use {@link #fromRefPart(String)} instead for 103 : * greater safety. 104 : * 105 : * @param name ref name 106 : * @return account ID, or null if not numeric. 107 : */ 108 : @Nullable 109 : public static Id fromRefSuffix(String name) { 110 3 : Integer id = RefNames.parseRefSuffix(name); 111 3 : return id != null ? Account.id(id) : null; 112 : } 113 : 114 : abstract int id(); 115 : 116 : public int get() { 117 158 : return id(); 118 : } 119 : 120 : @Override 121 : public final int compareTo(Id o) { 122 68 : return Integer.compare(id(), o.id()); 123 : } 124 : 125 : @Override 126 : public final String toString() { 127 153 : return Integer.toString(get()); 128 : } 129 : } 130 : 131 : public abstract Id id(); 132 : 133 : /** Date and time the user registered with the review server. */ 134 : public abstract Instant registeredOn(); 135 : 136 : /** Full name of the user ("Given-name Surname" style). */ 137 : @Nullable 138 : public abstract String fullName(); 139 : 140 : /** Optional display name of the user to be shown in the UI. */ 141 : @Nullable 142 : public abstract String displayName(); 143 : 144 : /** Email address the user prefers to be contacted through. */ 145 : @Nullable 146 : public abstract String preferredEmail(); 147 : 148 : /** 149 : * Is this user inactive? This is used to avoid showing some users (eg. former employees) in 150 : * auto-suggest. 151 : */ 152 : public abstract boolean inactive(); 153 : 154 : /** The user-settable status of this account (e.g. busy, OOO, available) */ 155 : @Nullable 156 : public abstract String status(); 157 : 158 : /** ID of the user branch from which the account was read. */ 159 : @Nullable 160 : public abstract String metaId(); 161 : 162 : /** 163 : * Create a new account. 164 : * 165 : * @param newId unique id, see {@link com.google.gerrit.server.notedb.Sequences#nextAccountId()}. 166 : * @param registeredOn when the account was registered. 167 : */ 168 : public static Account.Builder builder(Account.Id newId, Instant registeredOn) { 169 153 : return new AutoValue_Account.Builder() 170 153 : .setInactive(false) 171 153 : .setId(newId) 172 153 : .setRegisteredOn(registeredOn); 173 : } 174 : 175 : /** 176 : * Formats an account name. 177 : * 178 : * <p>The return value goes into NoteDb commits and audit logs, so it should not be changed. 179 : * 180 : * <p>This method deliberately does not use {@code Anonymous Coward} because it can be changed 181 : * using a {@code gerrit.config} option which is a problem for NoteDb commits that still refer to 182 : * a previously defined value. 183 : * 184 : * @return the fullname, if present, otherwise the preferred email, if present, as a last resort a 185 : * generic string containing the accountId. 186 : */ 187 : public String getName() { 188 152 : if (fullName() != null) { 189 144 : return fullName(); 190 : } 191 23 : if (preferredEmail() != null) { 192 6 : return preferredEmail(); 193 : } 194 23 : return getName(id()); 195 : } 196 : 197 : public static String getName(Account.Id accountId) { 198 23 : return "GerritAccount #" + accountId.get(); 199 : } 200 : 201 : /** 202 : * Get the name and email address. 203 : * 204 : * <p>Example output: 205 : * 206 : * <ul> 207 : * <li>{@code A U. Thor <author@example.com>}: full populated 208 : * <li>{@code A U. Thor (12)}: missing email address 209 : * <li>{@code Anonymous Coward <author@example.com>}: missing name 210 : * <li>{@code Anonymous Coward (12)}: missing name and email address 211 : * </ul> 212 : */ 213 : public String getNameEmail(String anonymousCowardName) { 214 23 : String name = fullName() != null ? fullName() : anonymousCowardName; 215 23 : StringBuilder b = new StringBuilder(); 216 23 : b.append(name); 217 23 : if (preferredEmail() != null) { 218 23 : b.append(" <"); 219 23 : b.append(preferredEmail()); 220 23 : b.append(">"); 221 : } else { 222 6 : b.append(" ("); 223 6 : b.append(id().get()); 224 6 : b.append(")"); 225 : } 226 23 : return b.toString(); 227 : } 228 : 229 : public boolean isActive() { 230 150 : return !inactive(); 231 : } 232 : 233 : public abstract Builder toBuilder(); 234 : 235 : @AutoValue.Builder 236 153 : public abstract static class Builder { 237 : public abstract Id id(); 238 : 239 : abstract Builder setId(Id id); 240 : 241 : public abstract Instant registeredOn(); 242 : 243 : abstract Builder setRegisteredOn(Instant registeredOn); 244 : 245 : @Nullable 246 : public abstract String fullName(); 247 : 248 : public abstract Builder setFullName(String fullName); 249 : 250 : @Nullable 251 : public abstract String displayName(); 252 : 253 : public abstract Builder setDisplayName(String displayName); 254 : 255 : @Nullable 256 : public abstract String preferredEmail(); 257 : 258 : public abstract Builder setPreferredEmail(String preferredEmail); 259 : 260 : public abstract boolean inactive(); 261 : 262 : public abstract Builder setInactive(boolean inactive); 263 : 264 : public Builder setActive(boolean active) { 265 151 : return setInactive(!active); 266 : } 267 : 268 : @Nullable 269 : public abstract String status(); 270 : 271 : public abstract Builder setStatus(String status); 272 : 273 : @Nullable 274 : public abstract String metaId(); 275 : 276 : public abstract Builder setMetaId(@Nullable String metaId); 277 : 278 : public abstract Account build(); 279 : } 280 : 281 : @Override 282 : public final String toString() { 283 0 : return getName(); 284 : } 285 : }