users.rb 19.1 KB
Newer Older
1
module API
2
  class Users < Grape::API
3 4
    include PaginationParams

5 6 7 8
    before do
      allow_access_with_scope :read_user if request.get?
      authenticate!
    end
9

10
    resource :users, requirements: { uid: /[0-9]*/, id: /[0-9]*/ } do
Robert Schilling's avatar
Robert Schilling committed
11
      helpers do
12
        def find_user(params)
13 14
          id = params[:user_id] || params[:id]
          User.find_by(id: id) || not_found!('User')
15 16
        end

Robert Schilling's avatar
Robert Schilling committed
17 18 19 20 21 22 23
        params :optional_attributes do
          optional :skype, type: String, desc: 'The Skype username'
          optional :linkedin, type: String, desc: 'The LinkedIn username'
          optional :twitter, type: String, desc: 'The Twitter username'
          optional :website_url, type: String, desc: 'The website of the user'
          optional :organization, type: String, desc: 'The organization of the user'
          optional :projects_limit, type: Integer, desc: 'The number of projects a user can create'
24
          optional :extern_uid, type: String, desc: 'The external authentication provider UID'
Robert Schilling's avatar
Robert Schilling committed
25 26 27 28 29
          optional :provider, type: String, desc: 'The external provider'
          optional :bio, type: String, desc: 'The biography of the user'
          optional :location, type: String, desc: 'The location of the user'
          optional :admin, type: Boolean, desc: 'Flag indicating the user is an administrator'
          optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
30
          optional :skip_confirmation, type: Boolean, default: false, desc: 'Flag indicating the account is confirmed'
Robert Schilling's avatar
Robert Schilling committed
31 32 33 34 35 36 37 38 39
          optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
          all_or_none_of :extern_uid, :provider
        end
      end

      desc 'Get the list of users' do
        success Entities::UserBasic
      end
      params do
40
        # CE
Robert Schilling's avatar
Robert Schilling committed
41
        optional :username, type: String, desc: 'Get a single user with a specific username'
42 43
        optional :extern_uid, type: String, desc: 'Get a single user with a specific external authentication provider UID'
        optional :provider, type: String, desc: 'The external provider'
Robert Schilling's avatar
Robert Schilling committed
44 45 46 47
        optional :search, type: String, desc: 'Search for a username'
        optional :active, type: Boolean, default: false, desc: 'Filters only active users'
        optional :external, type: Boolean, default: false, desc: 'Filters only external users'
        optional :blocked, type: Boolean, default: false, desc: 'Filters only blocked users'
48
        all_or_none_of :extern_uid, :provider
49

50
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
51
      end
52
      get do
53
        unless can?(current_user, :read_users_list)
Felipe Artur's avatar
Felipe Artur committed
54 55 56
          render_api_error!("Not authorized.", 403)
        end

57 58 59 60 61 62 63 64 65 66 67
        authenticated_as_admin! if params[:external].present? || (params[:extern_uid].present? && params[:provider].present?)

        users = User.all
        users = User.where(username: params[:username]) if params[:username]
        users = users.active if params[:active]
        users = users.search(params[:search]) if params[:search].present?
        users = users.blocked if params[:blocked]

        if current_user.admin?
          users = users.joins(:identities).merge(Identity.with_extern_uid(params[:provider], params[:extern_uid])) if params[:extern_uid] && params[:provider]
          users = users.external if params[:external]
68
        end
69

70
        entity = current_user.admin? ? Entities::UserPublic : Entities::UserBasic
Robert Schilling's avatar
Robert Schilling committed
71
        present paginate(users), with: entity
72 73
      end

Robert Schilling's avatar
Robert Schilling committed
74 75 76 77 78 79
      desc 'Get a single user' do
        success Entities::UserBasic
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
80
      get ":id" do
Robert Schilling's avatar
Robert Schilling committed
81 82
        user = User.find_by(id: params[:id])
        not_found!('User') unless user
83

84
        if current_user && current_user.admin?
85
          present user, with: Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
86 87
        elsif can?(current_user, :read_user, user)
          present user, with: Entities::User
Felipe Artur's avatar
Felipe Artur committed
88 89
        else
          render_api_error!("User not found.", 404)
