Commit ae564c97d48bf728745c57720734cb40378fd90f

Authored by Dmitriy Zaporozhets
1 parent d5b0f29c

Dont expose user email via API

To prevent leaking of users info we reduce amount of user information
retrieved via API for normal users.

What user can get via API:

* if not admin: only id, state, name, username and avatar_url
* if admin: all user information
* about himself: all informaion

Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
app/assets/javascripts/project_users_select.js.coffee
... ... @@ -37,13 +37,9 @@
37 37  
38 38 projectUserFormatResult: (user) ->
39 39 if user.avatar_url
40   - avatar = gon.relative_url_root + user.avatar_url
41   - else if gon.gravatar_enabled
42   - avatar = gon.gravatar_url
43   - avatar = avatar.replace('%{hash}', md5(user.email))
44   - avatar = avatar.replace('%{size}', '24')
  40 + avatar = user.avatar_url
45 41 else
46   - avatar = gon.relative_url_root + "#{image_path('no_avatar.png')}"
  42 + avatar = gon.default_avatar_url
47 43  
48 44 if user.id == ''
49 45 avatarMarkup = ''
... ...
app/assets/javascripts/users_select.js.coffee
1 1 $ ->
2 2 userFormatResult = (user) ->
3 3 if user.avatar_url
4   - avatar = gon.relative_url_root + user.avatar_url
5   - else if gon.gravatar_enabled
6   - avatar = gon.gravatar_url
7   - avatar = avatar.replace('%{hash}', md5(user.email))
8   - avatar = avatar.replace('%{size}', '24')
  4 + avatar = user.avatar_url
9 5 else
10   - avatar = gon.relative_url_root + "#{image_path('no_avatar.png')}"
  6 + avatar = gon.default_avatar_url
11 7  
12 8 "<div class='user-result'>
13 9 <div class='user-image'><img class='avatar s24' src='#{avatar}'></div>
... ...
app/controllers/application_controller.rb
... ... @@ -164,9 +164,8 @@ class ApplicationController &lt; ActionController::Base
164 164 def add_gon_variables
165 165 gon.default_issues_tracker = Project.issues_tracker.default_value
166 166 gon.api_version = API::API.version
167   - gon.gravatar_url = request.ssl? || Gitlab.config.gitlab.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
168 167 gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
169   - gon.gravatar_enabled = Gitlab.config.gravatar.enabled
  168 + gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
170 169  
171 170 if current_user
172 171 gon.current_user_id = current_user.id
... ...
app/helpers/application_helper.rb
... ... @@ -60,23 +60,21 @@ module ApplicationHelper
60 60  
61 61 def avatar_icon(user_email = '', size = nil)
62 62 user = User.find_by(email: user_email)
63   - if user && user.avatar.present?
64   - user.avatar.url
  63 +
  64 + if user
  65 + user.avatar_url(size) || default_avatar
65 66 else
66 67 gravatar_icon(user_email, size)
67 68 end
68 69 end
69 70  
70 71 def gravatar_icon(user_email = '', size = nil)
71   - size = 40 if size.nil? || size <= 0
  72 + GravatarService.new.execute(user_email, size) ||
  73 + default_avatar
  74 + end
72 75  
73   - if !Gitlab.config.gravatar.enabled || user_email.blank?
74   - image_path('no_avatar.png')
75   - else
76   - gravatar_url = request.ssl? || gitlab_config.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
77   - user_email.strip!
78   - sprintf gravatar_url, hash: Digest::MD5.hexdigest(user_email.downcase), size: size, email: user_email
79   - end
  76 + def default_avatar
  77 + image_path('no_avatar.png')
80 78 end
81 79  
82 80 def last_commit(project)
... ...
app/models/user.rb
... ... @@ -482,4 +482,12 @@ class User &lt; ActiveRecord::Base
482 482 def public_profile?
483 483 authorized_projects.public_only.any?
484 484 end
  485 +
  486 + def avatar_url(size = nil)
  487 + if avatar.present?
  488 + URI::join(Gitlab.config.gitlab.url, avatar.url).to_s
  489 + else
  490 + GravatarService.new.execute(email)
  491 + end
  492 + end
