Commit 6c809dfa authored by Timothy Andrew's avatar Timothy Andrew

Allow creating personal access tokens / OAuth applications with scopes.

parent 1d0ccec6
...@@ -262,3 +262,13 @@ table.u2f-registrations { ...@@ -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;
}
}
\ No newline at end of file
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 ...@@ -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 ...@@ -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
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 ...@@ -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 ...@@ -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 ...@@ -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
......
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 ...@@ -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 ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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'
= 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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 ...@@ -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!
......
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