001/* 002 * SonarQube 003 * Copyright (C) 2009-2017 SonarSource SA 004 * mailto:info AT sonarsource DOT com 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with this program; if not, write to the Free Software Foundation, 018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 */ 020package org.sonar.api.server.authentication; 021 022import com.google.common.base.Predicate; 023import java.util.HashSet; 024import java.util.Set; 025import javax.annotation.CheckForNull; 026import javax.annotation.Nonnull; 027import javax.annotation.Nullable; 028import javax.annotation.concurrent.Immutable; 029 030import static com.google.common.base.Preconditions.checkArgument; 031import static com.google.common.base.Preconditions.checkNotNull; 032import static com.google.common.collect.FluentIterable.from; 033import static org.apache.commons.lang.StringUtils.isNotBlank; 034import static org.sonar.api.user.UserGroupValidation.validateGroupName; 035 036/** 037 * User information provided by the Identity Provider to be register into the platform. 038 * 039 * @since 5.4 040 */ 041@Immutable 042public final class UserIdentity { 043 044 private final String providerLogin; 045 private final String login; 046 private final String name; 047 private final String email; 048 private final boolean groupsProvided; 049 private final Set<String> groups; 050 051 private UserIdentity(Builder builder) { 052 this.providerLogin = builder.providerLogin; 053 this.login = builder.login; 054 this.name = builder.name; 055 this.email = builder.email; 056 this.groupsProvided = builder.groupsProvided; 057 this.groups = builder.groups; 058 } 059 060 /** 061 * Non-blank user login for the related {@link IdentityProvider}. 062 */ 063 public String getProviderLogin() { 064 return providerLogin; 065 } 066 067 /** 068 * Non-blank user login, unique for the SonarQube platform. 069 * If two {@link IdentityProvider} define two users with the same login, then users are considered as identical. 070 */ 071 public String getLogin() { 072 return login; 073 } 074 075 /** 076 * Non-blank display name. Uniqueness is not mandatory, even it's recommended for easier search of users 077 * in webapp. 078 */ 079 public String getName() { 080 return name; 081 } 082 083 /** 084 * Optional non-blank email. If defined, then it must be unique among all the users defined by all 085 * {@link IdentityProvider}. If not unique, then authentication will fail. 086 */ 087 @CheckForNull 088 public String getEmail() { 089 return email; 090 } 091 092 /** 093 * Return true if groups should be synchronized for this user. 094 * 095 * @since 5.5 096 */ 097 public boolean shouldSyncGroups() { 098 return groupsProvided; 099 } 100 101 /** 102 * List of group membership of the user. Only existing groups in SonarQube will be synchronized. 103 * 104 * @since 5.5 105 */ 106 public Set<String> getGroups() { 107 return groups; 108 } 109 110 public static Builder builder() { 111 return new Builder(); 112 } 113 114 public static class Builder { 115 private String providerLogin; 116 private String login; 117 private String name; 118 private String email; 119 private boolean groupsProvided = false; 120 private Set<String> groups = new HashSet<>(); 121 122 private Builder() { 123 } 124 125 /** 126 * @see UserIdentity#getProviderLogin() 127 */ 128 public Builder setProviderLogin(String providerLogin) { 129 this.providerLogin = providerLogin; 130 return this; 131 } 132 133 /** 134 * @see UserIdentity#getLogin() () 135 */ 136 public Builder setLogin(String login) { 137 this.login = login; 138 return this; 139 } 140 141 /** 142 * @see UserIdentity#getName() 143 */ 144 public Builder setName(String name) { 145 this.name = name; 146 return this; 147 } 148 149 /** 150 * @see UserIdentity#getEmail() 151 */ 152 public Builder setEmail(@Nullable String email) { 153 this.email = email; 154 return this; 155 } 156 157 /** 158 * Set group membership of the user. This method should only be used when synchronization of groups should be done. 159 * <ul> 160 * <li>When groups are not empty, group membership is synchronized when user logs in : 161 * <ul> 162 * <li>User won't belong anymore to a group that is not in the list (even the default group defined in 'sonar-users')</li> 163 * <li>User will belong only to groups that exist in SonarQube</li> 164 * <li>Groups that don't exist in SonarQube are silently ignored</li> 165 * </ul> 166 * <li>When groups are empty, user won't belong to any group</li> 167 * </ul> 168 * 169 * @throws NullPointerException when groups is null 170 * @since 5.5 171 */ 172 public Builder setGroups(Set<String> groups) { 173 checkNotNull(groups, "Groups cannot be null, please don't use this method if groups should not be synchronized."); 174 from(groups).filter(ValidateGroupName.INSTANCE).toList(); 175 this.groupsProvided = true; 176 this.groups = groups; 177 return this; 178 } 179 180 public UserIdentity build() { 181 validateProviderLogin(providerLogin); 182 validateLogin(login); 183 validateName(name); 184 validateEmail(email); 185 return new UserIdentity(this); 186 } 187 188 private static void validateProviderLogin(String providerLogin) { 189 checkArgument(isNotBlank(providerLogin), "Provider login must not be blank"); 190 checkArgument(providerLogin.length() <= 255, "Provider login size is incorrect (maximum 255 characters)"); 191 } 192 193 private static void validateLogin(String login) { 194 checkArgument(isNotBlank(login), "User login must not be blank"); 195 checkArgument(login.length() <= 255 && login.length() >= 2, "User login size is incorrect (Between 2 and 255 characters)"); 196 } 197 198 private static void validateName(String name) { 199 checkArgument(isNotBlank(name), "User name must not be blank"); 200 checkArgument(name.length() <= 200, "User name size is too big (200 characters max)"); 201 } 202 203 private static void validateEmail(@Nullable String email) { 204 checkArgument(email == null || email.length() <= 100, "User email size is too big (100 characters max)"); 205 } 206 } 207 208 private enum ValidateGroupName implements Predicate<String> { 209 INSTANCE; 210 211 @Override 212 public boolean apply(@Nonnull String input) { 213 validateGroupName(input); 214 return true; 215 } 216 } 217}