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