3
.mailmap
|
@ -182,7 +182,8 @@ Jesús Macias <jmacias@solidgear.es> Jesus Macias <jmacias@full-on-net.com>
|
||||||
jknockaert <jasper@knockaert.nl>
|
jknockaert <jasper@knockaert.nl>
|
||||||
Joan <aseques@gmail.com>
|
Joan <aseques@gmail.com>
|
||||||
Joar Wandborg <git@wandborg.com>
|
Joar Wandborg <git@wandborg.com>
|
||||||
Joas Schilling <nickvergessen@owncloud.com> Joas Schilling <nickvergessen@gmx.de>
|
Joas Schilling <coding@schilljs.com> Joas Schilling <nickvergessen@gmx.de>
|
||||||
|
Joas Schilling <coding@schilljs.com> Joas Schilling <nickvergessen@owncloud.com>
|
||||||
joel hansson <joel.hansson@gmail.com>
|
joel hansson <joel.hansson@gmail.com>
|
||||||
Johan Björk <johanimon@gmail.com>
|
Johan Björk <johanimon@gmail.com>
|
||||||
Johannes Twittmann <github.com@deryo.de>
|
Johannes Twittmann <github.com@deryo.de>
|
||||||
|
|
|
@ -1079,22 +1079,27 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
||||||
'lastmodified' => time(),
|
'lastmodified' => time(),
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach($this->subscriptionPropertyMap as $xmlName=>$dbName) {
|
$propertiesBoolean = ['striptodos', 'stripalarms', 'stripattachments'];
|
||||||
if (isset($properties[$xmlName])) {
|
|
||||||
|
|
||||||
$values[$dbName] = $properties[$xmlName];
|
foreach($this->subscriptionPropertyMap as $xmlName=>$dbName) {
|
||||||
$fieldNames[] = $dbName;
|
if (array_key_exists($xmlName, $properties)) {
|
||||||
|
$values[$dbName] = $properties[$xmlName];
|
||||||
|
if (in_array($dbName, $propertiesBoolean)) {
|
||||||
|
$values[$dbName] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$valuesToInsert = array();
|
||||||
|
|
||||||
$query = $this->db->getQueryBuilder();
|
$query = $this->db->getQueryBuilder();
|
||||||
|
|
||||||
|
foreach (array_keys($values) as $name) {
|
||||||
|
$valuesToInsert[$name] = $query->createNamedParameter($values[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
$query->insert('calendarsubscriptions')
|
$query->insert('calendarsubscriptions')
|
||||||
->values([
|
->values($valuesToInsert)
|
||||||
'principaluri' => $query->createNamedParameter($values['principaluri']),
|
|
||||||
'uri' => $query->createNamedParameter($values['uri']),
|
|
||||||
'source' => $query->createNamedParameter($values['source']),
|
|
||||||
'lastmodified' => $query->createNamedParameter($values['lastmodified']),
|
|
||||||
])
|
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
return $this->db->lastInsertId('*PREFIX*calendarsubscriptions');
|
return $this->db->lastInsertId('*PREFIX*calendarsubscriptions');
|
||||||
|
|
|
@ -333,15 +333,20 @@ EOD;
|
||||||
|
|
||||||
public function testSubscriptions() {
|
public function testSubscriptions() {
|
||||||
$id = $this->backend->createSubscription(self::UNIT_TEST_USER, 'Subscription', [
|
$id = $this->backend->createSubscription(self::UNIT_TEST_USER, 'Subscription', [
|
||||||
'{http://calendarserver.org/ns/}source' => new Href('test-source')
|
'{http://calendarserver.org/ns/}source' => new Href('test-source'),
|
||||||
|
'{http://apple.com/ns/ical/}calendar-color' => '#1C4587',
|
||||||
|
'{http://calendarserver.org/ns/}subscribed-strip-todos' => ''
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$subscriptions = $this->backend->getSubscriptionsForUser(self::UNIT_TEST_USER);
|
$subscriptions = $this->backend->getSubscriptionsForUser(self::UNIT_TEST_USER);
|
||||||
$this->assertEquals(1, count($subscriptions));
|
$this->assertEquals(1, count($subscriptions));
|
||||||
|
$this->assertEquals('#1C4587', $subscriptions[0]['{http://apple.com/ns/ical/}calendar-color']);
|
||||||
|
$this->assertEquals(true, $subscriptions[0]['{http://calendarserver.org/ns/}subscribed-strip-todos']);
|
||||||
$this->assertEquals($id, $subscriptions[0]['id']);
|
$this->assertEquals($id, $subscriptions[0]['id']);
|
||||||
|
|
||||||
$patch = new PropPatch([
|
$patch = new PropPatch([
|
||||||
'{DAV:}displayname' => 'Unit test',
|
'{DAV:}displayname' => 'Unit test',
|
||||||
|
'{http://apple.com/ns/ical/}calendar-color' => '#ac0606',
|
||||||
]);
|
]);
|
||||||
$this->backend->updateSubscription($id, $patch);
|
$this->backend->updateSubscription($id, $patch);
|
||||||
$patch->commit();
|
$patch->commit();
|
||||||
|
@ -350,6 +355,7 @@ EOD;
|
||||||
$this->assertEquals(1, count($subscriptions));
|
$this->assertEquals(1, count($subscriptions));
|
||||||
$this->assertEquals($id, $subscriptions[0]['id']);
|
$this->assertEquals($id, $subscriptions[0]['id']);
|
||||||
$this->assertEquals('Unit test', $subscriptions[0]['{DAV:}displayname']);
|
$this->assertEquals('Unit test', $subscriptions[0]['{DAV:}displayname']);
|
||||||
|
$this->assertEquals('#ac0606', $subscriptions[0]['{http://apple.com/ns/ical/}calendar-color']);
|
||||||
|
|
||||||
$this->backend->deleteSubscription($id);
|
$this->backend->deleteSubscription($id);
|
||||||
$subscriptions = $this->backend->getSubscriptionsForUser(self::UNIT_TEST_USER);
|
$subscriptions = $this->backend->getSubscriptionsForUser(self::UNIT_TEST_USER);
|
||||||
|
|
|
@ -217,28 +217,32 @@ class FederatedShareProvider implements IShareProvider {
|
||||||
$share->getPermissions(),
|
$share->getPermissions(),
|
||||||
$token
|
$token
|
||||||
);
|
);
|
||||||
$sharedByFederatedId = $share->getSharedBy();
|
|
||||||
if ($this->userManager->userExists($sharedByFederatedId)) {
|
|
||||||
$sharedByFederatedId = $sharedByFederatedId . '@' . $this->addressHandler->generateRemoteURL();
|
|
||||||
}
|
|
||||||
$send = $this->notifications->sendRemoteShare(
|
|
||||||
$token,
|
|
||||||
$share->getSharedWith(),
|
|
||||||
$share->getNode()->getName(),
|
|
||||||
$shareId,
|
|
||||||
$share->getShareOwner(),
|
|
||||||
$share->getShareOwner() . '@' . $this->addressHandler->generateRemoteURL(),
|
|
||||||
$share->getSharedBy(),
|
|
||||||
$sharedByFederatedId
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($send === false) {
|
try {
|
||||||
$data = $this->getRawShare($shareId);
|
$sharedByFederatedId = $share->getSharedBy();
|
||||||
$share = $this->createShareObject($data);
|
if ($this->userManager->userExists($sharedByFederatedId)) {
|
||||||
$this->removeShareFromTable($share);
|
$sharedByFederatedId = $sharedByFederatedId . '@' . $this->addressHandler->generateRemoteURL();
|
||||||
$message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.',
|
}
|
||||||
[$share->getNode()->getName(), $share->getSharedWith()]);
|
$send = $this->notifications->sendRemoteShare(
|
||||||
throw new \Exception($message_t);
|
$token,
|
||||||
|
$share->getSharedWith(),
|
||||||
|
$share->getNode()->getName(),
|
||||||
|
$shareId,
|
||||||
|
$share->getShareOwner(),
|
||||||
|
$share->getShareOwner() . '@' . $this->addressHandler->generateRemoteURL(),
|
||||||
|
$share->getSharedBy(),
|
||||||
|
$sharedByFederatedId
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($send === false) {
|
||||||
|
$message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.',
|
||||||
|
[$share->getNode()->getName(), $share->getSharedWith()]);
|
||||||
|
throw new \Exception($message_t);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->logger->error('Failed to notify remote server of federated share, removing share (' . $e->getMessage() . ')');
|
||||||
|
$this->removeShareFromTableById($shareId);
|
||||||
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $shareId;
|
return $shareId;
|
||||||
|
@ -526,13 +530,22 @@ class FederatedShareProvider implements IShareProvider {
|
||||||
* @param IShare $share
|
* @param IShare $share
|
||||||
*/
|
*/
|
||||||
public function removeShareFromTable(IShare $share) {
|
public function removeShareFromTable(IShare $share) {
|
||||||
|
$this->removeShareFromTableById($share->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove share from table
|
||||||
|
*
|
||||||
|
* @param string $shareId
|
||||||
|
*/
|
||||||
|
private function removeShareFromTableById($shareId) {
|
||||||
$qb = $this->dbConnection->getQueryBuilder();
|
$qb = $this->dbConnection->getQueryBuilder();
|
||||||
$qb->delete('share')
|
$qb->delete('share')
|
||||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
|
||||||
$qb->execute();
|
$qb->execute();
|
||||||
|
|
||||||
$qb->delete('federated_reshares')
|
$qb->delete('federated_reshares')
|
||||||
->where($qb->expr()->eq('share_id', $qb->createNamedParameter($share->getId())));
|
->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId)));
|
||||||
$qb->execute();
|
$qb->execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -217,10 +217,6 @@ class FederatedShareProviderTest extends \Test\TestCase {
|
||||||
'sharedBy@http://localhost/'
|
'sharedBy@http://localhost/'
|
||||||
)->willReturn(false);
|
)->willReturn(false);
|
||||||
|
|
||||||
$this->rootFolder->expects($this->once())
|
|
||||||
->method('getUserFolder')
|
|
||||||
->with('shareOwner')
|
|
||||||
->will($this->returnSelf());
|
|
||||||
$this->rootFolder->method('getById')
|
$this->rootFolder->method('getById')
|
||||||
->with('42')
|
->with('42')
|
||||||
->willReturn([$node]);
|
->willReturn([$node]);
|
||||||
|
@ -244,6 +240,62 @@ class FederatedShareProviderTest extends \Test\TestCase {
|
||||||
$this->assertFalse($data);
|
$this->assertFalse($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCreateException() {
|
||||||
|
$share = $this->shareManager->newShare();
|
||||||
|
|
||||||
|
$node = $this->getMock('\OCP\Files\File');
|
||||||
|
$node->method('getId')->willReturn(42);
|
||||||
|
$node->method('getName')->willReturn('myFile');
|
||||||
|
|
||||||
|
$share->setSharedWith('user@server.com')
|
||||||
|
->setSharedBy('sharedBy')
|
||||||
|
->setShareOwner('shareOwner')
|
||||||
|
->setPermissions(19)
|
||||||
|
->setNode($node);
|
||||||
|
|
||||||
|
$this->tokenHandler->method('generateToken')->willReturn('token');
|
||||||
|
|
||||||
|
$this->addressHandler->expects($this->any())->method('generateRemoteURL')
|
||||||
|
->willReturn('http://localhost/');
|
||||||
|
$this->addressHandler->expects($this->any())->method('splitUserRemote')
|
||||||
|
->willReturn(['user', 'server.com']);
|
||||||
|
|
||||||
|
$this->notifications->expects($this->once())
|
||||||
|
->method('sendRemoteShare')
|
||||||
|
->with(
|
||||||
|
$this->equalTo('token'),
|
||||||
|
$this->equalTo('user@server.com'),
|
||||||
|
$this->equalTo('myFile'),
|
||||||
|
$this->anything(),
|
||||||
|
'shareOwner',
|
||||||
|
'shareOwner@http://localhost/',
|
||||||
|
'sharedBy',
|
||||||
|
'sharedBy@http://localhost/'
|
||||||
|
)->willThrowException(new \Exception('dummy'));
|
||||||
|
|
||||||
|
$this->rootFolder->method('getById')
|
||||||
|
->with('42')
|
||||||
|
->willReturn([$node]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$share = $this->provider->create($share);
|
||||||
|
$this->fail();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->assertEquals('dummy', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb = $this->connection->getQueryBuilder();
|
||||||
|
$stmt = $qb->select('*')
|
||||||
|
->from('share')
|
||||||
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$data = $stmt->fetch();
|
||||||
|
$stmt->closeCursor();
|
||||||
|
|
||||||
|
$this->assertFalse($data);
|
||||||
|
}
|
||||||
|
|
||||||
public function testCreateShareWithSelf() {
|
public function testCreateShareWithSelf() {
|
||||||
$share = $this->shareManager->newShare();
|
$share = $this->shareManager->newShare();
|
||||||
|
|
||||||
|
|
|
@ -513,7 +513,7 @@
|
||||||
* Event handler for when the URL changed
|
* Event handler for when the URL changed
|
||||||
*/
|
*/
|
||||||
_onUrlChanged: function(e) {
|
_onUrlChanged: function(e) {
|
||||||
if (e && e.dir) {
|
if (e && _.isString(e.dir)) {
|
||||||
this.changeDirectory(e.dir, false, true);
|
this.changeDirectory(e.dir, false, true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1397,6 +1397,16 @@
|
||||||
return OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/');
|
return OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_isValidPath: function(path) {
|
||||||
|
var sections = path.split('/');
|
||||||
|
for (var i = 0; i < sections.length; i++) {
|
||||||
|
if (sections[i] === '..') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current directory name and updates the breadcrumb.
|
* Sets the current directory name and updates the breadcrumb.
|
||||||
* @param targetDir directory to display
|
* @param targetDir directory to display
|
||||||
|
@ -1404,7 +1414,11 @@
|
||||||
* @param {string} [fileId] file id
|
* @param {string} [fileId] file id
|
||||||
*/
|
*/
|
||||||
_setCurrentDir: function(targetDir, changeUrl, fileId) {
|
_setCurrentDir: function(targetDir, changeUrl, fileId) {
|
||||||
targetDir = targetDir.replace(/\\/g, '/').replace(/\/\.\.\//g, '/');
|
targetDir = targetDir.replace(/\\/g, '/');
|
||||||
|
if (!this._isValidPath(targetDir)) {
|
||||||
|
targetDir = '/';
|
||||||
|
changeUrl = true;
|
||||||
|
}
|
||||||
var previousDir = this.getCurrentDirectory(),
|
var previousDir = this.getCurrentDirectory(),
|
||||||
baseDir = OC.basename(targetDir);
|
baseDir = OC.basename(targetDir);
|
||||||
|
|
||||||
|
@ -1415,6 +1429,9 @@
|
||||||
this.setPageTitle();
|
this.setPageTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (targetDir.length > 0 && targetDir[0] !== '/') {
|
||||||
|
targetDir = '/' + targetDir;
|
||||||
|
}
|
||||||
this._currentDirectory = targetDir;
|
this._currentDirectory = targetDir;
|
||||||
|
|
||||||
// legacy stuff
|
// legacy stuff
|
||||||
|
|
|
@ -1334,13 +1334,32 @@ describe('OCA.Files.FileList tests', function() {
|
||||||
fileList.changeDirectory('/another\\subdir');
|
fileList.changeDirectory('/another\\subdir');
|
||||||
expect(fileList.getCurrentDirectory()).toEqual('/another/subdir');
|
expect(fileList.getCurrentDirectory()).toEqual('/another/subdir');
|
||||||
});
|
});
|
||||||
it('converts backslashes to slashes and removes traversals when calling changeDirectory()', function() {
|
it('switches to root dir when current directory is invalid', function() {
|
||||||
fileList.changeDirectory('/another\\subdir/../foo\\../bar\\..\\file/..\\folder/../');
|
_.each([
|
||||||
expect(fileList.getCurrentDirectory()).toEqual('/another/subdir/foo/bar/file/folder/');
|
'..',
|
||||||
|
'/..',
|
||||||
|
'../',
|
||||||
|
'/../',
|
||||||
|
'/../abc',
|
||||||
|
'/abc/..',
|
||||||
|
'/abc/../',
|
||||||
|
'/../abc/',
|
||||||
|
'/another\\subdir/../foo\\../bar\\..\\file/..\\folder/../'
|
||||||
|
], function(path) {
|
||||||
|
fileList.changeDirectory(path);
|
||||||
|
expect(fileList.getCurrentDirectory()).toEqual('/');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
it('does not convert folders with a ".." in the name', function() {
|
it('allows paths with dotdot at the beginning or end', function() {
|
||||||
fileList.changeDirectory('/abc../def');
|
_.each([
|
||||||
expect(fileList.getCurrentDirectory()).toEqual('/abc../def');
|
'/..abc',
|
||||||
|
'/def..',
|
||||||
|
'/...',
|
||||||
|
'/abc../def'
|
||||||
|
], function(path) {
|
||||||
|
fileList.changeDirectory(path);
|
||||||
|
expect(fileList.getCurrentDirectory()).toEqual(path);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
it('switches to root dir when current directory does not exist', function() {
|
it('switches to root dir when current directory does not exist', function() {
|
||||||
fileList.changeDirectory('/unexist');
|
fileList.changeDirectory('/unexist');
|
||||||
|
@ -1404,6 +1423,12 @@ describe('OCA.Files.FileList tests', function() {
|
||||||
setDirSpy.restore();
|
setDirSpy.restore();
|
||||||
getFolderContentsStub.restore();
|
getFolderContentsStub.restore();
|
||||||
});
|
});
|
||||||
|
it('prepends a slash to directory if none was given', function() {
|
||||||
|
fileList.changeDirectory('');
|
||||||
|
expect(fileList.getCurrentDirectory()).toEqual('/');
|
||||||
|
fileList.changeDirectory('noslash');
|
||||||
|
expect(fileList.getCurrentDirectory()).toEqual('/noslash');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
describe('breadcrumb events', function() {
|
describe('breadcrumb events', function() {
|
||||||
var deferredList;
|
var deferredList;
|
||||||
|
|
|
@ -98,15 +98,30 @@ class Backends extends Base {
|
||||||
$result = [
|
$result = [
|
||||||
'name' => $data['name'],
|
'name' => $data['name'],
|
||||||
'identifier' => $data['identifier'],
|
'identifier' => $data['identifier'],
|
||||||
'configuration' => array_map(function (DefinitionParameter $parameter) {
|
'configuration' => $this->formatConfiguration($data['configuration'])
|
||||||
return $parameter->getTypeName();
|
|
||||||
}, $data['configuration'])
|
|
||||||
];
|
];
|
||||||
if ($backend instanceof Backend) {
|
if ($backend instanceof Backend) {
|
||||||
$result['storage_class'] = $backend->getStorageClass();
|
$result['storage_class'] = $backend->getStorageClass();
|
||||||
$authBackends = $this->backendService->getAuthMechanismsByScheme(array_keys($backend->getAuthSchemes()));
|
$authBackends = $this->backendService->getAuthMechanismsByScheme(array_keys($backend->getAuthSchemes()));
|
||||||
$result['supported_authentication_backends'] = array_keys($authBackends);
|
$result['supported_authentication_backends'] = array_keys($authBackends);
|
||||||
|
$authConfig = array_map(function (AuthMechanism $auth) {
|
||||||
|
return $this->serializeAuthBackend($auth)['configuration'];
|
||||||
|
}, $authBackends);
|
||||||
|
$result['authentication_configuration'] = array_combine(array_keys($authBackends), $authConfig);
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DefinitionParameter[] $parameters
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
private function formatConfiguration(array $parameters) {
|
||||||
|
$configuration = array_filter($parameters, function (DefinitionParameter $parameter) {
|
||||||
|
return $parameter->getType() !== DefinitionParameter::VALUE_HIDDEN;
|
||||||
|
});
|
||||||
|
return array_map(function (DefinitionParameter $parameter) {
|
||||||
|
return $parameter->getTypeName();
|
||||||
|
}, $configuration);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,42 @@
|
||||||
var permission = parseInt($tr.attr('data-permissions')) | OC.PERMISSION_DELETE;
|
var permission = parseInt($tr.attr('data-permissions')) | OC.PERMISSION_DELETE;
|
||||||
$tr.attr('data-permissions', permission);
|
$tr.attr('data-permissions', permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add row with expiration date for link only shares - influenced by _createRow of filelist
|
||||||
|
if (this._linksOnly) {
|
||||||
|
var expirationTimestamp = 0;
|
||||||
|
if(fileData.shares[0].expiration !== null) {
|
||||||
|
expirationTimestamp = moment(fileData.shares[0].expiration).valueOf();
|
||||||
|
}
|
||||||
|
$tr.attr('data-expiration', expirationTimestamp);
|
||||||
|
|
||||||
|
// date column (1000 milliseconds to seconds, 60 seconds, 60 minutes, 24 hours)
|
||||||
|
// difference in days multiplied by 5 - brightest shade for expiry dates in more than 32 days (160/5)
|
||||||
|
var modifiedColor = Math.round((expirationTimestamp - (new Date()).getTime()) / 1000 / 60 / 60 / 24 * 5);
|
||||||
|
// ensure that the brightest color is still readable
|
||||||
|
if (modifiedColor >= 160) {
|
||||||
|
modifiedColor = 160;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expirationTimestamp > 0) {
|
||||||
|
formatted = OC.Util.formatDate(expirationTimestamp);
|
||||||
|
text = OC.Util.relativeModifiedDate(expirationTimestamp);
|
||||||
|
} else {
|
||||||
|
formatted = t('files_sharing', 'No expiration date set');
|
||||||
|
text = '';
|
||||||
|
modifiedColor = 160;
|
||||||
|
}
|
||||||
|
td = $('<td></td>').attr({"class": "date"});
|
||||||
|
td.append($('<span></span>').attr({
|
||||||
|
"class": "modified",
|
||||||
|
"title": formatted,
|
||||||
|
"style": 'color:rgb(' + modifiedColor + ',' + modifiedColor + ',' + modifiedColor + ')'
|
||||||
|
}).text(text)
|
||||||
|
.tooltip({placement: 'top'})
|
||||||
|
);
|
||||||
|
|
||||||
|
$tr.append(td);
|
||||||
|
}
|
||||||
return $tr;
|
return $tr;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -98,6 +134,11 @@
|
||||||
// root has special permissions
|
// root has special permissions
|
||||||
this.$el.find('#emptycontent').toggleClass('hidden', !this.isEmpty);
|
this.$el.find('#emptycontent').toggleClass('hidden', !this.isEmpty);
|
||||||
this.$el.find('#filestable thead th').toggleClass('hidden', this.isEmpty);
|
this.$el.find('#filestable thead th').toggleClass('hidden', this.isEmpty);
|
||||||
|
|
||||||
|
// hide expiration date header for non link only shares
|
||||||
|
if (!this._linksOnly) {
|
||||||
|
this.$el.find('th.column-expiration').addClass('hidden');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
OCA.Files.FileList.prototype.updateEmptyContent.apply(this, arguments);
|
OCA.Files.FileList.prototype.updateEmptyContent.apply(this, arguments);
|
||||||
|
@ -249,6 +290,7 @@
|
||||||
type: share.share_type,
|
type: share.share_type,
|
||||||
target: share.share_with,
|
target: share.share_with,
|
||||||
stime: share.stime * 1000,
|
stime: share.stime * 1000,
|
||||||
|
expiration: share.expiration,
|
||||||
};
|
};
|
||||||
if (self._sharedWithUser) {
|
if (self._sharedWithUser) {
|
||||||
file.shareOwner = share.displayname_owner;
|
file.shareOwner = share.displayname_owner;
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
<th id="headerDate" class="hidden column-mtime">
|
<th id="headerDate" class="hidden column-mtime">
|
||||||
<a id="modified" class="columntitle" data-sort="mtime"><span><?php p($l->t( 'Share time' )); ?></span><span class="sort-indicator"></span></a>
|
<a id="modified" class="columntitle" data-sort="mtime"><span><?php p($l->t( 'Share time' )); ?></span><span class="sort-indicator"></span></a>
|
||||||
</th>
|
</th>
|
||||||
|
<th class="hidden column-expiration">
|
||||||
|
<a class="columntitle"><span><?php p($l->t( 'Expiration date' )); ?></span></a>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="fileList">
|
<tbody id="fileList">
|
||||||
|
|
|
@ -30,6 +30,9 @@ use OCP\Share\IShare;
|
||||||
use OCP\Share\IManager;
|
use OCP\Share\IManager;
|
||||||
use OCP\Files\Mount\IMountPoint;
|
use OCP\Files\Mount\IMountPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group DB
|
||||||
|
*/
|
||||||
class MountProviderTest extends \Test\TestCase {
|
class MountProviderTest extends \Test\TestCase {
|
||||||
|
|
||||||
/** @var MountProvider */
|
/** @var MountProvider */
|
||||||
|
|
|
@ -38,6 +38,9 @@ describe('OCA.Sharing.FileList tests', function() {
|
||||||
'<th class="hidden column-mtime">' +
|
'<th class="hidden column-mtime">' +
|
||||||
'<a class="columntitle" data-sort="mtime"><span class="sort-indicator"></span></a>' +
|
'<a class="columntitle" data-sort="mtime"><span class="sort-indicator"></span></a>' +
|
||||||
'</th>' +
|
'</th>' +
|
||||||
|
'<th class="column-expiration">' +
|
||||||
|
'<a class="columntitle"><span>Expiration date</span></a>' +
|
||||||
|
'</th>' +
|
||||||
'</tr></thead>' +
|
'</tr></thead>' +
|
||||||
'<tbody id="fileList"></tbody>' +
|
'<tbody id="fileList"></tbody>' +
|
||||||
'<tfoot></tfoot>' +
|
'<tfoot></tfoot>' +
|
||||||
|
@ -512,6 +515,9 @@ describe('OCA.Sharing.FileList tests', function() {
|
||||||
|
|
||||||
fileList.reload();
|
fileList.reload();
|
||||||
|
|
||||||
|
var expirationDateInADay = moment()
|
||||||
|
.add(1, 'days').format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
|
||||||
/* jshint camelcase: false */
|
/* jshint camelcase: false */
|
||||||
ocsResponse = {
|
ocsResponse = {
|
||||||
ocs: {
|
ocs: {
|
||||||
|
@ -528,12 +534,28 @@ describe('OCA.Sharing.FileList tests', function() {
|
||||||
path: '/local path/local name.txt',
|
path: '/local path/local name.txt',
|
||||||
permissions: 1,
|
permissions: 1,
|
||||||
stime: 11111,
|
stime: 11111,
|
||||||
|
expiration: null,
|
||||||
share_type: OC.Share.SHARE_TYPE_LINK,
|
share_type: OC.Share.SHARE_TYPE_LINK,
|
||||||
share_with: null,
|
share_with: null,
|
||||||
token: 'abc',
|
token: 'abc',
|
||||||
mimetype: 'text/plain',
|
mimetype: 'text/plain',
|
||||||
uid_owner: 'user1',
|
uid_owner: 'user1',
|
||||||
displayname_owner: 'User One'
|
displayname_owner: 'User One'
|
||||||
|
},{
|
||||||
|
id: 8,
|
||||||
|
item_type: 'file',
|
||||||
|
item_source: 50,
|
||||||
|
file_source: 50,
|
||||||
|
path: '/local path2/local name2.txt',
|
||||||
|
permissions: 1,
|
||||||
|
stime: 11112,
|
||||||
|
expiration: expirationDateInADay,
|
||||||
|
share_type: OC.Share.SHARE_TYPE_LINK,
|
||||||
|
share_with: null,
|
||||||
|
token: 'abcd',
|
||||||
|
mimetype: 'text/plain2',
|
||||||
|
uid_owner: 'user2',
|
||||||
|
displayname_owner: 'User One2'
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -570,10 +592,10 @@ describe('OCA.Sharing.FileList tests', function() {
|
||||||
JSON.stringify(ocsResponse)
|
JSON.stringify(ocsResponse)
|
||||||
);
|
);
|
||||||
|
|
||||||
// only renders the link share entry
|
// only renders the link share entries
|
||||||
var $rows = fileList.$el.find('tbody tr');
|
var $rows = fileList.$el.find('tbody tr');
|
||||||
var $tr = $rows.eq(0);
|
var $tr = $rows.eq(0);
|
||||||
expect($rows.length).toEqual(1);
|
expect($rows.length).toEqual(2);
|
||||||
expect($tr.attr('data-id')).toEqual('49');
|
expect($tr.attr('data-id')).toEqual('49');
|
||||||
expect($tr.attr('data-type')).toEqual('file');
|
expect($tr.attr('data-type')).toEqual('file');
|
||||||
expect($tr.attr('data-file')).toEqual('local name.txt');
|
expect($tr.attr('data-file')).toEqual('local name.txt');
|
||||||
|
@ -588,8 +610,17 @@ describe('OCA.Sharing.FileList tests', function() {
|
||||||
expect($tr.find('a.name').attr('href')).toEqual(
|
expect($tr.find('a.name').attr('href')).toEqual(
|
||||||
OC.webroot + '/remote.php/webdav/local%20path/local%20name.txt'
|
OC.webroot + '/remote.php/webdav/local%20path/local%20name.txt'
|
||||||
);
|
);
|
||||||
|
expect($tr.attr('data-expiration')).toEqual('0');
|
||||||
|
expect($tr.find('td:last-child span').text()).toEqual('');
|
||||||
|
|
||||||
expect($tr.find('.nametext').text().trim()).toEqual('local name.txt');
|
expect($tr.find('.nametext').text().trim()).toEqual('local name.txt');
|
||||||
|
|
||||||
|
// change to next row
|
||||||
|
$tr = $rows.eq(1);
|
||||||
|
expect($tr.attr('data-id')).toEqual('50');
|
||||||
|
expect($tr.attr('data-file')).toEqual('local name2.txt');
|
||||||
|
expect($tr.attr('data-expiration')).not.toEqual('0');
|
||||||
|
expect($tr.find('td:last-child span').text()).toEqual('in a day');
|
||||||
});
|
});
|
||||||
it('does not show virtual token recipient as recipient when password was set', function() {
|
it('does not show virtual token recipient as recipient when password was set', function() {
|
||||||
/* jshint camelcase: false */
|
/* jshint camelcase: false */
|
||||||
|
@ -613,7 +644,7 @@ describe('OCA.Sharing.FileList tests', function() {
|
||||||
// only renders the link share entry
|
// only renders the link share entry
|
||||||
var $rows = fileList.$el.find('tbody tr');
|
var $rows = fileList.$el.find('tbody tr');
|
||||||
var $tr = $rows.eq(0);
|
var $tr = $rows.eq(0);
|
||||||
expect($rows.length).toEqual(1);
|
expect($rows.length).toEqual(2);
|
||||||
expect($tr.attr('data-id')).toEqual('49');
|
expect($tr.attr('data-id')).toEqual('49');
|
||||||
expect($tr.attr('data-type')).toEqual('file');
|
expect($tr.attr('data-type')).toEqual('file');
|
||||||
expect($tr.attr('data-file')).toEqual('local name.txt');
|
expect($tr.attr('data-file')).toEqual('local name.txt');
|
||||||
|
|
|
@ -645,7 +645,7 @@ class Storage {
|
||||||
//distance between two version too small, mark to delete
|
//distance between two version too small, mark to delete
|
||||||
$toDelete[$key] = $version['path'] . '.v' . $version['version'];
|
$toDelete[$key] = $version['path'] . '.v' . $version['version'];
|
||||||
$size += $version['size'];
|
$size += $version['size'];
|
||||||
\OCP\Util::writeLog('files_versions', 'Mark to expire '. $version['path'] .' next version should be ' . $nextVersion . " or smaller. (prevTimestamp: " . $prevTimestamp . "; step: " . $step, \OCP\Util::DEBUG);
|
\OCP\Util::writeLog('files_versions', 'Mark to expire '. $version['path'] .' next version should be ' . $nextVersion . " or smaller. (prevTimestamp: " . $prevTimestamp . "; step: " . $step, \OCP\Util::INFO);
|
||||||
} else {
|
} else {
|
||||||
$nextVersion = $version['version'] - $step;
|
$nextVersion = $version['version'] - $step;
|
||||||
$prevTimestamp = $version['version'];
|
$prevTimestamp = $version['version'];
|
||||||
|
@ -766,7 +766,7 @@ class Storage {
|
||||||
self::deleteVersion($versionsFileview, $path);
|
self::deleteVersion($versionsFileview, $path);
|
||||||
\OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
|
\OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $path, 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
|
||||||
unset($allVersions[$key]); // update array with the versions we keep
|
unset($allVersions[$key]); // update array with the versions we keep
|
||||||
\OCP\Util::writeLog('files_versions', "Expire: " . $path, \OCP\Util::DEBUG);
|
\OCP\Util::writeLog('files_versions', "Expire: " . $path, \OCP\Util::INFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if enough space is available after versions are rearranged.
|
// Check if enough space is available after versions are rearranged.
|
||||||
|
@ -782,7 +782,7 @@ class Storage {
|
||||||
\OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
|
\OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
|
||||||
self::deleteVersion($versionsFileview, $version['path'] . '.v' . $version['version']);
|
self::deleteVersion($versionsFileview, $version['path'] . '.v' . $version['version']);
|
||||||
\OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
|
\OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_QUOTA_EXCEEDED));
|
||||||
\OCP\Util::writeLog('files_versions', 'running out of space! Delete oldest version: ' . $version['path'].'.v'.$version['version'] , \OCP\Util::DEBUG);
|
\OCP\Util::writeLog('files_versions', 'running out of space! Delete oldest version: ' . $version['path'].'.v'.$version['version'] , \OCP\Util::INFO);
|
||||||
$versionsSize -= $version['size'];
|
$versionsSize -= $version['size'];
|
||||||
$availableSpace += $version['size'];
|
$availableSpace += $version['size'];
|
||||||
next($allVersions);
|
next($allVersions);
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
namespace OCA\User_LDAP\User;
|
namespace OCA\User_LDAP\User;
|
||||||
|
|
||||||
|
use OC\Cache\CappedMemoryCache;
|
||||||
use OCA\User_LDAP\LogWrapper;
|
use OCA\User_LDAP\LogWrapper;
|
||||||
use OCA\User_LDAP\FilesystemHelper;
|
use OCA\User_LDAP\FilesystemHelper;
|
||||||
use OCP\IAvatarManager;
|
use OCP\IAvatarManager;
|
||||||
|
@ -62,14 +63,13 @@ class Manager {
|
||||||
protected $avatarManager;
|
protected $avatarManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* array['byDN'] \OCA\User_LDAP\User\User[]
|
* @var CappedMemoryCache $usersByDN
|
||||||
* ['byUid'] \OCA\User_LDAP\User\User[]
|
|
||||||
* @var array $users
|
|
||||||
*/
|
*/
|
||||||
protected $users = array(
|
protected $usersByDN;
|
||||||
'byDN' => array(),
|
/**
|
||||||
'byUid' => array(),
|
* @var CappedMemoryCache $usersByUid
|
||||||
);
|
*/
|
||||||
|
protected $usersByUid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param IConfig $ocConfig
|
* @param IConfig $ocConfig
|
||||||
|
@ -93,6 +93,8 @@ class Manager {
|
||||||
$this->image = $image;
|
$this->image = $image;
|
||||||
$this->db = $db;
|
$this->db = $db;
|
||||||
$this->userManager = $userManager;
|
$this->userManager = $userManager;
|
||||||
|
$this->usersByDN = new CappedMemoryCache();
|
||||||
|
$this->usersByUid = new CappedMemoryCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,8 +118,8 @@ class Manager {
|
||||||
$user = new User($uid, $dn, $this->access, $this->ocConfig,
|
$user = new User($uid, $dn, $this->access, $this->ocConfig,
|
||||||
$this->ocFilesystem, clone $this->image, $this->ocLog,
|
$this->ocFilesystem, clone $this->image, $this->ocLog,
|
||||||
$this->avatarManager, $this->userManager);
|
$this->avatarManager, $this->userManager);
|
||||||
$this->users['byDN'][$dn] = $user;
|
$this->usersByDN[$dn] = $user;
|
||||||
$this->users['byUid'][$uid] = $user;
|
$this->usersByUid[$uid] = $user;
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,10 +221,10 @@ class Manager {
|
||||||
*/
|
*/
|
||||||
public function get($id) {
|
public function get($id) {
|
||||||
$this->checkAccess();
|
$this->checkAccess();
|
||||||
if(isset($this->users['byDN'][$id])) {
|
if(isset($this->usersByDN[$id])) {
|
||||||
return $this->users['byDN'][$id];
|
return $this->usersByDN[$id];
|
||||||
} else if(isset($this->users['byUid'][$id])) {
|
} else if(isset($this->usersByUid[$id])) {
|
||||||
return $this->users['byUid'][$id];
|
return $this->usersByUid[$id];
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->access->stringResemblesDN($id) ) {
|
if($this->access->stringResemblesDN($id) ) {
|
||||||
|
|
|
@ -228,6 +228,10 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$newDn = $this->access->getUserDnByUuid($uuid);
|
$newDn = $this->access->getUserDnByUuid($uuid);
|
||||||
|
//check if renamed user is still valid by reapplying the ldap filter
|
||||||
|
if(!is_array($this->access->readAttribute($newDn, '', $this->access->connection->ldapUserFilter))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$this->access->getUserMapper()->setDNbyUUID($newDn, $uuid);
|
$this->access->getUserMapper()->setDNbyUUID($newDn, $uuid);
|
||||||
return true;
|
return true;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
</button>
|
</button>
|
||||||
<a href="<?php p(link_to_docs('admin-ldap')); ?>"
|
<a href="<?php p(link_to_docs('admin-ldap')); ?>"
|
||||||
target="_blank" rel="noreferrer">
|
target="_blank" rel="noreferrer">
|
||||||
<img src="<?php print_unescaped(image_path('', 'actions/info.png')); ?>"
|
<img src="<?php print_unescaped(image_path('', 'actions/info.svg')); ?>"
|
||||||
style="height:1.75ex" />
|
style="height:1.75ex" />
|
||||||
<?php p($l->t('Help'));?>
|
<?php p($l->t('Help'));?>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
</button>
|
</button>
|
||||||
<a href="<?php p(link_to_docs('admin-ldap')); ?>"
|
<a href="<?php p(link_to_docs('admin-ldap')); ?>"
|
||||||
target="_blank" rel="noreferrer">
|
target="_blank" rel="noreferrer">
|
||||||
<img src="<?php print_unescaped(image_path('', 'actions/info.png')); ?>"
|
<img src="<?php print_unescaped(image_path('', 'actions/info.svg')); ?>"
|
||||||
style="height:1.75ex" />
|
style="height:1.75ex" />
|
||||||
<span class="ldap_grey"><?php p($l->t('Help'));?></span>
|
<span class="ldap_grey"><?php p($l->t('Help'));?></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -26,6 +26,21 @@ Feature: sharing
|
||||||
Then the OCS status code should be "100"
|
Then the OCS status code should be "100"
|
||||||
And the HTTP status code should be "200"
|
And the HTTP status code should be "200"
|
||||||
|
|
||||||
|
Scenario: Creating a new share with user who already received a share through their group
|
||||||
|
Given As an "admin"
|
||||||
|
And user "user0" exists
|
||||||
|
And user "user1" exists
|
||||||
|
And group "sharing-group" exists
|
||||||
|
And user "user1" belongs to group "sharing-group"
|
||||||
|
And file "welcome.txt" of user "user0" is shared with group "sharing-group"
|
||||||
|
And As an "user0"
|
||||||
|
Then sending "POST" to "/apps/files_sharing/api/v1/shares" with
|
||||||
|
| path | welcome.txt |
|
||||||
|
| shareWith | user1 |
|
||||||
|
| shareType | 0 |
|
||||||
|
Then the OCS status code should be "100"
|
||||||
|
And the HTTP status code should be "200"
|
||||||
|
|
||||||
Scenario: Creating a new public share
|
Scenario: Creating a new public share
|
||||||
Given user "user0" exists
|
Given user "user0" exists
|
||||||
And As an "user0"
|
And As an "user0"
|
||||||
|
|
|
@ -32,7 +32,6 @@ use OC\AppFramework\Utility\TimeFactory;
|
||||||
use OC\Core\Controller\AvatarController;
|
use OC\Core\Controller\AvatarController;
|
||||||
use OC\Core\Controller\LoginController;
|
use OC\Core\Controller\LoginController;
|
||||||
use OC\Core\Controller\LostController;
|
use OC\Core\Controller\LostController;
|
||||||
use OC\Core\Controller\OccController;
|
|
||||||
use OC\Core\Controller\TokenController;
|
use OC\Core\Controller\TokenController;
|
||||||
use OC\Core\Controller\TwoFactorChallengeController;
|
use OC\Core\Controller\TwoFactorChallengeController;
|
||||||
use OC\Core\Controller\UserController;
|
use OC\Core\Controller\UserController;
|
||||||
|
@ -126,18 +125,6 @@ class Application extends App {
|
||||||
$c->query('SecureRandom')
|
$c->query('SecureRandom')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
$container->registerService('OccController', function(SimpleContainer $c) {
|
|
||||||
return new OccController(
|
|
||||||
$c->query('AppName'),
|
|
||||||
$c->query('Request'),
|
|
||||||
$c->query('Config'),
|
|
||||||
new \OC\Console\Application(
|
|
||||||
$c->query('Config'),
|
|
||||||
$c->query('ServerContainer')->getEventDispatcher(),
|
|
||||||
$c->query('Request')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core class wrappers
|
* Core class wrappers
|
||||||
|
|
|
@ -1,147 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* @author Victor Dubiniuk <dubiniuk@owncloud.com>
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
|
||||||
* @license AGPL-3.0
|
|
||||||
*
|
|
||||||
* This code is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License, version 3,
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program 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 Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace OC\Core\Controller;
|
|
||||||
|
|
||||||
use OCP\AppFramework\Controller;
|
|
||||||
use OCP\AppFramework\Http\JSONResponse;
|
|
||||||
use OC\Console\Application;
|
|
||||||
use OCP\IConfig;
|
|
||||||
use OCP\IRequest;
|
|
||||||
use Symfony\Component\Console\Input\ArrayInput;
|
|
||||||
use Symfony\Component\Console\Output\BufferedOutput;
|
|
||||||
|
|
||||||
class OccController extends Controller {
|
|
||||||
|
|
||||||
/** @var array */
|
|
||||||
private $allowedCommands = [
|
|
||||||
'app:disable',
|
|
||||||
'app:enable',
|
|
||||||
'app:getpath',
|
|
||||||
'app:list',
|
|
||||||
'check',
|
|
||||||
'config:list',
|
|
||||||
'maintenance:mode',
|
|
||||||
'status',
|
|
||||||
'upgrade'
|
|
||||||
];
|
|
||||||
|
|
||||||
/** @var IConfig */
|
|
||||||
private $config;
|
|
||||||
/** @var Application */
|
|
||||||
private $console;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OccController constructor.
|
|
||||||
*
|
|
||||||
* @param string $appName
|
|
||||||
* @param IRequest $request
|
|
||||||
* @param IConfig $config
|
|
||||||
* @param Application $console
|
|
||||||
*/
|
|
||||||
public function __construct($appName, IRequest $request,
|
|
||||||
IConfig $config, Application $console) {
|
|
||||||
parent::__construct($appName, $request);
|
|
||||||
$this->config = $config;
|
|
||||||
$this->console = $console;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @PublicPage
|
|
||||||
* @NoCSRFRequired
|
|
||||||
*
|
|
||||||
* Execute occ command
|
|
||||||
* Sample request
|
|
||||||
* POST http://domain.tld/index.php/occ/status',
|
|
||||||
* {
|
|
||||||
* 'params': {
|
|
||||||
* '--no-warnings':'1',
|
|
||||||
* '--output':'json'
|
|
||||||
* },
|
|
||||||
* 'token': 'someToken'
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @param string $command
|
|
||||||
* @param string $token
|
|
||||||
* @param array $params
|
|
||||||
*
|
|
||||||
* @return JSONResponse
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function execute($command, $token, $params = []) {
|
|
||||||
try {
|
|
||||||
$this->validateRequest($command, $token);
|
|
||||||
|
|
||||||
$output = new BufferedOutput();
|
|
||||||
$formatter = $output->getFormatter();
|
|
||||||
$formatter->setDecorated(false);
|
|
||||||
$this->console->setAutoExit(false);
|
|
||||||
$this->console->loadCommands(new ArrayInput([]), $output);
|
|
||||||
|
|
||||||
$inputArray = array_merge(['command' => $command], $params);
|
|
||||||
$input = new ArrayInput($inputArray);
|
|
||||||
|
|
||||||
$exitCode = $this->console->run($input, $output);
|
|
||||||
$response = $output->fetch();
|
|
||||||
|
|
||||||
$json = [
|
|
||||||
'exitCode' => $exitCode,
|
|
||||||
'response' => $response
|
|
||||||
];
|
|
||||||
|
|
||||||
} catch (\UnexpectedValueException $e){
|
|
||||||
$json = [
|
|
||||||
'exitCode' => 126,
|
|
||||||
'response' => 'Not allowed',
|
|
||||||
'details' => $e->getMessage()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return new JSONResponse($json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if command is allowed and has a valid security token
|
|
||||||
* @param $command
|
|
||||||
* @param $token
|
|
||||||
*/
|
|
||||||
protected function validateRequest($command, $token){
|
|
||||||
if (!in_array($this->request->getRemoteAddress(), ['::1', '127.0.0.1', 'localhost'])) {
|
|
||||||
throw new \UnexpectedValueException('Web executor is not allowed to run from a different host');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!in_array($command, $this->allowedCommands)) {
|
|
||||||
throw new \UnexpectedValueException(sprintf('Command "%s" is not allowed to run via web request', $command));
|
|
||||||
}
|
|
||||||
|
|
||||||
$coreToken = $this->config->getSystemValue('updater.secret', '');
|
|
||||||
if ($coreToken === '') {
|
|
||||||
throw new \UnexpectedValueException(
|
|
||||||
'updater.secret is undefined in config/config.php. Either browse the admin settings in your ownCloud and click "Open updater" or define a strong secret using <pre>php -r \'echo password_hash("MyStrongSecretDoUseYourOwn!", PASSWORD_DEFAULT)."\n";\'</pre> and set this in the config.php.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!password_verify($token, $coreToken)) {
|
|
||||||
throw new \UnexpectedValueException(
|
|
||||||
'updater.secret does not match the provided token'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 132 B |
Before Width: | Height: | Size: 128 B |
Before Width: | Height: | Size: 127 B |
Before Width: | Height: | Size: 233 B |
Before Width: | Height: | Size: 267 B |
Before Width: | Height: | Size: 251 B |
Before Width: | Height: | Size: 134 B |
Before Width: | Height: | Size: 134 B |
Before Width: | Height: | Size: 135 B |
Before Width: | Height: | Size: 138 B |
Before Width: | Height: | Size: 130 B |
Before Width: | Height: | Size: 134 B |
Before Width: | Height: | Size: 279 B |
Before Width: | Height: | Size: 212 B |
Before Width: | Height: | Size: 266 B |
Before Width: | Height: | Size: 493 B |
Before Width: | Height: | Size: 323 B |
Before Width: | Height: | Size: 132 B |
Before Width: | Height: | Size: 208 B |
Before Width: | Height: | Size: 236 B |
Before Width: | Height: | Size: 208 B |
Before Width: | Height: | Size: 295 B |
Before Width: | Height: | Size: 192 B |
Before Width: | Height: | Size: 134 B |
Before Width: | Height: | Size: 169 B |
Before Width: | Height: | Size: 228 B |
Before Width: | Height: | Size: 228 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 392 B |
Before Width: | Height: | Size: 249 B |
Before Width: | Height: | Size: 324 B |
Before Width: | Height: | Size: 315 B |
Before Width: | Height: | Size: 355 B |
Before Width: | Height: | Size: 229 B |
Before Width: | Height: | Size: 106 B |
Before Width: | Height: | Size: 122 B |
Before Width: | Height: | Size: 159 B |
Before Width: | Height: | Size: 92 B |
Before Width: | Height: | Size: 96 B |
Before Width: | Height: | Size: 163 B |
Before Width: | Height: | Size: 136 B |
Before Width: | Height: | Size: 150 B |
Before Width: | Height: | Size: 163 B |
Before Width: | Height: | Size: 127 B |
Before Width: | Height: | Size: 307 B |
Before Width: | Height: | Size: 378 B |
Before Width: | Height: | Size: 424 B |
Before Width: | Height: | Size: 317 B |
Before Width: | Height: | Size: 336 B |
Before Width: | Height: | Size: 321 B |
Before Width: | Height: | Size: 193 B |
Before Width: | Height: | Size: 309 B |
Before Width: | Height: | Size: 293 B |
Before Width: | Height: | Size: 256 B |
Before Width: | Height: | Size: 264 B |
Before Width: | Height: | Size: 264 B |
Before Width: | Height: | Size: 118 B |
Before Width: | Height: | Size: 180 B |
Before Width: | Height: | Size: 496 B |
Before Width: | Height: | Size: 492 B |
Before Width: | Height: | Size: 122 B |
Before Width: | Height: | Size: 120 B |
Before Width: | Height: | Size: 238 B |
Before Width: | Height: | Size: 149 B |
Before Width: | Height: | Size: 128 B |
Before Width: | Height: | Size: 128 B |
Before Width: | Height: | Size: 142 B |
Before Width: | Height: | Size: 142 B |
Before Width: | Height: | Size: 300 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 959 B |
Before Width: | Height: | Size: 648 B |
Before Width: | Height: | Size: 804 B |
Before Width: | Height: | Size: 984 B |
Before Width: | Height: | Size: 376 B |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 892 B |
Before Width: | Height: | Size: 805 B |
Before Width: | Height: | Size: 640 B |