mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Implement Miniflux user creation
This commit is contained in:
parent
ee0c3c9449
commit
197922f92f
3 changed files with 111 additions and 20 deletions
|
@ -333,6 +333,33 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function editUser(string $user, array $data): array {
|
||||||
|
// map Miniflux properties to internal metadata properties
|
||||||
|
$in = [];
|
||||||
|
foreach (self::USER_META_MAP as $i => [$o,,]) {
|
||||||
|
if (isset($data[$i])) {
|
||||||
|
if ($i === "entry_sorting_direction") {
|
||||||
|
$in[$o] = $data[$i] === "asc";
|
||||||
|
} else {
|
||||||
|
$in[$o] = $data[$i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// make any requested changes
|
||||||
|
$tr = Arsse::$user->begin();
|
||||||
|
if ($in) {
|
||||||
|
Arsse::$user->propertiesSet($user, $in);
|
||||||
|
}
|
||||||
|
// read out the newly-modified user and commit the changes
|
||||||
|
$out = $this->listUsers([$user], true)[0];
|
||||||
|
$tr->commit();
|
||||||
|
// add the input password if a password change was requested
|
||||||
|
if (isset($data['password'])) {
|
||||||
|
$out['password'] = $data['password'];
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
protected function discoverSubscriptions(array $data): ResponseInterface {
|
protected function discoverSubscriptions(array $data): ResponseInterface {
|
||||||
try {
|
try {
|
||||||
$list = Feed::discoverAll((string) $data['url'], (string) $data['username'], (string) $data['password']);
|
$list = Feed::discoverAll((string) $data['url'], (string) $data['username'], (string) $data['password']);
|
||||||
|
@ -378,6 +405,33 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
return new Response($this->listUsers([Arsse::$user->id], false)[0] ?? new \stdClass);
|
return new Response($this->listUsers([Arsse::$user->id], false)[0] ?? new \stdClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function createUser(array $data): ResponseInterface {
|
||||||
|
if ($data['username'] === null) {
|
||||||
|
return new ErrorResponse(["MissingInputValue", 'field' => "username"], 422);
|
||||||
|
} elseif ($data['password'] === null) {
|
||||||
|
return new ErrorResponse(["MissingInputValue", 'field' => "password"], 422);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$tr = Arsse::$user->begin();
|
||||||
|
$data['password'] = Arsse::$user->add($data['username'], $data['password']);
|
||||||
|
$out = $this->editUser($data['username'], $data);
|
||||||
|
$tr->commit();
|
||||||
|
} catch (UserException $e) {
|
||||||
|
switch ($e->getCode()) {
|
||||||
|
case 10403:
|
||||||
|
return new ErrorResponse(["DuplicateUser", 'user' => $data['username']], 409);
|
||||||
|
case 10441:
|
||||||
|
return new ErrorResponse(["InvalidInputValue", 'field' => "timezone"], 422);
|
||||||
|
case 10443:
|
||||||
|
return new ErrorResponse(["InvalidInputValue", 'field' => "entries_per_page"], 422);
|
||||||
|
case 10444:
|
||||||
|
return new ErrorResponse(["InvalidInputValue", 'field' => "username"], 422);
|
||||||
|
}
|
||||||
|
throw $e; // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
return new Response($out, 201);
|
||||||
|
}
|
||||||
|
|
||||||
protected function updateUserByNum(array $path, array $data): ResponseInterface {
|
protected function updateUserByNum(array $path, array $data): ResponseInterface {
|
||||||
// this function is restricted to admins unless the affected user and calling user are the same
|
// this function is restricted to admins unless the affected user and calling user are the same
|
||||||
$user = Arsse::$user->propertiesGet(Arsse::$user->id, false);
|
$user = Arsse::$user->propertiesGet(Arsse::$user->id, false);
|
||||||
|
@ -396,17 +450,6 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
return new ErrorResponse("404", 404);
|
return new ErrorResponse("404", 404);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// map Miniflux properties to internal metadata properties
|
|
||||||
$in = [];
|
|
||||||
foreach (self::USER_META_MAP as $i => [$o,,]) {
|
|
||||||
if (isset($data[$i])) {
|
|
||||||
if ($i === "entry_sorting_direction") {
|
|
||||||
$in[$o] = $data[$i] === "asc";
|
|
||||||
} else {
|
|
||||||
$in[$o] = $data[$i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// make any requested changes
|
// make any requested changes
|
||||||
try {
|
try {
|
||||||
$tr = Arsse::$user->begin();
|
$tr = Arsse::$user->begin();
|
||||||
|
@ -417,11 +460,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
if (isset($data['password'])) {
|
if (isset($data['password'])) {
|
||||||
Arsse::$user->passwordSet($user, $data['password']);
|
Arsse::$user->passwordSet($user, $data['password']);
|
||||||
}
|
}
|
||||||
if ($in) {
|
$out = $this->editUser($user, $data);
|
||||||
Arsse::$user->propertiesSet($user, $in);
|
|
||||||
}
|
|
||||||
// read out the newly-modified user and commit the changes
|
|
||||||
$out = $this->listUsers([$user], true)[0];
|
|
||||||
$tr->commit();
|
$tr->commit();
|
||||||
} catch (UserException $e) {
|
} catch (UserException $e) {
|
||||||
switch ($e->getCode()) {
|
switch ($e->getCode()) {
|
||||||
|
@ -436,10 +475,6 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
}
|
}
|
||||||
throw $e; // @codeCoverageIgnore
|
throw $e; // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
// add the input password if a password change was requested
|
|
||||||
if (isset($data['password'])) {
|
|
||||||
$out['password'] = $data['password'];
|
|
||||||
}
|
|
||||||
return new Response($out);
|
return new Response($out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ return [
|
||||||
'API.Miniflux.Error.401' => 'Access Unauthorized',
|
'API.Miniflux.Error.401' => 'Access Unauthorized',
|
||||||
'API.Miniflux.Error.403' => 'Access Forbidden',
|
'API.Miniflux.Error.403' => 'Access Forbidden',
|
||||||
'API.Miniflux.Error.404' => 'Resource Not Found',
|
'API.Miniflux.Error.404' => 'Resource Not Found',
|
||||||
|
'API.Miniflux.Error.MissingInputValue' => 'Required key "{field}" was not present in input',
|
||||||
'API.Miniflux.Error.InvalidBodyJSON' => 'Invalid JSON payload: {0}',
|
'API.Miniflux.Error.InvalidBodyJSON' => 'Invalid JSON payload: {0}',
|
||||||
'API.Miniflux.Error.InvalidInputType' => 'Input key "{field}" of type {actual} was expected as {expected}',
|
'API.Miniflux.Error.InvalidInputType' => 'Input key "{field}" of type {actual} was expected as {expected}',
|
||||||
'API.Miniflux.Error.InvalidInputValue' => 'Supplied value is not valid for input key "{field}"',
|
'API.Miniflux.Error.InvalidInputValue' => 'Supplied value is not valid for input key "{field}"',
|
||||||
|
|
|
@ -321,6 +321,61 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @dataProvider provideUserAdditions */
|
||||||
|
public function testAddAUser(array $body, $in1, $out1, $in2, $out2, ResponseInterface $exp): void {
|
||||||
|
$this->h = $this->createPartialMock(V1::class, ["now"]);
|
||||||
|
$this->h->method("now")->willReturn(Date::normalize(self::NOW));
|
||||||
|
Arsse::$user = $this->createMock(User::class);
|
||||||
|
Arsse::$user->method("begin")->willReturn($this->transaction);
|
||||||
|
Arsse::$user->method("propertiesGet")->willReturnCallback(function(string $u, bool $includeLarge) {
|
||||||
|
if ($u === "john.doe@example.com") {
|
||||||
|
return ['num' => 1, 'admin' => true];
|
||||||
|
} else {
|
||||||
|
return ['num' => 2, 'admin' => false];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if ($out1 instanceof \Exception) {
|
||||||
|
Arsse::$user->method("add")->willThrowException($out1);
|
||||||
|
} else {
|
||||||
|
Arsse::$user->method("add")->willReturn($in1[1] ?? "");
|
||||||
|
}
|
||||||
|
if ($out2 instanceof \Exception) {
|
||||||
|
Arsse::$user->method("propertiesSet")->willThrowException($out2);
|
||||||
|
} else {
|
||||||
|
Arsse::$user->method("propertiesSet")->willReturn($out2 ?? []);
|
||||||
|
}
|
||||||
|
if ($in1 === null) {
|
||||||
|
Arsse::$user->expects($this->exactly(0))->method("add");
|
||||||
|
} else {
|
||||||
|
Arsse::$user->expects($this->exactly(1))->method("add")->with(...($in1 ?? []));
|
||||||
|
}
|
||||||
|
if ($in2 === null) {
|
||||||
|
Arsse::$user->expects($this->exactly(0))->method("propertiesSet");
|
||||||
|
} else {
|
||||||
|
Arsse::$user->expects($this->exactly(1))->method("propertiesSet")->with($body['username'], $in2);
|
||||||
|
}
|
||||||
|
$this->assertMessage($exp, $this->req("POST", "/users", $body));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideUserAdditions(): iterable {
|
||||||
|
$resp1 = array_merge($this->users[1], ['username' => "ook", 'password' => "eek"]);
|
||||||
|
return [
|
||||||
|
[[], null, null, null, null, new ErrorResponse(["MissingInputValue", 'field' => "username"], 422)],
|
||||||
|
[['username' => "ook"], null, null, null, null, new ErrorResponse(["MissingInputValue", 'field' => "password"], 422)],
|
||||||
|
[['username' => "ook", 'password' => "eek"], ["ook", "eek"], new ExceptionConflict("alreadyExists"), null, null, new ErrorResponse(["DuplicateUser", 'user' => "ook"], 409)],
|
||||||
|
[['username' => "j:k", 'password' => "eek"], ["j:k", "eek"], new UserExceptionInput("invalidUsername"), null, null, new ErrorResponse(["InvalidInputValue", 'field' => "username"], 422)],
|
||||||
|
[['username' => "ook", 'password' => "eek", 'timezone' => "ook"], ["ook", "eek"], "eek", ['tz' => "ook"], new UserExceptionInput("invalidTimezone"), new ErrorResponse(["InvalidInputValue", 'field' => "timezone"], 422)],
|
||||||
|
[['username' => "ook", 'password' => "eek", 'entries_per_page' => -1], ["ook", "eek"], "eek", ['page_size' => -1], new UserExceptionInput("invalidNonZeroInteger"), new ErrorResponse(["InvalidInputValue", 'field' => "entries_per_page"], 422)],
|
||||||
|
[['username' => "ook", 'password' => "eek", 'theme' => "default"], ["ook", "eek"], "eek", ['theme' => "default"], ['theme' => "default"], new Response($resp1, 201)],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddAUserWithoutAuthority(): void {
|
||||||
|
Arsse::$user = $this->createMock(User::class);
|
||||||
|
Arsse::$user->method("propertiesGet")->willReturn(['num' => 1, 'admin' => false]);
|
||||||
|
$this->assertMessage(new ErrorResponse("403", 403), $this->req("POST", "/users", []));
|
||||||
|
}
|
||||||
|
|
||||||
public function testListCategories(): void {
|
public function testListCategories(): void {
|
||||||
\Phake::when(Arsse::$db)->folderList->thenReturn(new Result($this->v([
|
\Phake::when(Arsse::$db)->folderList->thenReturn(new Result($this->v([
|
||||||
['id' => 1, 'name' => "Science"],
|
['id' => 1, 'name' => "Science"],
|
||||||
|
|
Loading…
Reference in a new issue