users.rb 18.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 40 41 42 43 44
          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
        optional :username, type: String, desc: 'Get a single user with a specific username'
        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'
45
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
46
      end
47
      get do
48
        unless can?(current_user, :read_users_list)
Felipe Artur's avatar
Felipe Artur committed
49 50 51
          render_api_error!("Not authorized.", 403)
        end

52
        if params[:username].present?
Robert Schilling's avatar
Robert Schilling committed
53
          users = User.where(username: params[:username])
54
        else
Robert Schilling's avatar
Robert Schilling committed
55 56 57 58 59
          users = User.all
          users = users.active if params[:active]
          users = users.search(params[:search]) if params[:search].present?
          users = users.blocked if params[:blocked]
          users = users.external if params[:external] && current_user.is_admin?
60
        end
61

62
        entity = current_user.is_admin? ? Entities::UserPublic : Entities::UserBasic
Robert Schilling's avatar
Robert Schilling committed
63
        present paginate(users), with: entity
64 65
      end

Robert Schilling's avatar
Robert Schilling committed
66 67 68 69 70 71
      desc 'Get a single user' do
        success Entities::UserBasic
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
72
      get ":id" do
Robert Schilling's avatar
Robert Schilling committed
73 74
        user = User.find_by(id: params[:id])
        not_found!('User') unless user
75

Felipe Artur's avatar
Felipe Artur committed
76
        if current_user && current_user.is_admin?
77
          present user, with: Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
78 79
        elsif can?(current_user, :read_user, user)
          present user, with: Entities::User
Felipe Artur's avatar
Felipe Artur committed
80 81
        else
          render_api_error!("User not found.", 404)
82
        end
83
      end
84

Robert Schilling's avatar
Robert Schilling committed
85
      desc 'Create a user. Available only for admins.' do
86
        success Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
87 88 89
      end
      params do
        requires :email, type: String, desc: 'The email of the user'
90 91 92
        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
93 94 95 96
        requires :name, type: String, desc: 'The name of the user'
        requires :username, type: String, desc: 'The username of the user'
        use :optional_attributes
      end
97 98
      post do
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
99

100 101
        params = declared_params(include_missing: false)
        user = ::Users::CreateService.new(current_user, params).execute
102

103
        if user.persisted?
104
          present user, with: Entities::UserPublic
105
        else
106 107 108
          conflict!('Email has already been taken') if User.
              where(email: user.email).
              count > 0
109

110 111 112
          conflict!('Username has already been taken') if User.
              where(username: user.username).
              count > 0
113 114

          render_validation_error!(user)
115 116
        end
      end
117

Robert Schilling's avatar
Robert Schilling committed
118
      desc 'Update a user. Available only for admins.' do
119
        success Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
120 121 122 123 124 125 126 127 128 129 130 131 132
      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
133 134
      put ":id" do
        authenticated_as_admin!
135

Robert Schilling's avatar
Robert Schilling committed
136
        user = User.find_by(id: params.delete(:id))
137
        not_found!('User') unless user
138

Robert Schilling's avatar
Robert Schilling committed
139
        conflict!('Email has already been taken') if params[:email] &&
140 141
            User.where(email: params[:email]).
                where.not(id: user.id).count > 0
142

Robert Schilling's avatar
Robert Schilling committed
143
        conflict!('Username has already been taken') if params[:username] &&
144 145
            User.where(username: params[:username]).
                where.not(id: user.id).count > 0
146

147 148
        user_params = declared_params(include_missing: false)
        identity_attrs = user_params.slice(:provider, :extern_uid)
Robert Schilling's avatar
Robert Schilling committed
149

150 151
        if identity_attrs.any?
          identity = user.identities.find_by(provider: identity_attrs[:provider])
Robert Schilling's avatar
Robert Schilling committed
152

153 154 155 156 157 158 159 160
          if identity
            identity.update_attributes(identity_attrs)
          else
            identity = user.identities.build(identity_attrs)
            identity.save
          end
        end

161
        user_params[:password_expires_at] = Time.now if user_params[:password].present?
162

163
        if user.update_attributes(user_params.except(:extern_uid, :provider))
164
          present user, with: Entities::UserPublic
165
        else
166
          render_validation_error!(user)
167 168 169
        end
      end

