Merge branch 'master' into backup-scripts
[gitorious:mainline.git] / lib / gitorious / ssh / client.rb
1 # encoding: utf-8
2 #--
3 #   Copyright (C) 2012 Gitorious AS
4 #   Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
5 #   Copyright (C) 2008 Tor Arne Vestbø <tavestbo@trolltech.com>
6 #   Copyright (C) 2007, 2008 Johan Sørensen <johan@johansorensen.com>
7 #
8 #   This program is free software: you can redistribute it and/or modify
9 #   it under the terms of the GNU Affero General Public License as published by
10 #   the Free Software Foundation, either version 3 of the License, or
11 #   (at your option) any later version.
12 #
13 #   This program is distributed in the hope that it will be useful,
14 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #   GNU Affero General Public License for more details.
17 #
18 #   You should have received a copy of the GNU Affero General Public License
19 #   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #++
21
22 require "net/http"
23 require "uri"
24 require File.expand_path(File.dirname(__FILE__) + "../../../../app/models/repository_root")
25
26 module Gitorious
27   module SSH
28     class AccessDeniedError < StandardError; end
29
30     class Client
31       def initialize(strainer, username)
32         @strainer = strainer
33         @project_name, @repository_name = strainer.path.split("/", 2)
34         @repository_name.gsub!(/\.git$/, "")
35         @user_name = username
36         @configuration = {}
37       end
38       attr_accessor :project_name, :repository_name, :user_name
39
40       def writable_by_user?
41         query_for_permission_and_path
42         @writable == "true"
43       end
44
45       def assure_user_can_write!
46         writable_by_user? || raise(AccessDeniedError)
47       end
48
49       # The full URL
50       def writable_by_query_url
51         writable_by_query_uri.to_s
52       end
53
54       # The path only
55       def writable_by_query_path
56         writable_by_query_uri.request_uri
57       end
58
59       def real_path
60         if !configuration["real_path"] || configuration["real_path"] == "nil"
61           raise AccessDeniedError
62         end
63         full_real_path = File.join(RepositoryRoot.default_base_path,
64                                    configuration["real_path"])
65         raise AccessDeniedError unless File.exist?(full_real_path)
66         full_real_path
67       end
68
69       def force_pushing_denied?
70         configuration["force_pushing_denied"] == "true"
71       end
72
73       def to_git_shell_argument
74         "#{@strainer.verb} '#{real_path}'"
75       end
76
77       def pre_receive_hook_exists?
78         filename = File.join(real_path, "hooks", "pre-receive")
79         File.executable?(filename)
80       end
81
82       def configuration
83         if @configuration.empty?
84           query_url = "/#{@project_name}/#{@repository_name}/config?username=#@user_name"
85           # $stderr.puts "Querying #{query_url}" if $DEBUG
86           resp = connection.get(query_url)
87           # $stderr.puts resp
88           parse_configuration(resp.body)
89         end
90         @configuration
91       end
92
93       protected
94       def parse_configuration(raw_config)
95         raw_config.split("\n").each do |line|
96           key, val = line.split(":")
97           if key && val
98             @configuration[key] = val
99           end
100         end
101       end
102
103       def connection
104         port = GitoriousConfig["gitorious_client_port"]
105         host = GitoriousConfig["gitorious_client_host"]
106         @connection ||= Net::HTTP.start(host, port)
107       end
108
109       # Returns an actual URI object
110       def writable_by_query_uri
111         path = "/#{@project_name}/#{@repository_name}/writable_by"
112         query = "username=#{@user_name}"
113         host = GitoriousConfig["gitorious_client_host"]
114         _port = GitoriousConfig["gitorious_client_port"]
115         # Ruby 1.9 expects a number, while 1.8 expects a string. Oh well
116         port = RUBY_VERSION > "1.9" ? _port : _port.to_s
117
118         URI::HTTP.build(:host => host, :port => port, :path => path, :query => query)
119       end
120     end
121   end
122 end