Fix groups members and filtering

Fixes issue #1
This commit is contained in:
Julien Schneider 2023-01-02 15:33:24 +01:00
parent f4a10277b0
commit 3a277bae44
8 changed files with 86 additions and 21 deletions

View file

@ -31,7 +31,9 @@ class MockGroupAdapter extends AbstractAdapter
$coreGroupMembers = []; $coreGroupMembers = [];
foreach ($mockGroup->getMembers() as $mockGroupMember) { foreach ($mockGroup->getMembers() as $mockGroupMember) {
$coreGroupMember = new MultiValuedAttribute(); $coreGroupMember = new MultiValuedAttribute();
$coreGroupMember->setValue($mockGroupMember); $coreGroupMember->setValue($mockGroupMember->getValue());
$coreGroupMember->setDisplay($mockGroupMember->getDisplay());
$coreGroupMember->setRef($mockGroupMember->getRef());
$coreGroupMembers[] = $coreGroupMember; $coreGroupMembers[] = $coreGroupMember;
} }
@ -60,12 +62,15 @@ class MockGroupAdapter extends AbstractAdapter
if ($coreGroup->getMembers() !== null && !empty($coreGroup->getMembers())) { if ($coreGroup->getMembers() !== null && !empty($coreGroup->getMembers())) {
$mockGroupMembers = []; $mockGroupMembers = [];
foreach ($coreGroup->getMembers() as $coreGroupMember) { foreach ($coreGroup->getMembers() as $coreGroupMember) {
$mockGroupMembers[] = $coreGroupMember->getValue(); $mockGroupMember = new MultiValuedAttribute();
$mockGroupMember->setValue($coreGroupMember->getValue());
$mockGroupMember->setDisplay($coreGroupMember->getDisplay());
$mockGroupMember->setRef($coreGroupMember->getRef());
$mockGroupMembers[] = $mockGroupMember;
} }
$mockGroup->setMembers($mockGroupMembers); $mockGroup->setMembers($mockGroupMembers);
} }
return $mockGroup; return $mockGroup;
} }
} }

View file

@ -109,8 +109,10 @@ class MockGroupDataAccess
$insertRes = $insertStatement->execute([ $insertRes = $insertStatement->execute([
$groupToCreate->getId(), $groupToCreate->getId(),
$groupToCreate->getDisplayName(), $groupToCreate->getDisplayName(),
// we serialize the whole members array and store it as is in the database
// this is relatively dirty, but fine enough for a mock group implementation
$groupToCreate->getMembers() !== null && !empty($groupToCreate->getMembers()) $groupToCreate->getMembers() !== null && !empty($groupToCreate->getMembers())
? $groupToCreate->getMembers() : "", ? serialize($groupToCreate->getMembers()) : "",
$dateNow, $dateNow,
$dateNow $dateNow
]); ]);
@ -149,7 +151,7 @@ class MockGroupDataAccess
$query = $query . "members = ?, "; $query = $query . "members = ?, ";
// We need to transform the string array of user IDs to a single string // We need to transform the string array of user IDs to a single string
$values[] = implode(",", $groupToUpdate->getMembers()); $values[] = serialize($groupToUpdate->getMembers());
} }
if (empty($query)) { if (empty($query)) {

View file

@ -7,7 +7,7 @@ class MockGroup extends MockCommonEntity
/** @var string|null $displayName */ /** @var string|null $displayName */
private $displayName; private $displayName;
/** @var array<string>|null $members */ /** @var \Opf\Models\SCIM\MultiValuedAttribute|null $members */
private $members; private $members;
public function mapFromArray($properties = null): bool public function mapFromArray($properties = null): bool
@ -36,7 +36,8 @@ class MockGroup extends MockCommonEntity
} }
if (strcasecmp($key, 'members') === 0) { if (strcasecmp($key, 'members') === 0) {
$this->members = $value; // the members array is stored as a serialized array in the DB
$this->members = unserialize($value);
continue; continue;
} }
$result = false; $result = false;

View file

@ -62,7 +62,9 @@ class CoreGroup extends CommonEntity
$members = []; $members = [];
foreach ($data['members'] as $member) { foreach ($data['members'] as $member) {
$scimMember = new MultiValuedAttribute(); $scimMember = new MultiValuedAttribute();
$scimMember->setValue($member); $scimMember->setValue($member['value']);
$scimMember->setDisplay($member['display']);
$scimMember->setRef($member['$ref']);
$members[] = $scimMember; $members[] = $scimMember;
} }
$this->setMembers($members); $this->setMembers($members);

View file