90
        end
91
      end
92

Robert Schilling's avatar
Robert Schilling committed
93
      desc 'Create a user. Available only for admins.' do
94
        success Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
95 96 97
      end
      params do
        requires :email, type: String, desc: 'The email of the user'
98 99 100
        optional :password, type: String, desc: 'The password of the new user'
        optional :reset_password, type: Boolean, desc: 'Flag indicating the user will be sent a password reset token'
        at_least_one_of :password, :reset_password
Robert Schilling's avatar
Robert Schilling committed
101 102 103 104
        requires :name, type: String, desc: 'The name of the user'
        requires :username, type: String, desc: 'The username of the user'
        use :optional_attributes
      end
105 106
      post do
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
107

108 109
        params = declared_params(include_missing: false)
        user = ::Users::CreateService.new(current_user, params).execute
110

111
        if user.persisted?
112
          present user, with: Entities::UserPublic
113
        else
114 115 116
          conflict!('Email has already been taken') if User.
              where(email: user.email).
              count > 0
117

118 119 120
          conflict!('Username has already been taken') if User.
              where(username: user.username).
              count > 0
121 122

          render_validation_error!(user)
123 124
        end
      end
125

Robert Schilling's avatar
Robert Schilling committed
126
      desc 'Update a user. Available only for admins.' do
127
        success Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
128 129 130 131 132 133 134 135 136 137 138 139 140
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
        optional :email, type: String, desc: 'The email of the user'
        optional :password, type: String, desc: 'The password of the new user'
        optional :name, type: String, desc: 'The name of the user'
        optional :username, type: String, desc: 'The username of the user'
        use :optional_attributes
        at_least_one_of :email, :password, :name, :username, :skype, :linkedin,
                        :twitter, :website_url, :organization, :projects_limit,
                        :extern_uid, :provider, :bio, :location, :admin,
                        :can_create_group, :confirm, :external
      end
141 142
      put ":id" do
        authenticated_as_admin!
143

Robert Schilling's avatar
Robert Schilling committed
144
        user = User.find_by(id: params.delete(:id))
145
        not_found!('User') unless user
146

Robert Schilling's avatar
Robert Schilling committed
147
        conflict!('Email has already been taken') if params[:email] &&
148 149
            User.where(email: params[:email]).
                where.not(id: user.id).count > 0
150

Robert Schilling's avatar
Robert Schilling committed
151
        conflict!('Username has already been taken') if params[:username] &&
152 153
            User.where(username: params[:username]).
                where.not(id: user.id).count > 0
154

155 156
        user_params = declared_params(include_missing: false)
        identity_attrs = user_params.slice(:provider, :extern_uid)
Robert Schilling's avatar
Robert Schilling committed
157

158 159
        if identity_attrs.any?
          identity = user.identities.find_by(provider: identity_attrs[:provider])
Robert Schilling's avatar
Robert Schilling committed
160

161 162 163 164 165 166 167 168
          if identity
            identity.update_attributes(identity_attrs)
          else
            identity = user.identities.build(identity_attrs)
            identity.save
          end
        end

169
        user_params[:password_expires_at] = Time.now if user_params[:password].present?
170

171
        if user.update_attributes(user_params.except(:extern_uid, :provider))
172
          present user, with: Entities::UserPublic
173
        else
174
          render_validation_error!(user)
175 176 177
        end
      end

Robert Schilling's avatar
Robert Schilling committed
178 179 180 181 182 183 184 185
      desc 'Add an SSH key to a specified user. Available only for admins.' do
        success Entities::SSHKey
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
        requires :key, type: String, desc: 'The new SSH key'
        requires :title, type: String, desc: 'The title of the new SSH key'
      end
Angus MacArthur's avatar
Angus MacArthur committed
186 187
      post ":id/keys" do
        authenticated_as_admin!
188

Robert Schilling's avatar
Robert Schilling committed
189 190 191 192 193
        user = User.find_by(id: params.delete(:id))
        not_found!('User') unless user

        key = user.keys.new(declared_params(include_missing: false))

Angus MacArthur's avatar
Angus MacArthur committed
194 195 196
        if key.save
          present key, with: Entities::SSHKey
        else
