Deprecate global channel removal. There are a few issues which need to be addressed. The first of which is a mechanism to tell other servers to destroy your clones. The second is securing this against forgery attempts. In its current form a lot of your data is removed from around the network but the channel clones themselves may remain.

This commit is contained in:
zotlabs 2019-06-04 23:27:11 -07:00
parent e292219db0
commit 0359acb32e
7 changed files with 137 additions and 105 deletions

View file

@ -132,16 +132,18 @@ class Libzotdir {
$pubforums = self::get_directory_setting($observer, 'chantype');
$hide_local = intval(get_config('system','localdir_hide'));
if($hide_local)
if ($hide_local) {
$globaldir = 1;
}
// Build urls without order and pubforums so it's easy to tack on the changed value
// Probably there's an easier way to do this
$directory_sort_order = get_config('system','directory_sort_order');
if(! $directory_sort_order)
if (! $directory_sort_order) {
$directory_sort_order = 'date';
}
$current_order = (($_REQUEST['order']) ? $_REQUEST['order'] : $directory_sort_order);
$suggest = (($_REQUEST['suggest']) ? '&suggest=' . $_REQUEST['suggest'] : '');

View file

@ -1,72 +1,76 @@
<?php
namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
class Removeaccount extends \Zotlabs\Web\Controller {
class Removeaccount extends Controller {
function post() {
if(! local_channel())
if (! local_channel()) {
return;
}
if($_SESSION['delegate'])
if ($_SESSION['delegate']) {
return;
}
if((! x($_POST,'qxz_password')) || (! strlen(trim($_POST['qxz_password']))))
if ((! x($_POST,'qxz_password')) || (! strlen(trim($_POST['qxz_password'])))) {
return;
}
if((! x($_POST,'verify')) || (! strlen(trim($_POST['verify']))))
if ((! x($_POST,'verify')) || (! strlen(trim($_POST['verify'])))) {
return;
}
if($_POST['verify'] !== $_SESSION['remove_account_verify'])
if ($_POST['verify'] !== $_SESSION['remove_account_verify']) {
return;
}
$account = \App::get_account();
$account = App::get_account();
$account_id = get_account_id();
if (! $account_id) {
if (! ($account && $account_id)) {
return;
}
$x = account_verify_password($account['account_email'],$_POST['qxz_password']);
if(! ($x && $x['account']))
if (! ($x && $x['account'])) {
return;
}
if($account['account_password_changed'] > NULL_DATE) {
if ($account['account_password_changed'] > NULL_DATE) {
$d1 = datetime_convert('UTC','UTC','now - 48 hours');
if($account['account_password_changed'] > d1) {
if ($account['account_password_changed'] > d1) {
notice( t('Account removals are not allowed within 48 hours of changing the account password.') . EOL);
return;
}
}
$global_remove = intval($_POST['global']);
account_remove($account_id, 1 - $global_remove);
account_remove($account_id);
}
function get() {
if(! local_channel())
if (! local_channel()) {
goaway(z_root());
}
$hash = random_string();
$_SESSION['remove_account_verify'] = $hash;
$tpl = get_markup_template('removeaccount.tpl');
$o .= replace_macros($tpl, array(
$o .= replace_macros(get_markup_template('removeaccount.tpl'), [
'$basedir' => z_root(),
'$hash' => $hash,
'$title' => t('Remove This Account'),
'$desc' => array(t('WARNING: '), t('This account and all its channels will be completely removed from the network. '), t('This action is permanent and can not be undone!')),
'$desc' => [ t('WARNING: '), t('This account and all its channels will be completely removed from this server. '), t('This action is permanent and can not be undone!') ],
'$passwd' => t('Please enter your password for verification:'),
'$global' => array('global', t('Remove this account, all its channels and all its channel clones from the network'), false, t('By default only the instances of the channels located on this hub will be removed from the network')),
'$submit' => t('Remove Account')
));
]);
return $o;
}
}

View file

@ -1,35 +1,45 @@
<?php
namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
class Removeme extends \Zotlabs\Web\Controller {
class Removeme extends Controller {
function post() {
if(! local_channel())
if (! local_channel()) {
return;
}
if($_SESSION['delegate'])
if ($_SESSION['delegate']) {
return;
}
if((! x($_POST,'qxz_password')) || (! strlen(trim($_POST['qxz_password']))))
if ((! x($_POST,'qxz_password')) || (! strlen(trim($_POST['qxz_password'])))) {
return;
}
if((! x($_POST,'verify')) || (! strlen(trim($_POST['verify']))))
if ((! x($_POST,'verify')) || (! strlen(trim($_POST['verify'])))) {
return;
}
if($_POST['verify'] !== $_SESSION['remove_account_verify'])
if ($_POST['verify'] !== $_SESSION['remove_channel_verify']) {
return;
}
$account = App::get_account();
$account = \App::get_account();
if (! $account) {
return;
}
$x = account_verify_password($account['account_email'],$_POST['qxz_password']);
if(! ($x && $x['account']))
if (! ($x && $x['account'])) {
return;
}
if($account['account_password_changed'] > NULL_DATE) {
if ($account['account_password_changed'] > NULL_DATE) {
$d1 = datetime_convert('UTC','UTC','now - 48 hours');
if($account['account_password_changed'] > d1) {
notice( t('Channel removals are not allowed within 48 hours of changing the account password.') . EOL);
@ -37,35 +47,30 @@ class Removeme extends \Zotlabs\Web\Controller {
}
}
$global_remove = intval($_POST['global']);
channel_remove(local_channel(),1 - $global_remove,true);
channel_remove(local_channel(),true,true);
}
function get() {
if(! local_channel())
if (! local_channel()) {
goaway(z_root());
}
$hash = random_string();
$_SESSION['remove_account_verify'] = $hash;
$_SESSION['remove_channel_verify'] = $hash;
$tpl = get_markup_template('removeme.tpl');
$o .= replace_macros($tpl, array(
$o .= replace_macros(get_markup_template('removeme.tpl'), [
'$basedir' => z_root(),
'$hash' => $hash,
'$title' => t('Remove This Channel'),
'$desc' => [ t('WARNING: '), t('This channel will be completely removed from the network. '), t('This action is permanent and can not be undone!') ],
'$desc' => [ t('WARNING: '), t('This channel will be completely removed from this server. '), t('This action is permanent and can not be undone!') ],
'$passwd' => t('Please enter your password for verification:'),
'$global' => [ 'global', t('Remove this channel and all its clones from the network'), false, t('By default only the instance of the channel located on this hub will be removed from the network'), [ t('No'),t('Yes') ] ],
'$submit' => t('Remove Channel')
));
]);
return $o;
}
}

View file

@ -2040,7 +2040,10 @@ function account_remove($account_id, $local = true, $unset_session = true) {
logger('account_remove: ' . $account_id);
if(! intval($account_id)) {
// Global removal (all clones) not currently supported
$local = true;
if (! intval($account_id)) {
logger('No account.');
return false;
}
@ -2051,7 +2054,7 @@ function account_remove($account_id, $local = true, $unset_session = true) {
intval(ACCOUNT_ROLE_ADMIN)
);
if($r !== false && count($r) == 1 && $r[0]['account_id'] == $account_id) {
if ($r !== false && count($r) == 1 && $r[0]['account_id'] == $account_id) {
logger("Unable to remove the only remaining admin account");
return false;
}
@ -2059,18 +2062,19 @@ function account_remove($account_id, $local = true, $unset_session = true) {
$r = q("select * from account where account_id = %d limit 1",
intval($account_id)
);
$account_email=$r[0]['account_email'];
if(! $r) {
if (! $r) {
logger('No account with id: ' . $account_id);
return false;
}
$account_email = $r[0]['account_email'];
$x = q("select channel_id from channel where channel_account_id = %d",
intval($account_id)
);
if($x) {
foreach($x as $xx) {
if ($x) {
foreach ($x as $xx) {
channel_remove($xx['channel_id'],$local,false);
}
}
@ -2097,14 +2101,21 @@ function account_remove($account_id, $local = true, $unset_session = true) {
*/
function channel_remove($channel_id, $local = true, $unset_session = false) {
if(! $channel_id)
if (! $channel_id) {
return;
}
// global removal (all clones) not currently supported
// hence this operation _may_ leave orphan data on remote servers
$local = true;
logger('Removing channel: ' . $channel_id);
logger('local only: ' . intval($local));
$r = q("select * from channel where channel_id = %d limit 1", intval($channel_id));
if(! $r) {
if (! $r) {
logger('channel not found: ' . $channel_id);
return;
}
@ -2118,9 +2129,12 @@ function channel_remove($channel_id, $local = true, $unset_session = false) {
*/
call_hooks('channel_remove', $r[0]);
if(! $local) {
if(intval($r[0]['channel_removed'])) {
if (! $local) {
// global removal (all clones) not currently supported
if (intval($r[0]['channel_removed'])) {
// already removed. do not propagate deletion of a channel which
// may have been removed locally at some previous time.
return;
@ -2131,7 +2145,7 @@ function channel_remove($channel_id, $local = true, $unset_session = false) {
intval($channel_id)
);
q("delete from pconfig where uid = %d",
$r = q("delete from pconfig where uid = %d",
intval($channel_id)
);
@ -2153,8 +2167,8 @@ function channel_remove($channel_id, $local = true, $unset_session = false) {
where item.uid = %d",
intval($channel_id)
);
if($r) {
foreach($r as $rr) {
if ($r) {
foreach ($r as $rr) {
q("delete from iconfig where iid = %d",
intval($rr['iid'])
);
@ -2183,22 +2197,19 @@ function channel_remove($channel_id, $local = true, $unset_session = false) {
q("DELETE FROM source WHERE src_channel_id = %d", intval($channel_id));
$r = q("select hash FROM attach WHERE uid = %d", intval($channel_id));
if($r) {
foreach($r as $rv) {
if ($r) {
foreach ($r as $rv) {
attach_delete($channel_id,$rv['hash']);
}
}
$r = q("select id from item where uid = %d", intval($channel_id));
if($r) {
foreach($r as $rv) {
if ($r) {
foreach ($r as $rv) {
drop_item($rv['id'],false);
}
}
q("delete from abook where abook_xchan = '%s' and abook_self = 1 ",
dbesc($channel['channel_hash'])
);
@ -2209,15 +2220,15 @@ function channel_remove($channel_id, $local = true, $unset_session = false) {
);
// if this was the default channel, set another one as default
if(App::$account['account_default_channel'] == $channel_id) {
if (App::$account['account_default_channel'] == $channel_id) {
$r = q("select channel_id from channel where channel_account_id = %d and channel_removed = 0 limit 1",
intval(App::$account['account_id']),
intval(PAGE_REMOVED));
if ($r) {
$rr = q("update account set account_default_channel = %d where account_id = %d",
intval($r[0]['channel_id']),
intval(App::$account['account_id']));
intval(App::$account['account_id'])
);
logger("Default channel deleted, changing default to channel_id " . $r[0]['channel_id']);
}
else {
@ -2241,10 +2252,10 @@ function channel_remove($channel_id, $local = true, $unset_session = false) {
$r = q("select hubloc_id from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0",
dbesc($channel['channel_hash'])
);
if($r)
if ($r)
$hublocs = count($r);
if(! $hublocs) {
if (! $hublocs) {
$r = q("update xchan set xchan_deleted = 1 where xchan_hash = '%s' ",
dbesc($channel['channel_hash'])
);
@ -2252,7 +2263,6 @@ function channel_remove($channel_id, $local = true, $unset_session = false) {
//remove from file system
$f = 'store/' . $channel['channel_address'];
if(is_dir($f)) {
@rrmdir($f);
@ -2260,7 +2270,7 @@ function channel_remove($channel_id, $local = true, $unset_session = false) {
Master::Summon([ 'Directory', $channel_id ]);
if($channel_id == local_channel() && $unset_session) {
if ($channel_id == local_channel() && $unset_session) {
App::$session->nuke();
goaway(z_root());
}
@ -2274,7 +2284,6 @@ function channel_remove_final($channel_id) {
q("delete from abconfig where chan = %d", intval($channel_id));
q("delete from pconfig where uid = %d", intval($channel_id));
}

View file

@ -270,12 +270,10 @@ function mark_orphan_hubsxchans() {
function remove_all_xchan_resources($xchan, $channel_id = 0) {
if(intval($channel_id)) {
// $channel_id is reserved for future use.
}
else {
if(intval($channel_id) === 0) {
if (! $xchan) {
return;
@ -283,16 +281,26 @@ function remove_all_xchan_resources($xchan, $channel_id = 0) {
$dirmode = intval(get_config('system','directory_mode'));
// This removes photos this person posted to the photo albums of others.
// it isn't a common activity and there typically won't be any except for
// repository or group permission roles which allow uploads.
$r = q("delete from photo where xchan = '%s'",
// Repeating the above check that $xchan isn't empty because this is the query
// we really need to protect from an empty xchan. Yes it is redundant but please do
// not remove it or this documentation about why it is here.
if ($xchan) {
$r = q("delete from photo where xchan = '%s' and photo_usage = 0",
dbesc($xchan)
);
}
$r = q("select resource_id, resource_type, uid, id from item where ( author_xchan = '%s' or owner_xchan = '%s' ) ",
dbesc($xchan),
dbesc($xchan)
);
if($r) {
foreach($r as $rr) {
if ($r) {
foreach ($r as $rr) {
drop_item($rr,false);
}
}
@ -316,7 +324,7 @@ function remove_all_xchan_resources($xchan, $channel_id = 0) {
);
if($dirmode === false || $dirmode == DIRECTORY_MODE_NORMAL) {
if ($dirmode === false || $dirmode == DIRECTORY_MODE_NORMAL) {
$r = q("delete from xchan where xchan_hash = '%s'",
dbesc($xchan)
@ -347,18 +355,22 @@ function remove_all_xchan_resources($xchan, $channel_id = 0) {
function contact_remove($channel_id, $abook_id) {
if((! $channel_id) || (! $abook_id))
if ((! $channel_id) || (! $abook_id)) {
return false;
}
logger('removing contact ' . $abook_id . ' for channel ' . $channel_id,LOGGER_DEBUG);
logger('removing connection ' . $abook_id . ' for channel ' . $channel_id,LOGGER_DEBUG);
$x = [ 'channel_id' => $channel_id, 'abook_id' => $abook_id ];
$x = [
'channel_id' => $channel_id,
'abook_id' => $abook_id
];
call_hooks('connection_remove',$x);
$archive = get_pconfig($channel_id, 'system','archive_removed_contacts');
if($archive) {
if ($archive) {
q("update abook set abook_archived = 1 where abook_id = %d and abook_channel = %d",
intval($abook_id),
intval($channel_id)
@ -371,20 +383,22 @@ function contact_remove($channel_id, $abook_id) {
intval($channel_id)
);
if(! $r)
if (! $r) {
return false;
}
$abook = $r[0];
if(intval($abook['abook_self']))
if (intval($abook['abook_self'])) {
return false;
}
// remove items in the background as this can take some time
Master::Summon( [ 'Delxitems', $channel_id, $abook['abook_xchan'] ] );
q("delete from abook where abook_id = %d and abook_channel = %d",
$r = q("delete from abook where abook_id = %d and abook_channel = %d",
intval($abook['abook_id']),
intval($channel_id)
);
@ -425,7 +439,7 @@ function remove_abook_items($channel_id,$xchan_hash) {
}
$already_saved = [];
foreach($r as $rr) {
foreach ($r as $rr) {
$w = $x = $y = null;
// optimise so we only process newly seen parent items

View file

@ -12,7 +12,6 @@
<label id="remove-account-pass-label" for="remove-account-pass">{{$passwd}}</label>
<input class="form-control" type="password" id="remove-account-pass" autocomplete="off" name="qxz_password" value=" " />
</div>
{{include file="field_checkbox.tpl" field=$global}}
<button type="submit" name="submit" class="btn btn-danger">{{$submit}}</button>
</form>
</div>

View file

@ -12,7 +12,6 @@
<label id="remove-account-pass-label" for="remove-account-pass">{{$passwd}}</label>
<input class="form-control" type="password" id="remove-account-pass" autocomplete="off" name="qxz_password" value=" " />
</div>
{{include file="field_checkbox.tpl" field=$global}}
<button type="submit" name="submit" class="btn btn-danger">{{$submit}}</button>
</form>
</div>