Commit ace5a69c authored by Jonas Wälter's avatar Jonas Wälter Committed by Imre Farkas

Support group applications

Currently OAuth applications can be only global or per-user.  This adds
applications on a group level.
parent 742d0e41
# frozen_string_literal: true
module Groups
module Settings
class ApplicationsController < Groups::ApplicationController
include OauthApplications
prepend_before_action :authorize_admin_group!
before_action :set_application, only: [:show, :edit, :update, :destroy]
before_action :load_scopes, only: [:index, :create, :edit, :update]
feature_category :authentication_and_authorization
def index
set_index_vars
end
def show
end
def edit
end
def create
@application = Applications::CreateService.new(current_user, application_params).execute(request)
if @application.persisted?
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
redirect_to group_settings_application_url(@group, @application)
else
set_index_vars
render :index
end
end
def update
if @application.update(application_params)
redirect_to group_settings_application_path(@group, @application), notice: _('Application was successfully updated.')
else
render :edit
end
end
def destroy
@application.destroy
redirect_to group_settings_applications_url(@group), status: :found, notice: _('Application was successfully destroyed.')
end
private
def set_index_vars
# TODO: Remove limit(100) and implement pagination
# https://gitlab.com/gitlab-org/gitlab/-/issues/324187
@applications = @group.oauth_applications.limit(100)
# Don't overwrite a value possibly set by `create`
@application ||= Doorkeeper::Application.new
end
def set_application
@application = @group.oauth_applications.find(params[:id])
end
def application_params
params
.require(:doorkeeper_application)
.permit(:name, :redirect_uri, :trusted, :scopes, :confidential)
.tap do |params|
params[:owner] = @group
end
end
end
end
end
......@@ -17,6 +17,6 @@ class ApplicationsFinder
def by_id(applications)
return applications unless params[:id]
Doorkeeper::Application.find_by(id: params[:id]) # rubocop: disable CodeReuse/ActiveRecord
applications.find_by(id: params[:id]) # rubocop: disable CodeReuse/ActiveRecord
end
end
......@@ -22,6 +22,9 @@ module GroupsHelper
ldap_group_links#index
hooks#index
pipeline_quota#index
applications#index
applications#show
applications#edit
packages_and_registries#index
]
end
......
......@@ -70,6 +70,7 @@ class Group < Namespace
has_many :group_deploy_keys, through: :group_deploy_keys_groups
has_many :group_deploy_tokens
has_many :deploy_tokens, through: :group_deploy_tokens
has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_one :dependency_proxy_setting, class_name: 'DependencyProxy::GroupSetting'
has_many :dependency_proxy_blobs, class_name: 'DependencyProxy::Blob'
......
- page_title _("Edit"), @application.name, _("Applications")
- @content_class = "limit-container-width" unless fluid_layout
%h3.page-title= _('Edit application')
= render 'form', application: @application
= render 'shared/doorkeeper/applications/form', url: doorkeeper_submit_path(@application)
- page_title _("Applications")
- @content_class = "limit-container-width" unless fluid_layout
.row.gl-mt-3
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0
= page_title
%p
- if user_oauth_applications?
= _("Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account.")
- else
= _("Manage applications that you've authorized to use your account.")
.col-lg-8
- if user_oauth_applications?
%h5.gl-mt-0
= _('Add new application')
= render 'form', application: @application
%hr
- else
.bs-callout.bs-callout-disabled
= _('Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission')
- if user_oauth_applications?
.oauth-applications
%h5
= _("Your applications (%{size})") % { size: @applications.size }
- if @applications.any?
.table-responsive
%table.table
%thead
%tr
%th= _('Name')
%th= _('Callback URL')
%th= _('Clients')
%th.last-heading
%tbody
- @applications.each do |application|
%tr{ id: "application_#{application.id}" }
%td= link_to application.name, oauth_application_path(application)
%td
- application.redirect_uri.split.each do |uri|
%div= uri
%td= application.access_tokens.count
%td.gl-display-flex
= link_to edit_oauth_application_path(application), class: "gl-button btn btn-default gl-mr-2" do
%span.sr-only
= _('Edit')
= sprite_icon('pencil')
= render 'delete_form', application: application, small: true
- else
.settings-message.text-center
= _("You don't have any applications")
.oauth-authorized-applications.prepend-top-20.gl-mb-3
- if user_oauth_applications?
%h5
= _("Authorized applications (%{size})") % { size: @authorized_apps.size + @authorized_anonymous_tokens.size }
- if @authorized_tokens.any?
.table-responsive
%table.table.table-striped
%thead
%tr
%th= _('Name')
%th= _('Authorized At')
%th= _('Scope')
%th
%tbody
- @authorized_apps.each do |app|
- token = app.authorized_tokens.order('created_at desc').first # rubocop: disable CodeReuse/ActiveRecord
%tr{ id: "application_#{app.id}" }
%td= app.name
%td= token.created_at
%td= token.scopes
%td= render 'doorkeeper/authorized_applications/delete_form', application: app
- @authorized_anonymous_tokens.each do |token|
%tr
%td
= _('Anonymous')
.form-text.text-muted
%em= _("Authorization was granted by entering your username and password in the application.")
%td= token.created_at
%td= token.scopes
%td= render 'doorkeeper/authorized_applications/delete_form', token: token
- else
.settings-message.text-center
= _("You don't have any authorized applications")
= render 'shared/doorkeeper/applications/index',
oauth_applications_enabled: user_oauth_applications?,
oauth_authorized_applications_enabled: true,
form_url: doorkeeper_submit_path(@application),
application_url: ->(application) { oauth_application_path(application) },
edit_application_url: ->(application) { edit_oauth_application_path(application) }
......@@ -6,42 +6,4 @@
%h3.page-title
= _("Application: %{name}") % { name: @application.name }
.table-holder.oauth-application-show
%table.table
%tr
%td
= _('Application ID')
%td
.clipboard-group
.input-group
%input.label.label-monospace.monospace{ id: "application_id", type: "text", autocomplete: 'off', value: @application.uid, readonly: true }
.input-group-append
= clipboard_button(target: '#application_id', title: _("Copy ID"), class: "gl-button btn btn-default")
%tr
%td
= _('Secret')
%td
.clipboard-group
.input-group
%input.label.label-monospace.monospace{ id: "secret", type: "text", autocomplete: 'off', value: @application.secret, readonly: true }
.input-group-append
= clipboard_button(target: '#secret', title: _("Copy secret"), class: "gl-button btn btn-default")
%tr
%td
= _('Callback URL')
%td
- @application.redirect_uri.split.each do |uri|
%div
%span.monospace= uri
%tr
%td
= _('Confidential')
%td
= @application.confidential? ? _('Yes') : _('No')
= render "shared/tokens/scopes_list", token: @application
.form-actions
= link_to _('Edit'), edit_oauth_application_path(@application), class: 'gl-button btn btn-confirm wide float-left'
= render 'delete_form', application: @application, submit_btn_css: 'gl-button btn btn-danger gl-ml-3'
= render 'shared/doorkeeper/applications/show', edit_path: edit_oauth_application_path(@application), delete_path: oauth_application_path(@application)
- page_title _("Edit"), @application.name, _("Group applications")
- @content_class = "limit-container-width" unless fluid_layout
%h3.page-title= _('Edit group application')
= render 'shared/doorkeeper/applications/form', url: group_settings_application_path(@group, @application)
- page_title _("Group applications")
= render 'shared/doorkeeper/applications/index',
oauth_applications_enabled: user_oauth_applications?,
oauth_authorized_applications_enabled: false,
form_url: group_settings_applications_path(@group),
application_url: ->(application) { group_settings_application_path(@group, application) },
edit_application_url: ->(application) { edit_group_settings_application_path(@group, application) }
- add_to_breadcrumbs _("Group applications"), group_settings_applications_path(@group)
- breadcrumb_title @application.name
- page_title @application.name, _("Group applications")
- @content_class = "limit-container-width" unless fluid_layout
%h3.page-title
= _("Group application: %{name}") % { name: @application.name }
= render 'shared/doorkeeper/applications/show', edit_path: edit_group_settings_application_path(@group, @application), delete_path: group_settings_application_path(@group, @application)
......@@ -145,7 +145,7 @@
%span.nav-item-name.qa-group-settings-item
= _('Settings')
%ul.sidebar-sub-level-items.qa-group-sidebar-submenu{ data: { testid: 'group-settings-menu' } }
= nav_link(path: %w[groups#projects groups#edit badges#index ci_cd#show], html_options: { class: "fly-out-top-item" } ) do
= nav_link(path: %w[groups#projects groups#edit badges#index ci_cd#show groups/applications#index], html_options: { class: "fly-out-top-item" } ) do
= link_to edit_group_path(@group) do
%strong.fly-out-top-item-name
= _('Settings')
......@@ -175,6 +175,11 @@
%span
= _('CI/CD')
= nav_link(controller: :applications) do
= link_to group_settings_applications_path(@group), title: _('Applications') do
%span
= _('Applications')
= render 'groups/sidebar/packages_settings'
= render_if_exists "groups/ee/settings_nav"
......
- submit_btn_css ||= 'gl-button btn btn-danger btn-sm'
= form_tag oauth_application_path(application) do
%input{ :name => "_method", :type => "hidden", :value => "delete" }/
= form_tag path do
%input{ :name => "_method", :type => "hidden", :value => "delete" }
- if defined? small
= button_tag type: "submit", class: "gl-button btn btn-default", data: { confirm: _("Are you sure?") } do
%span.sr-only
......
= form_for application, url: doorkeeper_submit_path(application), html: { role: 'form', class: 'doorkeeper-app-form' } do |f|
= form_errors(application)
= form_for @application, url: url, html: { role: 'form', class: 'doorkeeper-app-form' } do |f|
= form_errors(@application)
.form-group
= f.label :name, class: 'label-bold'
......@@ -20,7 +20,7 @@
.form-group
= f.label :scopes, class: 'label-bold'
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: @application, scopes: @scopes
.gl-mt-3
= f.submit _('Save application'), class: "gl-button btn btn-confirm"
- @content_class = "limit-container-width" unless fluid_layout
.row.gl-mt-3
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0
= page_title
%p
- if oauth_applications_enabled
- if oauth_authorized_applications_enabled
= _("Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account.")
- else
= _("Manage applications that can use GitLab as an OAuth provider.")
- else
= _("Manage applications that you've authorized to use your account.")
.col-lg-8
- if oauth_applications_enabled
%h5.gl-mt-0
= _('Add new application')
= render 'shared/doorkeeper/applications/form', url: form_url
%hr
- else
.bs-callout.bs-callout-disabled
= _('Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission')
- if oauth_applications_enabled
.oauth-applications
%h5
= _("Your applications (%{size})") % { size: @applications.size }
- if @applications.any?
.table-responsive
%table.table
%thead
%tr
%th= _('Name')
%th= _('Callback URL')
%th= _('Clients')
%th.last-heading
%tbody
- @applications.each do |application|
%tr{ id: "application_#{application.id}" }
%td= link_to application.name, application_url.call(application)
%td
- application.redirect_uri.split.each do |uri|
%div= uri
%td= application.access_tokens.count
%td.gl-display-flex
= link_to edit_application_url.call(application), class: "gl-button btn btn-default gl-mr-2" do
%span.sr-only
= _('Edit')
= sprite_icon('pencil')
= render 'shared/doorkeeper/applications/delete_form', path: application_url.call(application), small: true
- else
.settings-message.text-center
= _("You don't have any applications")
- if oauth_authorized_applications_enabled
.oauth-authorized-applications.prepend-top-20.gl-mb-3
- if oauth_applications_enabled
%h5
= _("Authorized applications (%{size})") % { size: @authorized_apps.size + @authorized_anonymous_tokens.size }
- if @authorized_tokens.any?
.table-responsive
%table.table.table-striped
%thead
%tr
%th= _('Name')
%th= _('Authorized At')
%th= _('Scope')
%th
%tbody
- @authorized_apps.each do |app|
- token = app.authorized_tokens.order('created_at desc').first # rubocop: disable CodeReuse/ActiveRecord
%tr{ id: "application_#{app.id}" }
%td= app.name
%td= token.created_at
%td= token.scopes
%td= render 'doorkeeper/authorized_applications/delete_form', application: app
- @authorized_anonymous_tokens.each do |token|
%tr
%td
= _('Anonymous')
.form-text.text-muted
%em= _("Authorization was granted by entering your username and password in the application.")
%td= token.created_at
%td= token.scopes
%td= render 'doorkeeper/authorized_applications/delete_form', token: token
- else
.settings-message.text-center
= _("You don't have any authorized applications")
.table-holder.oauth-application-show
%table.table
%tr
%td
= _('Application ID')
%td
.clipboard-group
.input-group
%input.label.label-monospace.monospace{ id: "application_id", type: "text", autocomplete: 'off', value: @application.uid, readonly: true }
.input-group-append
= clipboard_button(target: '#application_id', title: _("Copy ID"), class: "gl-button btn btn-default")
%tr
%td
= _('Secret')
%td
.clipboard-group
.input-group
%input.label.label-monospace.monospace{ id: "secret", type: "text", autocomplete: 'off', value: @application.secret, readonly: true }
.input-group-append
= clipboard_button(target: '#secret', title: _("Copy secret"), class: "gl-button btn btn-default")
%tr
%td
= _('Callback URL')
%td
- @application.redirect_uri.split.each do |uri|
%div
%span.monospace= uri
%tr
%td
= _('Confidential')
%td
= @application.confidential? ? _('Yes') : _('No')
= render "shared/tokens/scopes_list", token: @application
.form-actions
= link_to _('Edit'), edit_path, class: 'gl-button btn btn-confirm wide float-left'
= render 'shared/doorkeeper/applications/delete_form', path: delete_path, submit_btn_css: 'gl-button btn btn-danger gl-ml-3'
---
title: Support group applications
merge_request: 55152
author: Jonas Wälter @wwwjon, Bastian Blank
type: added
......@@ -50,6 +50,8 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
end
end
resources :applications
resources :packages_and_registries, only: [:index]
end
......
......@@ -407,7 +407,7 @@ Pages access control is disabled by default. To enable it:
```
1. [Restart GitLab](../restart_gitlab.md#installations-from-source).
1. Create a new [system OAuth application](../../integration/oauth_provider.md#add-an-application-through-the-profile).
1. Create a new [system OAuth application](../../integration/oauth_provider.md#user-owned-applications).
This should be called `GitLab Pages` and have a `Redirect URL` of
`https://projects.example.io/auth`. It does not need to be a "trusted"
application, but it does need the `api` scope.
......
......@@ -8,11 +8,13 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/8160) in GitLab 10.5.
Applications API operates on OAuth applications for:
The Applications API operates on instance-wide OAuth applications for:
- [Using GitLab as an authentication provider](../integration/oauth_provider.md).
- [Allowing access to GitLab resources on a user's behalf](oauth2.md).
The Applications API cannot be used to manage group applications or applications of individual users.
NOTE:
Only administrator users can use the Applications API.
......
......@@ -33,17 +33,18 @@ OAuth 2 can be used:
The 'GitLab Importer' feature also uses OAuth 2 to give access
to repositories without sharing user credentials to your GitLab.com account.
GitLab supports two ways of adding a new OAuth 2 application to an instance:
GitLab supports several ways of adding a new OAuth 2 application to an instance:
- As a regular user, for applications owned by an individual.
- Through the Admin Area menu for instance-wide apps.
- [User owned applications](#user-owned-applications)
- [Group owned applications](#group-owned-applications)
- [Instance-wide applications](#instance-wide-applications)
The only difference between these two methods is the [permission](../user/permissions.md)
The only difference between these methods is the [permission](../user/permissions.md)
levels. The default callback URL is `http://your-gitlab.example.com/users/auth/gitlab/callback`.
## Add an application through the profile
## User owned applications
To add a new application via your profile:
To add a new application for your user:
1. In the top-right corner, select your avatar.
1. Select **Edit profile**.
......@@ -55,7 +56,20 @@ To add a new application via your profile:
- Application ID: OAuth 2 Client ID.
- Secret: OAuth 2 Client Secret.
## OAuth applications in the Admin Area
## Group owned applications
To add a new application for a group:
1. Navigate to the desired group.
1. In the left sidebar, select **Settings > Applications**.
1. Enter a **Name**, **Redirect URI** and OAuth 2 scopes as defined in [Authorized Applications](#authorized-applications).
The **Redirect URI** is the URL where users are sent after they authorize with GitLab.
1. Select **Save application**. GitLab displays:
- Application ID: OAuth 2 Client ID.
- Secret: OAuth 2 Client Secret.
## Instance-wide applications
To create an application for your GitLab instance, select
**Admin Area > Applications > New application**.
......
......@@ -8,15 +8,16 @@ module EE
override :execute
def execute(request)
super.tap do |application|
audit_event_service(request.remote_ip).for_user(full_path: application.name, entity_id: application.id).security_event
entity = application.owner || current_user
audit_event_service(entity, request.remote_ip).for_user(full_path: application.name, entity_id: application.id).security_event
end
end
def audit_event_service(ip_address)
def audit_event_service(entity, ip_address)
::AuditEventService.new(current_user,
current_user,
entity,
action: :custom,
custom_message: 'OAuth access granted',
custom_message: 'OAuth application added',
ip_address: ip_address)
end
end
......
......@@ -3,15 +3,33 @@
require 'spec_helper'
RSpec.describe ::Applications::CreateService do
include TestRequestHelpers
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:params) { attributes_for(:application) }
let(:request) { ActionController::TestRequest.new({ remote_ip: "127.0.0.1" }, ActionController::TestSession.new, nil) }
subject { described_class.new(user, params) }
it 'creates an AuditEvent' do
stub_licensed_features(extended_audit_events: true)
describe '#audit_event_service' do
using RSpec::Parameterized::TableSyntax
where(:case_name, :owner, :entity_type) do
'instance application' | nil | 'User'
'group application' | group | 'Group'
'user application' | user | 'User'
end
with_them do
before do
stub_licensed_features(extended_audit_events: true)
params[:owner] = owner
end
expect { subject.execute(request) }.to change { AuditEvent.count }.by(1)
it 'creates AuditEvent with correct entity type' do
expect { subject.execute(test_request) }.to change(AuditEvent, :count).by(1)
expect(AuditEvent.last.entity_type).to eq(entity_type)
end
end
end
end
......@@ -41,6 +41,8 @@ module API
desc 'Delete an application'
delete ':id' do
application = ApplicationsFinder.new(params).execute
break not_found!('Application') unless application
application.destroy
no_content!
......
......@@ -11203,6 +11203,9 @@ msgstr ""
msgid "Edit fork in Web IDE"
msgstr ""
msgid "Edit group application"
msgstr ""
msgid "Edit group: %{group_name}"
msgstr ""
......@@ -14553,6 +14556,12 @@ msgstr ""
msgid "Group Wikis"
msgstr ""
msgid "Group application: %{name}"
msgstr ""
msgid "Group applications"
msgstr ""
msgid "Group avatar"
msgstr ""
......@@ -18547,6 +18556,9 @@ msgstr ""
msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
msgstr ""
msgid "Manage applications that can use GitLab as an OAuth provider."
msgstr ""
msgid "Manage applications that you've authorized to use your account."
msgstr ""
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Groups::Settings::ApplicationsController do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:application) { create(:oauth_application, owner_id: group.id, owner_type: 'Namespace') }
before do
sign_in(user)
end
describe 'GET #index' do
context 'when user is owner' do
before do
group.add_owner(user)
end
it 'renders the application form' do
get :index, params: { group_id: group }
expect(response).to render_template :index
expect(assigns[:scopes]).to be_kind_of(Doorkeeper::OAuth::Scopes)
end
end
context 'when user is not owner' do
before do
group.add_maintainer(user)
end
it 'renders a 404' do
get :index, params: { group_id: group }
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'GET #edit' do
context 'when user is owner' do
before do
group.add_owner(user)
end
it 'renders the application form' do
get :edit, params: { group_id: group, id: application.id }
expect(response).to render_template :edit
expect(assigns[:scopes]).to be_kind_of(Doorkeeper::OAuth::Scopes)
end
end
context 'when user is not owner' do
before do
group.add_maintainer(user)
end
it 'renders a 404' do
get :edit, params: { group_id: group, id: application.id }
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'POST #create' do
context 'when user is owner' do
before do
group.add_owner(user)
end
it 'creates the application' do
create_params = attributes_for(:application, trusted: true, confidential: false, scopes: ['api'])
expect do
post :create, params: { group_id: group, doorkeeper_application: create_params }
end.to change { Doorkeeper::Application.count }.by(1)
application = Doorkeeper::Application.last
expect(response).to redirect_to(group_settings_application_path(group, application))
expect(application).to have_attributes(create_params.except(:uid, :owner_type))
end
it 'renders the application form on errors' do
expect do
post :create, params: { group_id: group, doorkeeper_application: attributes_for(:application).merge(redirect_uri: nil) }
end.not_to change { Doorkeeper::Application.count }
expect(response).to render_template :index
expect(assigns[:scopes]).to be_kind_of(Doorkeeper::OAuth::Scopes)
end
context 'when the params are for a confidential application' do
it 'creates a confidential application' do
create_params = attributes_for(:application, confidential: true, scopes: ['read_user'])
expect do
post :create, params: { group_id: group, doorkeeper_application: create_params }
end.to change { Doorkeeper::Application.count }.by(1)
application = Doorkeeper::Application.last
expect(response).to redirect_to(group_settings_application_path(group, application))
expect(application).to have_attributes(create_params.except(:uid, :owner_type))
end
end
context 'when scopes are not present' do
it 'renders the application form on errors' do
create_params = attributes_for(:application, trusted: true, confidential: false)
expect do
post :create, params: { group_id: group, doorkeeper_application: create_params }
end.not_to change { Doorkeeper::Application.count }
expect(response).to render_template :index
end
end
end
context 'when user is not owner' do
before do
group.add_maintainer(user)
end
it 'renders a 404' do
create_params = attributes_for(:application, trusted: true, confidential: false, scopes: ['api'])
post :create, params: { group_id: group, doorkeeper_application: create_params }
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'PATCH #update' do
context 'when user is owner' do
before do
group.add_owner(user)
end
it 'updates the application' do
doorkeeper_params = { redirect_uri: 'http://example.com/', trusted: true, confidential: false }
patch :update, params: { group_id: group, id: application.id, doorkeeper_application: doorkeeper_params }
application.reload
expect(response).to redirect_to(group_settings_application_path(group, application))
expect(application)
.to have_attributes(redirect_uri: 'http://example.com/', trusted: true, confidential: false)
end
it 'renders the application form on errors' do
patch :update, params: { group_id: group, id: application.id, doorkeeper_application: { redirect_uri: nil } }
expect(response).to render_template :edit
expect(assigns[:scopes]).to be_kind_of(Doorkeeper::OAuth::Scopes)
end
context 'when updating the application to be confidential' do
it 'successfully sets the application to confidential' do
doorkeeper_params = { confidential: true }
patch :update, params: { group_id: group, id: application.id, doorkeeper_application: doorkeeper_params }
application.reload
expect(response).to redirect_to(group_settings_application_path(group, application))
expect(application).to be_confidential
end
end
end
context 'when user is not owner' do
before do
group.add_maintainer(user)
end
it 'renders a 404' do
doorkeeper_params = { redirect_uri: 'http://example.com/', trusted: true, confidential: false }
patch :update, params: { group_id: group, id: application.id, doorkeeper_application: doorkeeper_params }
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'DELETE #destroy' do
context 'when user is owner' do
before do
group.add_owner(user)
end
it 'deletes the application' do
delete :destroy, params: { group_id: group, id: application.id }
expect(Doorkeeper::Application.exists?(application.id)).to be_falsy
expect(response).to redirect_to(group_settings_applications_url(group))
end
end
context 'when user is not owner' do
before do
group.add_maintainer(user)
end
it 'renders a 404' do
delete :destroy, params: { group_id: group, id: application.id }
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
......@@ -5,18 +5,48 @@ require 'spec_helper'
RSpec.describe ApplicationsFinder do
let(:application1) { create(:application, name: 'some_application', owner: nil, redirect_uri: 'http://some_application.url', scopes: '') }
let(:application2) { create(:application, name: 'another_application', owner: nil, redirect_uri: 'http://other_application.url', scopes: '') }
let(:user_application) { create(:application, name: 'user_application', owner: create(:user), redirect_uri: 'http://user_application.url', scopes: '') }
let(:group_application) { create(:application, name: 'group_application', owner: create(:group), redirect_uri: 'http://group_application.url', scopes: '') }
describe '#execute' do
it 'returns an array of applications' do
it 'returns an array of instance applications' do
found = described_class.new.execute
expect(found).to match_array([application1, application2])
end
it 'returns the application by id' do
params = { id: application1.id }
found = described_class.new(params).execute
expect(found).to match(application1)
context 'by_id' do
context 'with existing id' do
it 'returns the application' do
params = { id: application1.id }
found = described_class.new(params).execute
expect(found).to match(application1)
end
end
context 'with invalid id' do
it 'returns nil for user application' do
params = { id: user_application.id }
found = described_class.new(params).execute
expect(found).to be_nil
end
it 'returns nil for group application' do
params = { id: group_application.id }
found = described_class.new(params).execute
expect(found).to be_nil
end
it 'returns nil for non-existing application' do
params = { id: non_existing_record_id }
found = described_class.new(params).execute
expect(found).to be_nil
end
end
end
end
end
......@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe API::Applications, :api do
let(:admin_user) { create(:user, admin: true) }
let(:user) { create(:user, admin: false) }
let!(:application) { create(:application, name: 'another_application', redirect_uri: 'http://other_application.url', scopes: '') }
let!(:application) { create(:application, name: 'another_application', owner: nil, redirect_uri: 'http://other_application.url', scopes: '') }
describe 'POST /applications' do
context 'authenticated and authorized user' do
......@@ -143,6 +143,12 @@ RSpec.describe API::Applications, :api do
expect(response).to have_gitlab_http_status(:no_content)
end
it 'cannot delete non-existing application' do
delete api("/applications/#{non_existing_record_id}", admin_user)
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'authorized user without authorization' do
......
......@@ -139,6 +139,7 @@ RSpec.shared_context 'group navbar structure' do
_('Projects'),
_('Repository'),
_('CI/CD'),
_('Applications'),
_('Packages & Registries'),
_('Webhooks')
]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment