Indefero

Indefero Commit Details


Date:2010-08-11 16:48:09 (14 years 4 months ago)
Author:Thomas Keller
Branch:develop, feature-issue_links, feature.better-home, feature.content-md5, feature.diff-whitespace, feature.download-md5, feature.issue-links, feature.issue-of-others, feature.issue-summary, feature.search-filter, feature.webrepos, feature.wiki-default-page, master, release-1.1, release-1.2, release-1.3
Commit:e47d51d14ce6b97bfa9c244e41fbef9c9d3582fb
Parents: ce436cc6ec519b6a372c89abb41aa73a1af22c4e
Message:Add the possibility to save mtn public keys per user * src/IDF/Key.php: new column "type" which is either "ssh" or "mtn"; utility functions to query the mtn key name and id as well as all available key types for the current IDF installation * src/IDF/Migrations/16KeyType.php: needed migration script * src/IDF/Plugin/SyncGit/Cron.php: ensure only SSH keys are handled * adapt forms and templates accordingly

Changes:

File differences

src/IDF/Form/Admin/UserCreate.php
7777
7878
7979
80
80
8181
8282
8383
8484
85
85
8686
87
87
8888
8989
9090
9191
9292
9393
94
94
95
96
97
98
99
100
95101
96102
97103
......
138144
139145
140146
141
147
142148
143149
144
150
151
145152
146153
147154
......
162169
163170
164171
165
166
167
168
169
170172
171173
172174
173175
174
176
175177
176178
177179
......
181183
182184
183185
184
186
185187
186188
187189
......
211213
212214
213215
216
217
218
219
220
221
222
223
224
225
226
227
214228
'initial' => '',
'widget' => 'Pluf_Form_Widget_SelectInput',
'widget_attrs' => array(
'choices' =>
'choices' =>
Pluf_L10n::getInstalledLanguages()
),
));
$this->fields['ssh_key'] = new Pluf_Form_Field_Varchar(
$this->fields['public_key'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Add a public SSH key'),
'label' => __('Add a public key'),
'initial' => '',
'widget_attrs' => array('rows' => 3,
'cols' => 40),
'widget' => 'Pluf_Form_Widget_TextareaInput',
'help_text' => __('Be careful to provide the public key and not the private key!')
));
$this->fields['public_key_type'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Key type'),
'initial' => 'ssh',
'widget_attrs' => array('choices' => IDF_Key::getAvailableKeyTypes()),
'widget' => 'Pluf_Form_Widget_SelectInput',
));
}
Pluf_Signal::send('Pluf_User::passwordUpdated',
'IDF_Form_Admin_UserCreate', $params);
// Create the ssh key as needed
if ('' !== $this->cleaned_data['ssh_key']) {
if ('' !== $this->cleaned_data['public_key']) {
$key = new IDF_Key();
$key->user = $user;
$key->content = $this->cleaned_data['ssh_key'];
$key->content = $this->cleaned_data['public_key'];
$key->type = $this->cleaned_data['public_key_type'];
$key->create();
}
// Send an email to the user with the password
return $user;
}
function clean_ssh_key()
{
return IDF_Form_UserAccount::checkSshKey($this->cleaned_data['ssh_key']);
}
function clean_last_name()
{
$last_name = trim($this->cleaned_data['last_name']);
if ($last_name == mb_strtoupper($last_name)) {
return mb_convert_case(mb_strtolower($last_name),
return mb_convert_case(mb_strtolower($last_name),
MB_CASE_TITLE, 'UTF-8');
}
return $last_name;
{
$first_name = trim($this->cleaned_data['first_name']);
if ($first_name == mb_strtoupper($first_name)) {
return mb_convert_case(mb_strtolower($first_name),
return mb_convert_case(mb_strtolower($first_name),
MB_CASE_TITLE, 'UTF-8');
}
return $first_name;
}
return $this->cleaned_data['login'];
}
/**
* Checks whether any given public key is valid
*/
public function clean()
{
$this->cleaned_data['public_key'] =
IDF_Form_UserAccount::checkPublicKey($this->cleaned_data['public_key'],
$this->cleaned_data['public_key_type']);
return $this->cleaned_data;
}
}
src/IDF/Form/UserAccount.php
6565
6666
6767
68
68
6969
7070
7171
......
9292
9393
9494
95
95
9696
97
97
9898
9999
100100
101101
102102
103103
104
104
105
106
107
108
109
110
105111
106112
107113
......
151157
152158
153159
154
160
155161
156162
157
163
164
158165
159166
160167
......
190197
191198
192199
193
200
194201
195202
196203
197204
198205
206
199207
200208
201209
202
210
203211
204212
205
213
214
206215
207216
208
209
210
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
211245
212
213
214
215
216
217
218
219
220
246
247
248
249
250
251
252
253
254
221255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
222279
280
223281
224
282
283
225284
226
227
285
286
287
228288
229
230
289
290
291
292
293
231294
232295
233296
234297
235298
236299
237
238
239
240
241
242
243300
244301
245302
246303
247
304
248305
249306
250307
......
254311
255312
256313
257
314
258315
259316
260317
......
264321
265322
266323
267
324
268325
269326
270327
......
273330
274331
275332
276
333
334
277335
278336
279337
280
338
281339
282340
283341
......
285343
286344
287345
346
347
348
349
350
351
288352
289353
290354
'initial' => $this->user->language,
'widget' => 'Pluf_Form_Widget_SelectInput',
'widget_attrs' => array(
'choices' =>
'choices' =>
Pluf_L10n::getInstalledLanguages()
),
));
),
));
$this->fields['ssh_key'] = new Pluf_Form_Field_Varchar(
$this->fields['public_key'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Add a public SSH key'),
'label' => __('Add a public key'),
'initial' => '',
'widget_attrs' => array('rows' => 3,
'cols' => 40),
'widget' => 'Pluf_Form_Widget_TextareaInput',
'help_text' => __('Be careful to provide your public key and not your private key!')
));
$this->fields['public_key_type'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Key type'),
'initial' => 'ssh',
'widget_attrs' => array('choices' => IDF_Key::getAvailableKeyTypes()),
'widget' => 'Pluf_Form_Widget_SelectInput',
));
}
}
$this->user->setFromFormData($this->cleaned_data);
// Add key as needed.
if ('' !== $this->cleaned_data['ssh_key']) {
if ('' !== $this->cleaned_data['public_key']) {
$key = new IDF_Key();
$key->user = $this->user;
$key->content = $this->cleaned_data['ssh_key'];
$key->content = $this->cleaned_data['public_key'];
$key->type = $this->cleaned_data['public_key_type'];
if ($commit) {
$key->create();
}
}
/**
* Check an ssh key.
* Check arbitrary public keys.
*
* It will throw a Pluf_Form_Invalid exception if it cannot
* validate the key.
*
* @param $key string The key
* @param $key string The type ('ssh' or 'mtn')
* @param $user int The user id of the user of the key (0)
* @return string The clean key
*/
public static function checkSshKey($key, $user=0)
public static function checkPublicKey($key, $type, $user=0)
{
$key = trim($key);
if (strlen($key) == 0) {
if (strlen($key) == 0)
{
return '';
}
$key = str_replace(array("\n", "\r"), '', $key);
if (!preg_match('#^ssh\-[a-z]{3}\s(\S+)\s\S+$#', $key, $matches)) {
throw new Pluf_Form_Invalid(__('The format of the key is not valid. It must start with ssh-dss or ssh-rsa, a long string on a single line and at the end a comment.'));
if ($type == 'ssh')
{
$key = str_replace(array("\n", "\r"), '', $key);
if (!preg_match('#^ssh\-[a-z]{3}\s\S+\s\S+$#', $key))
{
throw new Pluf_Form_Invalid(
__('The format of the key is not valid. It must start '.
'with ssh-dss or ssh-rsa, a long string on a single '.
'line and at the end a comment.')
);
}
if (Pluf::f('idf_strong_key_check', false))
{
$tmpfile = Pluf::f('tmp_folder', '/tmp').'/'.$user.'-key';
file_put_contents($tmpfile, $key, LOCK_EX);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').
'ssh-keygen -l -f '.escapeshellarg($tmpfile).' > /dev/null 2>&1';
exec($cmd, $out, $return);
unlink($tmpfile);
if ($return != 0)
{
throw new Pluf_Form_Invalid(
__('Please check the key as it does not appears '.
'to be a valid key.')
);
}
}
}
if (Pluf::f('idf_strong_key_check', false)) {
$tmpfile = Pluf::f('tmp_folder', '/tmp').'/'.$user.'-key';
file_put_contents($tmpfile, $key, LOCK_EX);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').
'ssh-keygen -l -f '.escapeshellarg($tmpfile).' > /dev/null 2>&1';
exec($cmd, $out, $return);
unlink($tmpfile);
if ($return != 0) {
throw new Pluf_Form_Invalid(__('Please check the key as it does not appears to be a valid key.'));
else if ($type == 'mtn')
{
if (!preg_match('#^\[pubkey [^\]]+\]\s*\S+\s*\[end\]$#', $key))
{
throw new Pluf_Form_Invalid(
__('The format of the key is not valid. It must start '.
'with [pubkey KEYNAME], contain a long string on a single '.
'line and end with [end] in the final third line.')
);
}
if (Pluf::f('idf_strong_key_check', false))
{
// if monotone can read it, it should be valid
$mtn_opts = implode(' ', Pluf::f('mtn_opts', array()));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').
sprintf('%s %s -d :memory: read >/tmp/php-out 2>&1',
Pluf::f('mtn_path', 'mtn'), $mtn_opts);
$fp = popen($cmd, 'w');
fwrite($fp, $key);
$return = pclose($fp);
if ($return != 0)
{
throw new Pluf_Form_Invalid(
__('Please check the key as it does not appears '.
'to be a valid key.')
);
}
}
}
else
{
throw new Pluf_Form_Invalid(__('Unknown key type'));
}
// If $user, then check if not the same key stored
if ($user) {
if ($user)
{
$ruser = Pluf::factory('Pluf_User', $user);
if ($ruser->id > 0) {
$sql = new Pluf_SQL('content=%s', array($key));
if ($ruser->id > 0)
{
$sql = new Pluf_SQL('content=%s AND type=%s', array($key, $type));
$keys = Pluf::factory('IDF_Key')->getList(array('filter' => $sql->gen()));
if (count($keys) > 0) {
throw new Pluf_Form_Invalid(__('You already have uploaded this SSH key.'));
if (count($keys) > 0)
{
throw new Pluf_Form_Invalid(
__('You already have uploaded this key.')
);
}
}
}
return $key;
}
function clean_ssh_key()
{
return self::checkSshKey($this->cleaned_data['ssh_key'],
$this->user->id);
}
function clean_last_name()
{
$last_name = trim($this->cleaned_data['last_name']);
if ($last_name == mb_strtoupper($last_name)) {
return mb_convert_case(mb_strtolower($last_name),
return mb_convert_case(mb_strtolower($last_name),
MB_CASE_TITLE, 'UTF-8');
}
return $last_name;
{
$first_name = trim($this->cleaned_data['first_name']);
if ($first_name == mb_strtoupper($first_name)) {
return mb_convert_case(mb_strtolower($first_name),
return mb_convert_case(mb_strtolower($first_name),
MB_CASE_TITLE, 'UTF-8');
}
return $first_name;
{
$this->cleaned_data['email'] = mb_strtolower(trim($this->cleaned_data['email']));
$guser = new Pluf_User();
$sql = new Pluf_SQL('email=%s AND id!=%s',
$sql = new Pluf_SQL('email=%s AND id!=%s',
array($this->cleaned_data['email'], $this->user->id));
if ($guser->getCount(array('filter' => $sql->gen())) > 0) {
throw new Pluf_Form_Invalid(sprintf(__('The email "%s" is already used.'), $this->cleaned_data['email']));
}
/**
* Check to see if the 2 passwords are the same.
* Check to see if the 2 passwords are the same and if any
* given public key is valid
*/
public function clean()
{
if (!isset($this->errors['password'])
if (!isset($this->errors['password'])
&& !isset($this->errors['password2'])) {
$password1 = $this->cleaned_data['password'];
$password2 = $this->cleaned_data['password2'];
throw new Pluf_Form_Invalid(__('The passwords do not match. Please give them again.'));
}
}
$this->cleaned_data['public_key'] =
self::checkPublicKey($this->cleaned_data['public_key'],
$this->cleaned_data['public_key_type'],
$this->user->id);
return $this->cleaned_data;
}
}
src/IDF/Key.php
2222
2323
2424
25
25
2626
2727
2828
......
3939
4040
4141
42
42
4343
44
44
4545
4646
4747
......
5252
5353
5454
55
55
56
57
58
59
60
61
62
5663
5764
5865
5966
60
67
6168
62
69
6370
6471
6572
......
7582
7683
7784
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
78125
79126
80127
......
89136
90137
91138
92
139
93140
94141
95142
......
128175
129176
130177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
131193
# ***** END LICENSE BLOCK ***** */
/**
* Storage of the SSH keys.
* Storage of the public keys (ssh or monotone).
*
*/
class IDF_Key extends Pluf_Model
array(
'type' => 'Pluf_DB_Field_Sequence',
//It is automatically added.
'blank' => true,
'blank' => true,
),
'user' =>
'user' =>
array(
'type' => 'Pluf_DB_Field_Foreignkey',
'model' => 'Pluf_User',
array(
'type' => 'Pluf_DB_Field_Text',
'blank' => false,
'verbose' => __('ssh key'),
'verbose' => __('public key'),
),
'type' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'size' => 3,
'blank' => false,
'verbose' => __('key type'),
),
);
// WARNING: Not using getSqlTable on the Pluf_User object to
// avoid recursion.
$t_users = $this->_con->pfx.'users';
$t_users = $this->_con->pfx.'users';
$this->_a['views'] = array(
'join_user' =>
'join_user' =>
array(
'join' => 'LEFT JOIN '.$t_users
.' ON '.$t_users.'.id='.$this->_con->qn('user'),
return Pluf_Template::markSafe(Pluf_esc(substr($this->content, 0, 25)).' [...] '.Pluf_esc(substr($this->content, -55)));
}
private function parseMonotoneKeyData()
{
if ($this->type != "mtn")
throw new IDF_Exception("key is not a monotone key type");
preg_match("#^\[pubkey ([^\]]+)\]\s*(\S+)\s*\[end\]$#", $this->content, $m);
if (count($m) != 3)
throw new IDF_Exception("invalid key data detected");
return array($m[1], $m[2]);
}
/**
* Returns the key name of the key, i.e. most of the time the email
* address, which not neccessarily has to be unique across a project.
*
* @return string
*/
function getMonotoneKeyName()
{
list($keyName, ) = $this->parseMonotoneKeyData();
return $keyName;
}
/**
* This function should be used to calculate the key id from the
* public key hash for authentication purposes. This avoids clashes
* in case the key name is not unique across the project
*
* And yes, this is actually how monotone itself calculates the key
* id...
*
* @return string
*/
function getMonotoneKeyId()
{
list($keyName, $keyData) = $this->parseMonotoneKeyData();
return sha1($keyName.":".$keyData);
}
function postSave($create=false)
{
/**
* [description]
*
* This signal allows an application to perform special
* operations after the saving of a SSH Key.
* operations after the saving of a public Key.
*
* [parameters]
*
'IDF_Key', $params);
}
/**
* Returns an associative array with available key types for this
* idf installation, ready for consumption for a <select> widget
*
* @return array
*/
public static function getAvailableKeyTypes()
{
$key_types = array(__("SSH") => 'ssh');
if (array_key_exists('mtn', Pluf::f('allowed_scm', array())))
{
$key_types[__("monotone")] = 'mtn';
}
return $key_types;
}
}
src/IDF/Migrations/16KeyType.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 CĂ©ondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Add the type column in the keys table.
*/
function IDF_Migrations_16KeyType_up($params=null)
{
$table = Pluf::factory('IDF_Keys')->getSqlTable();
$sql = array();
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' ADD COLUMN "type" VARCHAR(3) DEFAULT \'ssh\'';
$sql['MySQL'] = 'ALTER TABLE '.$table.' ADD COLUMN `type` VARCHAR(3) DEFAULT \'ssh\'';
$db = Pluf::db();
$engine = Pluf::f('db_engine');
if (!isset($sql[$engine])) {
throw new Exception('SQLite complex migration not supported.');
}
$db->execute($sql[$engine]);
}
function IDF_Migrations_16KeyType_down($params=null)
{
$table = Pluf::factory('IDF_Keys')->getSqlTable();
$sql = array();
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' DROP COLUMN "type"';
$sql['MySQL'] = 'ALTER TABLE '.$table.' DROP COLUMN `type`';
$db = Pluf::db();
$engine = Pluf::f('db_engine');
if (!isset($sql[$engine])) {
throw new Exception('SQLite complex migration not supported.');
}
$db->execute($sql[$engine]);
}
src/IDF/Plugin/SyncGit/Cron.php
4848
4949
5050
51
51
5252
5353
5454
5555
5656
57
57
5858
5959
6060
$out = '';
$keys = Pluf::factory('IDF_Key')->getList(array('view'=>'join_user'));
foreach ($keys as $key) {
if (strlen($key->content) > 40 // minimal check
if ($key->type == 'ssh' && strlen($key->content) > 40 // minimal check
and preg_match('/^[a-zA-Z][a-zA-Z0-9_.-]*(@[a-zA-Z][a-zA-Z0-9.-]*)?$/', $key->login)) {
$content = trim(str_replace(array("\n", "\r"), '', $key->content));
$out .= sprintf($template, $cmd, $key->login, $content)."\n";
}
}
file_put_contents($authorized_keys, $out, LOCK_EX);
file_put_contents($authorized_keys, $out, LOCK_EX);
}
/**
src/IDF/Views/User.php
134134
135135
136136
137
137
138138
139139
140140
......
148148
149149
150150
151
151
152152
153153
154154
}
/**
* Delete a SSH key.
* Delete a public key.
*
* This is redirecting to the preferences
*/
return new Pluf_HTTP_Response_Forbidden($request);
}
$key->delete();
$request->user->setMessage(__('The SSH key has been deleted.'));
$request->user->setMessage(__('The public key has been deleted.'));
}
return new Pluf_HTTP_Response_Redirect($url);
}
src/IDF/templates/idf/gadmin/users/create.html
4343
4444
4545
46
47
48
49
46
47
48
49
50
51
52
53
5054
5155
5256
</td>
</tr>
<tr>
<th>{$form.f.ssh_key.labelTag}:</th>
<td>{if $form.f.ssh_key.errors}{$form.f.ssh_key.fieldErrors}{/if}
{$form.f.ssh_key|unsafe}<br />
<span class="helptext">{$form.f.ssh_key.help_text}</span>
<th>{$form.f.public_key.labelTag}:</th>
<td>{if $form.f.public_key.errors}{$form.f.public_key.fieldErrors}{/if}
{$form.f.public_key|unsafe}<br />
{$form.f.public_key_type.labelTag} {$form.f.public_key_type|unsafe}<br />
<span class="helptext">
{$form.f.public_key.help_text}
{$form.f.public_key_type.help_text}
</span>
</td>
</tr>
<tr>
src/IDF/templates/idf/user/myaccount.html
5454
5555
5656
57
58
59
60
57
58
59
60
61
62
63
64
6165
6266
6367
......
8286
8387
8488
85
89
8690
8791
8892
</td>
</tr>
<tr>
<th>{$form.f.ssh_key.labelTag}:</th>
<td>{if $form.f.ssh_key.errors}{$form.f.ssh_key.fieldErrors}{/if}
{$form.f.ssh_key|unsafe}<br />
<span class="helptext">{$form.f.ssh_key.help_text}</span>
<th>{$form.f.public_key.labelTag}:</th>
<td>{if $form.f.public_key.errors}{$form.f.public_key.fieldErrors}{/if}
{$form.f.public_key|unsafe}<br />
{$form.f.public_key_type.labelTag} {$form.f.public_key_type|unsafe}<br />
<span class="helptext">
{$form.f.public_key.help_text}
{$form.f.public_key_type.help_text}
</span>
</td>
</tr>
<tr class="pass-info" id="extra-password">
{if count($keys)}
<table summary=" " class="recent-issues">
<tr><th colspan="2">{trans 'Your Current SSH Keys'}</th></tr>
<tr><th colspan="2">{trans 'Your Current Public Keys'}</th></tr>
{foreach $keys as $key}<tr><td>
<span class="mono">{$key.showCompact()}</span></td><td> <form class="star" method="post" action="{url 'IDF_Views_User::deleteKey', array($key.id)}"><input type="image" src="{media '/idf/img/trash.png'}" name="submit" value="{trans 'Delete this key'}" /></form>
</td>

Archive Download the corresponding diff file

Page rendered in 0.11268s using 13 queries.