gitauth 0.0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,310 @@
1
+ #--
2
+ # Copyright (C) 2009 Brown Beagle Software
3
+ # Copyright (C) 2009 Darcy Laycock <sutto@sutto.net>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #++
18
+
19
+ require 'rack'
20
+ require 'sinatra'
21
+ require 'digest/sha2'
22
+
23
+ module GitAuth
24
+ class WebApp < Sinatra::Base
25
+ include GitAuth::Loggable
26
+
27
+ cattr_accessor :current_server
28
+
29
+ def self.has_auth?
30
+ username = GitAuth::Settings["web_username"]
31
+ password = GitAuth::Settings["web_password_hash"]
32
+ !(username.blank? || password.blank?)
33
+ end
34
+
35
+ def self.update_auth
36
+ raw_username = Readline.readline('GitAuth Username (default is \'gitauth\'): ')
37
+ raw_username = 'gitauth' if raw_username.blank?
38
+ raw_password = ''
39
+ while raw_password.blank?
40
+ system "stty -echo"
41
+ raw_password = Readline.readline('GitAuth Password: ')
42
+ system "stty echo"
43
+ print "\n"
44
+ puts "You need to provide a password, please try again" if raw_password.blank?
45
+ end
46
+ password_confirmation = nil
47
+ while password_confirmation != raw_password
48
+ system "stty -echo"
49
+ password_confirmation = Readline.readline('Confirm Password: ')
50
+ system "stty echo"
51
+ print "\n"
52
+ puts "The confirmation doesn't match your password, please try again" if raw_password != password_confirmation
53
+ end
54
+ GitAuth::Settings.update!({
55
+ :web_username => raw_username,
56
+ :web_password_hash => Digest::SHA256.hexdigest(raw_password)
57
+ })
58
+ end
59
+
60
+ def self.check_auth
61
+ GitAuth.prepare
62
+ if !has_auth?
63
+ if $stderr.tty?
64
+ logger.verbose = true
65
+ puts "For gitauth to continue, you need to provide a username and password."
66
+ update_auth
67
+ else
68
+ logger.fatal "You need to provide a username and password for GitAuth to function; Please run 'gitauth webapp` once"
69
+ exit!
70
+ end
71
+ end
72
+ end
73
+
74
+ def self.run(options = {})
75
+ check_auth
76
+ set options
77
+ handler = detect_rack_handler
78
+ handler_name = handler.name.gsub(/.*::/, '')
79
+ logger.info "Starting up web server on #{port}"
80
+ handler.run self, :Host => host, :Port => port do |server|
81
+ GitAuth::WebApp.current_server = server
82
+ set :running, true
83
+ end
84
+ rescue Errno::EADDRINUSE => e
85
+ logger.fatal "Server is already running on port #{port}"
86
+ end
87
+
88
+ def self.stop
89
+ if current_server.present?
90
+ current_server.respond_to?(:stop!) ? current_server.stop! : current_server.stop
91
+ end
92
+ exit!
93
+ logger.debug "Stopped Server."
94
+ end
95
+
96
+ unless GitAuth::ApacheAuthentication.setup?
97
+
98
+ use GitAuth::AuthSetupMiddleware
99
+
100
+ use Rack::Auth::Basic do |username, password|
101
+ [username, Digest::SHA256.hexdigest(password)] == [GitAuth::Settings["web_username"], GitAuth::Settings["web_password_hash"]]
102
+ end
103
+
104
+ end
105
+
106
+ configure do
107
+ set :port, 8998
108
+ set :views, GitAuth::BASE_DIR.join("views")
109
+ set :public, GitAuth::BASE_DIR.join("public")
110
+ set :static, true
111
+ set :methodoverride, true
112
+ end
113
+
114
+ before { GitAuth.reload_models! }
115
+
116
+ helpers do
117
+ include Rack::Utils
118
+ alias_method :h, :escape_html
119
+
120
+ def link_to(text, link)
121
+ "<a href='#{link}'>#{text}</a>"
122
+ end
123
+
124
+ def delete_link(text, url)
125
+ id = "deleteable-#{Digest::SHA256.hexdigest(url.to_s)[0, 6]}"
126
+ html = "<div class='deletable-container' style='display: none; margin: 0; padding: 0;'>"
127
+ html << "<form method='post' action='#{url}' id='#{id}'>"
128
+ html << "<input name='_method' type='hidden' value='delete' />"
129
+ html << "</form></div>"
130
+ html << "<a href='#' onclick='if(confirm(\"Are you sure you want to do that? Deletion can not be reversed.\")) $(\"##{id}\").submit(); return false;'>#{text}</a>"
131
+ return html
132
+ end
133
+
134
+ def auto_link(member)
135
+ member = member.to_s
136
+ url = (member[0] == ?@ ? "/groups/#{URI.encode(member[1..-1])}" : "/users/#{URI.encode(member)}")
137
+ return link_to(member, url)
138
+ end
139
+
140
+ end
141
+
142
+ get '/' do
143
+ @repos = GitAuth::Repo.all
144
+ @users = GitAuth::User.all
145
+ @groups = GitAuth::Group.all
146
+ erb :index
147
+ end
148
+
149
+ # Listing / Index Page
150
+
151
+ get '/repos/:name' do
152
+ @repo = GitAuth::Repo.get(params[:name])
153
+ if @repo.nil?
154
+ redirect root_with_message("The given repository couldn't be found.")
155
+ else
156
+ read_perms, write_perms = (@repo.permissions[:read]||[]), (@repo.permissions[:write]||[])
157
+ @all_access = read_perms & write_perms
158
+ @read_only = read_perms - @all_access
159
+ @write_only = write_perms - @all_access
160
+ erb :repo
161
+ end
162
+ end
163
+
164
+ get '/users/:name' do
165
+ @user = GitAuth::User.get(params[:name])
166
+ if @user.nil?
167
+ redirect root_with_message("The given user couldn't be found.")
168
+ else
169
+ repos = GitAuth::Repo.all
170
+ read_perms = repos.select { |r| r.readable_by?(@user) }
171
+ write_perms = repos.select { |r| r.writeable_by?(@user) }
172
+ @all_access = read_perms & write_perms
173
+ @read_only = read_perms - @all_access
174
+ @write_only = write_perms - @all_access
175
+ @groups = GitAuth::Group.all.select { |g| g.member?(@user) }
176
+ erb :user
177
+ end
178
+ end
179
+
180
+ get '/groups/:name' do
181
+ @group = GitAuth::Group.get(params[:name])
182
+ if @group.nil?
183
+ redirect root_with_message("The given group could not be found.")
184
+ else
185
+ erb :group
186
+ end
187
+ end
188
+
189
+ # Create and update repos
190
+
191
+ post '/repos' do
192
+ name = params[:repo][:name]
193
+ path = params[:repo][:path]
194
+ path = name if path.to_s.strip.empty?
195
+ if repo = GitAuth::Repo.create(name, path)
196
+ make_empty = (params[:repo][:make_empty] == "1")
197
+ repo.make_empty! if make_empty
198
+ if repo.execute_post_create_hook!
199
+ redirect "/?repo_name=#{URI.encode(name)}&made_empty=#{make_empty ? "yes" : "no"}"
200
+ else
201
+ redirect root_with_message("Repository added but the post-create hook exited unsuccessfully.")
202
+ end
203
+ else
204
+ redirect root_with_message("There was an error adding the repository.")
205
+ end
206
+ end
207
+
208
+ post '/repos/:name' do
209
+ repo = GitAuth::Repo.get(params[:name])
210
+ if repo.nil?
211
+ redirect root_with_message("The given repository couldn't be found.")
212
+ else
213
+ new_permissions = Hash.new([])
214
+ [:all, :read, :write].each do |k|
215
+ if params[:repo][k]
216
+ perm_lines = params[:repo][k].to_s.split("\n")
217
+ new_permissions[k] = perm_lines.map do |l|
218
+ i = GitAuth.get_user_or_group(l.strip)
219
+ i.nil? ? nil : i.to_s
220
+ end.compact
221
+ end
222
+ end
223
+ all = new_permissions.delete(:all)
224
+ new_permissions[:read] |= all
225
+ new_permissions[:write] |= all
226
+ new_permissions.each_value { |v| v.uniq! }
227
+ repo.permissions = new_permissions
228
+ GitAuth::Repo.save!
229
+ redirect "/repos/#{URI.encode(repo.name)}"
230
+ end
231
+ end
232
+
233
+ delete '/repos/:name' do
234
+ repo = GitAuth::Repo.get(params[:name])
235
+ if repo.nil?
236
+ redirect root_with_message("The given repository couldn't be found.")
237
+ else
238
+ repo.destroy!
239
+ redirect root_with_message("Repository removed.")
240
+ end
241
+ end
242
+
243
+ # Create, delete and update users
244
+
245
+ post '/users' do
246
+ name = params[:user][:name]
247
+ admin = params[:user][:admin].to_s == "1"
248
+ key = params[:user][:key]
249
+ if GitAuth::User.create(name, admin, key)
250
+ redirect root_with_message("User Added")
251
+ else
252
+ redirect root_with_message("There was an error adding the requested user.")
253
+ end
254
+ end
255
+
256
+ delete '/users/:name' do
257
+ user = GitAuth::User.get(params[:name])
258
+ if user.nil?
259
+ redirect root_with_message("The specified user couldn't be found.")
260
+ else
261
+ user.destroy!
262
+ redirect root_with_message("User removed.")
263
+ end
264
+ end
265
+
266
+ # Create and Update Groups
267
+
268
+ post '/groups' do
269
+ if GitAuth::Group.create(params[:group][:name])
270
+ redirect root_with_message("Group added")
271
+ else
272
+ redirect root_with_message("There was an error adding the requested group.")
273
+ end
274
+ end
275
+
276
+ post '/groups/:name' do
277
+ group = GitAuth::Group.get(params[:name])
278
+ if group.nil?
279
+ redirect root_with_message("The specified group couldn't be found.")
280
+ else
281
+ if params[:group][:members]
282
+ member_lines = params[:group][:members].to_s.split("\n")
283
+ group.members = member_lines.map do |l|
284
+ i = GitAuth.get_user_or_group(l.strip)
285
+ i.nil? ? nil : i.to_s
286
+ end.compact - [group.to_s]
287
+ GitAuth::Group.save!
288
+ end
289
+ redirect "/groups/#{URI.encode(group.name)}"
290
+ end
291
+ end
292
+
293
+ delete '/groups/:name' do
294
+ group = GitAuth::Group.get(params[:name])
295
+ if group.nil?
296
+ redirect root_with_message("The specified group couldn't be found.")
297
+ else
298
+ group.destroy!
299
+ redirect root_with_message("Group removed.")
300
+ end
301
+ end
302
+
303
+ # Misc Helpers
304
+
305
+ def root_with_message(message)
306
+ "/?message=#{URI.encode(message)}"
307
+ end
308
+
309
+ end
310
+ end
@@ -0,0 +1,316 @@
1
+ /* Reset */
2
+ html, body, div, span, applet, object, iframe,
3
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
4
+ a, abbr, acronym, address, big, cite, code,
5
+ del, dfn, em, font, img, ins, kbd, q, s, samp,
6
+ small, strike, strong, sub, sup, tt, var,
7
+ dl, dt, dd, ol, ul, li,
8
+ fieldset, form, label, legend,
9
+ table, caption, tbody, tfoot, thead, tr, th, td {
10
+ margin: 0;
11
+ padding: 0;
12
+ border: 0;
13
+ outline: 0;
14
+ font-weight: inherit;
15
+ font-style: inherit;
16
+ font-size: 100%;
17
+ font-family: inherit;
18
+ vertical-align: baseline;
19
+ }
20
+ /* remember to define focus styles! */
21
+ :focus {
22
+ outline: 0;
23
+ }
24
+ body {
25
+ line-height: 1;
26
+ color: black;
27
+ background: white;
28
+ }
29
+ ol, ul {
30
+ list-style: none;
31
+ }
32
+ /* tables still need 'cellspacing="0"' in the markup */
33
+ table {
34
+ border-collapse: separate;
35
+ border-spacing: 0;
36
+ }
37
+ caption, th, td {
38
+ text-align: left;
39
+ font-weight: normal;
40
+ }
41
+ blockquote:before, blockquote:after,
42
+ q:before, q:after {
43
+ content: "";
44
+ }
45
+ blockquote, q {
46
+ quotes: "" "";
47
+ }
48
+
49
+ /* Other Stuff */
50
+
51
+ body {
52
+ background: #222;
53
+ }
54
+
55
+ #container {
56
+ width: 640px;
57
+ margin: 2em auto;
58
+ padding: 1em;
59
+ background: #FFF;
60
+ -moz-border-radius: 0.5em;
61
+ -webkit-border-radius: 0.5em;
62
+ border-radius: 0.5em;
63
+ }
64
+
65
+ #header {
66
+ text-align: center;
67
+ margin: 0.5em 0 1em;
68
+ }
69
+
70
+ #header h1 {
71
+ font-size: 2em;
72
+ font-weight: bold;
73
+ }
74
+
75
+ #header h1 a {
76
+ text-decoration: none;
77
+ color: #000;
78
+ }
79
+
80
+ #footer {
81
+ color: #777;
82
+ text-align: center;
83
+ margin: 1em 0 0;
84
+ }
85
+
86
+ .section {
87
+ margin: 0.5em 0 1.5em;
88
+ }
89
+
90
+ .section h2 {
91
+ padding: 0.5em;
92
+ text-transform: uppercase;
93
+ background: #EEE;
94
+ border-top: 0.1em solid #999;
95
+ border-bottom: 0.1em solid #999;
96
+ font-weight: bold;
97
+ font-size: 1.25em;
98
+ color: #333;
99
+ }
100
+
101
+ .section h2 a {
102
+ text-decoration: none;
103
+ color: #233967;
104
+ margin-left: 0.15em;
105
+ }
106
+
107
+ form {
108
+ background: #FDFFE9;
109
+ border: 0.5em solid #F0EBD4;
110
+ padding: 0.5em;
111
+ margin: 1em;
112
+ }
113
+
114
+ .hidden {
115
+ display: none;
116
+ }
117
+
118
+ form .row {
119
+ margin: 0 0.5em 0.5em;
120
+ line-height: 1.8em;
121
+ display: inline-block;
122
+ display: block;
123
+ }
124
+
125
+ form .row:after {
126
+ clear: both;
127
+ content: '.';
128
+ display: block;
129
+ visibility: hidden;
130
+ height: 0;
131
+ }
132
+
133
+ form .row label {
134
+ display: block;
135
+ float: left;
136
+ zoom: 1;
137
+ width: 30%;
138
+ font-weight: bold;
139
+ font-size: 1.1em;
140
+ padding-top: 0.15em;
141
+ }
142
+
143
+ form .row input, form .row textarea {
144
+ font-size: 1.1em;
145
+ padding: 0.25em;
146
+ border: 0.15em solid #AAA;
147
+ }
148
+
149
+ form .row input {
150
+ width: 60%;
151
+ }
152
+
153
+ form .row input:focus, form .row textarea:focus {
154
+ border-color: #3E5299;
155
+ }
156
+
157
+ form .row p.hint {
158
+ color: #24813D;
159
+ padding-left: 30%;
160
+ }
161
+
162
+ form .buttons {
163
+ text-align: center;
164
+ padding: 0.75em;
165
+ }
166
+
167
+ form .buttons a {
168
+ text-decoration: none;
169
+ color: #204B99;
170
+ }
171
+
172
+ div.message {
173
+ font-size: 1.2em;
174
+ text-align: center;
175
+ padding: 0.5em 1em 1.5em;
176
+ color: #DE701E;
177
+ }
178
+
179
+ /* Listings */
180
+
181
+ .section ul {
182
+ line-height: 1.8em;
183
+ margin: 0.5em 1em;
184
+ font-family: Helvetica, Arial, Sans-Serif;
185
+ }
186
+
187
+ .section ul li {
188
+ padding: 0.5em 0;
189
+ color: #333;
190
+ }
191
+
192
+ .section ul li span.tag {
193
+ display: inline-block;
194
+ width: 4em;
195
+ color: white;
196
+ background: #666;
197
+ font-size: 0.65em;
198
+ text-transform: uppercase;
199
+ font-weight: bold;
200
+ text-align: center;
201
+ padding: 0 0.5em;
202
+ margin-right: 0.5em;
203
+ -moz-border-radius: 0.6em;
204
+ -webkit-border-radius: 0.6em;
205
+ font-family: Helvetica, Arial, Sans-Serif;
206
+ }
207
+
208
+ #users .tag.admin {
209
+ background: #163F10;
210
+ }
211
+
212
+ #users .tag.user {
213
+ background: #141A3F;
214
+ }
215
+
216
+ #repositories .tag.repo {
217
+ background: #720E12;
218
+ }
219
+
220
+ #groups .tag.group {
221
+ background: #17768A;
222
+ }
223
+
224
+ .section ul li a, #repository-details a {
225
+ text-decoration: none;
226
+ color: #395F99;
227
+ }
228
+
229
+ /* View Stuff */
230
+
231
+ .view h2 {
232
+ margin: 0 0 1em;
233
+ text-align: center;
234
+ font-weight: bold;
235
+ font-size: 1.3em;
236
+ color: #333;
237
+ border-top: 0.1em solid #DDD;
238
+ border-bottom: 0.1em solid #DDD;
239
+ padding: 0.5em;
240
+ }
241
+
242
+ .view h3 {
243
+ margin: 0.5em 1em;
244
+ font-weight: bold;
245
+ color: #555;
246
+ font-size: 1.2em;
247
+ }
248
+
249
+ .view ul {
250
+ margin: 0.5em 2em;
251
+ line-height: 1.8em;
252
+ font-size: 1.1em;
253
+ list-style: disc inside;
254
+ }
255
+
256
+ .view ul li {
257
+ }
258
+
259
+ .view ul li.empty {
260
+ color: #4A5258;
261
+ }
262
+
263
+ br.clear { clear: both; }
264
+
265
+ .view ul li a {
266
+ text-decoration: none;
267
+ color: #416999;
268
+ }
269
+
270
+ #repository-details {
271
+ line-height: 1.8em;
272
+ padding: 0.5em 1em;
273
+ margin-bottom: 2em;
274
+ }
275
+
276
+ #repository-details h3 {
277
+ font-weight: bold;
278
+ font-size: 1.1em;
279
+ text-align: center;
280
+ margin-bottom: 0.75em;
281
+ color: #C55E25;
282
+ }
283
+
284
+ #repository-details code {
285
+ display: block;
286
+ white-space: pre;
287
+ padding: 0.5em;
288
+ font-family: Consolas, Lucida Console, Monaco, monospace;
289
+ background: #F4F4F4;
290
+ margin: 0.25em 0;
291
+ }
292
+
293
+ #error-container {
294
+ padding: 1em 3em 1.5em;
295
+ }
296
+
297
+ #error-container h2 {
298
+ font-size: 1.5em;
299
+ text-align: center;
300
+ font-weight: bold;
301
+ padding: 0 0 0.5em;
302
+ color: red;
303
+ }
304
+
305
+ #error-container p {
306
+ font-size: 1.2em;
307
+ line-height: 1.5em;
308
+ color: #222;
309
+ text-align: center;
310
+ }
311
+
312
+ #error-container p code {
313
+ font-style: italic;
314
+ color: black;
315
+ font-weight: bold;
316
+ }