From 4cb3c0b404f256be8bc80430eaf25b468e30ee44 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 27 Oct 2016 10:20:06 +0200 Subject: Grapify the users API --- lib/api/users.rb | 508 +++++++++++++++++++++++++++---------------------------- 1 file changed, 250 insertions(+), 258 deletions(-) (limited to 'lib/api') diff --git a/lib/api/users.rb b/lib/api/users.rb index 298c401a816..c07539194ed 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -4,89 +4,93 @@ module API before { authenticate! } resource :users, requirements: { uid: /[0-9]*/, id: /[0-9]*/ } do - # Get a users list - # - # Example Request: - # GET /users - # GET /users?search=Admin - # GET /users?username=root - # GET /users?active=true - # GET /users?external=true - # GET /users?blocked=true + helpers do + 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' + optional :extern_uid, type: Integer, desc: 'The external authentication provider UID' + 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' + optional :confirm, type: Boolean, desc: 'Flag indicating the account needs to be confirmed' + 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' + end get do unless can?(current_user, :read_users_list, nil) render_api_error!("Not authorized.", 403) end if params[:username].present? - @users = User.where(username: params[:username]) + users = User.where(username: params[:username]) else - @users = User.all - @users = @users.active if to_boolean(params[:active]) - @users = @users.search(params[:search]) if params[:search].present? - @users = @users.blocked if to_boolean(params[:blocked]) - @users = @users.external if to_boolean(params[:external]) && current_user.is_admin? - @users = paginate @users + 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? end - if current_user.is_admin? - present @users, with: Entities::UserFull - else - present @users, with: Entities::UserBasic - end + entity = current_user.is_admin? ? Entities::UserFull : Entities::UserBasic + present paginate(users), with: entity end - # Get a single user - # - # Parameters: - # id (required) - The ID of a user - # Example Request: - # GET /users/:id + desc 'Get a single user' do + success Entities::UserBasic + end + params do + requires :id, type: Integer, desc: 'The ID of the user' + end get ":id" do - @user = User.find(params[:id]) + user = User.find_by(id: params[:id]) + not_found!('User') unless user if current_user && current_user.is_admin? - present @user, with: Entities::UserFull - elsif can?(current_user, :read_user, @user) - present @user, with: Entities::User + present user, with: Entities::UserFull + elsif can?(current_user, :read_user, user) + present user, with: Entities::User else render_api_error!("User not found.", 404) end end - # Create user. Available only for admin - # - # Parameters: - # email (required) - Email - # password (required) - Password - # name (required) - Name - # username (required) - Name - # skype - Skype ID - # linkedin - Linkedin - # twitter - Twitter account - # website_url - Website url - # organization - Organization - # projects_limit - Number of projects user can create - # extern_uid - External authentication provider UID - # provider - External provider - # bio - Bio - # location - Location of the user - # admin - User is admin - true or false (default) - # can_create_group - User can create groups - true or false - # confirm - Require user confirmation - true (default) or false - # external - Flags the user as external - true or false(default) - # Example Request: - # POST /users + desc 'Create a user. Available only for admins.' do + success Entities::UserFull + end + params do + requires :email, type: String, desc: 'The email of the user' + requires :password, type: String, desc: 'The password of the new user' + requires :name, type: String, desc: 'The name of the user' + requires :username, type: String, desc: 'The username of the user' + use :optional_attributes + end post do authenticated_as_admin! - required_attributes! [:email, :password, :name, :username] - attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :bio, :location, :can_create_group, :admin, :confirm, :external, :organization] - admin = attrs.delete(:admin) - confirm = !(attrs.delete(:confirm) =~ /(false|f|no|0)$/i) - user = User.build_user(attrs) - user.admin = admin unless admin.nil? + + # Filter out params which are used later + identity_attrs = params.slice(:provider, :extern_uid) + confirm = params.delete(:confirm) + + user = User.build_user(declared_params(include_missing: false)) user.skip_confirmation! unless confirm - identity_attrs = attributes_for_keys [:provider, :extern_uid] if identity_attrs.any? user.identities.build(identity_attrs) @@ -107,46 +111,40 @@ module API end end - # Update user. Available only for admin - # - # Parameters: - # email - Email - # name - Name - # password - Password - # skype - Skype ID - # linkedin - Linkedin - # twitter - Twitter account - # website_url - Website url - # organization - Organization - # projects_limit - Limit projects each user can create - # bio - Bio - # location - Location of the user - # admin - User is admin - true or false (default) - # can_create_group - User can create groups - true or false - # external - Flags the user as external - true or false(default) - # Example Request: - # PUT /users/:id + desc 'Update a user. Available only for admins.' do + success Entities::UserFull + 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 put ":id" do authenticated_as_admin! - attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :website_url, :projects_limit, :username, :bio, :location, :can_create_group, :admin, :external, :organization] - user = User.find(params[:id]) + user = User.find_by(id: params.delete(:id)) not_found!('User') unless user - admin = attrs.delete(:admin) - user.admin = admin unless admin.nil? - - conflict!('Email has already been taken') if attrs[:email] && - User.where(email: attrs[:email]). + conflict!('Email has already been taken') if params[:email] && + User.where(email: params[:email]). where.not(id: user.id).count > 0 - conflict!('Username has already been taken') if attrs[:username] && - User.where(username: attrs[:username]). + conflict!('Username has already been taken') if params[:username] && + User.where(username: params[:username]). where.not(id: user.id).count > 0 - identity_attrs = attributes_for_keys [:provider, :extern_uid] + identity_attrs = params.slice(:provider, :extern_uid) + if identity_attrs.any? identity = user.identities.find_by(provider: identity_attrs[:provider]) + if identity identity.update_attributes(identity_attrs) else @@ -155,28 +153,33 @@ module API end end - if user.update_attributes(attrs) + # Delete already handled parameters + params.delete(:extern_uid) + params.delete(:provider) + + if user.update_attributes(declared_params(include_missing: false)) present user, with: Entities::UserFull else render_validation_error!(user) end end - # Add ssh key to a specified user. Only available to admin users. - # - # Parameters: - # id (required) - The ID of a user - # key (required) - New SSH Key - # title (required) - New SSH Key's title - # Example Request: - # POST /users/:id/keys + 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 post ":id/keys" do authenticated_as_admin! - required_attributes! [:title, :key] - user = User.find(params[:id]) - attrs = attributes_for_keys [:title, :key] - key = user.keys.new attrs + user = User.find_by(id: params.delete(:id)) + not_found!('User') unless user + + key = user.keys.new(declared_params(include_missing: false)) + if key.save present key, with: Entities::SSHKey else @@ -184,55 +187,55 @@ module API end end - # Get ssh keys of a specified user. Only available to admin users. - # - # Parameters: - # uid (required) - The ID of a user - # Example Request: - # GET /users/:uid/keys - get ':uid/keys' do + 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' + end + get ':id/keys' do authenticated_as_admin! - user = User.find_by(id: params[:uid]) + + user = User.find_by(id: params[:id]) not_found!('User') unless user present user.keys, with: Entities::SSHKey end - # Delete existing ssh key of a specified user. Only available to admin - # users. - # - # Parameters: - # uid (required) - The ID of a user - # id (required) - SSH Key ID - # Example Request: - # DELETE /users/:uid/keys/:id - delete ':uid/keys/:id' do + 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 authenticated_as_admin! - user = User.find_by(id: params[:uid]) + + user = User.find_by(id: params[:id]) not_found!('User') unless user - begin - key = user.keys.find params[:id] - key.destroy - rescue ActiveRecord::RecordNotFound - not_found!('Key') - end + key = user.keys.find_by(id: params[:key_id]) + not_found!('Key') unless key + + present key.destroy, with: Entities::SSHKey end - # Add email to a specified user. Only available to admin users. - # - # Parameters: - # id (required) - The ID of a user - # email (required) - Email address - # Example Request: - # POST /users/:id/emails + 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 post ":id/emails" do authenticated_as_admin! - required_attributes! [:email] - user = User.find(params[:id]) - attrs = attributes_for_keys [:email] - email = user.emails.new attrs + user = User.find_by(id: params.delete(:id)) + not_found!('User') unless user + + email = user.emails.new(declared_params(include_missing: false)) + if email.save NotificationService.new.new_email(email) present email, with: Entities::Email @@ -241,101 +244,94 @@ module API end end - # Get emails of a specified user. Only available to admin users. - # - # Parameters: - # uid (required) - The ID of a user - # Example Request: - # GET /users/:uid/emails - get ':uid/emails' do + 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' + end + get ':id/emails' do authenticated_as_admin! - user = User.find_by(id: params[:uid]) + user = User.find_by(id: params[:id]) not_found!('User') unless user present user.emails, with: Entities::Email end - # Delete existing email of a specified user. Only available to admin - # users. - # - # Parameters: - # uid (required) - The ID of a user - # id (required) - Email ID - # Example Request: - # DELETE /users/:uid/emails/:id - delete ':uid/emails/:id' do + 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 authenticated_as_admin! - user = User.find_by(id: params[:uid]) + user = User.find_by(id: params[:id]) not_found!('User') unless user - begin - email = user.emails.find params[:id] - email.destroy + email = user.emails.find_by(id: params[:email_id]) + not_found!('Email') unless email - user.update_secondary_emails! - rescue ActiveRecord::RecordNotFound - not_found!('Email') - end + email.destroy + user.update_secondary_emails! end - # Delete user. Available only for admin - # - # Example Request: - # DELETE /users/:id + 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 delete ":id" do authenticated_as_admin! user = User.find_by(id: params[:id]) + not_found!('User') unless user - if user - DeleteUserService.new(current_user).execute(user) - else - not_found!('User') - end + DeleteUserService.new(current_user).execute(user) end - # Block user. Available only for admin - # - # Example Request: - # PUT /users/:id/block + desc 'Block a user. Available only for admins.' + params do + requires :id, type: Integer, desc: 'The ID of the user' + end put ':id/block' do authenticated_as_admin! user = User.find_by(id: params[:id]) + not_found!('User') unless user - if !user - not_found!('User') - elsif !user.ldap_blocked? + if !user.ldap_blocked? user.block else forbidden!('LDAP blocked users cannot be modified by the API') end end - # Unblock user. Available only for admin - # - # Example Request: - # PUT /users/:id/unblock + desc 'Unblock a user. Available only for admins.' + params do + requires :id, type: Integer, desc: 'The ID of the user' + end put ':id/unblock' do authenticated_as_admin! user = User.find_by(id: params[:id]) + not_found!('User') unless user - if !user - not_found!('User') - elsif user.ldap_blocked? + if user.ldap_blocked? forbidden!('LDAP blocked users cannot be unblocked by the API') else user.activate end end - desc 'Get contribution events of a specified user' do + desc 'Get the contribution events of a specified user' do detail 'This feature was introduced in GitLab 8.13.' success Entities::Event end params do - requires :id, type: String, desc: 'The user ID' + requires :id, type: Integer, desc: 'The ID of the user' end get ':id/events' do - user = User.find_by(id: declared(params).id) + user = User.find_by(id: params[:id]) not_found!('User') unless user events = user.events. @@ -349,43 +345,43 @@ module API end resource :user do - # Get currently authenticated user - # - # Example Request: - # GET /user + desc 'Get the currently authenticated user' do + success Entities::UserFull + end get do - present @current_user, with: Entities::UserFull + present current_user, with: Entities::UserFull end - # Get currently authenticated user's keys - # - # Example Request: - # GET /user/keys + desc "Get the currently authenticated user's SSH keys" do + success Entities::SSHKey + end get "keys" do present current_user.keys, with: Entities::SSHKey end - # Get single key owned by currently authenticated user - # - # Example Request: - # GET /user/keys/:id - get "keys/:id" do - key = current_user.keys.find params[:id] + 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 + present key, with: Entities::SSHKey end - # Add new ssh key to currently authenticated user - # - # Parameters: - # key (required) - New SSH Key - # title (required) - New SSH Key's title - # Example Request: - # POST /user/keys + 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 post "keys" do - required_attributes! [:title, :key] + key = current_user.keys.new(declared_params) - attrs = attributes_for_keys [:title, :key] - key = current_user.keys.new attrs if key.save present key, with: Entities::SSHKey else @@ -393,48 +389,48 @@ module API end end - # Delete existing ssh key of currently authenticated user - # - # Parameters: - # id (required) - SSH Key ID - # Example Request: - # DELETE /user/keys/:id - delete "keys/:id" do - begin - key = current_user.keys.find params[:id] - key.destroy - rescue - end + 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 + + present key.destroy, with: Entities::SSHKey end - # Get currently authenticated user's emails - # - # Example Request: - # GET /user/emails + desc "Get the currently authenticated user's email addresses" do + success Entities::Email + end get "emails" do present current_user.emails, with: Entities::Email end - # Get single email owned by currently authenticated user - # - # Example Request: - # GET /user/emails/:id - get "emails/:id" do - email = current_user.emails.find params[:id] + 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 + present email, with: Entities::Email end - # Add new email to currently authenticated user - # - # Parameters: - # email (required) - Email address - # Example Request: - # POST /user/emails + 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 post "emails" do - required_attributes! [:email] + email = current_user.emails.new(declared_params) - attrs = attributes_for_keys [:email] - email = current_user.emails.new attrs if email.save NotificationService.new.new_email(email) present email, with: Entities::Email @@ -443,20 +439,16 @@ module API end end - # Delete existing email of currently authenticated user - # - # Parameters: - # id (required) - EMail ID - # Example Request: - # DELETE /user/emails/:id - delete "emails/:id" do - begin - email = current_user.emails.find params[:id] - email.destroy + 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 - current_user.update_secondary_emails! - rescue - end + email.destroy + current_user.update_secondary_emails! end end end -- cgit v1.2.3