197
          render_validation_error!(key)
Angus MacArthur's avatar
Angus MacArthur committed
198 199 200
        end
      end

Robert Schilling's avatar
Robert Schilling committed
201 202 203 204 205
      desc 'Get the SSH keys of a specified user. Available only for admins.' do
        success Entities::SSHKey
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
206
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
207 208
      end
      get ':id/keys' do
209
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
210 211

        user = User.find_by(id: params[:id])
212 213
        not_found!('User') unless user

214
        present paginate(user.keys), with: Entities::SSHKey
215 216
      end

Robert Schilling's avatar
Robert Schilling committed
217 218 219 220 221 222 223 224
      desc 'Delete an existing SSH key from a specified user. Available only for admins.' do
        success Entities::SSHKey
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
        requires :key_id, type: Integer, desc: 'The ID of the SSH key'
      end
      delete ':id/keys/:key_id' do
225
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
226 227

        user = User.find_by(id: params[:id])
228 229
        not_found!('User') unless user

Robert Schilling's avatar
Robert Schilling committed
230 231 232
        key = user.keys.find_by(id: params[:key_id])
        not_found!('Key') unless key

233
        key.destroy
234 235
      end

Robert Schilling's avatar
Robert Schilling committed
236 237 238 239 240 241 242
      desc 'Add an email address to a specified user. Available only for admins.' do
        success Entities::Email
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
        requires :email, type: String, desc: 'The email of the user'
      end
243 244 245
      post ":id/emails" do
        authenticated_as_admin!

Robert Schilling's avatar
Robert Schilling committed
246 247 248 249 250
        user = User.find_by(id: params.delete(:id))
        not_found!('User') unless user

        email = user.emails.new(declared_params(include_missing: false))

251 252 253 254 255 256 257 258
        if email.save
          NotificationService.new.new_email(email)
          present email, with: Entities::Email
        else
          render_validation_error!(email)
        end
      end

Robert Schilling's avatar
Robert Schilling committed
259 260 261 262 263
      desc 'Get the emails addresses of a specified user. Available only for admins.' do
        success Entities::Email
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
264
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
265 266
      end
      get ':id/emails' do
267
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
268
        user = User.find_by(id: params[:id])
269 270
        not_found!('User') unless user

271
        present paginate(user.emails), with: Entities::Email
272 273
      end

Robert Schilling's avatar
Robert Schilling committed
274 275 276 277 278 279 280 281
      desc 'Delete an email address of a specified user. Available only for admins.' do
        success Entities::Email
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
        requires :email_id, type: Integer, desc: 'The ID of the email'
      end
      delete ':id/emails/:email_id' do
282
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
283
        user = User.find_by(id: params[:id])
284 285
        not_found!('User') unless user

Robert Schilling's avatar
Robert Schilling committed
286 287
        email = user.emails.find_by(id: params[:email_id])
        not_found!('Email') unless email
288

Robert Schilling's avatar
Robert Schilling committed
289 290
        email.destroy
        user.update_secondary_emails!
291 292
      end

Robert Schilling's avatar
Robert Schilling committed
293 294 295 296 297 298
      desc 'Delete a user. Available only for admins.' do
        success Entities::Email
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
299 300
      delete ":id" do
        authenticated_as_admin!
skv's avatar
skv committed
301
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
302
        not_found!('User') unless user
303

Stan Hu's avatar
Stan Hu committed
304
        DeleteUserWorker.perform_async(current_user.id, user.id)
305
      end
306

Robert Schilling's avatar
Robert Schilling committed
307 308 309 310
      desc 'Block a user. Available only for admins.'
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
311
      post ':id/block' do
312 313
        authenticated_as_admin!
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
314
        not_found!('User') unless user
315

Robert Schilling's avatar
Robert Schilling committed
316
        if !user.ldap_blocked?
317 318
          user.block
        else
319
          forbidden!('LDAP blocked users cannot be modified by the API')
320 321 322
        end
      end

Robert Schilling's avatar
Robert Schilling committed
323 324 325 326
      desc 'Unblock a user. Available only for admins.'
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
327
      post ':id/unblock' do
