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