Commit 9c14ed06 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'public-deploy-keys' into 'master'

Allow admin to create public deploy keys that are accessible to any project.

Addresses private issue https://dev.gitlab.org/gitlab/gitlabhq/issues/1774.

Project settings:

![Screen_Shot_2015-03-27_at_14.46.48](https://gitlab.com/gitlab-org/gitlab-ce/uploads/01799ff912671ba6db3f828ea1aca1a6/Screen_Shot_2015-03-27_at_14.46.48.png)

The "Public deploy keys" section is only shown when there are any. If there are public deploy keys but no project deploy keys, only public deploy keys are shown. If there are no public deploy keys and no project deploy keys, the current "Deploy keys from projects you have access to will be displayed here" placeholder is shown.

The list of projects below the public key has been changed to only show projects the user has access to.

"Public deploy key" seems to be repeated on the left, but the first is just the title. The label is always visible for public deploy keys.

Admin index:

![Screen_Shot_2015-03-27_at_14.47.06](https://gitlab.com/gitlab-org/gitlab-ce/uploads/ea889d274cfd3f0694d47d602f4f3e94/Screen_Shot_2015-03-27_at_14.47.06.png)

Admin detail page:

![Screen_Shot_2015-03-27_at_14.47.16](https://gitlab.com/gitlab-org/gitlab-ce/uploads/8c8475e05bf6b497da3b9f1bc102329f/Screen_Shot_2015-03-27_at_14.47.16.png)

Projects using the deploy key are listed on the left and can be disabled easily.

See merge request !469
parents 0c24192f 6237cae1
......@@ -77,6 +77,8 @@ v 7.10.0 (unreleased)
v 7.9.3
- Contains no changes
- Add icons to Add dropdown items.
- Allow admin to create public deploy keys that are accessible to any project.
v 7.9.2
- Contains no changes
......
class Admin::DeployKeysController < Admin::ApplicationController
before_filter :deploy_keys, only: [:index]
before_filter :deploy_key, only: [:show, :destroy]
def index
end
def show
end
def new
@deploy_key = deploy_keys.new
end
def create
@deploy_key = deploy_keys.new(deploy_key_params)
if @deploy_key.save
redirect_to admin_deploy_keys_path
else
render "new"
end
end
def destroy
deploy_key.destroy
respond_to do |format|
format.html { redirect_to admin_deploy_keys_path }
format.json { head :ok }
end
end
protected
def deploy_key
@deploy_key ||= deploy_keys.find(params[:id])
end
def deploy_keys
@deploy_keys ||= DeployKey.are_public
end
def deploy_key_params
params.require(:deploy_key).permit(:key, :title)
end
end
......@@ -8,7 +8,14 @@ class Projects::DeployKeysController < Projects::ApplicationController
def index
@enabled_keys = @project.deploy_keys
@available_keys = available_keys - @enabled_keys
@available_keys = accessible_keys - @enabled_keys
@available_project_keys = current_user.project_deploy_keys - @enabled_keys
@available_public_keys = DeployKey.are_public - @enabled_keys
# Public keys that are already used by another accessible project are already
# in @available_project_keys.
@available_public_keys -= @available_project_keys
end
def show
......@@ -32,18 +39,9 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
end
def destroy
@key = @project.deploy_keys.find(params[:id])
@key.destroy
respond_to do |format|
format.html { redirect_to namespace_project_deploy_keys_path(@project.namespace, @project) }
format.js { render nothing: true }
end
end
def enable
@project.deploy_keys << available_keys.find(params[:id])
@key = accessible_keys.find(params[:id])
@project.deploy_keys << @key
redirect_to namespace_project_deploy_keys_path(@project.namespace,
@project)
......@@ -52,14 +50,13 @@ class Projects::DeployKeysController < Projects::ApplicationController
def disable
@project.deploy_keys_projects.find_by(deploy_key_id: params[:id]).destroy
redirect_to namespace_project_deploy_keys_path(@project.namespace,
@project)
redirect_to :back
end
protected
def available_keys
@available_keys ||= current_user.accessible_deploy_keys
def accessible_keys
@accessible_keys ||= current_user.accessible_deploy_keys
end
def deploy_key_params
......
......@@ -127,6 +127,14 @@ module ProjectsHelper
html + count_html
end
def project_for_deploy_key(deploy_key)
if deploy_key.projects.include?(@project)
@project
else
deploy_key.projects.find { |project| can?(current_user, :read_project, project) }
end
end
private
def get_project_nav_tabs(project, current_user)
......
......@@ -7,6 +7,7 @@
# created_at :datetime
# updated_at :datetime
# key :text
# public :boolean default(FALSE)
# title :string(255)
# type :string(255)
# fingerprint :string(255)
......@@ -17,4 +18,21 @@ class DeployKey < Key
has_many :projects, through: :deploy_keys_projects
scope :in_projects, ->(projects) { joins(:deploy_keys_projects).where('deploy_keys_projects.project_id in (?)', projects) }
scope :are_public, -> { where(public: true) }
def private?
!public?
end
def orphaned?
self.deploy_keys_projects.length == 0
end
def almost_orphaned?
self.deploy_keys_projects.length == 1
end
def destroyed_when_orphaned?
self.private?
end
end
......@@ -22,6 +22,8 @@ class DeployKeysProject < ActiveRecord::Base
private
def destroy_orphaned_deploy_key
self.deploy_key.destroy if self.deploy_key.deploy_keys_projects.length == 0
return unless self.deploy_key.destroyed_when_orphaned? && self.deploy_key.orphaned?
self.deploy_key.destroy
end
end
......@@ -414,8 +414,16 @@ class User < ActiveRecord::Base
@ldap_identity ||= identities.find_by(["provider LIKE ?", "ldap%"])
end
def project_deploy_keys
DeployKey.in_projects(self.authorized_projects.pluck(:id))
end
def accessible_deploy_keys
DeployKey.in_projects(self.authorized_projects.pluck(:id)).uniq
@accessible_deploy_keys ||= begin
key_ids = project_deploy_keys.pluck(:id)
key_ids.push(*DeployKey.are_public.pluck(:id))
DeployKey.where(id: key_ids)
end
end
def created_by
......
.panel.panel-default
.panel-heading
Public deploy keys (#{@deploy_keys.count})
.panel-head-actions
= link_to 'New Deploy Key', new_admin_deploy_key_path, class: "btn btn-new btn-sm"
- if @deploy_keys.any?
%table.table
%thead.panel-heading
%tr
%th Title
%th Fingerprint
%th Added at
%th
%tbody
- @deploy_keys.each do |deploy_key|
%tr
%td
= link_to admin_deploy_key_path(deploy_key) do
%strong= deploy_key.title
%td
%span
(#{deploy_key.fingerprint})
%td
%span.cgray
added #{time_ago_with_tooltip(deploy_key.created_at)}
%td
= link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-sm btn-remove delete-key pull-right"
%h3.page-title New public deploy key
%hr
%div
= form_for [:admin, @deploy_key], html: { class: 'deploy-key-form form-horizontal' } do |f|
-if @deploy_key.errors.any?
.alert.alert-danger
%ul
- @deploy_key.errors.full_messages.each do |msg|
%li= msg
.form-group
= f.label :title, class: "control-label"
.col-sm-10= f.text_field :title, class: 'form-control'
.form-group
= f.label :key, class: "control-label"
.col-sm-10
%p.light
Paste a machine public key here. Read more about how to generate it
= link_to "here", help_page_path("ssh", "README")
= f.text_area :key, class: "form-control thin_area", rows: 5
.form-actions
= f.submit 'Create', class: "btn-create btn"
= link_to "Cancel", admin_deploy_keys_path, class: "btn btn-cancel"
.row
.col-md-4
.panel.panel-default
.panel-heading
Deploy Key
%ul.well-list
%li
%span.light Title:
%strong= @deploy_key.title
%li
%span.light Created on:
%strong= @deploy_key.created_at.stamp("Aug 21, 2011")
.panel.panel-default
.panel-heading Projects (#{@deploy_key.deploy_keys_projects.count})
- if @deploy_key.deploy_keys_projects.any?
%ul.well-list
- @deploy_key.projects.each do |project|
%li
%span
%strong
= link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project]
.pull-right
= link_to disable_namespace_project_deploy_key_path(project.namespace, project, @deploy_key), data: { confirm: "Are you sure?" }, method: :put, class: "btn-xs btn btn-remove", title: 'Remove deploy key from project' do
%i.fa.fa-times.fa-inverse
.col-md-8
%p
%span.light Fingerprint:
%strong= @deploy_key.fingerprint
%pre.well-pre
= @deploy_key.key
.pull-right
= link_to 'Remove', admin_deploy_key_path(@deploy_key), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key"
......@@ -19,6 +19,11 @@
%i.fa.fa-group
%span
Groups
= nav_link(controller: :deploy_keys) do
= link_to admin_deploy_keys_path, title: 'Deploy Keys' do
%i.fa.fa-key
%span
Deploy Keys
= nav_link(controller: :logs) do
= link_to admin_logs_path, title: 'Logs' do
%i.fa.fa-file-text
......
......@@ -5,21 +5,32 @@
%i.fa.fa-plus
Enable
- else
- if deploy_key.projects.count > 1
- if deploy_key.destroyed_when_orphaned? && deploy_key.almost_orphaned?
= link_to 'Remove', disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), data: { confirm: 'You are going to remove deploy key. Are you sure?'}, method: :put, class: "btn btn-remove delete-key btn-sm pull-right"
- else
= link_to disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: 'btn btn-sm', method: :put do
%i.fa.fa-power-off
Disable
- else
= link_to 'Remove', namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), data: { confirm: 'You are going to remove deploy key. Are you sure?'}, method: :delete, class: "btn btn-remove delete-key btn-sm pull-right"
- key_project = deploy_key.projects.include?(@project) ? @project : deploy_key.projects.first
= link_to namespace_project_deploy_key_path(key_project.namespace, key_project, deploy_key) do
- if project = project_for_deploy_key(deploy_key)
= link_to namespace_project_deploy_key_path(project.namespace, project, deploy_key) do
%i.fa.fa-key
%strong= deploy_key.title
- else
%i.fa.fa-key
%strong= deploy_key.title
%p.light.prepend-top-10
- deploy_key.projects.map(&:name_with_namespace).each do |project_name|
%span.label.label-gray.deploy-project-label= project_name
- if deploy_key.public?
%span.label.label-info.deploy-project-label
Public deploy key
- deploy_key.projects.each do |project|
- if can?(current_user, :read_project, project)
%span.label.label-gray.deploy-project-label
= link_to namespace_project_path(project.namespace, project) do
= project.name_with_namespace
%small.pull-right
Created #{time_ago_with_tooltip(deploy_key.created_at)}
......@@ -22,11 +22,20 @@
.light-well
.nothing-here-block Create a #{link_to 'new deploy key', new_namespace_project_deploy_key_path(@project.namespace, @project)} or add an existing one
.col-md-6.available-keys
%h5
%strong Deploy keys
from projects available to you
%ul.bordered-list
= render @available_keys
- if @available_keys.blank?
.light-well
.nothing-here-block Deploy keys from projects you have access to will be displayed here
- # If there are available public deploy keys but no available project deploy keys, only public deploy keys are shown.
- if @available_project_keys.any? || @available_public_keys.blank?
%h5
%strong Deploy keys
from projects you have access to
%ul.bordered-list
= render @available_project_keys
- if @available_project_keys.blank?
.light-well
.nothing-here-block Deploy keys from projects you have access to will be displayed here
- if @available_public_keys.any?
%h5
%strong Public deploy keys
available to any project
%ul.bordered-list
= render @available_public_keys
......@@ -145,6 +145,8 @@ Gitlab::Application.routes.draw do
end
end
resources :deploy_keys, only: [:index, :show, :new, :create, :destroy]
resources :hooks, only: [:index, :create, :destroy] do
get :test
end
......@@ -393,7 +395,7 @@ Gitlab::Application.routes.draw do
end
end
resources :deploy_keys, constraints: { id: /\d+/ } do
resources :deploy_keys, constraints: { id: /\d+/ }, only: [:index, :show, :new, :create] do
member do
put :enable
put :disable
......
class AddPublicToKey < ActiveRecord::Migration
def change
add_column :keys, :public, :boolean, default: false, null: false
end
end
......@@ -132,6 +132,7 @@ ActiveRecord::Schema.define(version: 20150411180045) do
t.string "title"
t.string "type"
t.string "fingerprint"
t.boolean "public", default: false, null: false
end
add_index "keys", ["created_at", "id"], name: "index_keys_on_created_at_and_id", using: :btree
......
@admin
Feature: Admin Deploy Keys
Background:
Given I sign in as an admin
And there are public deploy keys in system
Scenario: Deploy Keys list
When I visit admin deploy keys page
Then I should see all public deploy keys
Scenario: Deploy Keys show
When I visit admin deploy keys page
And I click on first deploy key
Then I should see deploy key details
Scenario: Deploy Keys new
When I visit admin deploy keys page
And I click 'New Deploy Key'
And I submit new deploy key
Then I should be on admin deploy keys page
And I should see newly created deploy key
......@@ -6,7 +6,17 @@ Feature: Project Deploy Keys
Scenario: I should see deploy keys list
Given project has deploy key
When I visit project deploy keys page
Then I should see project deploy keys
Then I should see project deploy key
Scenario: I should see project deploy keys
Given other project has deploy key
When I visit project deploy keys page
Then I should see other project deploy key
Scenario: I should see public deploy keys
Given public deploy key exists
When I visit project deploy keys page
Then I should see public deploy key
Scenario: I add new deploy key
Given I visit project deploy keys page
......@@ -15,9 +25,16 @@ Feature: Project Deploy Keys
Then I should be on deploy keys page
And I should see newly created deploy key
Scenario: I attach deploy key to project
Scenario: I attach other project deploy key to project
Given other project has deploy key
And I visit project deploy keys page
When I click attach deploy key
Then I should be on deploy keys page
And I should see newly created deploy key
Scenario: I attach public deploy key to project
Given public deploy key exists
And I visit project deploy keys page
When I click attach deploy key
Then I should be on deploy keys page
And I should see newly created deploy key
class Spinach::Features::AdminDeployKeys < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedAdmin
step 'there are public deploy keys in system' do
create(:deploy_key, public: true)
create(:another_deploy_key, public: true)
end
step 'I should see all public deploy keys' do
DeployKey.are_public.each do |p|
page.should have_content p.title
end
end
step 'I click on first deploy key' do
click_link DeployKey.are_public.first.title
end
step 'I should see deploy key details' do
deploy_key = DeployKey.are_public.first
current_path.should == admin_deploy_key_path(deploy_key)
page.should have_content(deploy_key.title)
page.should have_content(deploy_key.key)
end
step 'I visit admin deploy key page' do
visit admin_deploy_key_path(deploy_key)
end
step 'I visit admin deploy keys page' do
visit admin_deploy_keys_path
end
step 'I click \'New Deploy Key\'' do
click_link 'New Deploy Key'
end
step 'I submit new deploy key' do
fill_in "deploy_key_title", with: "laptop"
fill_in "deploy_key_key", with: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzrEJUIR6Y03TCE9rIJ+GqTBvgb8t1jI9h5UBzCLuK4VawOmkLornPqLDrGbm6tcwM/wBrrLvVOqi2HwmkKEIecVO0a64A4rIYScVsXIniHRS6w5twyn1MD3sIbN+socBDcaldECQa2u1dI3tnNVcs8wi77fiRe7RSxePsJceGoheRQgC8AZ510UdIlO+9rjIHUdVN7LLyz512auAfYsgx1OfablkQ/XJcdEwDNgi9imI6nAXhmoKUm1IPLT2yKajTIC64AjLOnE0YyCh6+7RFMpiMyu1qiOCpdjYwTgBRiciNRZCH8xIedyCoAmiUgkUT40XYHwLuwiPJICpkAzp7Q== user@laptop"
click_button "Create"
end
step 'I should be on admin deploy keys page' do
current_path.should == admin_deploy_keys_path
end
step 'I should see newly created deploy key' do
page.should have_content(deploy_key.title)
end
def deploy_key
@deploy_key ||= DeployKey.are_public.first
end
end
......@@ -7,12 +7,24 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
create(:deploy_keys_project, project: @project)
end
step 'I should see project deploy keys' do
step 'I should see project deploy key' do
within '.enabled-keys' do
page.should have_content deploy_key.title
end
end
step 'I should see other project deploy key' do
within '.available-keys' do
page.should have_content other_deploy_key.title
end
end
step 'I should see public deploy key' do
within '.available-keys' do
page.should have_content public_deploy_key.title
end
end
step 'I click \'New Deploy Key\'' do
click_link 'New Deploy Key'
end
......@@ -39,6 +51,10 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
create(:deploy_keys_project, project: @second_project)
end
step 'public deploy key exists' do
create(:deploy_key, public: true)
end
step 'I click attach deploy key' do
within '.available-keys' do
click_link 'Enable'
......@@ -50,4 +66,12 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
def deploy_key
@project.deploy_keys.last
end
def other_deploy_key
@second_project.deploy_keys.last
end
def public_deploy_key
DeployKey.are_public.last
end
end
......@@ -111,6 +111,9 @@ FactoryGirl.define do
key do
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDmTillFzNTrrGgwaCKaSj+QCz81E6jBc/s9av0+3b1Hwfxgkqjl4nAK/OD2NjgyrONDTDfR8cRN4eAAy6nY8GLkOyYBDyuc5nTMqs5z3yVuTwf3koGm/YQQCmo91psZ2BgDFTor8SVEE5Mm1D1k3JDMhDFxzzrOtRYFPci9lskTJaBjpqWZ4E9rDTD2q/QZntCqbC3wE9uSemRQB5f8kik7vD/AD8VQXuzKladrZKkzkONCPWsXDspUitjM8HkQdOf0PsYn1CMUC1xKYbCxkg5TkEosIwGv6CoEArUrdu/4+10LVslq494mAvEItywzrluCLCnwELfW+h/m8UHoVhZ"
end
factory :another_deploy_key, class: 'DeployKey' do
end
end
factory :invalid_key do
......
......@@ -28,17 +28,32 @@ describe DeployKeysProject do
let(:deploy_key) { subject.deploy_key }
context "when the deploy key is only used by this project" do
it "destroys the deploy key" do
subject.destroy
context "when the deploy key is public" do
before do
deploy_key.update_attribute(:public, true)
end
expect {
deploy_key.reload
}.to raise_error(ActiveRecord::RecordNotFound)
it "doesn't destroy the deploy key" do
subject.destroy
expect {
deploy_key.reload
}.not_to raise_error(ActiveRecord::RecordNotFound)
end
end
context "when the deploy key is private" do
it "destroys the deploy key" do
subject.destroy
expect {
deploy_key.reload
}.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
context "when the deploy key is used by more than one project" do
let!(:other_project) { create(:project) }
before do
......
......@@ -168,12 +168,11 @@ end
# project_deploy_keys GET /:project_id/deploy_keys(.:format) deploy_keys#index
# POST /:project_id/deploy_keys(.:format) deploy_keys#create
# new_project_deploy_key GET /:project_id/deploy_keys/new(.:format) deploy_keys#new
# edit_project_deploy_key GET /:project_id/deploy_keys/:id/edit(.:format) deploy_keys#edit
# project_deploy_key GET /:project_id/deploy_keys/:id(.:format) deploy_keys#show
# PUT /:project_id/deploy_keys/:id(.:format) deploy_keys#update
# DELETE /:project_id/deploy_keys/:id(.:format) deploy_keys#destroy
describe Projects::DeployKeysController, 'routing' do
it_behaves_like 'RESTful project resources' do
let(:actions) { [:index, :show, :new, :create] }
let(:controller) { 'deploy_keys' }
end
end
......
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