@ -2,6 +2,7 @@
namespace Opf\Util\Filters; namespace Opf\Util\Filters;
use Attribute;
use Opf\Models\SCIM\Standard\Filters\AttributeExpression; use Opf\Models\SCIM\Standard\Filters\AttributeExpression;
use Opf\Models\SCIM\Standard\Filters\FilterException; use Opf\Models\SCIM\Standard\Filters\FilterException;
use Opf\Models\SCIM\Standard\Filters\FilterExpression; use Opf\Models\SCIM\Standard\Filters\FilterExpression;
@ -24,16 +25,35 @@ class FilterParser
); );
} }
$splitFilterExpression = explode(" ", $filterExpression); $splitAttributePathFromFilterExpression = explode(" ", $filterExpression, 2);
if (count($splitFilterExpression) < 2 || count($splitFilterExpression) > 3) {
if (!isset($splitAttributePathFromFilterExpression[1]) || empty($splitAttributePathFromFilterExpression[1])) {
throw new FilterException("Incorrectly formatted AttributeExpression"); throw new FilterException("Incorrectly formatted AttributeExpression");
} else if (strcmp($splitAttributePathFromFilterExpression[1], "pr") === 0) {
$attributeExpression = new AttributeExpression(
$splitAttributePathFromFilterExpression[0], // The attribute path
"pr", // The comparison operator (which must be "pr" in this case)
null // The comparison value (must be null due to "pr")
);
} else {
$splitFilterExpressionWithoutAttributePath = explode(" ", $splitAttributePathFromFilterExpression[1], 2);
$attributeExpression = new AttributeExpression(
$splitAttributePathFromFilterExpression[0], // The attribute path
$splitFilterExpressionWithoutAttributePath[0], // The comparison operator (which is different from "pr")
$splitFilterExpressionWithoutAttributePath[1] // The comparison value (can contain spaces)
);
} }
$attributeExpression = new AttributeExpression(
$splitFilterExpression[0], // if (count($splitFilterExpression) < 2 || count($splitFilterExpression) > 3) {
$splitFilterExpression[1], // throw new FilterException("Incorrectly formatted AttributeExpression");
$splitFilterExpression[2] // }
);
// $attributeExpression = new AttributeExpression(
// $splitFilterExpression[0],
// $splitFilterExpression[1],
// $splitFilterExpression[2]
// );
return $attributeExpression; return $attributeExpression;
} }

View file

@ -15,5 +15,10 @@
"userName": "testuser3", "userName": "testuser3",
"externalId": "testuser3external", "externalId": "testuser3external",
"profileUrl": "http://example.com/testuser3" "profileUrl": "http://example.com/testuser3"
},
{
"userName": "some user",
"externalId": "testsome_userexternal",
"profileUrl": "http://example.com/some_user"
} }
] ]

View file

@ -46,12 +46,25 @@ final class FilterParserTest extends TestCase
$parsedFilterExpression = FilterParser::parseFilterExpression($filterString); $parsedFilterExpression = FilterParser::parseFilterExpression($filterString);
} }
public function testParseTooLongFilterExpression() public function testFilterExpressionWithSpacesInValue()
{
$filterString = "userName eq \"some value\"";
$parsedFilterExpression = FilterParser::parseFilterExpression($filterString);
$this->assertInstanceOf(FilterExpression::class, $parsedFilterExpression);
$this->assertInstanceOf(AttributeExpression::class, $parsedFilterExpression);
$this->assertEquals("userName", $parsedFilterExpression->getAttributePath());
$this->assertEquals(AttributeOperator::OP_EQ, $parsedFilterExpression->getCompareOperator());
$this->assertEquals("\"some value\"", $parsedFilterExpression->getComparisonValue());
}
public function testParseIncorrectExpression()
{ {
$this->expectException(FilterException::class); $this->expectException(FilterException::class);
$this->expectExceptionMessage("Incorrectly formatted AttributeExpression"); $this->expectExceptionMessage("Invalid AttributeOperation passed to AttributeExpression");
$filterString = "userName eq some value"; $filterString = "userName blabla \"some moreblabla\"";
$parsedFilterExpression = FilterParser::parseFilterExpression($filterString); $parsedFilterExpression = FilterParser::parseFilterExpression($filterString);
} }
} }

View file

@ -42,15 +42,32 @@ final class FilterUtilTest extends TestCase
$filterString = "userName sw testuser"; $filterString = "userName sw testuser";
$filteredScimUsers = FilterUtil::performFiltering($filterString, $this->scimUsers); $filteredScimUsers = FilterUtil::performFiltering($filterString, $this->scimUsers);
$this->assertEquals($this->scimUsers, $filteredScimUsers); $this->assertEquals(array_splice($this->scimUsers, 0, 3), $filteredScimUsers);
} }
public function testInvalidFiltering() public function testInvalidFiltering()
{ {
$this->expectException(FilterException::class); $this->expectException(FilterException::class);
$this->expectExceptionMessage("Incorrectly formatted AttributeExpression"); $this->expectExceptionMessage("Invalid AttributeOperation passed to AttributeExpression");
$filterString = "externalId eq some value"; $filterString = "externalId bla some value";
$filteredScimUsers = FilterUtil::performFiltering($filterString, $this->scimUsers); $filteredScimUsers = FilterUtil::performFiltering($filterString, $this->scimUsers);
} }
public function testFilteringWithSpaces()
{
$filterString = "userName eq some user";
$filteredScimUsers = FilterUtil::performFiltering($filterString, $this->scimUsers);
$this->assertEquals(array($this->scimUsers[3]), $filteredScimUsers);
}
public function testIncorrectPRFilterExpression()
{
$this->expectException(FilterException::class);
$this->expectExceptionMessage("\"pr\" filter operator must be used without a comparison value");
$filterString = "userName pr \"some blabla\"";
$parsedFilterExpression = FilterUtil::performFiltering($filterString, $this->scimUsers);
}
} }