Allow creating personal access tokens / OAuth applications with scopes.
This commit is contained in:
parent
1d0ccec6dd
commit
6c809dfae8
|
@ -262,3 +262,13 @@ table.u2f-registrations {
|
||||||
border-right: solid 1px transparent;
|
border-right: solid 1px transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.oauth-application-show {
|
||||||
|
.scope-name {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scopes-list {
|
||||||
|
padding-left: 18px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
class Admin::ApplicationsController < Admin::ApplicationController
|
class Admin::ApplicationsController < Admin::ApplicationController
|
||||||
|
include OauthApplications
|
||||||
|
|
||||||
before_action :set_application, only: [:show, :edit, :update, :destroy]
|
before_action :set_application, only: [:show, :edit, :update, :destroy]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@ -10,9 +12,11 @@ class Admin::ApplicationsController < Admin::ApplicationController
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@application = Doorkeeper::Application.new
|
@application = Doorkeeper::Application.new
|
||||||
|
@scopes = Doorkeeper.configuration.scopes
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
|
@scopes = Doorkeeper.configuration.scopes
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -47,6 +51,6 @@ class Admin::ApplicationsController < Admin::ApplicationController
|
||||||
|
|
||||||
# Only allow a trusted parameter "white list" through.
|
# Only allow a trusted parameter "white list" through.
|
||||||
def application_params
|
def application_params
|
||||||
params[:doorkeeper_application].permit(:name, :redirect_uri)
|
params[:doorkeeper_application].permit(:name, :redirect_uri, :scopes)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
module OauthApplications
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
before_action :prepare_scopes, only: [:create, :update]
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepare_scopes
|
||||||
|
scopes = params.dig(:doorkeeper_application, :scopes)
|
||||||
|
if scopes
|
||||||
|
params[:doorkeeper_application][:scopes] = scopes.join(' ')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
|
||||||
include Gitlab::CurrentSettings
|
include Gitlab::CurrentSettings
|
||||||
include Gitlab::GonHelper
|
include Gitlab::GonHelper
|
||||||
include PageLayoutHelper
|
include PageLayoutHelper
|
||||||
|
include OauthApplications
|
||||||
|
|
||||||
before_action :verify_user_oauth_applications_enabled
|
before_action :verify_user_oauth_applications_enabled
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
|
@ -13,6 +14,10 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
|
||||||
set_index_vars
|
set_index_vars
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@scopes = Doorkeeper.configuration.scopes
|
||||||
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@application = Doorkeeper::Application.new(application_params)
|
@application = Doorkeeper::Application.new(application_params)
|
||||||
|
|
||||||
|
@ -40,6 +45,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
|
||||||
@authorized_tokens = current_user.oauth_authorized_tokens
|
@authorized_tokens = current_user.oauth_authorized_tokens
|
||||||
@authorized_anonymous_tokens = @authorized_tokens.reject(&:application)
|
@authorized_anonymous_tokens = @authorized_tokens.reject(&:application)
|
||||||
@authorized_apps = @authorized_tokens.map(&:application).uniq.reject(&:nil?)
|
@authorized_apps = @authorized_tokens.map(&:application).uniq.reject(&:nil?)
|
||||||
|
@scopes = Doorkeeper.configuration.scopes
|
||||||
|
|
||||||
# Don't overwrite a value possibly set by `create`
|
# Don't overwrite a value possibly set by `create`
|
||||||
@application ||= Doorkeeper::Application.new
|
@application ||= Doorkeeper::Application.new
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
|
class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
|
||||||
before_action :load_personal_access_tokens, only: :index
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@personal_access_token = current_user.personal_access_tokens.build
|
set_index_vars
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -12,7 +10,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
|
||||||
flash[:personal_access_token] = @personal_access_token.token
|
flash[:personal_access_token] = @personal_access_token.token
|
||||||
redirect_to profile_personal_access_tokens_path, notice: "Your new personal access token has been created."
|
redirect_to profile_personal_access_tokens_path, notice: "Your new personal access token has been created."
|
||||||
else
|
else
|
||||||
load_personal_access_tokens
|
set_index_vars
|
||||||
render :index
|
render :index
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -32,10 +30,12 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def personal_access_token_params
|
def personal_access_token_params
|
||||||
params.require(:personal_access_token).permit(:name, :expires_at)
|
params.require(:personal_access_token).permit(:name, :expires_at, scopes: [])
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_personal_access_tokens
|
def set_index_vars
|
||||||
|
@personal_access_token ||= current_user.personal_access_tokens.build
|
||||||
|
@scopes = Gitlab::Auth::SCOPES
|
||||||
@active_personal_access_tokens = current_user.personal_access_tokens.active.order(:expires_at)
|
@active_personal_access_tokens = current_user.personal_access_tokens.active.order(:expires_at)
|
||||||
@inactive_personal_access_tokens = current_user.personal_access_tokens.inactive
|
@inactive_personal_access_tokens = current_user.personal_access_tokens.inactive
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,6 +18,16 @@
|
||||||
Use
|
Use
|
||||||
%code= Doorkeeper.configuration.native_redirect_uri
|
%code= Doorkeeper.configuration.native_redirect_uri
|
||||||
for local tests
|
for local tests
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
= f.label :scopes, class: 'col-sm-2 control-label'
|
||||||
|
.col-sm-10
|
||||||
|
- @scopes.each do |scope|
|
||||||
|
%fieldset
|
||||||
|
= check_box_tag 'doorkeeper_application[scopes][]', scope, application.scopes.include?(scope), id: "doorkeeper_application_scopes_#{scope}"
|
||||||
|
= label_tag "doorkeeper_application_scopes_#{scope}", scope
|
||||||
|
%span= "(#{t(scope, scope: [:doorkeeper, :scopes])})"
|
||||||
|
|
||||||
.form-actions
|
.form-actions
|
||||||
= f.submit 'Submit', class: "btn btn-save wide"
|
= f.submit 'Submit', class: "btn btn-save wide"
|
||||||
= link_to "Cancel", admin_applications_path, class: "btn btn-default"
|
= link_to "Cancel", admin_applications_path, class: "btn btn-default"
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
%h3.page-title
|
%h3.page-title
|
||||||
Application: #{@application.name}
|
Application: #{@application.name}
|
||||||
|
|
||||||
|
.table-holder.oauth-application-show
|
||||||
.table-holder
|
|
||||||
%table.table
|
%table.table
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
|
@ -23,6 +22,18 @@
|
||||||
- @application.redirect_uri.split.each do |uri|
|
- @application.redirect_uri.split.each do |uri|
|
||||||
%div
|
%div
|
||||||
%span.monospace= uri
|
%span.monospace= uri
|
||||||
|
|
||||||
|
- if @application.scopes.present?
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
Scopes
|
||||||
|
%td
|
||||||
|
%ul.scopes-list.append-bottom-0
|
||||||
|
- @application.scopes.each do |scope|
|
||||||
|
%li
|
||||||
|
%span.scope-name= scope
|
||||||
|
= "(#{t(scope, scope: [:doorkeeper, :scopes])})"
|
||||||
|
|
||||||
.form-actions
|
.form-actions
|
||||||
= link_to 'Edit', edit_admin_application_path(@application), class: 'btn btn-primary wide pull-left'
|
= link_to 'Edit', edit_admin_application_path(@application), class: 'btn btn-primary wide pull-left'
|
||||||
= render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10'
|
= render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10'
|
||||||
|
|
|
@ -17,5 +17,14 @@
|
||||||
%code= Doorkeeper.configuration.native_redirect_uri
|
%code= Doorkeeper.configuration.native_redirect_uri
|
||||||
for local tests
|
for local tests
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
= f.label :scopes, class: 'label-light'
|
||||||
|
- @scopes.each do |scope|
|
||||||
|
%fieldset
|
||||||
|
= check_box_tag 'doorkeeper_application[scopes][]', scope, application.scopes.include?(scope), id: "doorkeeper_application_scopes_#{scope}"
|
||||||
|
= label_tag "doorkeeper_application_scopes_#{scope}", scope
|
||||||
|
%span= "(#{t(scope, scope: [:doorkeeper, :scopes])})"
|
||||||
|
|
||||||
|
|
||||||
.prepend-top-default
|
.prepend-top-default
|
||||||
= f.submit 'Save application', class: "btn btn-create"
|
= f.submit 'Save application', class: "btn btn-create"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
%h3.page-title
|
%h3.page-title
|
||||||
Application: #{@application.name}
|
Application: #{@application.name}
|
||||||
|
|
||||||
.table-holder
|
.table-holder.oauth-application-show
|
||||||
%table.table
|
%table.table
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
|
@ -22,6 +22,19 @@
|
||||||
- @application.redirect_uri.split.each do |uri|
|
- @application.redirect_uri.split.each do |uri|
|
||||||
%div
|
%div
|
||||||
%span.monospace= uri
|
%span.monospace= uri
|
||||||
|
|
||||||
|
- if @application.scopes.present?
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
Scopes
|
||||||
|
%td
|
||||||
|
%ul.scopes-list.append-bottom-0
|
||||||
|
- @application.scopes.each do |scope|
|
||||||
|
%li
|
||||||
|
%span.scope-name= scope
|
||||||
|
= "(#{t(scope, scope: [:doorkeeper, :scopes])})"
|
||||||
|
|
||||||
|
|
||||||
.form-actions
|
.form-actions
|
||||||
= link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide pull-left'
|
= link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide pull-left'
|
||||||
= render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10'
|
= render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10'
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
= form_for [:profile, @personal_access_token], method: :post, html: { class: 'js-requires-input' } do |f|
|
||||||
|
|
||||||
|
= form_errors(@personal_access_token)
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
= f.label :name, class: 'label-light'
|
||||||
|
= f.text_field :name, class: "form-control", required: true
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
= f.label :expires_at, class: 'label-light'
|
||||||
|
= f.text_field :expires_at, class: "datepicker form-control", required: false
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
= f.label :scopes, class: 'label-light'
|
||||||
|
- @scopes.each do |scope|
|
||||||
|
%fieldset
|
||||||
|
= check_box_tag 'personal_access_token[scopes][]', scope, @personal_access_token.scopes.include?(scope), id: "personal_access_token_scopes_#{scope}"
|
||||||
|
= label_tag "personal_access_token_scopes_#{scope}", scope
|
||||||
|
%span= "(#{t(scope, scope: [:doorkeeper, :scopes])})"
|
||||||
|
|
||||||
|
.prepend-top-default
|
||||||
|
= f.submit 'Create Personal Access Token', class: "btn btn-create"
|
|
@ -28,21 +28,8 @@
|
||||||
Add a Personal Access Token
|
Add a Personal Access Token
|
||||||
%p.profile-settings-content
|
%p.profile-settings-content
|
||||||
Pick a name for the application, and we'll give you a unique token.
|
Pick a name for the application, and we'll give you a unique token.
|
||||||
= form_for [:profile, @personal_access_token],
|
|
||||||
method: :post, html: { class: 'js-requires-input' } do |f|
|
|
||||||
|
|
||||||
= form_errors(@personal_access_token)
|
= render "form"
|
||||||
|
|
||||||
.form-group
|
|
||||||
= f.label :name, class: 'label-light'
|
|
||||||
= f.text_field :name, class: "form-control", required: true
|
|
||||||
|
|
||||||
.form-group
|
|
||||||
= f.label :expires_at, class: 'label-light'
|
|
||||||
= f.text_field :expires_at, class: "datepicker form-control", required: false
|
|
||||||
|
|
||||||
.prepend-top-default
|
|
||||||
= f.submit 'Create Personal Access Token', class: "btn btn-create"
|
|
||||||
|
|
||||||
%hr
|
%hr
|
||||||
|
|
||||||
|
@ -56,6 +43,7 @@
|
||||||
%th Name
|
%th Name
|
||||||
%th Created
|
%th Created
|
||||||
%th Expires
|
%th Expires
|
||||||
|
%th Scopes
|
||||||
%th
|
%th
|
||||||
%tbody
|
%tbody
|
||||||
- @active_personal_access_tokens.each do |token|
|
- @active_personal_access_tokens.each do |token|
|
||||||
|
@ -67,6 +55,7 @@
|
||||||
= token.expires_at.to_date.to_s(:medium)
|
= token.expires_at.to_date.to_s(:medium)
|
||||||
- else
|
- else
|
||||||
%span.personal-access-tokens-never-expires-label Never
|
%span.personal-access-tokens-never-expires-label Never
|
||||||
|
%td= token.scopes.present? ? token.scopes.join(", ") : "<no scopes selected>"
|
||||||
%td= link_to "Revoke", revoke_profile_personal_access_token_path(token), method: :put, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to revoke this token? This action cannot be undone." }
|
%td= link_to "Revoke", revoke_profile_personal_access_token_path(token), method: :put, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to revoke this token? This action cannot be undone." }
|
||||||
|
|
||||||
- else
|
- else
|
||||||
|
|
|
@ -51,6 +51,32 @@ describe 'Profile > Personal Access Tokens', feature: true, js: true do
|
||||||
expect(active_personal_access_tokens).to have_text(Date.today.next_month.at_beginning_of_month.to_s(:medium))
|
expect(active_personal_access_tokens).to have_text(Date.today.next_month.at_beginning_of_month.to_s(:medium))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "scopes" do
|
||||||
|
it "allows creation of a token with scopes" do
|
||||||
|
visit profile_personal_access_tokens_path
|
||||||
|
fill_in "Name", with: FFaker::Product.brand
|
||||||
|
|
||||||
|
check "api"
|
||||||
|
check "read_user"
|
||||||
|
|
||||||
|
expect {click_on "Create Personal Access Token"}.to change { PersonalAccessToken.count }.by(1)
|
||||||
|
expect(created_personal_access_token).to eq(PersonalAccessToken.last.token)
|
||||||
|
expect(PersonalAccessToken.last.scopes).to match_array(['api', 'read_user'])
|
||||||
|
expect(active_personal_access_tokens).to have_text('api')
|
||||||
|
expect(active_personal_access_tokens).to have_text('read_user')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows creation of a token with no scopes" do
|
||||||
|
visit profile_personal_access_tokens_path
|
||||||
|
fill_in "Name", with: FFaker::Product.brand
|
||||||
|
|
||||||
|
expect {click_on "Create Personal Access Token"}.to change { PersonalAccessToken.count }.by(1)
|
||||||
|
expect(created_personal_access_token).to eq(PersonalAccessToken.last.token)
|
||||||
|
expect(PersonalAccessToken.last.scopes).to eq([])
|
||||||
|
expect(active_personal_access_tokens).to have_text('no scopes')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "when creation fails" do
|
context "when creation fails" do
|
||||||
it "displays an error message" do
|
it "displays an error message" do
|
||||||
disallow_personal_access_token_saves!
|
disallow_personal_access_token_saves!
|
||||||
|
|
Loading…
Reference in New Issue