Robert Schilling's avatar
Robert Schilling committed
170 171 172 173 174 175 176 177
      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
178 179
      post ":id/keys" do
        authenticated_as_admin!
180

Robert Schilling's avatar
Robert Schilling committed
181 182 183 184 185
        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
186 187 188
        if key.save
          present key, with: Entities::SSHKey
        else
189
          render_validation_error!(key)
Angus MacArthur's avatar
Angus MacArthur committed
190 191 192
        end
      end

Robert Schilling's avatar
Robert Schilling committed
193 194 195 196 197
      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'
198
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
199 200
      end
      get ':id/keys' do
201
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
202 203

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

206
        present paginate(user.keys), with: Entities::SSHKey
207 208
      end

Robert Schilling's avatar
Robert Schilling committed
209 210 211 212 213 214 215 216
      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
217
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
218 219

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

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

225
        key.destroy
226 227
      end

Robert Schilling's avatar
Robert Schilling committed
228 229 230 231 232 233 234
      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
235 236 237
      post ":id/emails" do
        authenticated_as_admin!

Robert Schilling's avatar
Robert Schilling committed
238 239 240 241 242
        user = User.find_by(id: params.delete(:id))
        not_found!('User') unless user

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

243 244 245 246 247 248 249 250
        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
251 252 253 254 255
      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'
256
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
257 258
      end
      get ':id/emails' do
259
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
260
        user = User.find_by(id: params[:id])
261 262
        not_found!('User') unless user

263
        present paginate(user.emails), with: Entities::Email
264 265
      end

Robert Schilling's avatar
Robert Schilling committed
266 267 268 269 270 271 272 273
      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
274
        authenticated_as_admin!
Robert Schilling's avatar
Robert Schilling committed
275
        user = User.find_by(id: params[:id])
276 277
        not_found!('User') unless user

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

Robert Schilling's avatar
Robert Schilling committed
281 282
        email.destroy
        user.update_secondary_emails!
283 284
      end

Robert Schilling's avatar
Robert Schilling committed
285 286 287 288 289 290
      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
291 292
      delete ":id" do
        authenticated_as_admin!
skv's avatar
skv committed
293
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
294
        not_found!('User') unless user
295

296
        ::Users::DestroyService.new(current_user).execute(user)
297
      end
298

Robert Schilling's avatar
Robert Schilling committed
299 300 301 302
      desc 'Block a user. Available only for admins.'
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
303
      post ':id/block' do
304 305
        authenticated_as_admin!
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
306
        not_found!('User') unless user
307

Robert Schilling's avatar
Robert Schilling committed
308
        if !user.ldap_blocked?
309 310
          user.block
        else
311
          forbidden!('LDAP blocked users cannot be modified by the API')
312 313 314
        end
      end

Robert Schilling's avatar
Robert Schilling committed
315 316 317 318
      desc 'Unblock a user. Available only for admins.'
      params do
        requires :id, type: Integer, desc: 'The ID of the user'
      end
319
      post ':id/unblock' do
320 321
        authenticated_as_admin!
        user = User.find_by(id: params[:id])
Robert Schilling's avatar
Robert Schilling committed
322
        not_found!('User') unless user
323

Robert Schilling's avatar
Robert Schilling committed
324
        if user.ldap_blocked?
325
          forbidden!('LDAP blocked users cannot be unblocked by the API')
Gabriel Mazetto's avatar
Gabriel Mazetto committed
326 327
        else
          user.activate
328 329
        end
      end
330

Robert Schilling's avatar
Robert Schilling committed
331
      desc 'Get the contribution events of a specified user' do
332 333 334 335
        detail 'This feature was introduced in GitLab 8.13.'
        success Entities::Event
      end
      params do
Robert Schilling's avatar
Robert Schilling committed
336
        requires :id, type: Integer, desc: 'The ID of the user'
337
        use :pagination
338 339
      end
      get ':id/events' do
Robert Schilling's avatar
Robert Schilling committed
340
        user = User.find_by(id: params[:id])
341 342
        not_found!('User') unless user

343 344 345 346 347
        events = user.events.
          merge(ProjectsFinder.new.execute(current_user)).
          references(:project).
          with_associations.
          recent
348 349 350

        present paginate(events), with: Entities::Event
      end