328 329
        authenticated_as_admin!
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
330
        not_found!('User') unless user
331

Robert Schilling's avatar
Robert Schilling committed
332
        if user.ldap_blocked?
333
          forbidden!('LDAP blocked users cannot be unblocked by the API')
Gabriel Mazetto's avatar
Gabriel Mazetto committed
334 335
        else
          user.activate
336 337
        end
      end
338

Robert Schilling's avatar
Robert Schilling committed
339
      desc 'Get the contribution events of a specified user' do
340 341 342 343
        detail 'This feature was introduced in GitLab 8.13.'
        success Entities::Event
      end
      params do
Robert Schilling's avatar
Robert Schilling committed
344
        requires :id, type: Integer, desc: 'The ID of the user'
345
        use :pagination
346 347
      end
      get ':id/events' do
Robert Schilling's avatar
Robert Schilling committed
348
        user = User.find_by(id: params[:id])
349 350
        not_found!('User') unless user

351
        events = user.events.
352
          merge(ProjectsFinder.new(current_user: current_user).execute).
353 354 355
          references(:project).
          with_associations.
          recent
356 357 358

        present paginate(events), with: Entities::Event
      end
359 360

      params do
361 362 363 364 365 366 367 368 369 370 371 372 373
        requires :user_id, type: Integer, desc: 'The ID of the user'
      end
      segment ':user_id' do
        resource :impersonation_tokens do
          helpers do
            def finder(options = {})
              user = find_user(params)
              PersonalAccessTokensFinder.new({ user: user, impersonation: true }.merge(options))
            end

            def find_impersonation_token
              finder.find_by(id: declared_params[:impersonation_token_id]) || not_found!('Impersonation Token')
            end
374
          end
375

376 377 378
          before { authenticated_as_admin! }

          desc 'Retrieve impersonation tokens. Available only for admins.' do
379
            detail 'This feature was introduced in GitLab 9.0'
380
            success Entities::ImpersonationToken
381 382
          end
          params do
383
            use :pagination
384
            optional :state, type: String, default: 'all', values: %w[all active inactive], desc: 'Filters (all|active|inactive) impersonation_tokens'
385
          end
386
          get { present paginate(finder(declared_params(include_missing: false)).execute), with: Entities::ImpersonationToken }
387

388
          desc 'Create a impersonation token. Available only for admins.' do
389
            detail 'This feature was introduced in GitLab 9.0'
390
            success Entities::ImpersonationToken
391 392
          end
          params do
393 394 395
            requires :name, type: String, desc: 'The name of the impersonation token'
            optional :expires_at, type: Date, desc: 'The expiration date in the format YEAR-MONTH-DAY of the impersonation token'
            optional :scopes, type: Array, desc: 'The array of scopes of the impersonation token'
396 397
          end
          post do
398
            impersonation_token = finder.build(declared_params(include_missing: false))
399

400 401
            if impersonation_token.save
              present impersonation_token, with: Entities::ImpersonationToken
402
            else
403
              render_validation_error!(impersonation_token)
404 405
            end
          end
406

407
          desc 'Retrieve impersonation token. Available only for admins.' do
408
            detail 'This feature was introduced in GitLab 9.0'
409
            success Entities::ImpersonationToken
410 411
          end
          params do
412
            requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token'
413
          end
414 415
          get ':impersonation_token_id' do
            present find_impersonation_token, with: Entities::ImpersonationToken
416
          end
417

418
          desc 'Revoke a impersonation token. Available only for admins.' do
419 420 421
            detail 'This feature was introduced in GitLab 9.0'
          end
          params do
422
            requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token'
423
          end
424 425
          delete ':impersonation_token_id' do
            find_impersonation_token.revoke!
426 427
          end
        end
428
      end
429 430
    end

431
    resource :user do
Robert Schilling's avatar
Robert Schilling committed
432
      desc 'Get the currently authenticated user' do
433
        success Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
434
      end
435
      get do
436
        present current_user, with: sudo? ? Entities::UserWithPrivateToken : Entities::UserPublic
437 438
      end

Robert Schilling's avatar
Robert Schilling committed
439 440 441
      desc "Get the currently authenticated user's SSH keys" do
        success Entities::SSHKey
      end
