... | ... |
@@ -119,6 +119,7 @@ sub startup { |
119 | 119 |
# Model |
120 | 120 |
my $models = [ |
121 | 121 |
{table => 'user', primary_key => 'id'}, |
122 |
+ {table => 'ssh_public_key', primary_key => ['user_id', 'key']}, |
|
122 | 123 |
{table => 'project', primary_key => ['user_id', 'name']}, |
123 | 124 |
{table => 'number', primary_key => 'key'}, |
124 | 125 |
{table => 'collaboration', primary_key => ['user_id', 'project_name', 'collaborator_id']} |
... | ... |
@@ -185,6 +186,9 @@ sub startup { |
185 | 186 |
|
186 | 187 |
# Custom routes |
187 | 188 |
{ |
189 |
+ # Show ssh keys |
|
190 |
+ $r->get('/:user.keys' => template '/user-keys'); |
|
191 |
+ |
|
188 | 192 |
# User |
189 | 193 |
my $r = $r->route('/:user'); |
190 | 194 |
{ |
... | ... |
@@ -195,7 +199,7 @@ sub startup { |
195 | 199 |
$r->get('/_settings' => template '/user-settings'); |
196 | 200 |
|
197 | 201 |
# SSH keys |
198 |
- $r->get('/_settings/ssh' => template '/user-settings/ssh'); |
|
202 |
+ $r->any('/_settings/ssh' => template '/user-settings/ssh'); |
|
199 | 203 |
} |
200 | 204 |
|
201 | 205 |
# Smart HTTP |
... | ... |
@@ -306,7 +306,7 @@ EOS |
306 | 306 |
$dbi->execute($sql); |
307 | 307 |
}; |
308 | 308 |
|
309 |
- # Create usert columns |
|
309 |
+ # Create user columns |
|
310 | 310 |
my $user_columns = [ |
311 | 311 |
"admin not null default '0'", |
312 | 312 |
"password not null default ''", |
... | ... |
@@ -323,6 +323,35 @@ EOS |
323 | 323 |
$self->app->log->error($error); |
324 | 324 |
croak $error; |
325 | 325 |
} |
326 |
+ |
|
327 |
+ # Create ssh_public_key table |
|
328 |
+ eval { |
|
329 |
+ my $sql = <<"EOS"; |
|
330 |
+create table ssh_public_key ( |
|
331 |
+ row_id integer primary key autoincrement, |
|
332 |
+ user_id not null default '', |
|
333 |
+ key not null default '', |
|
334 |
+ unique(user_id, key) |
|
335 |
+); |
|
336 |
+EOS |
|
337 |
+ $dbi->execute($sql); |
|
338 |
+ }; |
|
339 |
+ |
|
340 |
+ # Create ssh_public_key columns |
|
341 |
+ my $ssh_public_key_columns = [ |
|
342 |
+ "title not null default ''", |
|
343 |
+ ]; |
|
344 |
+ for my $column (@$ssh_public_key_columns) { |
|
345 |
+ eval { $dbi->execute("alter table ssh_public_key add column $column") }; |
|
346 |
+ } |
|
347 |
+ |
|
348 |
+ # Check ssh_public_key table |
|
349 |
+ eval { $dbi->select([qw/row_id user_id key title/], table => 'ssh_public_key') }; |
|
350 |
+ if ($@) { |
|
351 |
+ my $error = "Can't create ssh_public_key table properly: $@"; |
|
352 |
+ $self->app->log->error($error); |
|
353 |
+ croak $error; |
|
354 |
+ } |
|
326 | 355 |
|
327 | 356 |
# Create project table |
328 | 357 |
eval { |
... | ... |
@@ -5,7 +5,7 @@ |
5 | 5 |
<div class="alert alert-error"> |
6 | 6 |
<button type="button" class="close" data-dismiss="alert">×</button> |
7 | 7 |
% for my $error (@$errors) { |
8 |
- <p><%= $error %></p> |
|
8 |
+ <p style="margin:0;padding:0"><%= $error %></p> |
|
9 | 9 |
% } |
10 | 10 |
</div> |
11 | 11 |
% } |
... | ... |
@@ -0,0 +1,11 @@ |
1 |
+<% |
|
2 |
+ my $user = param('user'); |
|
3 |
+ my $keys = app->dbi->model('ssh_public_key')->select(where => {user_id => $user})->all; |
|
4 |
+ warn dumper $keys; |
|
5 |
+ my $keys_str = ''; |
|
6 |
+ for my $key (@$keys) { |
|
7 |
+ $keys_str .= "$key->{key}\n"; |
|
8 |
+ } |
|
9 |
+ $self->render(text => $keys_str); |
|
10 |
+ return; |
|
11 |
+%> |
... | ... |
@@ -12,37 +12,103 @@ |
12 | 12 |
return; |
13 | 13 |
} |
14 | 14 |
|
15 |
- my $keys = [ |
|
16 |
- { |
|
17 |
- key => 'key1', |
|
18 |
- hash => '7d:d7:ec:86:f6:96:cf:8f:63:07:79:01:f4:cb:f7:78', |
|
19 |
- mtime => 'Last used on May 16, 2014' |
|
20 |
- }, |
|
21 |
- { |
|
22 |
- key => 'key1', |
|
23 |
- hash => '7d:d7:ec:86:f6:96:cf:8f:63:07:79:01:f4:cb:f7:78', |
|
24 |
- mtime => 'Last used on May 16, 2014' |
|
25 |
- }, |
|
26 |
- ]; |
|
27 |
-%> |
|
28 |
- |
|
29 |
-% layout 'common', title => 'Your Profile'; |
|
30 |
- |
|
31 |
- %= javascript begin |
|
32 |
- $(document).ready(function () { |
|
33 |
- $('#show-add-key-form').on('click', function () { |
|
34 |
- var display = $('#add-key-form').css('display'); |
|
15 |
+ # Process form |
|
16 |
+ my $errors; |
|
17 |
+ if (lc $self->req->method eq 'post') { |
|
18 |
+ # Add ssh key |
|
19 |
+ if ($op eq 'add') { |
|
20 |
+ |
|
21 |
+ # Paramters |
|
22 |
+ my $params = $api->params; |
|
23 |
+ |
|
24 |
+ # Rule |
|
25 |
+ my $rule = [ |
|
26 |
+ title => [ |
|
27 |
+ ['not_blank' => 'title is empty'], |
|
28 |
+ ['ascii' => 'title contains invalid character'], |
|
29 |
+ ], |
|
30 |
+ key => [ |
|
31 |
+ ['not_blank' => 'key is empty'], |
|
32 |
+ sub { |
|
33 |
+ my ($original_key, $args, $vc) = @_; |
|
34 |
+ |
|
35 |
+ my $type; |
|
36 |
+ my $original_key_edit; |
|
37 |
+ if ($original_key =~ /^(ssh-rsa|ssh-dss|ecdsa-sha2-nistp25|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521) +(\S+)/) { |
|
38 |
+ $type = $1; |
|
39 |
+ $original_key_edit = $2; |
|
40 |
+ } |
|
41 |
+ |
|
42 |
+ if ($type) { |
|
43 |
+ if ($vc->constraints->{ascii}->($original_key_edit)) { |
|
44 |
+ my $key = "$type $original_key_edit"; |
|
45 |
+ |
|
46 |
+ my $row = app->dbi->model('ssh_public_key')->select(id => [$user, $key])->one; |
|
47 |
+ |
|
48 |
+ if ($row) { |
|
49 |
+ return {result => 0, message => 'Key already exists'}; |
|
50 |
+ } |
|
51 |
+ else { |
|
52 |
+ return {result => 1, output => $key} |
|
53 |
+ } |
|
54 |
+ } |
|
55 |
+ else { |
|
56 |
+ return { |
|
57 |
+ result => 0, |
|
58 |
+ message => "Key contains invalid character." |
|
59 |
+ } |
|
60 |
+ } |
|
61 |
+ } |
|
62 |
+ else { |
|
63 |
+ return { |
|
64 |
+ result => 0, |
|
65 |
+ message => "Key is invalid. It must begin with 'ssh-rsa', 'ssh-dss', 'ecdsa-sha2-nistp256'," |
|
66 |
+ . "'ecdsa-sha2-nistp384', or 'ecdsa-sha2-nistp521'. Check that you're copying the public half of the key" |
|
67 |
+ }; |
|
68 |
+ } |
|
69 |
+ } |
|
70 |
+ ] |
|
71 |
+ ]; |
|
72 |
+ |
|
73 |
+ # Validation |
|
74 |
+ my $vresult = app->vc->validate($params, $rule); |
|
75 |
+ |
|
76 |
+ # Register ssh key |
|
77 |
+ if ($vresult->is_ok) { |
|
78 |
+ my $safe_params = $vresult->data; |
|
79 |
+ my $title = $safe_params->{title}; |
|
80 |
+ my $key = $safe_params->{key}; |
|
81 |
+ |
|
82 |
+ my $p = { |
|
83 |
+ user_id => $user, |
|
84 |
+ title => $title, |
|
85 |
+ key => $key |
|
86 |
+ }; |
|
87 |
+ eval { |
|
88 |
+ app->dbi->model('ssh_public_key')->insert($p); |
|
89 |
+ }; |
|
35 | 90 |
|
36 |
- if (display === 'block') { |
|
37 |
- $('#add-key-form').css('display', 'none'); |
|
91 |
+ if (my $e = $@) { |
|
92 |
+ app->log->error(url_for . ":$e"); |
|
93 |
+ $errors = ['Internal error']; |
|
38 | 94 |
} |
39 | 95 |
else { |
40 |
- $('#add-key-form').css('display', 'block'); |
|
96 |
+ flash('message' => 'Success: ssh key is added'); |
|
97 |
+ $self->redirect_to('current'); |
|
98 |
+ return; |
|
41 | 99 |
} |
42 |
- }); |
|
43 |
- }); |
|
44 |
- % end |
|
100 |
+ } |
|
101 |
+ else { |
|
102 |
+ $errors = $vresult->messages; |
|
103 |
+ } |
|
104 |
+ } |
|
105 |
+ } |
|
45 | 106 |
|
107 |
+ my $keys = app->dbi->model('ssh_public_key')->select(where => {user_id => $user})->all; |
|
108 |
+%> |
|
109 |
+ |
|
110 |
+% layout 'common', title => 'SSH keys'; |
|
111 |
+ |
|
46 | 112 |
%= include '/include/header'; |
47 | 113 |
|
48 | 114 |
<div class="container"> |
... | ... |
@@ -64,18 +130,12 @@ |
64 | 130 |
</ul> |
65 | 131 |
</div> |
66 | 132 |
<div class="span10"> |
67 |
- <div class="border-gray bk-gray-light radius-top" style="padding:5px;font-weight:bold;font-size:17px"> |
|
68 |
- <div class="row"> |
|
69 |
- <div class="span7" style="width:600px"> |
|
70 |
- <div style="font-size:15px;padding:5px"> |
|
71 |
- SSH Keys |
|
72 |
- </div> |
|
73 |
- </div> |
|
74 |
- <div class="span2"> |
|
75 |
- <div style="text-align:right"> |
|
76 |
- <a id="show-add-key-form" class="btn" href="javascript:void(0)">Add SSH Key</a> |
|
77 |
- </div> |
|
78 |
- </div> |
|
133 |
+ <%= include '/include/errors', errors => $errors %> |
|
134 |
+ <%= include '/include/message', message => flash('message') %> |
|
135 |
+ |
|
136 |
+ <div class="border-gray bk-gray-light radius-top" style="padding:5px;"> |
|
137 |
+ <div style="padding:5px"> |
|
138 |
+ <span style="font-size:15px;font-weight:bold;">SSH Keys</span> (<a href="<%= url_for("/$user.keys") %>">see</a>) |
|
79 | 139 |
</div> |
80 | 140 |
</div> |
81 | 141 |
<div style="margin-bottom:30px"> |
... | ... |
@@ -89,19 +149,13 @@ |
89 | 149 |
<div class="span7" style="width:600px"> |
90 | 150 |
<div style="font-size:15px;padding:10px"> |
91 | 151 |
<div> |
92 |
- <b><%= $key->{key} %></b> |
|
93 |
- </div> |
|
94 |
- <div class="muted"> |
|
95 |
- <%= $key->{hash} %> |
|
96 |
- </div> |
|
97 |
- <div> |
|
98 |
- <%= $key->{mtime} %> |
|
152 |
+ <b><%= $key->{title} %></b> |
|
99 | 153 |
</div> |
100 | 154 |
</div> |
101 | 155 |
</div> |
102 | 156 |
<div class="span2"> |
103 |
- <div style="padding-top:20px;text-align:right"> |
|
104 |
- <a class="btn btn-danger" href="<%= url_for("/reset-password")->query(user => $user) %>">Delete</a> |
|
157 |
+ <div style="padding:5px;text-align:right"> |
|
158 |
+ <a class="btn btn-danger" href="javascript:void(0)">Delete</a> |
|
105 | 159 |
</div> |
106 | 160 |
</div> |
107 | 161 |
</div> |
... | ... |
@@ -114,14 +168,14 @@ |
114 | 168 |
% } |
115 | 169 |
</div> |
116 | 170 |
|
117 |
- <div id="add-key-form" style="display:none"> |
|
171 |
+ <div> |
|
118 | 172 |
<div class="border-gray bk-gray-light radius-top" style="padding:5px;font-weight:bold;font-size:17px"> |
119 | 173 |
<div style="font-size:15px;padding:5px"> |
120 | 174 |
Add an SSH Key |
121 | 175 |
</div> |
122 | 176 |
</div> |
123 | 177 |
<div class="border-gray" style="margin-bottom:30px;border-top:none;padding:10px"> |
124 |
- <form> |
|
178 |
+ <form action="<%= url_for->query(op => 'add') %>" method="post" %> |
|
125 | 179 |
<div style="margin-bottom:5px"> |
126 | 180 |
Title |
127 | 181 |
</div> |