Commit cbd78922ee43c0124458e2867071f752cae712f4
Exists in
master
and in
4 other branches
Merge branch 'deploy_keys'
Conflicts: app/views/layouts/project.html.haml db/schema.rb
Showing
19 changed files
with
234 additions
and
8 deletions
Show diff stats
| @@ -0,0 +1,44 @@ | @@ -0,0 +1,44 @@ | ||
| 1 | +class DeployKeysController < ApplicationController | ||
| 2 | + respond_to :js, :html | ||
| 3 | + layout "project" | ||
| 4 | + before_filter :project | ||
| 5 | + | ||
| 6 | + # Authorize | ||
| 7 | + before_filter :add_project_abilities | ||
| 8 | + before_filter :authorize_admin_project! | ||
| 9 | + | ||
| 10 | + def project | ||
| 11 | + @project ||= Project.find_by_code(params[:project_id]) | ||
| 12 | + end | ||
| 13 | + | ||
| 14 | + def index | ||
| 15 | + @keys = @project.deploy_keys.all | ||
| 16 | + end | ||
| 17 | + | ||
| 18 | + def show | ||
| 19 | + @key = @project.deploy_keys.find(params[:id]) | ||
| 20 | + end | ||
| 21 | + | ||
| 22 | + def new | ||
| 23 | + @key = @project.deploy_keys.new | ||
| 24 | + | ||
| 25 | + respond_with(@key) | ||
| 26 | + end | ||
| 27 | + | ||
| 28 | + def create | ||
| 29 | + @key = @project.deploy_keys.new(params[:key]) | ||
| 30 | + @key.save | ||
| 31 | + | ||
| 32 | + respond_with(@key) | ||
| 33 | + end | ||
| 34 | + | ||
| 35 | + def destroy | ||
| 36 | + @key = @project.deploy_keys.find(params[:id]) | ||
| 37 | + @key.destroy | ||
| 38 | + | ||
| 39 | + respond_to do |format| | ||
| 40 | + format.html { redirect_to project_deploy_keys_url } | ||
| 41 | + format.js { render :nothing => true } | ||
| 42 | + end | ||
| 43 | + end | ||
| 44 | +end |
app/models/key.rb
| 1 | class Key < ActiveRecord::Base | 1 | class Key < ActiveRecord::Base |
| 2 | belongs_to :user | 2 | belongs_to :user |
| 3 | + belongs_to :project | ||
| 3 | 4 | ||
| 4 | validates :title, | 5 | validates :title, |
| 5 | :presence => true, | 6 | :presence => true, |
| @@ -15,7 +16,11 @@ class Key < ActiveRecord::Base | @@ -15,7 +16,11 @@ class Key < ActiveRecord::Base | ||
| 15 | after_destroy :repository_delete_key | 16 | after_destroy :repository_delete_key |
| 16 | 17 | ||
| 17 | def set_identifier | 18 | def set_identifier |
| 18 | - self.identifier = "#{user.identifier}_#{Time.now.to_i}" | 19 | + if is_deploy_key |
| 20 | + self.identifier = "deploy_#{project.code}_#{Time.now.to_i}" | ||
| 21 | + else | ||
| 22 | + self.identifier = "#{user.identifier}_#{Time.now.to_i}" | ||
| 23 | + end | ||
| 19 | end | 24 | end |
| 20 | 25 | ||
| 21 | def update_repository | 26 | def update_repository |
| @@ -31,10 +36,18 @@ class Key < ActiveRecord::Base | @@ -31,10 +36,18 @@ class Key < ActiveRecord::Base | ||
| 31 | c.update_projects(projects) | 36 | c.update_projects(projects) |
| 32 | end | 37 | end |
| 33 | end | 38 | end |
| 39 | + | ||
| 40 | + def is_deploy_key | ||
| 41 | + true if project_id | ||
| 42 | + end | ||
| 34 | 43 | ||
| 35 | #projects that has this key | 44 | #projects that has this key |
| 36 | def projects | 45 | def projects |
| 37 | - user.projects | 46 | + if is_deploy_key |
| 47 | + [project] | ||
| 48 | + else | ||
| 49 | + user.projects | ||
| 50 | + end | ||
| 38 | end | 51 | end |
| 39 | end | 52 | end |
| 40 | # == Schema Information | 53 | # == Schema Information |
app/models/project.rb
| @@ -14,6 +14,7 @@ class Project < ActiveRecord::Base | @@ -14,6 +14,7 @@ class Project < ActiveRecord::Base | ||
| 14 | has_many :users, :through => :users_projects | 14 | has_many :users, :through => :users_projects |
| 15 | has_many :notes, :dependent => :destroy | 15 | has_many :notes, :dependent => :destroy |
| 16 | has_many :snippets, :dependent => :destroy | 16 | has_many :snippets, :dependent => :destroy |
| 17 | + has_many :deploy_keys, :dependent => :destroy, :foreign_key => "project_id", :class_name => "Key" | ||
| 17 | has_many :web_hooks, :dependent => :destroy | 18 | has_many :web_hooks, :dependent => :destroy |
| 18 | 19 | ||
| 19 | acts_as_taggable | 20 | acts_as_taggable |
| @@ -199,7 +200,7 @@ class Project < ActiveRecord::Base | @@ -199,7 +200,7 @@ class Project < ActiveRecord::Base | ||
| 199 | def repository_readers | 200 | def repository_readers |
| 200 | keys = Key.joins({:user => :users_projects}). | 201 | keys = Key.joins({:user => :users_projects}). |
| 201 | where("users_projects.project_id = ? AND users_projects.repo_access = ?", id, Repository::REPO_R) | 202 | where("users_projects.project_id = ? AND users_projects.repo_access = ?", id, Repository::REPO_R) |
| 202 | - keys.map(&:identifier) | 203 | + keys.map(&:identifier) + deploy_keys.map(&:identifier) |
| 203 | end | 204 | end |
| 204 | 205 | ||
| 205 | def repository_writers | 206 | def repository_writers |
| @@ -0,0 +1,16 @@ | @@ -0,0 +1,16 @@ | ||
| 1 | +%div | ||
| 2 | + = form_for [@project, @key], :url => project_deploy_keys_path, :remote => true do |f| | ||
| 3 | + -if @key.errors.any? | ||
| 4 | + %ul | ||
| 5 | + - @key.errors.full_messages.each do |msg| | ||
| 6 | + %li= msg | ||
| 7 | + | ||
| 8 | + .form-row | ||
| 9 | + = f.label :title | ||
| 10 | + = f.text_field :title, :style => "width:300px" | ||
| 11 | + .form-row | ||
| 12 | + = f.label :key | ||
| 13 | + = f.text_area :key, :style => "width:300px; height:130px" | ||
| 14 | + .form-row | ||
| 15 | + = f.submit 'Save', :class => "grey-button" | ||
| 16 | + |
| @@ -0,0 +1,9 @@ | @@ -0,0 +1,9 @@ | ||
| 1 | +- if @key.valid? | ||
| 2 | + :plain | ||
| 3 | + $("#new_key_dialog").dialog("close"); | ||
| 4 | + $("#keys-table .data").append("#{escape_javascript(render(:partial => 'show', :locals => {:key => @key} ))}"); | ||
| 5 | + $("#no_ssh_key_defined").hide(); | ||
| 6 | +- else | ||
| 7 | + :plain | ||
| 8 | + $("#new_key_dialog").empty(); | ||
| 9 | + $("#new_key_dialog").append("#{escape_javascript(render('form'))}"); |
| @@ -0,0 +1,11 @@ | @@ -0,0 +1,11 @@ | ||
| 1 | += render "repositories/head" | ||
| 2 | + | ||
| 3 | +%div#keys-table{ :class => "update-data ui-box ui-box-small ui-box-big" } | ||
| 4 | + .data | ||
| 5 | + - @keys.each do |key| | ||
| 6 | + = render(:partial => 'show', :locals => {:key => key}) | ||
| 7 | + | ||
| 8 | +:javascript | ||
| 9 | + $('.delete-key').live('ajax:success', function() { | ||
| 10 | + $(this).closest('.update-item').fadeOut(); }); | ||
| 11 | + |
| @@ -0,0 +1,11 @@ | @@ -0,0 +1,11 @@ | ||
| 1 | +:plain | ||
| 2 | + var new_key_dialog = $("<div id='new_key_dialog'></div>"); | ||
| 3 | + new_key_dialog.html("#{escape_javascript(render('form'))}"); | ||
| 4 | + $(new_key_dialog).dialog({ | ||
| 5 | + width: 350, | ||
| 6 | + resizable: false, | ||
| 7 | + draggable: false, | ||
| 8 | + title: "Add new public key", | ||
| 9 | + close: function(event, ui) { $("#new_key_dialog").remove();}, | ||
| 10 | + modal: true | ||
| 11 | + }); |
| @@ -0,0 +1,10 @@ | @@ -0,0 +1,10 @@ | ||
| 1 | +.ui-box.width-100p | ||
| 2 | + %h3= @key.title | ||
| 3 | + .data | ||
| 4 | + %pre= @key.key | ||
| 5 | + .clear | ||
| 6 | + .buttons | ||
| 7 | + = link_to 'Remove', project_deploy_key_path(@key.project, @key), :confirm => 'Are you sure?', :method => :delete, :class => "red-button delete-key right" | ||
| 8 | + .clear | ||
| 9 | + | ||
| 10 | + |
app/views/layouts/project.html.haml
app/views/repositories/_head.html.haml
| @@ -11,13 +11,18 @@ | @@ -11,13 +11,18 @@ | ||
| 11 | = link_to project_hooks_path, :class => "tab #{'active' if controller.controller_name == "hooks" }" do | 11 | = link_to project_hooks_path, :class => "tab #{'active' if controller.controller_name == "hooks" }" do |
| 12 | %span | 12 | %span |
| 13 | Hooks | 13 | Hooks |
| 14 | - -#= link_to "#", :class => "tab" do | ||
| 15 | - %span | ||
| 16 | - Deploy Keys | 14 | + - if can? current_user, :admin_project, @project |
| 15 | + = link_to project_deploy_keys_path(@project), :class => "tab #{'active' if controller.controller_name == "deploy_keys"}" do | ||
| 16 | + %span | ||
| 17 | + Deploy Keys | ||
| 17 | 18 | ||
| 18 | - if current_page?(project_hooks_path(@project)) | 19 | - if current_page?(project_hooks_path(@project)) |
| 19 | - if can? current_user, :admin_project, @project | 20 | - if can? current_user, :admin_project, @project |
| 20 | = link_to new_project_hook_path(@project), :class => "add_new", :title => "New Web Hook" do | 21 | = link_to new_project_hook_path(@project), :class => "add_new", :title => "New Web Hook" do |
| 21 | = image_tag "add_new.png", :width => 14 | 22 | = image_tag "add_new.png", :width => 14 |
| 22 | 23 | ||
| 24 | + - if current_page?(project_deploy_keys_path(@project)) | ||
| 25 | + - if can? current_user, :admin_project, @project | ||
| 26 | + = link_to new_project_deploy_key_path(@project), :class => "add_new", :title => "New Deploy Key", :remote => true do | ||
| 27 | + = image_tag "add_new.png", :width => 14 | ||
| 23 | 28 |
config/routes.rb
| @@ -53,6 +53,8 @@ Gitlab::Application.routes.draw do | @@ -53,6 +53,8 @@ Gitlab::Application.routes.draw do | ||
| 53 | end | 53 | end |
| 54 | end | 54 | end |
| 55 | 55 | ||
| 56 | + resources :deploy_keys | ||
| 57 | + | ||
| 56 | resources :refs, :only => [], :path => "/" do | 58 | resources :refs, :only => [], :path => "/" do |
| 57 | collection do | 59 | collection do |
| 58 | get "switch" | 60 | get "switch" |
db/schema.rb
| @@ -39,12 +39,13 @@ ActiveRecord::Schema.define(:version => 20120110180749) do | @@ -39,12 +39,13 @@ ActiveRecord::Schema.define(:version => 20120110180749) do | ||
| 39 | end | 39 | end |
| 40 | 40 | ||
| 41 | create_table "keys", :force => true do |t| | 41 | create_table "keys", :force => true do |t| |
| 42 | - t.integer "user_id", :null => false | 42 | + t.integer "user_id" |
| 43 | t.datetime "created_at" | 43 | t.datetime "created_at" |
| 44 | t.datetime "updated_at" | 44 | t.datetime "updated_at" |
| 45 | t.text "key" | 45 | t.text "key" |
| 46 | t.string "title" | 46 | t.string "title" |
| 47 | t.string "identifier" | 47 | t.string "identifier" |
| 48 | + t.integer "project_id" | ||
| 48 | end | 49 | end |
| 49 | 50 | ||
| 50 | create_table "merge_requests", :force => true do |t| | 51 | create_table "merge_requests", :force => true do |t| |
spec/models/key_spec.rb
| @@ -2,7 +2,7 @@ require 'spec_helper' | @@ -2,7 +2,7 @@ require 'spec_helper' | ||
| 2 | 2 | ||
| 3 | describe Key do | 3 | describe Key do |
| 4 | describe "Associations" do | 4 | describe "Associations" do |
| 5 | - it { should belong_to(:user) } | 5 | + it { should belong_to(:user) or belong_to(:project) } |
| 6 | end | 6 | end |
| 7 | 7 | ||
| 8 | describe "Validation" do | 8 | describe "Validation" do |
| @@ -0,0 +1,68 @@ | @@ -0,0 +1,68 @@ | ||
| 1 | +require 'spec_helper' | ||
| 2 | + | ||
| 3 | +describe "Projects", "DeployKeys" do | ||
| 4 | + let(:project) { Factory :project } | ||
| 5 | + | ||
| 6 | + before do | ||
| 7 | + login_as :user | ||
| 8 | + project.add_access(@user, :read, :write, :admin) | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + describe "GET /keys" do | ||
| 12 | + before do | ||
| 13 | + @key = Factory :key, :project => project | ||
| 14 | + visit project_deploy_keys_path(project) | ||
| 15 | + end | ||
| 16 | + | ||
| 17 | + subject { page } | ||
| 18 | + | ||
| 19 | + it { should have_content(@key.title) } | ||
| 20 | + | ||
| 21 | + describe "Destroy" do | ||
| 22 | + before { visit project_deploy_key_path(project, @key) } | ||
| 23 | + | ||
| 24 | + it "should remove entry" do | ||
| 25 | + expect { | ||
| 26 | + click_link "Remove" | ||
| 27 | + }.to change { project.deploy_keys.count }.by(-1) | ||
| 28 | + end | ||
| 29 | + end | ||
| 30 | + end | ||
| 31 | + | ||
| 32 | + describe "New key", :js => true do | ||
| 33 | + before do | ||
| 34 | + visit project_deploy_keys_path(project) | ||
| 35 | + click_link "New Deploy Key" | ||
| 36 | + end | ||
| 37 | + | ||
| 38 | + it "should open new key popup" do | ||
| 39 | + page.should have_content("Add new public key") | ||
| 40 | + end | ||
| 41 | + | ||
| 42 | + describe "fill in" do | ||
| 43 | + before do | ||
| 44 | + fill_in "key_title", :with => "laptop" | ||
| 45 | + fill_in "key_key", :with => "publickey234=" | ||
| 46 | + end | ||
| 47 | + | ||
| 48 | + it { expect { click_button "Save" }.to change {Key.count}.by(1) } | ||
| 49 | + | ||
| 50 | + it "should add new key to table" do | ||
| 51 | + click_button "Save" | ||
| 52 | + | ||
| 53 | + page.should_not have_content("Add new public key") | ||
| 54 | + page.should have_content "laptop" | ||
| 55 | + end | ||
| 56 | + end | ||
| 57 | + end | ||
| 58 | + | ||
| 59 | + describe "Show page" do | ||
| 60 | + before do | ||
| 61 | + @key = Factory :key, :project => project | ||
| 62 | + visit project_deploy_key_path(project, @key) | ||
| 63 | + end | ||
| 64 | + | ||
| 65 | + it { page.should have_content @key.title } | ||
| 66 | + it { page.should have_content @key.key[0..10] } | ||
| 67 | + end | ||
| 68 | +end |
spec/requests/projects_security_spec.rb
| @@ -105,6 +105,15 @@ describe "Projects" do | @@ -105,6 +105,15 @@ describe "Projects" do | ||
| 105 | it { edit_project_path(@project).should be_denied_for :visitor } | 105 | it { edit_project_path(@project).should be_denied_for :visitor } |
| 106 | end | 106 | end |
| 107 | 107 | ||
| 108 | + describe "GET /project_code/deploy_keys" do | ||
| 109 | + it { project_deploy_keys_path(@project).should be_allowed_for @u1 } | ||
| 110 | + it { project_deploy_keys_path(@project).should be_denied_for @u3 } | ||
| 111 | + it { project_deploy_keys_path(@project).should be_denied_for :admin } | ||
| 112 | + it { project_deploy_keys_path(@project).should be_denied_for @u2 } | ||
| 113 | + it { project_deploy_keys_path(@project).should be_denied_for :user } | ||
| 114 | + it { project_deploy_keys_path(@project).should be_denied_for :visitor } | ||
| 115 | + end | ||
| 116 | + | ||
| 108 | describe "GET /project_code/issues" do | 117 | describe "GET /project_code/issues" do |
| 109 | it { project_issues_path(@project).should be_allowed_for @u1 } | 118 | it { project_issues_path(@project).should be_allowed_for @u1 } |
| 110 | it { project_issues_path(@project).should be_allowed_for @u3 } | 119 | it { project_issues_path(@project).should be_allowed_for @u3 } |