351 352

      params do
353 354 355 356 357 358 359 360 361 362 363 364 365
        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
366
          end
367

368 369 370
          before { authenticated_as_admin! }

          desc 'Retrieve impersonation tokens. Available only for admins.' do
371
            detail 'This feature was introduced in GitLab 9.0'
372
            success Entities::ImpersonationToken
373 374
          end
          params do
375
            use :pagination
376
            optional :state, type: String, default: 'all', values: %w[all active inactive], desc: 'Filters (all|active|inactive) impersonation_tokens'
377
          end
378
          get { present paginate(finder(declared_params(include_missing: false)).execute), with: Entities::ImpersonationToken }
379

380
          desc 'Create a impersonation token. Available only for admins.' do
381
            detail 'This feature was introduced in GitLab 9.0'
382
            success Entities::ImpersonationToken
383 384
          end
          params do
385 386 387
            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'
388 389
          end
          post do
390
            impersonation_token = finder.build(declared_params(include_missing: false))
391

392 393
            if impersonation_token.save
              present impersonation_token, with: Entities::ImpersonationToken
394
            else
395
              render_validation_error!(impersonation_token)
396 397
            end
          end
398

399
          desc 'Retrieve impersonation token. Available only for admins.' do
400
            detail 'This feature was introduced in GitLab 9.0'
401
            success Entities::ImpersonationToken
402 403
          end
          params do
404
            requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token'
405
          end
406 407
          get ':impersonation_token_id' do
            present find_impersonation_token, with: Entities::ImpersonationToken
408
          end
409

410
          desc 'Revoke a impersonation token. Available only for admins.' do
411 412 413
            detail 'This feature was introduced in GitLab 9.0'
          end
          params do
414
            requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token'
415
          end
416 417
          delete ':impersonation_token_id' do
            find_impersonation_token.revoke!
418 419
          end
        end
420
      end
421 422
    end

423
    resource :user do
Robert Schilling's avatar
Robert Schilling committed
424
      desc 'Get the currently authenticated user' do
425
        success Entities::UserPublic
Robert Schilling's avatar
Robert Schilling committed
426
      end
427
      get do
428
        present current_user, with: sudo? ? Entities::UserWithPrivateToken : Entities::UserPublic
429 430
      end

Robert Schilling's avatar
Robert Schilling committed
431 432 433
      desc "Get the currently authenticated user's SSH keys" do
        success Entities::SSHKey
      end
434 435 436
      params do
        use :pagination
      end
437
      get "keys" do
438
        present paginate(current_user.keys), with: Entities::SSHKey
439 440
      end

Robert Schilling's avatar
Robert Schilling committed
441 442 443 444 445 446 447 448 449 450
      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

451 452 453
        present key, with: Entities::SSHKey
      end

Robert Schilling's avatar
Robert Schilling committed
454 455 456 457 458 459 460
      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
461
      post "keys" do
Robert Schilling's avatar
Robert Schilling committed
462
        key = current_user.keys.new(declared_params)
463

464 465 466
        if key.save
          present key, with: Entities::SSHKey
        else
467
          render_validation_error!(key)
468 469 470
        end
      end

Robert Schilling's avatar
Robert Schilling committed
471 472 473 474 475 476 477 478 479 480
      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

481
        key.destroy
482
      end
483

Robert Schilling's avatar
Robert Schilling committed
484 485 486
      desc "Get the currently authenticated user's email addresses" do
        success Entities::Email
      end
487 488 489
      params do
        use :pagination
      end
490
      get "emails" do
491
        present paginate(current_user.emails), with: Entities::Email
492 493
      end

Robert Schilling's avatar
Robert Schilling committed
494 495 496 497 498 499 500 501 502 503
      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

504 505 506
        present email, with: Entities::Email
      end

Robert Schilling's avatar
Robert Schilling committed
507 508 509 510 511 512
      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
513
      post "emails" do
Robert Schilling's avatar
Robert Schilling committed
514
        email = current_user.emails.new(declared_params)
515 516 517 518 519 520 521 522 523

        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
524 525 526 527 528 529 530
      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
531

Robert Schilling's avatar
Robert Schilling committed
532 533
        email.destroy
        current_user.update_secondary_emails!
534
      end
535 536 537
    end
  end
end