From 9083b7e343f377d15d61d60327c0107cdd3c6cd7 Mon Sep 17 00:00:00 2001
From: Sentinel T
Date: Sun, 18 Jan 2009 03:58:00 +0100
Subject: [PATCH] Redmine management of Git repositories
This patch introduces Redmine management of Git repositories.
The design is based on the Gitosis tool.
* To enable the feature check 'Serve Git repositories using Redmine's SSH account'
in global Settings/Repositories.
* Then each user can specify its public SSH key in the My Account page
* Redmine is configuring '~/.ssh/authorized_keys' file to allow SSH for Redmine users
authorized by SSH public key. The shell is restricted to Redmine's GitManager:serve function.
* This function then checks the corresponding rights of the user who logs in
to the repository he wants to access. Also it rewrites the URL to the repository,
so the user can specify simpler version: 'redmine@server_name:redmine_project_identifier'.
* If the repository does not exists, it is created on the fly
* Redmine also installs own hooks which further check the correctness of the commits
pushed to the repo, ex:
** Is the commiter of the commit equal to the user which pushes to the repository
** Is the push fast_forward
** Are the referenced issues correct
---
app/helpers/repositories_helper.rb | 6 +-
app/models/authorized_keys_entry.rb | 190 +++++++++++++++
app/models/changeset.rb | 82 ++++---
app/models/git_checks/committer.rb | 14 +
app/models/git_checks/delete_branch.rb | 9 +
app/models/git_checks/fast_forward.rb | 10 +
app/models/git_checks/initial_commit.rb | 9 +
app/models/git_checks/issue.rb | 29 +++
app/models/git_manager.rb | 256 ++++++++++++++++++++
app/models/user.rb | 51 ++++
app/views/my/account.rhtml | 5 +
app/views/settings/_repositories.rhtml | 8 +
config/settings.yml | 2 +
lang/bg.yml | 3 +
lang/ca.yml | 3 +
lang/cs.yml | 3 +
lang/da.yml | 3 +
lang/de.yml | 3 +
lang/en.yml | 3 +
lang/es.yml | 3 +
lang/fi.yml | 3 +
lang/fr.yml | 3 +
lang/he.yml | 3 +
lang/hu.yml | 3 +
lang/it.yml | 3 +
lang/ja.yml | 3 +
lang/ko.yml | 3 +
lang/lt.yml | 3 +
lang/nl.yml | 3 +
lang/no.yml | 3 +
lang/pl.yml | 3 +
lang/pt-br.yml | 3 +
lang/pt.yml | 3 +
lang/ro.yml | 3 +
lang/ru.yml | 3 +
lang/sk.yml | 3 +
lang/sr.yml | 3 +
lang/sv.yml | 3 +
lang/th.yml | 3 +
lang/tr.yml | 3 +
lang/uk.yml | 3 +
lang/vn.yml | 3 +
lang/zh-tw.yml | 3 +
lang/zh.yml | 3 +
lib/redmine/scm/adapters/git_adapter.rb | 38 +++-
lib/redmine/scm/adapters/git_templates/description | 1 +
.../scm/adapters/git_templates/hooks/post-receive | 1 +
.../scm/adapters/git_templates/hooks/pre-receive | 1 +
.../scm/adapters/git_templates/info/exclude | 6 +
public/stylesheets/application.css | 2 +-
test/unit/authorized_keys_entry_test.rb | 52 ++++
51 files changed, 829 insertions(+), 36 deletions(-)
create mode 100644 app/models/authorized_keys_entry.rb
create mode 100644 app/models/git_checks/committer.rb
create mode 100644 app/models/git_checks/delete_branch.rb
create mode 100644 app/models/git_checks/fast_forward.rb
create mode 100644 app/models/git_checks/initial_commit.rb
create mode 100644 app/models/git_checks/issue.rb
create mode 100644 app/models/git_manager.rb
create mode 100644 lib/redmine/scm/adapters/git_templates/description
create mode 100755 lib/redmine/scm/adapters/git_templates/hooks/post-receive
create mode 100755 lib/redmine/scm/adapters/git_templates/hooks/pre-receive
create mode 100644 lib/redmine/scm/adapters/git_templates/info/exclude
create mode 100644 test/unit/authorized_keys_entry_test.rb
diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb
index d41977d..14f1dc6 100644
--- a/app/helpers/repositories_helper.rb
+++ b/app/helpers/repositories_helper.rb
@@ -164,7 +164,11 @@ module RepositoriesHelper
end
def git_field_tags(form, repository)
- content_tag('p', form.text_field(:url, :label => 'Path to .git directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
+ if Setting.serve_git_repositories? and (repository == nil or repository.url.blank?)
+ content_tag('p', form.text_field(:url, :value => GitManager.repositories_root + '/' + repository.project.identifier + '.git'), :label => 'Path to .git directory', :size => 60, :required => true)
+ else
+ content_tag('p', form.text_field(:url, :label => 'Path to .git directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
+ end
end
def cvs_field_tags(form, repository)
diff --git a/app/models/authorized_keys_entry.rb b/app/models/authorized_keys_entry.rb
new file mode 100644
index 0000000..3f9f4b8
--- /dev/null
+++ b/app/models/authorized_keys_entry.rb
@@ -0,0 +1,190 @@
+require "strscan"
+require 'thread'
+
+class AuthorizedKeysEntry
+ COMMENT=/^#/
+ BLANK=/^\s+$/
+ AUTHORIZED_KEYS_PARSER=/^(?:(.+) )?(ssh-dss|ssh-rsa) ([^ ]+)(?: (.+))?$/
+ KEY_TYPES=['ssh-dss','ssh-rsa']
+ KEY_FORMAT=/\A[\w\/\+\=]+\z/
+ IDENTIFIER_FORMAT=/\A[\w@\.\-]+\z/
+ @@authorized_keys_filename="~/.ssh/authorized_keys"
+
+ attr_reader :identifier, :key, :type
+ attr_accessor :options
+
+ @authorized_keys_lock = Mutex.new
+
+ # Create object from string from authorized_keys
+ def initialize(authorized_keys_string = nil)
+ @key = ''
+ @options = []
+ create_from_authorized_keys(authorized_keys_string) if authorized_keys_string != nil
+ end
+
+ def identifier=(identifier)
+ @identifier = identifier
+ @validated = false
+ end
+
+ def key=(key)
+ @key = key
+ @validated = false
+ end
+
+ def type=(type)
+ @type = type
+ @validated = false
+ end
+
+ def save
+ raise "Invalid key format" if !validate
+ AuthorizedKeysEntry.save_entry(self)
+ end
+
+ def self.find_by_identifier(identifier)
+ read_entries do |entry|
+ return entry if entry.identifier == identifier
+ end
+ return nil
+ end
+
+ def validate
+ return true if @key == ""
+ return false if !@options.kind_of?(Array)
+ return true if @validated
+ # Clean key from any garbage user might have entered while copying the key
+ @key = @key.gsub(/\A(ssh-dss|ssh-rsa)\s/,"").gsub(/\s\w+@[\.\w\-\n]+\s*\Z/,"").gsub(/\s/,'')
+ return false if (@key=~KEY_FORMAT) == nil or !KEY_TYPES.include?(@type)
+ return false if (@identifier=~IDENTIFIER_FORMAT) == nil
+
+ Tempfile.open("redmine") do |file|
+ file << get_id_dsa_pub_format
+ file.close
+ @validated = system "ssh-keygen -B -f #{file.path()} &> /dev/null"
+ end
+ return @validated
+ end
+
+ def to_s
+ get_authorized_keys_format
+ end
+
+ def self.authorized_keys_filename=(filename)
+ @@authorized_keys_filename = filename
+ end
+
+ private
+
+ def create_from_authorized_keys(line)
+ m = AUTHORIZED_KEYS_PARSER.match(line)
+ raise ArgumentError, "Invalid format of authorized_keys entry" if m == nil
+
+ @options, @type, @key, @identifier = m[1], m[2], m[3], m[4]
+ @options = AuthorizedKeysEntry.parse_options(@options)
+ end
+
+ def get_authorized_keys_format
+ raise ArgumentError, 'Entry must have the type set' if @type == nil
+ s = ""
+ s += @options.join(",") + " " if !@options.empty?
+ s += @type + " " + @key
+ s += " " + @identifier if @identifier
+ return s
+ end
+
+ def get_id_dsa_pub_format
+ raise ArgumentError, 'Entry must have the type set' if @type == nil
+ s = ""
+ s += @type + " " + @key
+ s += " " + @identifier if @identifier
+ return s
+ end
+
+ def self.parse_options(options)
+ result = []
+ return result if options == nil
+ scanner = StringScanner.new(options)
+ while !scanner.eos?
+ scanner.skip(/[ \t]*/)
+ # scan a long option
+ if out = scanner.scan(/[-a-z0-9A-Z_]+=\".*?\"/) or out = scanner.scan(/[-a-z0-9A-Z_]+/)
+ result << out
+ else
+ # found an unscannable token, let's abort
+ break
+ end
+ # eat a comma
+ scanner.skip(/[\t]*,[\t]*/)
+ end
+ return result
+ end
+
+ def self.read_entries
+ filename = File.expand_path(@@authorized_keys_filename)
+ return if !File.exists?(filename)
+
+ File.open(filename) do |file|
+ file.flock(File::LOCK_SH)
+ begin
+ file.each_line do |line|
+ next if COMMENT.match(line) or BLANK.match(line)
+ yield AuthorizedKeysEntry.new(line)
+ end
+ ensure
+ file.flock(File::LOCK_UN)
+ end
+ end
+ end
+
+ def self.save_entry(entry)
+ filename = File.expand_path(@@authorized_keys_filename)
+
+ FileUtils.mkdir_p(File.dirname(filename))
+
+ found = false
+ entries = []
+ file = nil
+ begin
+ if File.exist?(filename)
+ file = File.open(filename, "r")
+ # TODO: somehow handle flock blocking
+ file.flock(File::LOCK_EX)
+ file.each_line do |line|
+ next if COMMENT.match(line) or BLANK.match(line)
+ entries << AuthorizedKeysEntry.new(line)
+ end
+
+ entries.each do |e|
+ if entry.identifier == e.identifier
+ return if entry.key == e.key and entry.type == e.type
+
+ found = true
+ if entry.key != ""
+ e.key = entry.key
+ e.type = entry.type
+ e.options = entry.options
+ else
+ entries.delete(e)
+ end
+ break
+ end
+ end
+ end
+
+ if !found and entry.key != ""
+ entries << entry
+ end
+
+ File.open(filename, "w") do |write_file|
+ write_file.flock(File::LOCK_EX) if file == nil
+ entries.each do |entry|
+ write_file << entry.to_s + "\n"
+ end
+ end
+ ensure
+ file.close if file != nil
+ end
+ end
+end
+
diff --git a/app/models/changeset.rb b/app/models/changeset.rb
index 759d240..6d4e6fb 100644
--- a/app/models/changeset.rb
+++ b/app/models/changeset.rb
@@ -70,56 +70,74 @@ class Changeset < ActiveRecord::Base
scan_comment_for_issue_ids
end
require 'pp'
-
- def scan_comment_for_issue_ids
- return if comments.blank?
+
+ # returns issue ids found in message
+ # three arrays are returned, references issues, fixed ones and wrong numbers (not Issues)
+ def self.find_issue_ids(message, project)
+ return [[],[]] if message.blank?
# keywords used to reference issues
ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
# keywords used to fix issues
fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
- # status and optional done ratio applied
- fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id)
- done_ratio = Setting.commit_fix_done_ratio.blank? ? nil : Setting.commit_fix_done_ratio.to_i
kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
- return if kw_regexp.blank?
+ return [[],[]] if kw_regexp.blank?
referenced_issues = []
+ fixed_issues = []
+ wrong_issue_ids = []
if ref_keywords.delete('*')
# find any issue ID in the comments
target_issue_ids = []
- comments.scan(%r{([\s\(,-]|^)#(\d+)(?=[[:punct:]]|\s|<|$)}).each { |m| target_issue_ids << m[1] }
- referenced_issues += repository.project.issues.find_all_by_id(target_issue_ids)
+ message.scan(%r{([\s\(,-]|^)#(\d+)(?=[[:punct:]]|\s|<|$)}).each { |m| target_issue_ids << m[1] }
+ found_issues = project.issues.find_all_by_id(target_issue_ids)
+ references_issues += found_issues
+ found_issues.each { |issue| target_issue_ids.delete(issue.id.to_s) }
+ wrong_issue_ids += target_issue_ids
end
- comments.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
+ message.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
action = match[0]
target_issue_ids = match[1].scan(/\d+/)
- target_issues = repository.project.issues.find_all_by_id(target_issue_ids)
- if fix_status && fix_keywords.include?(action.downcase)
- # update status of issues
- logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
- target_issues.each do |issue|
- # the issue may have been updated by the closure of another one (eg. duplicate)
- issue.reload
- # don't change the status is the issue is closed
- next if issue.status.is_closed?
- csettext = "r#{self.revision}"
- if self.scmid && (! (csettext =~ /^r[0-9]+$/))
- csettext = "commit:\"#{self.scmid}\""
- end
- journal = issue.init_journal(user || User.anonymous, l(:text_status_changed_by_changeset, csettext))
- issue.status = fix_status
- issue.done_ratio = done_ratio if done_ratio
- issue.save
- Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
- end
+ target_issues = project.issues.find_all_by_id(target_issue_ids)
+ target_issues.each { |issue| target_issue_ids.delete(issue.id.to_s) }
+ wrong_issue_ids += target_issue_ids
+ if fix_keywords.include?(action.downcase)
+ fixed_issues += target_issues
+ else
+ referenced_issues += target_issues
end
- referenced_issues += target_issues
end
-
- self.issues = referenced_issues.uniq
+ return [referenced_issues.uniq, fixed_issues.uniq, wrong_issue_ids.uniq]
+ end
+
+ def scan_comment_for_issue_ids
+ return if comments.blank?
+ # status and optional done ratio applied
+ fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id)
+ done_ratio = Setting.commit_fix_done_ratio.blank? ? nil : Setting.commit_fix_done_ratio.to_i
+
+ referenced_issues, fixed_issues, wrong_issues = Changeset.find_issue_ids(comments, repository.project)
+
+ # update status of issues
+ logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
+ fixed_issues.each do |issue|
+ # the issue may have been updated by the closure of another one (eg. duplicate)
+ issue.reload
+ # don't change the status is the issue is closed
+ next if issue.status.is_closed?
+ csettext = "r#{self.revision}"
+ if self.scmid && (! (csettext =~ /^r[0-9]+$/))
+ csettext = "commit:\"#{self.scmid}\""
+ end
+ journal = issue.init_journal(user || User.anonymous, l(:text_status_changed_by_changeset, csettext))
+ issue.status = fix_status
+ issue.done_ratio = done_ratio if done_ratio
+ issue.save
+ Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
+ end
+ self.issues = (referenced_issues + fixed_issues).uniq
end
# Returns the previous changeset
diff --git a/app/models/git_checks/committer.rb b/app/models/git_checks/committer.rb
new file mode 100644
index 0000000..017ce50
--- /dev/null
+++ b/app/models/git_checks/committer.rb
@@ -0,0 +1,14 @@
+class GitChecks::Committer < GitManager::CommitCheck
+ def self.check(revision, branch, user, role, project, git)
+ # Check: committer name and email
+ if revision.author != "#{user.name} <#{user.mail}>"
+ return [ "Commit author name or email is wrong",
+ " Execute following commands and _recreate_ commit:",
+ " git config --global user.name \"#{user.name}\"",
+ " git config --global user.email #{user.mail}",
+ ]
+ end
+ return []
+ end
+end
+
diff --git a/app/models/git_checks/delete_branch.rb b/app/models/git_checks/delete_branch.rb
new file mode 100644
index 0000000..94a96eb
--- /dev/null
+++ b/app/models/git_checks/delete_branch.rb
@@ -0,0 +1,9 @@
+class GitChecks::DeleteBranch < GitManager::RefCheck
+ def self.check(old_rev, new_rev, new_rev_type, branch, user, role, project, git)
+ if new_rev_type == "delete"
+ return "Deleting branch can be done only by repository manager"
+ end
+ return []
+ end
+end
+
diff --git a/app/models/git_checks/fast_forward.rb b/app/models/git_checks/fast_forward.rb
new file mode 100644
index 0000000..03b74c5
--- /dev/null
+++ b/app/models/git_checks/fast_forward.rb
@@ -0,0 +1,10 @@
+class GitChecks::FastForward < GitManager::RefCheck
+ def self.check(old_rev, new_rev, new_rev_type, branch, user, role, project, git)
+ # Check: fast forward
+ if old_rev != git.class::EMPTY_COMMIT and git.merge_base(old_rev, new_rev) != old_rev
+ return "Only fast-forward commits are allowed"
+ end
+ return []
+ end
+end
+
diff --git a/app/models/git_checks/initial_commit.rb b/app/models/git_checks/initial_commit.rb
new file mode 100644
index 0000000..c08344e
--- /dev/null
+++ b/app/models/git_checks/initial_commit.rb
@@ -0,0 +1,9 @@
+class GitChecks::InitialCommit < GitManager::RefCheck
+ def self.check(old_rev, new_rev, new_rev_type, branch, user, role, project, git)
+ if old_rev == git.class::EMPTY_COMMIT and !role.allowed_to?(:manage_repository)
+ return "Initial commit can be done only by repository manager"
+ end
+ return []
+ end
+end
+
diff --git a/app/models/git_checks/issue.rb b/app/models/git_checks/issue.rb
new file mode 100644
index 0000000..82f64f5
--- /dev/null
+++ b/app/models/git_checks/issue.rb
@@ -0,0 +1,29 @@
+class GitChecks::Issue < GitManager::CommitCheck
+ def self.check(revision, branch, user, role, project, git)
+ references_issues, fixes_issues, wrong_issues = Changeset.find_issue_ids(revision.message, project)
+ issues = references_issues + fixes_issues
+
+ errors = []
+
+ if !wrong_issues.empty?
+ errors << "Some issue numbers are wrong: #{wrong_issues.join(',')}"
+ end
+
+ issues.each do |issue_number|
+ issue = Issue.find(issue_number)
+ if issue.closed?
+ errors << "Issue \##{issue.id} is closed. Reopen it and commit again"
+ end
+ if issue.assigned_to != user
+ if issue.assigned_to != nil
+ owner_info = "It belongs to #{issue.assigned_to.firstname} #{issue.assigned_to.lastname}"
+ else
+ owner_info = "It is unassigned"
+ end
+ errors << "Issue \##{issue.id} is not assigned to You. #{owner_info}"
+ end
+ end
+ return errors
+ end
+end
+
diff --git a/app/models/git_manager.rb b/app/models/git_manager.rb
new file mode 100644
index 0000000..2136144
--- /dev/null
+++ b/app/models/git_manager.rb
@@ -0,0 +1,256 @@
+require 'net/http'
+require 'uri'
+require 'redmine/scm/adapters/git_adapter'
+
+class GitManager
+ COMMANDS_READONLY = [
+ 'git-upload-pack',
+ ]
+ COMMANDS_WRITE = [
+ 'git-receive-pack',
+ ]
+ # TODO: make configurable
+ REPOSITORIES_ROOT='~/repositories/'
+ REDMINE_LOGIN_ENV_NAME='REDMINE_LOGIN'
+
+ def self.repositories_root
+ return File.expand_path(REPOSITORIES_ROOT)
+ end
+
+
+ # Executed by SSH when somebody logs into Redmine's SSH account
+ # using public key.
+ # Restricts the user to access only Git repositories he is allowed to.
+ def self.serve
+ begin
+ command = serve_get_original_command
+ user = serve_get_user
+ git_command, project_identifier = parse_git_command(command)
+ project, repository = find_project_and_repo(project_identifier)
+ role = user.role_for_project(project)
+
+ if COMMANDS_READONLY.include?(git_command)
+ if !role.allowed_to?(:view_changesets)
+ raise "User #{user} (#{role.name}) is not allowed to read project #{project}\n"
+ end
+ if !repository.scm.info
+ raise "Empty repository. Push some commit first"
+ end
+ elsif COMMANDS_WRITE.include?(git_command)
+ if !role.allowed_to?(:commit_access)
+ raise "User #{user} (#{role.name}) is not allowed to write to project #{project}\n"
+ end
+ if !repository.scm.info
+ warn "Creating new repository.\n"
+ repository.scm.init(project.description)
+ end
+ else
+ raise "Unknown command '#{verb}'"
+ end
+
+ rescue
+ warn "Error: #{$!}.\n"
+ exit 1
+ end
+
+ ENV[REDMINE_LOGIN_ENV_NAME]=user.login
+ exec 'git', 'shell', '-c', "#{git_command} '#{repository.url}'"
+ end
+
+ def self.get_authorized_keys_options_for_login(login)
+ return [ 'command="ruby ' + RAILS_ROOT + '/script/runner GitManager.serve \'' + login + '\' -e ' + ENV["RAILS_ENV"] +
+ '"', 'no-port-forwarding','no-X11-forwarding','no-agent-forwarding','no-pty' ]
+ end
+
+
+ private
+ def self.serve_get_original_command
+ command = ENV["SSH_ORIGINAL_COMMAND"]
+ if command == nil or command==""
+ raise "SSH_ORIGINAL_COMMAND not set. Use Git to access this account"
+ end
+ if command=~/\n/
+ raise "Command contains new line character"
+ end
+ return command
+ end
+
+ def self.serve_get_user
+ login = ARGV[0]
+ if login == nil or login==""
+ raise "Needs login as parameter"
+ end
+ user = User.find_by_login(login)
+ if user == nil
+ raise "User not found #{login}"
+ end
+ return user
+ end
+
+ def self.parse_git_command(command)
+ verb, args = command.split(" ",2)
+ if verb == 'git'
+ subverb, args = args.split(" ",2)
+ verb = "%s-%s" % [ verb, subverb ]
+ end
+ project_identifier = args.gsub("'","")
+ return verb, project_identifier
+ end
+
+ def self.find_project_and_repo(project_identifier)
+ project = Project.find_by_identifier(project_identifier)
+ if project == nil
+ raise "Project not found \"#{project_identifier}\""
+ end
+
+ repository = project.repository
+ if repository == nil
+ raise "Project #{project} does not have repository\n"
+ end
+
+ if repository.class != Repository::Git
+ raise "Project #{project} has non git repository #{repository.type}"
+ end
+ return project, repository
+ end
+
+
+ public
+
+ class RefCheck
+ @@checks = []
+
+ # Check Git ref, subclassess should override
+ def check(old_rev, new_rev, new_rev_type, branch, user, role, project, git)
+ return true
+ end
+
+ def self.inherited(subclass)
+ @@checks << subclass
+ end
+
+ def self.get_checks
+ return @@checks
+ end
+ end
+ class CommitCheck
+ @@checks = []
+
+ # Check Git ref, subclassess should override
+ def check(revision, branch, user, role, project, git)
+ return true
+ end
+
+ def self.inherited(subclass)
+ @@checks << subclass
+ end
+
+ def self.get_checks
+ return @@checks
+ end
+ end
+
+ def self.load_checks
+ files = Dir.glob(RAILS_ROOT + "/app/models/git_checks/*.rb")
+ files.each do |f|
+ f.sub!(/\A#{RAILS_ROOT}/,'')
+ f.split('/')[3..-1].join('/').split('.').first.camelize.constantize
+ end
+ end
+ load_checks
+
+ def self.check_commits
+ login = ENV[REDMINE_LOGIN_ENV_NAME]
+ if login == nil
+ warn "Redmine: Local user, not running checks"
+ exit 0
+ end
+
+ begin
+ user = User.find_by_login(login)
+ raise "User not found" if user == nil
+ repository = Repository.find_by_url(Dir.getwd)
+ raise "Repository not managed by Redmine" if repository == nil
+ raise "Non git repository" if repository.class != Repository::Git
+ project = repository.project
+ role = user.role_for_project(project)
+ git = repository.scm
+
+ warn ""
+ warn "-------------------------------------------------------------"
+ warn "Redmine is checking your changes for correctness..."
+ warn "Authenticated as #{user.name} (#{role.name} in #{project.name})"
+
+ error_found = false
+
+ warn "Changes:"
+ STDIN.each_line do |line|
+ old_rev, new_rev, branch = line.split(" ")
+
+ if new_rev == git.class::EMPTY_COMMIT
+ new_rev_type = "delete"
+ else
+ new_rev_type = git.get_object_type(new_rev)
+ end
+ revisions = nil
+ if new_rev_type == "commit"
+ revisions = git.revisions("", old_rev, new_rev)
+ end
+ warn " Ref: #{branch} type: #{new_rev_type}"
+
+ errors = []
+ # TODO: make checks configurable
+ RefCheck.get_checks.each do |check|
+ result = check.check(old_rev, new_rev, new_rev_type, branch, user, role, project, git)
+ if result.kind_of?(Array)
+ errors += result
+ else
+ errors << result
+ end
+ end
+ errors.each do |error|
+ warn " Error: #{error}"
+ end
+ error_found = true if !errors.empty?
+
+ if revisions != nil
+ revisions.each do |revision|
+ warn " Commit: #{revision.identifier}"
+
+ errors = []
+ CommitCheck.get_checks.each do |check|
+ result = check.check(revision, branch, user, role, project, git)
+ if result.kind_of?(Array)
+ errors += result
+ else
+ errors << result
+ end
+ end
+ errors.each do |error|
+ warn " Error: #{error}"
+ end
+ error_found = true if !errors.empty?
+ end
+ end
+ end
+ end
+
+ if error_found
+ if !role.allowed_to?(:manage_repository)
+ warn "Some commits were rejected. Correct them and try the push again."
+ warn "-------------------------------------------------------------"
+ warn ""
+ exit 1
+ else
+ warn "You are repository manager. Checks results are ignored. "
+ warn "-------------------------------------------------------------"
+ exit 0
+ end
+ end
+ warn "Changes look OK"
+ warn "-------------------------------------------------------------"
+ exit 0
+ end
+
+end
+
diff --git a/app/models/user.rb b/app/models/user.rb
index 0005c85..897ee74 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -65,6 +65,38 @@ class User < ActiveRecord::Base
validates_length_of :password, :minimum => 4, :allow_nil => true
validates_confirmation_of :password, :allow_nil => true
+ def ssh_key
+ return nil if ! Setting.serve_git_repositories?
+ load_ssh_key if @ssh_key_entry == nil
+ return @ssh_key_entry.key
+ end
+
+ def ssh_key_type
+ return nil if ! Setting.serve_git_repositories?
+ load_ssh_key if @ssh_key_entry == nil
+ return @ssh_key_entry.type
+ end
+
+ def ssh_key=(key)
+ return if ! Setting.serve_git_repositories?
+ new_ssh_key if @ssh_key_entry == nil
+ key = key.strip if key != nil
+ @ssh_key_entry.key = key
+ end
+
+ def ssh_key_type=(key_type)
+ return if ! Setting.serve_git_repositories?
+ new_ssh_key if @ssh_key_entry == nil
+ @ssh_key_entry.type = key_type
+ end
+
+ def validate
+ return if !Setting.serve_git_repositories?
+ return if @ssh_key_entry == nil
+
+ errors.add(:ssh_key, "is not valid") if !@ssh_key_entry.validate
+ end
+
def before_create
self.mail_notification = false
true
@@ -74,6 +106,15 @@ class User < ActiveRecord::Base
# update hashed_password if password was set
self.hashed_password = User.hash_password(self.password) if self.password
end
+
+ def after_save
+ return if ! Setting.serve_git_repositories?
+ return if @ssh_key_entry == nil
+
+ @ssh_key_entry.options = GitManager.get_authorized_keys_options_for_login(login)
+
+ @ssh_key_entry.save
+ end
def reload(*args)
@name = nil
@@ -271,6 +312,16 @@ private
def self.hash_password(clear_password)
Digest::SHA1.hexdigest(clear_password || "")
end
+
+ def new_ssh_key
+ @ssh_key_entry = AuthorizedKeysEntry.new
+ @ssh_key_entry.identifier = login
+ end
+
+ def load_ssh_key
+ @ssh_key_entry = AuthorizedKeysEntry.find_by_identifier(login)
+ new_ssh_key if @ssh_key_entry == nil
+ end
end
class AnonymousUser < User
diff --git a/app/views/my/account.rhtml b/app/views/my/account.rhtml
index ef5b222..9f46176 100644
--- a/app/views/my/account.rhtml
+++ b/app/views/my/account.rhtml
@@ -16,6 +16,11 @@
<%= f.text_field :mail, :required => true %>
<%= f.select :language, lang_options_for_select %>
+<% if Setting.serve_git_repositories? %>
+ <%= f.select :ssh_key_type, AuthorizedKeysEntry::KEY_TYPES %>
+ <%= f.text_area :ssh_key, :rows => 10 %>
+<% end %>
+
<% @user.custom_field_values.select(&:editable?).each do |value| %>
<%= custom_field_tag_with_label :user, value %>
<% end %>
diff --git a/app/views/settings/_repositories.rhtml b/app/views/settings/_repositories.rhtml
index a8c9244..189648f 100644
--- a/app/views/settings/_repositories.rhtml
+++ b/app/views/settings/_repositories.rhtml
@@ -14,6 +14,14 @@
<%= hidden_field_tag 'settings[enabled_scm][]', '' %>
+<% # TODO: Should be disabled when Git SCM is not enabled
+%>
+
+<%= check_box_tag 'settings[serve_git_repositories]', 1, Setting.serve_git_repositories? %>
+<%= hidden_field_tag 'settings[serve_git_repositories]', 0 %>
+
+
+
<%= text_field_tag 'settings[repositories_encodings]', Setting.repositories_encodings, :size => 60 %>
<%= l(:text_comma_separated) %>
diff --git a/config/settings.yml b/config/settings.yml
index 5006445..a3af36c 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -77,6 +77,8 @@ autofetch_changesets:
default: 1
sys_api_enabled:
default: 0
+serve_git_repositories:
+ default: 0
commit_ref_keywords:
default: 'refs,references,IssueID'
commit_fix_keywords:
diff --git a/lang/bg.yml b/lang/bg.yml
index bb80f17..91d1d5c 100644
--- a/lang/bg.yml
+++ b/lang/bg.yml
@@ -92,6 +92,8 @@ field_is_required: Задължително
field_firstname: Име
field_lastname: Фамилия
field_mail: Email
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Файл
field_filesize: Големина
field_downloads: Downloads
@@ -181,6 +183,7 @@ setting_wiki_compression: Wiki компресиране на историята
setting_feeds_limit: Лимит на Feeds
setting_autofetch_changesets: Автоматично обработване на ревизиите
setting_sys_api_enabled: Разрешаване на WS за управление
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Отбелязващи ключови думи
setting_commit_fix_keywords: Приключващи ключови думи
setting_autologin: Автоматичен вход
diff --git a/lang/ca.yml b/lang/ca.yml
index b0bbee2..8da15be 100644
--- a/lang/ca.yml
+++ b/lang/ca.yml
@@ -106,6 +106,8 @@ field_is_required: Necessari
field_firstname: Nom
field_lastname: Cognom
field_mail: Correu electrònic
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Fitxer
field_filesize: Mida
field_downloads: Baixades
@@ -202,6 +204,7 @@ setting_feeds_limit: Límit de contingut del canal
setting_default_projects_public: Els projectes nous són públics per defecte
setting_autofetch_changesets: Omple automàticament les publicacions
setting_sys_api_enabled: Habilita el WS per a la gestió del dipòsit
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Paraules claus per a la referència
setting_commit_fix_keywords: Paraules claus per a la correcció
setting_autologin: Entrada automàtica
diff --git a/lang/cs.yml b/lang/cs.yml
index ab06248..e59d069 100644
--- a/lang/cs.yml
+++ b/lang/cs.yml
@@ -106,6 +106,8 @@ field_is_required: Povinné pole
field_firstname: Jméno
field_lastname: Příjmení
field_mail: Email
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Soubor
field_filesize: Velikost
field_downloads: Staženo
@@ -201,6 +203,7 @@ setting_feeds_limit: Feed content limit
setting_default_projects_public: Nové projekty nastavovat jako veřejné
setting_autofetch_changesets: Autofetch commits
setting_sys_api_enabled: Povolit WS pro správu repozitory
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Klíčová slova pro odkazy
setting_commit_fix_keywords: Klíčová slova pro uzavření
setting_autologin: Automatické přihlašování
diff --git a/lang/da.yml b/lang/da.yml
index b0df544..3545b64 100644
--- a/lang/da.yml
+++ b/lang/da.yml
@@ -106,6 +106,8 @@ field_is_required: Skal udfyldes
field_firstname: Fornavn
field_lastname: Efternavn
field_mail: E-mail
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Fil
field_filesize: Størrelse
field_downloads: Downloads
@@ -202,6 +204,7 @@ setting_feeds_limit: Antal objekter i feeds
setting_default_projects_public: Nye projekter er som standard offentlige
setting_autofetch_changesets: Hent automatisk commits
setting_sys_api_enabled: Aktiver webservice til versionsstyring
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Nøgleord for sagsreferencer
setting_commit_fix_keywords: Nøgleord for lukning af sager
setting_autologin: Autologin
diff --git a/lang/de.yml b/lang/de.yml
index e8a9d93..7600316 100644
--- a/lang/de.yml
+++ b/lang/de.yml
@@ -106,6 +106,8 @@ field_is_required: Erforderlich
field_firstname: Vorname
field_lastname: Nachname
field_mail: E-Mail
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Datei
field_filesize: Größe
field_downloads: Downloads
@@ -203,6 +205,7 @@ setting_feeds_limit: Max. Anzahl Einträge pro Atom-Feed
setting_default_projects_public: Neue Projekte sind standardmäßig öffentlich
setting_autofetch_changesets: Changesets automatisch abrufen
setting_sys_api_enabled: Webservice zur Verwaltung der Projektarchive benutzen
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Schlüsselwörter (Beziehungen)
setting_commit_fix_keywords: Schlüsselwörter (Status)
setting_autologin: Automatische Anmeldung
diff --git a/lang/en.yml b/lang/en.yml
index 36e4546..b711a74 100644
--- a/lang/en.yml
+++ b/lang/en.yml
@@ -108,6 +108,8 @@ field_is_required: Required
field_firstname: Firstname
field_lastname: Lastname
field_mail: Email
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: File
field_filesize: Size
field_downloads: Downloads
@@ -206,6 +208,7 @@ setting_feeds_limit: Feed content limit
setting_default_projects_public: New projects are public by default
setting_autofetch_changesets: Autofetch commits
setting_sys_api_enabled: Enable WS for repository management
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Referencing keywords
setting_commit_fix_keywords: Fixing keywords
setting_autologin: Autologin
diff --git a/lang/es.yml b/lang/es.yml
index c6e6234..52211be 100644
--- a/lang/es.yml
+++ b/lang/es.yml
@@ -150,6 +150,8 @@ field_last_login_on: Última conexión
field_lastname: Apellido
field_login: Identificador
field_mail: Correo electrónico
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_mail_notification: Notificaciones por correo
field_max_length: Longitud máxima
field_min_length: Longitud mínima
@@ -627,6 +629,7 @@ setting_repositories_encodings: Codificaciones del repositorio
setting_self_registration: Registro permitido
setting_sequential_project_identifiers: Generar identificadores de proyecto
setting_sys_api_enabled: Habilitar SW para la gestión del repositorio
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_text_formatting: Formato de texto
setting_time_format: Formato de hora
setting_user_format: Formato de nombre de usuario
diff --git a/lang/fi.yml b/lang/fi.yml
index dce1192..67137a9 100644
--- a/lang/fi.yml
+++ b/lang/fi.yml
@@ -101,6 +101,8 @@ field_is_required: Vaaditaan
field_firstname: Etunimi
field_lastname: Sukunimi
field_mail: Sähköposti
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Tiedosto
field_filesize: Koko
field_downloads: Latausta
@@ -194,6 +196,7 @@ setting_wiki_compression: Wiki historian pakkaus
setting_feeds_limit: Syötteen sisällön raja
setting_autofetch_changesets: Automaattisten muutosjoukkojen haku
setting_sys_api_enabled: Salli WS tietovaraston hallintaan
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Viittaavat hakusanat
setting_commit_fix_keywords: Korjaavat hakusanat
setting_autologin: Automaatinen kirjautuminen
diff --git a/lang/fr.yml b/lang/fr.yml
index 84f18e8..381c717 100644
--- a/lang/fr.yml
+++ b/lang/fr.yml
@@ -108,6 +108,8 @@ field_is_required: Obligatoire
field_firstname: Prénom
field_lastname: Nom
field_mail: Email
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Fichier
field_filesize: Taille
field_downloads: Téléchargements
@@ -206,6 +208,7 @@ setting_feeds_limit: Limite du contenu des flux RSS
setting_default_projects_public: Définir les nouveaux projects comme publics par défaut
setting_autofetch_changesets: Récupération auto. des commits
setting_sys_api_enabled: Activer les WS pour la gestion des dépôts
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Mot-clés de référencement
setting_commit_fix_keywords: Mot-clés de résolution
setting_autologin: Autologin
diff --git a/lang/he.yml b/lang/he.yml
index 2fae88e..e3bd99e 100644
--- a/lang/he.yml
+++ b/lang/he.yml
@@ -94,6 +94,8 @@ field_is_required: נדרש
field_firstname: שם פרטי
field_lastname: שם משפחה
field_mail: דוא"ל
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: קובץ
field_filesize: גודל
field_downloads: הורדות
@@ -184,6 +186,7 @@ setting_wiki_compression: כיווץ היסטורית WIKI
setting_feeds_limit: גבול תוכן הזנות
setting_autofetch_changesets: משיכה אוטומתי של עידכונים
setting_sys_api_enabled: אפשר WS לניהול המאגר
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: מילות מפתח מקשרות
setting_commit_fix_keywords: מילות מפתח מתקנות
setting_autologin: חיבור אוטומטי
diff --git a/lang/hu.yml b/lang/hu.yml
index eb1670c..24fdc7a 100644
--- a/lang/hu.yml
+++ b/lang/hu.yml
@@ -103,6 +103,8 @@ field_is_required: Kötelező
field_firstname: Keresztnév
field_lastname: Vezetéknév
field_mail: E-mail
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Fájl
field_filesize: Méret
field_downloads: Letöltések
@@ -198,6 +200,7 @@ setting_feeds_limit: RSS tartalom korlát
setting_default_projects_public: Az új projektek alapértelmezés szerint nyilvánosak
setting_autofetch_changesets: Commitok automatikus lehúzása
setting_sys_api_enabled: WS engedélyezése a tárolók kezeléséhez
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Hivatkozó kulcsszavak
setting_commit_fix_keywords: Javítások kulcsszavai
setting_autologin: Automatikus bejelentkezés
diff --git a/lang/it.yml b/lang/it.yml
index a5d6456..07a4700 100644
--- a/lang/it.yml
+++ b/lang/it.yml
@@ -92,6 +92,8 @@ field_is_required: Richiesto
field_firstname: Nome
field_lastname: Cognome
field_mail: Email
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: File
field_filesize: Dimensione
field_downloads: Download
@@ -181,6 +183,7 @@ setting_wiki_compression: Comprimi cronologia wiki
setting_feeds_limit: Limite contenuti del feed
setting_autofetch_changesets: Acquisisci automaticamente le commit
setting_sys_api_enabled: Abilita WS per la gestione del repository
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Referencing keywords
setting_commit_fix_keywords: Fixing keywords
setting_autologin: Login automatico
diff --git a/lang/ja.yml b/lang/ja.yml
index f6b696f..334bfcc 100644
--- a/lang/ja.yml
+++ b/lang/ja.yml
@@ -93,6 +93,8 @@ field_is_required: 必須
field_firstname: 名前
field_lastname: 苗字
field_mail: メールアドレス
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: ファイル
field_filesize: サイズ
field_downloads: ダウンロード
@@ -182,6 +184,7 @@ setting_wiki_compression: Wiki履歴を圧縮する
setting_feeds_limit: フィード内容の上限
setting_autofetch_changesets: コミットを自動取得する
setting_sys_api_enabled: リポジトリ管理用のWeb Serviceを有効にする
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: 参照用キーワード
setting_commit_fix_keywords: 修正用キーワード
setting_autologin: 自動ログイン
diff --git a/lang/ko.yml b/lang/ko.yml
index 8da9ac6..bf66c08 100644
--- a/lang/ko.yml
+++ b/lang/ko.yml
@@ -94,6 +94,8 @@ field_is_required: 필수
field_firstname: 이름
field_lastname: 성
field_mail: 메일
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: 파일
field_filesize: 크기
field_downloads: 다운로드
@@ -184,6 +186,7 @@ setting_wiki_compression: 위키 이력 압축
setting_feeds_limit: 내용 피드(RSS Feed) 제한 개수
setting_autofetch_changesets: 커밋된 변경묶음을 자동으로 가져오기
setting_sys_api_enabled: 저장소 관리자에 WS 를 허용
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: 일감 참조에 사용할 키워드들
setting_commit_fix_keywords: 일감 해결에 사용할 키워드들
setting_autologin: 자동 로그인
diff --git a/lang/lt.yml b/lang/lt.yml
index 9bd57f8..7fc5f52 100644
--- a/lang/lt.yml
+++ b/lang/lt.yml
@@ -106,6 +106,8 @@ field_is_required: Reikalaujama
field_firstname: Vardas
field_lastname: Pavardė
field_mail: Email
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Byla
field_filesize: Dydis
field_downloads: Atsiuntimai
@@ -203,6 +205,7 @@ setting_feeds_limit: Perdavimo turinio riba
setting_default_projects_public: Naujas projektas viešas pagal nutylėjimą
setting_autofetch_changesets: Automatinis pakeitimų siuntimas
setting_sys_api_enabled: Įgalinkite WS sandėlio vadybai
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Nurodymo reikšminiai žodžiai
setting_commit_fix_keywords: Fiksavimo reikšminiai žodžiai
setting_autologin: Autoregistracija
diff --git a/lang/nl.yml b/lang/nl.yml
index e223d13..ce68e59 100644
--- a/lang/nl.yml
+++ b/lang/nl.yml
@@ -92,6 +92,8 @@ field_is_required: Verplicht
field_firstname: Voornaam
field_lastname: Achternaam
field_mail: E-mail
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Bestand
field_filesize: Grootte
field_downloads: Downloads
@@ -181,6 +183,7 @@ setting_wiki_compression: Wikigeschiedenis comprimeren
setting_feeds_limit: Feedinhoud limiet
setting_autofetch_changesets: Haal commits automatisch op
setting_sys_api_enabled: Gebruik WS voor repository beheer
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Refererende trefwoorden
setting_commit_fix_keywords: Gefixeerde trefwoorden
setting_autologin: Automatisch inloggen
diff --git a/lang/no.yml b/lang/no.yml
index 0e84877..4742a63 100644
--- a/lang/no.yml
+++ b/lang/no.yml
@@ -105,6 +105,8 @@ field_is_required: Kreves
field_firstname: Fornavn
field_lastname: Etternavn
field_mail: E-post
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Fil
field_filesize: Størrelse
field_downloads: Nedlastinger
@@ -200,6 +202,7 @@ setting_feeds_limit: Innholdsgrense for Feed
setting_default_projects_public: Nye prosjekter er offentlige som standard
setting_autofetch_changesets: Autohenting av innsendinger
setting_sys_api_enabled: Aktiver webservice for depot-administrasjon
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Nøkkelord for referanse
setting_commit_fix_keywords: Nøkkelord for retting
setting_autologin: Autoinnlogging
diff --git a/lang/pl.yml b/lang/pl.yml
index 508216b..7c95dd5 100644
--- a/lang/pl.yml
+++ b/lang/pl.yml
@@ -161,6 +161,8 @@ field_last_login_on: Ostatnie połączenie
field_lastname: Nazwisko
field_login: Login
field_mail: Email
+field_ssh_key: Klucz publiczny SSH
+field_ssh_key_type: Typ klucza SSH
field_mail_notification: Powiadomienia Email
field_max_length: Maksymalna długość
field_min_length: Minimalna długość
@@ -657,6 +659,7 @@ setting_repositories_encodings: Kodowanie repozytoriów
setting_self_registration: Własna rejestracja umożliwiona
setting_sequential_project_identifiers: Generuj sekwencyjne identyfikatory projektów
setting_sys_api_enabled: Włączenie WS do zarządzania repozytorium
+setting_serve_git_repositories: Udostępnij repozytoria GIT poprzez konto SSH Redmine
setting_text_formatting: Formatowanie tekstu
setting_time_format: Format czasu
setting_user_format: Personalny format wyświetlania
diff --git a/lang/pt-br.yml b/lang/pt-br.yml
index d25260f..89de0f0 100644
--- a/lang/pt-br.yml
+++ b/lang/pt-br.yml
@@ -105,6 +105,8 @@ field_is_required: Obrigatório
field_firstname: Nome
field_lastname: Sobrenome
field_mail: Email
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Arquivo
field_filesize: Tamanho
field_downloads: Downloads
@@ -201,6 +203,7 @@ setting_feeds_limit: Limite do Feed
setting_default_projects_public: Novos projetos são públicos por padrão
setting_autofetch_changesets: Auto-obter commits
setting_sys_api_enabled: Ativa WS para gerenciamento do repositório
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Palavras de referência
setting_commit_fix_keywords: Palavras de fechamento
setting_autologin: Auto-login
diff --git a/lang/pt.yml b/lang/pt.yml
index 980a6e5..865f18a 100644
--- a/lang/pt.yml
+++ b/lang/pt.yml
@@ -107,6 +107,8 @@ field_is_required: Obrigatório
field_firstname: Nome
field_lastname: Apelido
field_mail: E-mail
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Ficheiro
field_filesize: Tamanho
field_downloads: Downloads
@@ -203,6 +205,7 @@ setting_feeds_limit: Limite de conteúdo do feed
setting_default_projects_public: Projectos novos são públicos por omissão
setting_autofetch_changesets: Buscar automaticamente commits
setting_sys_api_enabled: Activar Web Service para gestão do repositório
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Palavras-chave de referência
setting_commit_fix_keywords: Palavras-chave de fecho
setting_autologin: Login automático
diff --git a/lang/ro.yml b/lang/ro.yml
index bf1f117..bcdfa9e 100644
--- a/lang/ro.yml
+++ b/lang/ro.yml
@@ -92,6 +92,8 @@ field_is_required: Obligatoriu
field_firstname: Nume
field_lastname: Prenume
field_mail: Email
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Fisier
field_filesize: Marimea fisierului
field_downloads: Download
@@ -181,6 +183,7 @@ setting_wiki_compression: Compresie istoric wiki
setting_feeds_limit: Limita continut feed
setting_autofetch_changesets: Autofetch commits
setting_sys_api_enabled: Setare WS pentru managementul stocului (repository)
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Cuvinte cheie de referinta
setting_commit_fix_keywords: Cuvinte cheie de rezolvare
setting_autologin: Autentificare automata
diff --git a/lang/ru.yml b/lang/ru.yml
index 62ea8e3..28ba5b3 100644
--- a/lang/ru.yml
+++ b/lang/ru.yml
@@ -165,6 +165,8 @@ field_last_login_on: Последнее подключение
field_lastname: Фамилия
field_login: Пользователь
field_mail: Email
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_mail_notification: Уведомления по email
field_max_length: Максимальная длина
field_min_length: Минимальная длина
@@ -673,6 +675,7 @@ setting_repositories_encodings: Кодировки хранилища
setting_self_registration: Возможна саморегистрация
setting_sequential_project_identifiers: Генерировать последовательные идентификаторы проектов
setting_sys_api_enabled: Разрешить WS для управления хранилищем
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_text_formatting: Форматирование текста
setting_time_format: Формат времени
setting_user_format: Формат отображения имени
diff --git a/lang/sk.yml b/lang/sk.yml
index 3f7c7dc..727cd13 100644
--- a/lang/sk.yml
+++ b/lang/sk.yml
@@ -106,6 +106,8 @@ field_is_required: Povinné pole
field_firstname: Meno
field_lastname: Priezvisko
field_mail: Email
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Súbor
field_filesize: Veľkosť
field_downloads: Stiahnuté
@@ -201,6 +203,7 @@ setting_feeds_limit: Limit zobrazených položiek (Atom feed)
setting_default_projects_public: Nové projekty nastavovať ako verejné
setting_autofetch_changesets: Automatický prenos zmien
setting_sys_api_enabled: Povolit WS pre správu repozitory
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Klúčové slová pre odkazy
setting_commit_fix_keywords: Klúčové slová pre uzavretie
setting_autologin: Automatické prihlasovanie
diff --git a/lang/sr.yml b/lang/sr.yml
index 455173d..b1758db 100644
--- a/lang/sr.yml
+++ b/lang/sr.yml
@@ -96,6 +96,8 @@ field_is_required: Zahtevano
field_firstname: Ime
field_lastname: Prezime
field_mail: Email
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Fajl
field_filesize: Veličina
field_downloads: Preuzimanja
@@ -186,6 +188,7 @@ setting_wiki_compression: Kompresija wiki history-a
setting_feeds_limit: Feed content limit
setting_autofetch_changesets: Autofetch commits
setting_sys_api_enabled: Ukljuci WS za menadžment spremišta
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Referentne ključne reči
setting_commit_fix_keywords: Fiksne ključne reči
setting_autologin: Automatsko prijavljivanje
diff --git a/lang/sv.yml b/lang/sv.yml
index 4a85f15..a2cef45 100644
--- a/lang/sv.yml
+++ b/lang/sv.yml
@@ -106,6 +106,8 @@ field_is_required: Obligatorisk
field_firstname: Förnamn
field_lastname: Efternamn
field_mail: Mail
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Fil
field_filesize: Storlek
field_downloads: Nerladdningar
@@ -203,6 +205,7 @@ setting_feeds_limit: Innehållsgräns för Feed
setting_default_projects_public: Nya projekt är publika som standard
setting_autofetch_changesets: Automatisk hämtning av commits
setting_sys_api_enabled: Aktivera WS för repository-hantering
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Referens-nyckelord
setting_commit_fix_keywords: Fix-nyckelord
setting_autologin: Automatisk inloggning
diff --git a/lang/th.yml b/lang/th.yml
index 8d39fb5..6a119e2 100644
--- a/lang/th.yml
+++ b/lang/th.yml
@@ -103,6 +103,8 @@ field_is_required: ต้องใส่
field_firstname: ชื่อ
field_lastname: นามสกุล
field_mail: อีเมล์
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: แฟ้ม
field_filesize: ขนาด
field_downloads: ดาวน์โหลด
@@ -198,6 +200,7 @@ setting_feeds_limit: จำนวน Feed
setting_default_projects_public: โครงการใหม่มีค่าเริ่มต้นเป็น สาธารณะ
setting_autofetch_changesets: ดึง commits อัตโนมัติ
setting_sys_api_enabled: เปิดใช้ WS สำหรับการจัดการที่เก็บต้นฉบับ
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: คำสำคัญ Referencing
setting_commit_fix_keywords: คำสำคัญ Fixing
setting_autologin: เข้าระบบอัตโนมัติ
diff --git a/lang/tr.yml b/lang/tr.yml
index 8065592..3846ca7 100644
--- a/lang/tr.yml
+++ b/lang/tr.yml
@@ -102,6 +102,8 @@ field_is_required: Gerekli
field_firstname: Ad
field_lastname: Soyad
field_mail: E-Posta
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Dosya
field_filesize: Boyut
field_downloads: İndirilenler
@@ -197,6 +199,7 @@ setting_feeds_limit: Haber yayını içerik limiti
setting_default_projects_public: Yeni projeler varsayılan olarak herkese açık
setting_autofetch_changesets: Otomatik gönderi al
setting_sys_api_enabled: Depo yönetimi için WS'yi etkinleştir
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Başvuru Kelimeleri
setting_commit_fix_keywords: Sabitleme kelimeleri
setting_autologin: Otomatik Giriş
diff --git a/lang/uk.yml b/lang/uk.yml
index 75c6639..6d3a79a 100644
--- a/lang/uk.yml
+++ b/lang/uk.yml
@@ -97,6 +97,8 @@ field_is_required: Необхідно
field_firstname: Ім'я
field_lastname: Прізвище
field_mail: Ел. пошта
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Файл
field_filesize: Розмір
field_downloads: Завантаження
@@ -189,6 +191,7 @@ setting_wiki_compression: Стиснення історії Wiki
setting_feeds_limit: Обмеження змісту подачі
setting_autofetch_changesets: Автоматично доставати доповнення
setting_sys_api_enabled: Дозволити WS для управління репозиторієм
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Ключові слова для посилання
setting_commit_fix_keywords: Призначення ключових слів
setting_autologin: Автоматичний вхід
diff --git a/lang/vn.yml b/lang/vn.yml
index 0bff54b..9bbe1f6 100644
--- a/lang/vn.yml
+++ b/lang/vn.yml
@@ -106,6 +106,8 @@ field_is_required: Bắt buộc
field_firstname: Tên lót + Tên
field_lastname: Họ
field_mail: Email
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: Tập tin
field_filesize: Cỡ
field_downloads: Tải về
@@ -202,6 +204,7 @@ setting_feeds_limit: Giới hạn nội dung của feed
setting_default_projects_public: Dự án mặc định là công cộng
setting_autofetch_changesets: Autofetch commits
setting_sys_api_enabled: Enable WS for repository management
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: Từ khóa tham khảo
setting_commit_fix_keywords: Từ khóa chỉ vấn đề đã giải quyết
setting_autologin: Tự động đăng nhập
diff --git a/lang/zh-tw.yml b/lang/zh-tw.yml
index 028457e..cc65dfd 100644
--- a/lang/zh-tw.yml
+++ b/lang/zh-tw.yml
@@ -106,6 +106,8 @@ field_is_required: 必填
field_firstname: 名字
field_lastname: 姓氏
field_mail: 電子郵件
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: 檔案名稱
field_filesize: 大小
field_downloads: 下載次數
@@ -203,6 +205,7 @@ setting_feeds_limit: RSS 新聞限制
setting_autofetch_changesets: 自動取得送交版次
setting_default_projects_public: 新建立之專案預設為「公開」
setting_sys_api_enabled: 啟用管理版本庫之網頁服務 (Web Service)
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: 送交用於參照項目之關鍵字
setting_commit_fix_keywords: 送交用於修正項目之關鍵字
setting_autologin: 自動登入
diff --git a/lang/zh.yml b/lang/zh.yml
index d259639..850923d 100644
--- a/lang/zh.yml
+++ b/lang/zh.yml
@@ -106,6 +106,8 @@ field_is_required: 必填
field_firstname: 名字
field_lastname: 姓氏
field_mail: 邮件地址
+field_ssh_key: SSH Public Key
+field_ssh_key_type: SSH Public Key Type
field_filename: 文件
field_filesize: 大小
field_downloads: 下载次数
@@ -203,6 +205,7 @@ setting_feeds_limit: RSS Feed内容条数限制
setting_default_projects_public: 新建项目默认为公开项目
setting_autofetch_changesets: 自动获取程序变更
setting_sys_api_enabled: 启用用于版本库管理的Web Service
+setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account
setting_commit_ref_keywords: 用于引用问题的关键字
setting_commit_fix_keywords: 用于解决问题的关键字
setting_autologin: 自动登录
diff --git a/lib/redmine/scm/adapters/git_adapter.rb b/lib/redmine/scm/adapters/git_adapter.rb
index a9e1dda..77dd6ca 100644
--- a/lib/redmine/scm/adapters/git_adapter.rb
+++ b/lib/redmine/scm/adapters/git_adapter.rb
@@ -24,6 +24,25 @@ module Redmine
# Git executable name
GIT_BIN = "git"
+ EMPTY_COMMIT = "0000000000000000000000000000000000000000"
+
+ def init(description)
+ FileUtils.mkdir_p "#{url}"
+ cmd = "#{GIT_BIN} --git-dir #{target('')} init --template=#{RAILS_ROOT}/lib/redmine/scm/adapters/git_templates --bare"
+ shellout(cmd)
+ Dir["#{url}/**/*"].each do |file|
+ if File.file?(file)
+ File.open(file, "r") do |source|
+ File.open(file + ".new", "w", File.stat(file).mode) do |dest|
+ source.each_line do |line|
+ dest << line.gsub('#{description}', description).gsub('#{RAILS_ROOT}', RAILS_ROOT).gsub('#{RAILS_ENV}', ENV["RAILS_ENV"])
+ end
+ end
+ end
+ FileUtils.mv file + ".new", file
+ end
+ end
+ end
# Get the revision of a particuliar file
def get_rev (rev,path)
@@ -61,7 +80,7 @@ module Redmine
elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/
key = $1
value = $2
- if key == "Author"
+ if key == "Commit"
changeset[:author] = value
elsif key == "CommitDate"
changeset[:date] = value
@@ -140,7 +159,7 @@ module Redmine
cmd = "#{GIT_BIN} --git-dir #{target('')} log --raw --date=iso --pretty=fuller"
cmd << " --reverse" if options[:reverse]
cmd << " -n #{options[:limit].to_i} " if (!options.nil?) && options[:limit]
- cmd << " #{shell_quote(identifier_from + '..')} " if identifier_from
+ cmd << " #{shell_quote(identifier_from + '..')} " if identifier_from and identifier_from != EMPTY_COMMIT
cmd << " #{shell_quote identifier_to} " if identifier_to
shellout(cmd) do |io|
files=[]
@@ -262,6 +281,21 @@ module Redmine
return nil if $? && $?.exitstatus != 0
cat
end
+
+ def get_object_type(object)
+ cmd = "#{GIT_BIN} --git-dir #{target('')} cat-file -t #{object}"
+ type = ""
+ GitAdapter.shellout(cmd) do |io|
+ return io.read.strip
+ end
+ end
+
+ def merge_base(old_rev, new_rev)
+ cmd = "#{GIT_BIN} --git-dir #{target('')} merge-base #{old_rev} #{new_rev}"
+ GitAdapter.shellout(cmd) do |io|
+ return io.read.strip
+ end
+ end
end
end
end
diff --git a/lib/redmine/scm/adapters/git_templates/description b/lib/redmine/scm/adapters/git_templates/description
new file mode 100644
index 0000000..b0f81f9
--- /dev/null
+++ b/lib/redmine/scm/adapters/git_templates/description
@@ -0,0 +1 @@
+#{description}
\ No newline at end of file
diff --git a/lib/redmine/scm/adapters/git_templates/hooks/post-receive b/lib/redmine/scm/adapters/git_templates/hooks/post-receive
new file mode 100755
index 0000000..d9a4b23
--- /dev/null
+++ b/lib/redmine/scm/adapters/git_templates/hooks/post-receive
@@ -0,0 +1 @@
+ruby #{RAILS_ROOT}/script/runner Repository.fetch_changesets -e #{RAILS_ENV}
\ No newline at end of file
diff --git a/lib/redmine/scm/adapters/git_templates/hooks/pre-receive b/lib/redmine/scm/adapters/git_templates/hooks/pre-receive
new file mode 100755
index 0000000..6956009
--- /dev/null
+++ b/lib/redmine/scm/adapters/git_templates/hooks/pre-receive
@@ -0,0 +1 @@
+ruby #{RAILS_ROOT}/script/runner GitManager.check_commits -e #{RAILS_ENV}
\ No newline at end of file
diff --git a/lib/redmine/scm/adapters/git_templates/info/exclude b/lib/redmine/scm/adapters/git_templates/info/exclude
new file mode 100644
index 0000000..2c87b72
--- /dev/null
+++ b/lib/redmine/scm/adapters/git_templates/info/exclude
@@ -0,0 +1,6 @@
+# git-ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css
index a4251af..7a47bc2 100644
--- a/public/stylesheets/application.css
+++ b/public/stylesheets/application.css
@@ -243,7 +243,7 @@ ul.properties li span {font-style:italic;}
.total-hours span.hours-int { font-size: 120%; }
.autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em;}
-#user_firstname, #user_lastname, #user_mail, #my_account_form select { width: 90%; }
+#user_firstname, #user_lastname, #user_mail, #user_ssh_key, #my_account_form select { width: 90%; }
.pagination {font-size: 90%}
p.pagination {margin-top:8px;}
diff --git a/test/unit/authorized_keys_entry_test.rb b/test/unit/authorized_keys_entry_test.rb
new file mode 100644
index 0000000..8297091
--- /dev/null
+++ b/test/unit/authorized_keys_entry_test.rb
@@ -0,0 +1,52 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class AuthorizedKeysEntryTest < Test::Unit::TestCase
+ def setup
+ AuthorizedKeysEntry.authorized_keys_filename = File.dirname(__FILE__) + '/../../tmp/test/authorized_keys'
+ e = AuthorizedKeysEntry.new
+ e.identifier = 'admin'
+ e.key = 'ssh-dss AAAAB3NzaC1kc3MAAACBALDC8OcQ6OfKUMUEy4xeARLmIGMB6IH4GpSQPTAIpMkRvmyIc99NiRVl1TSlASQQephL0XSH6R2vP+XEzY/tSGIzGugyIkErV2AZ8JL/BwZI1euVE3tjBDoL+6xpdqMCgUwWh2vrL/Qe8DOoil2nTtnHJBTz0wpkYHe+DDKda7ttAAAAFQDZ13q/5MPUvT9AjyeUvWZe2lFC/QAAAIAikSLreTOTeDjKkSrRrhV2RByfT5wyat/0rulzU9wp6KOre9XtH5VNhlGAz0Sj5MgoxUJuHrlIjSNoxnx9S3h1TKHaZ2Ov5xKc4AtfnmH5LwyXdT9QzbsB1NL4xDAjBPHGQCEypxRVz3zLp9gt6gCzBszbS563pIfUmqikaI9iwgAAAIA8/GFBKSn0E4kFsWRSiUeRyEeXBKPbghMcFePOp/v23xZrNGTso6AKCXPMvLcVZbpiZY4wIL0nftYdQNcu3ubDIKWVKIfdisWpKxgejbKQZMQWQRPZZVxT/UtkPuF4mjJyynbdc0Fn78gxfySnmpvfoGhVMJYd+Xjjagr4sU8Hpw== sadf@sdfsd.kdl.sdfoij'
+ e.type = 'ssh-dss'
+ e.options = [ 'command="test"' ]
+
+ e.save
+ end
+
+ def test_read
+ e = AuthorizedKeysEntry.find_by_identifier('admin')
+ assert e.key =~ /\AAAAAB3NzaC1kc3/
+ assert e.type == 'ssh-dss'
+ assert e.options.kind_of?(Array)
+ end
+
+ def test_verify_wrong
+ e = AuthorizedKeysEntry.new
+ e.identifier = 'a'
+ e.key = 'b'
+ e.type = 'ssh-dss'
+ e.options = []
+ assert !e.validate
+ end
+
+ def test_replace
+ e = AuthorizedKeysEntry.new
+ e.identifier = 'admin'
+ e.key = 'AAAAB3NzaC1yc2EAAAABIwAAAEEAsUX/IBulIRL9y49x5AEuXad+nDmGTY1Ad+c2YFJPMQgJWtY+cDUrUNDBxyvYADDzFldeNMDAwAeBFSfQL8h3ew=='
+ e.type = 'ssh-rsa'
+ e.save
+
+ assert AuthorizedKeysEntry.find_by_identifier('admin').type = 'ssh-rsa'
+ end
+
+ def test_delete
+ e = AuthorizedKeysEntry.new
+ e.identifier = 'admin'
+ e.key = ''
+ e.type = ''
+ e.options = []
+ e.save
+
+ assert AuthorizedKeysEntry.find_by_identifier('admin') == nil
+ end
+end
+
--
1.6.1