485 493 end
... ...
lib/api/entities.rb
1 1 module API
2 2 module Entities
3   - class User < Grape::Entity
4   - expose :id, :username, :email, :name, :bio, :skype, :linkedin, :twitter, :website_url,
5   - :theme_id, :color_scheme_id, :state, :created_at, :extern_uid, :provider
6   - expose :is_admin?, as: :is_admin
7   - expose :can_create_group?, as: :can_create_group
8   - expose :can_create_project?, as: :can_create_project
  3 + class UserSafe < Grape::Entity
  4 + expose :name, :username
  5 + end
9 6  
10   - expose :avatar_url do |user, options|
11   - if user.avatar.present?
12   - user.avatar.url
13   - end
14   - end
  7 + class UserBasic < UserSafe
  8 + expose :id, :state, :avatar_url
15 9 end
16 10  
17   - class UserSafe < Grape::Entity
18   - expose :name, :username
  11 + class User < UserBasic
  12 + expose :created_at
  13 + expose :is_admin?, as: :is_admin
  14 + expose :bio, :skype, :linkedin, :twitter, :website_url
19 15 end
20 16  
21   - class UserBasic < Grape::Entity
22   - expose :id, :username, :email, :name, :state, :created_at
  17 + class UserFull < User
  18 + expose :email
  19 + expose :theme_id, :color_scheme_id, :extern_uid, :provider
  20 + expose :can_create_group?, as: :can_create_group
  21 + expose :can_create_project?, as: :can_create_project
23 22 end
24 23  
25   - class UserLogin < User
  24 + class UserLogin < UserFull
26 25 expose :private_token
27 26 end
28 27  
... ...
lib/api/internal.rb
... ... @@ -59,4 +59,3 @@ module API
59 59 end
60 60 end
61 61 end
62   -
... ...
lib/api/projects.rb
... ... @@ -209,7 +209,7 @@ module API
209 209 @users = User.where(id: user_project.team.users.map(&:id))
210 210 @users = @users.search(params[:search]) if params[:search].present?
211 211 @users = paginate @users
212   - present @users, with: Entities::User
  212 + present @users, with: Entities::UserBasic
213 213 end
214 214  
215 215 # Get a project labels
... ...
lib/api/users.rb
... ... @@ -13,7 +13,12 @@ module API
13 13 @users = @users.active if params[:active].present?
14 14 @users = @users.search(params[:search]) if params[:search].present?
15 15 @users = paginate @users
16   - present @users, with: Entities::User
  16 +
  17 + if current_user.is_admin?
  18 + present @users, with: Entities::UserFull
  19 + else
  20 + present @users, with: Entities::UserBasic
  21 + end
17 22 end
18 23  
19 24 # Get a single user
... ... @@ -24,7 +29,12 @@ module API
24 29 # GET /users/:id
25 30 get ":id" do
26 31 @user = User.find(params[:id])
27   - present @user, with: Entities::User
  32 +
  33 + if current_user.is_admin?
  34 + present @user, with: Entities::UserFull
  35 + else
  36 + present @user, with: Entities::UserBasic
  37 + end
28 38 end
29 39  
30 40 # Create user. Available only for admin
... ... @@ -53,7 +63,7 @@ module API
53 63 admin = attrs.delete(:admin)
54 64 user.admin = admin unless admin.nil?
55 65 if user.save
56   - present user, with: Entities::User
  66 + present user, with: Entities::UserFull
57 67 else
58 68 not_found!
59 69 end
... ... @@ -87,7 +97,7 @@ module API
87 97 admin = attrs.delete(:admin)
88 98 user.admin = admin unless admin.nil?
89 99 if user.update_attributes(attrs, as: :admin)
90   - present user, with: Entities::User
  100 + present user, with: Entities::UserFull
91 101 else
92 102 not_found!
93 103 end
... ...
spec/requests/api/users_spec.rb
... ... @@ -20,7 +20,7 @@ describe API::API, api: true do
20 20 get api("/users", user)
21 21 response.status.should == 200
22 22 json_response.should be_an Array
23   - json_response.first['email'].should == user.email
  23 + json_response.first['username'].should == user.username
24 24 end
25 25 end
26 26 end
... ... @@ -29,7 +29,7 @@ describe API::API, api: true do
29 29 it "should return a user by id" do
30 30 get api("/users/#{user.id}", user)
31 31 response.status.should == 200
32   - json_response['email'].should == user.email
  32 + json_response['username'].should == user.username
33 33 end
34 34  
35 35 it "should return a 401 if unauthenticated" do
... ...