442 443 444
      params do
        use :pagination
      end
445
      get "keys" do
446
        present paginate(current_user.keys), with: Entities::SSHKey
447 448
      end

Robert Schilling's avatar
Robert Schilling committed
449 450 451 452 453 454 455 456 457 458
      desc 'Get a single key owned by currently authenticated user' do
        success Entities::SSHKey
      end
      params do
        requires :key_id, type: Integer, desc: 'The ID of the SSH key'
      end
      get "keys/:key_id" do
        key = current_user.keys.find_by(id: params[:key_id])
        not_found!('Key') unless key

459 460 461
        present key, with: Entities::SSHKey
      end

Robert Schilling's avatar
Robert Schilling committed
462 463 464 465 466 467 468
      desc 'Add a new SSH key to the currently authenticated user' do
        success Entities::SSHKey
      end
      params do
        requires :key, type: String, desc: 'The new SSH key'
        requires :title, type: String, desc: 'The title of the new SSH key'
      end
469
      post "keys" do
Robert Schilling's avatar
Robert Schilling committed
470
        key = current_user.keys.new(declared_params)
471

472 473 474
        if key.save
          present key, with: Entities::SSHKey
        else
475
          render_validation_error!(key)
476 477 478
        end
      end

Robert Schilling's avatar
Robert Schilling committed
479 480 481 482 483 484 485 486 487 488
      desc 'Delete an SSH key from the currently authenticated user' do
        success Entities::SSHKey
      end
      params do
        requires :key_id, type: Integer, desc: 'The ID of the SSH key'
      end
      delete "keys/:key_id" do
        key = current_user.keys.find_by(id: params[:key_id])
        not_found!('Key') unless key

489
        key.destroy
490
      end
491

Robert Schilling's avatar
Robert Schilling committed
492 493 494
      desc "Get the currently authenticated user's email addresses" do
        success Entities::Email
      end
495 496 497
      params do
        use :pagination
      end
498
      get "emails" do
499
        present paginate(current_user.emails), with: Entities::Email
500 501
      end

Robert Schilling's avatar
Robert Schilling committed
502 503 504 505 506 507 508 509 510 511
      desc 'Get a single email address owned by the currently authenticated user' do
        success Entities::Email
      end
      params do
        requires :email_id, type: Integer, desc: 'The ID of the email'
      end
      get "emails/:email_id" do
        email = current_user.emails.find_by(id: params[:email_id])
        not_found!('Email') unless email

512 513 514
        present email, with: Entities::Email
      end

Robert Schilling's avatar
Robert Schilling committed
515 516 517 518 519 520
      desc 'Add new email address to the currently authenticated user' do
        success Entities::Email
      end
      params do
        requires :email, type: String, desc: 'The new email'
      end
521
      post "emails" do
Robert Schilling's avatar
Robert Schilling committed
522
        email = current_user.emails.new(declared_params)
523 524 525 526 527 528 529 530 531

        if email.save
          NotificationService.new.new_email(email)
          present email, with: Entities::Email
        else
          render_validation_error!(email)
        end
      end

Robert Schilling's avatar
Robert Schilling committed
532 533 534 535 536 537 538
      desc 'Delete an email address from the currently authenticated user'
      params do
        requires :email_id, type: Integer, desc: 'The ID of the email'
      end
      delete "emails/:email_id" do
        email = current_user.emails.find_by(id: params[:email_id])
        not_found!('Email') unless email
539

Robert Schilling's avatar
Robert Schilling committed
540 541
        email.destroy
        current_user.update_secondary_emails!
542
      end
543 544 545

      desc 'Get a list of user activities'
      params do
546
        optional :from, type: DateTime, default: 6.months.ago, desc: 'Date string in the format YEAR-MONTH-DAY'
547 548
        use :pagination
      end
549
      get "activities" do
550 551
        authenticated_as_admin!

552 553 554
        activities = User.
          where(User.arel_table[:last_activity_on].gteq(params[:from])).
          reorder(last_activity_on: :asc)
555

556
        present paginate(activities), with: Entities::UserActivity
557
      end
558 559 560
    end
  end
end