diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 87b75891..383aec7a 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -93,19 +93,24 @@ class RepositoriesController < ApplicationController @repository.project = @project if request.post? && @repository.save if(params[:repository_scm]=="Git") - system "htpasswd -mb "+@root_path+" "+@repo_name+" "+@repository_tag - system "echo -e '\n"+@repo_name+"-write:"+ - " "+@repo_name+"' >> "+@root_path+"group.passwd" - system "mkdir "+@root_path+"htdocs/"+User.current.login.to_s - system "git init --bare "+@project_path + @create_passwd = system "htpasswd -mb "+@root_path+" "+@repo_name+" "+@repository_tag + @create_group = system "echo -e '\n"+@repo_name+"-write:"+ + " "+@repo_name+"' >> "+@root_path+"group.passwd" + @create_repo_file = system "mkdir "+@root_path+"htdocs/"+User.current.login.to_s + @init_repository = system "git init --bare "+@project_path system "mv "+@project_path+"/hooks/post-update{.sample,}" system "chmod a+x "+@project_path+"/hooks/post-update" - system "echo -e 'Allow from all \n Order Deny,Allow \n "+ + @add_privilege = system "echo -e 'Allow from all \n Order Deny,Allow \n "+ " \n"+ "Require group "+@repo_name+"-write \n "+ " \n ' >> "+ @root_path+"htdocs/"+ @repository_name+"/.htaccess" - flag = system "cd "+@project_path+" ;git update-server-info" + @init_server_info = system "cd "+@project_path+" ;git update-server-info" + if(@create_repo_file&&@create_passwd&&@create_group&&@init_repository&&@add_privilege&&@init_server_info) + logger.error "An error occured when authenticating "+"create passwd"+@creat_passwd+"create_group"+ + @crate_group+"create repository file "+@create_repo_file+"init repository"+@init_repostory+ + "aad privilege to rpository"+@add_privilege+"init server infos"+@init_server_info + end @repository.update_attributes(:login => User.current.login.to_s) end redirect_to settings_project_path(@project, :tab => 'repositories') @@ -169,9 +174,13 @@ class RepositoriesController < ApplicationController @repository.destroy if request.delete? redirect_to settings_project_path(@project, :tab => 'repositories') if(@repository.type=="Repository::Git") - system "sed -i /"+@repo_name+"/{d} "+@root_path+"user.passwd" - system "sed -i /"+@middle+"/{d} "+@root_path+"group.passwd" - system "rm -r "+@root_path+"htdocs/"+@repository_name + @ed_user=system "sed -i /"+@repo_name+"/{d} "+@root_path+"user.passwd" + @sed_group=system "sed -i /"+@middle+"/{d} "+@root_path+"group.passwd" + @remove=system "rm -r "+@root_path+"htdocs/"+@repository_name + if(@sed_user&&@sed_group&&@remove) + logger.error "An error occured when destory the repository"+"delete form passwd: \n"+ + @sed_user+"delete from group"+@sed_group+"delete from file"+@remove + end end end diff --git a/lib/git/backend/grack_auth.rb b/lib/git/backend/grack_auth.rb new file mode 100644 index 00000000..e7217c7c --- /dev/null +++ b/lib/git/backend/grack_auth.rb @@ -0,0 +1,119 @@ +require_relative 'shell_env' +require_relative 'grack_ldap' +require_relative 'grack_helpers' + +module Grack + class Auth < Rack::Auth::Basic + include LDAP + include Helpers + + attr_accessor :user, :project, :ref, :env + + def call(env) + @env = env + @request = Rack::Request.new(env) + @auth = Request.new(env) + + # Need this patch due to the rails mount + @env['PATH_INFO'] = @request.path + @env['SCRIPT_NAME'] = "" + + auth! + end + + private + + def auth! + return render_not_found unless project + + if @auth.provided? + return bad_request unless @auth.basic? + + # Authentication with username and password + login, password = @auth.credentials + + @user = authenticate_user(login, password) + + if @user + Gitlab::ShellEnv.set_env(@user) + @env['REMOTE_USER'] = @auth.username + else + return unauthorized + end + + else + return unauthorized unless project.public + end + + if authorized_git_request? + @app.call(env) + else + unauthorized + end + end + + def authorized_git_request? + # Git upload and receive + if @request.get? + authorize_request(@request.params['service']) + elsif @request.post? + authorize_request(File.basename(@request.path)) + else + false + end + end + + def authenticate_user(login, password) + user = User.find_by_email(login) || User.find_by_username(login) + + # If the provided login was not a known email or username + # then user is nil + if user.nil? || user.ldap_user? + # Second chance - try LDAP authentication + return nil unless ldap_conf.enabled + + auth = Gitlab::Auth.new + auth.ldap_auth(login, password) + else + return user if user.valid_password?(password) + end + end + + def authorize_request(service) + case service + when 'git-upload-pack' + project.public || can?(user, :download_code, project) + when'git-receive-pack' + action = if project.protected_branch?(ref) + :push_code_to_protected_branches + else + :push_code + end + + can?(user, action, project) + else + false + end + end + + def project + @project ||= project_by_path(@request.path_info) + end + + def ref + @ref ||= parse_ref + end + + def parse_ref + input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/ + Zlib::GzipReader.new(@request.body).read + else + @request.body.read + end + + # Need to reset seek point + @request.body.rewind + /refs\/heads\/([\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last + end + end +end diff --git a/lib/git/backend/grack_helpers.rb b/lib/git/backend/grack_helpers.rb new file mode 100644 index 00000000..5ac9e9f3 --- /dev/null +++ b/lib/git/backend/grack_helpers.rb @@ -0,0 +1,28 @@ +module Grack + module Helpers + def project_by_path(path) + if m = /^\/([\w\.\/-]+)\.git/.match(path).to_a + path_with_namespace = m.last + path_with_namespace.gsub!(/\.wiki$/, '') + + Project.find_with_namespace(path_with_namespace) + end + end + + def render_not_found + [404, {"Content-Type" => "text/plain"}, ["Not Found"]] + end + + def can?(object, action, subject) + abilities.allowed?(object, action, subject) + end + + def abilities + @abilities ||= begin + abilities = Six.new + abilities << Ability + abilities + end + end + end +end diff --git a/lib/git/backend/grack_ldap.rb b/lib/git/backend/grack_ldap.rb new file mode 100644 index 00000000..45e98fba --- /dev/null +++ b/lib/git/backend/grack_ldap.rb @@ -0,0 +1,24 @@ +require 'omniauth-ldap' + +module Grack + module LDAP + def ldap_auth(login, password) + # Check user against LDAP backend if user is not authenticated + # Only check with valid login and password to prevent anonymous bind results + return nil unless ldap_conf.enabled && !login.blank? && !password.blank? + + ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf) + ldap_user = ldap.bind_as( + filter: Net::LDAP::Filter.eq(ldap.uid, login), + size: 1, + password: password + ) + + User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user + end + + def ldap_conf + @ldap_conf ||= Gitlab.config.ldap + end + end +end diff --git a/lib/git/backend/shell.rb b/lib/git/backend/shell.rb new file mode 100644 index 00000000..991648f3 --- /dev/null +++ b/lib/git/backend/shell.rb @@ -0,0 +1,148 @@ +module Gitlab + class Shell + class AccessDenied < StandardError; end + + # Init new repository + # + # name - project path with namespace + # + # Ex. + # add_repository("gitlab/gitlab-ci") + # + def add_repository(name) + system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "add-project", "#{name}.git" + end + + # Import repository + # + # name - project path with namespace + # + # Ex. + # import_repository("gitlab/gitlab-ci", "https://github.com/randx/six.git") + # + def import_repository(name, url) + system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "import-project", "#{name}.git", url + end + + # Move repository + # + # path - project path with namespace + # new_path - new project path with namespace + # + # Ex. + # mv_repository("gitlab/gitlab-ci", "randx/gitlab-ci-new.git") + # + def mv_repository(path, new_path) + system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "mv-project", "#{path}.git", "#{new_path}.git" + end + + # Fork repository to new namespace + # + # path - project path with namespace + # fork_namespace - namespace for forked project + # + # Ex. + # fork_repository("gitlab/gitlab-ci", "randx") + # + def fork_repository(path, fork_namespace) + system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "fork-project", "#{path}.git", fork_namespace + end + + # Remove repository from file system + # + # name - project path with namespace + # + # Ex. + # remove_repository("gitlab/gitlab-ci") + # + def remove_repository(name) + system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "rm-project", "#{name}.git" + end + + # Add new key to gitlab-shell + # + # Ex. + # add_key("key-42", "sha-rsa ...") + # + def add_key(key_id, key_content) + system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-keys", "add-key", key_id, key_content + end + + # Remove ssh key from gitlab shell + # + # Ex. + # remove_key("key-342", "sha-rsa ...") + # + def remove_key(key_id, key_content) + system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-keys", "rm-key", key_id, key_content + end + + # Add empty directory for storing repositories + # + # Ex. + # add_namespace("gitlab") + # + def add_namespace(name) + FileUtils.mkdir(full_path(name), mode: 0770) unless exists?(name) + end + + # Remove directory from repositories storage + # Every repository inside this directory will be removed too + # + # Ex. + # rm_namespace("gitlab") + # + def rm_namespace(name) + FileUtils.rm_r(full_path(name), force: true) + end + + # Move namespace directory inside repositories storage + # + # Ex. + # mv_namespace("gitlab", "gitlabhq") + # + def mv_namespace(old_name, new_name) + return false if exists?(new_name) || !exists?(old_name) + + FileUtils.mv(full_path(old_name), full_path(new_name)) + end + + # Remove GitLab Satellites for provided path (namespace or repo dir) + # + # Ex. + # rm_satellites("gitlab") + # + # rm_satellites("gitlab/gitlab-ci.git") + # + def rm_satellites(path) + raise ArgumentError.new("Path can't be blank") if path.blank? + + satellites_path = File.join(Gitlab.config.satellites.path, path) + FileUtils.rm_r(satellites_path, force: true) + end + + def url_to_repo path + Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git" + end + + protected + + def gitlab_shell_user_home + File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}") + end + + def repos_path + Gitlab.config.gitlab_shell.repos_path + end + + def full_path(dir_name) + raise ArgumentError.new("Directory name can't be blank") if dir_name.blank? + + File.join(repos_path, dir_name) + end + + def exists?(dir_name) + File.exists?(full_path(dir_name)) + end + end +end diff --git a/lib/git/backend/shell_adapter.rb b/lib/git/backend/shell_adapter.rb new file mode 100644 index 00000000..f247f459 --- /dev/null +++ b/lib/git/backend/shell_adapter.rb @@ -0,0 +1,12 @@ +# == GitLab Shell mixin +# +# Provide a shortcut to Gitlab::Shell instance by gitlab_shell +# +module Gitlab + module ShellAdapter + def gitlab_shell + Gitlab::Shell.new + end + end +end + diff --git a/lib/git/backend/shell_env.rb b/lib/git/backend/shell_env.rb new file mode 100644 index 00000000..15721875 --- /dev/null +++ b/lib/git/backend/shell_env.rb @@ -0,0 +1,17 @@ +module Gitlab + # This module provide 2 methods + # to set specific ENV variabled for GitLab Shell + module ShellEnv + extend self + + def set_env(user) + # Set GL_ID env variable + ENV['GL_ID'] = "user-#{user.id}" + end + + def reset_env + # Reset GL_ID env variable + ENV['GL_ID'] = nil + end + end +end