Skip to main content
Glama

OpenFGA MCP

RelationshipToolsTest.php14.8 kB
<?php declare(strict_types=1); use OpenFGA\ClientInterface; use OpenFGA\MCP\Tools\RelationshipTools; use OpenFGA\Models\Collections\TupleKeys; use OpenFGA\Models\TupleKey; use OpenFGA\Responses\{CheckResponseInterface, ListObjectsResponseInterface, WriteTuplesResponseInterface}; use OpenFGA\Results\{FailureInterface, SuccessInterface}; beforeEach(function (): void { // Set up online mode for unit tests putenv('OPENFGA_MCP_API_URL=http://localhost:8080'); // Enable write operations for unit tests putenv('OPENFGA_MCP_API_WRITEABLE=true'); $this->client = Mockery::mock(ClientInterface::class); $this->relationshipTools = new RelationshipTools($this->client); }); afterEach(function (): void { Mockery::close(); putenv('OPENFGA_MCP_API_URL='); putenv('OPENFGA_MCP_API_WRITEABLE='); putenv('OPENFGA_MCP_API_RESTRICT='); putenv('OPENFGA_MCP_API_STORE='); putenv('OPENFGA_MCP_API_MODEL='); }); describe('checkPermission', function (): void { it('checks permission and returns allowed', function (): void { $store = 'store-123'; $model = 'model-456'; $user = 'user:1'; $relation = 'reader'; $object = 'document:1'; $mockResponse = Mockery::mock(CheckResponseInterface::class); $mockResponse->shouldReceive('getAllowed')->andReturn(true); $mockPromise = Mockery::mock(SuccessInterface::class); $mockPromise->shouldReceive('failure')->andReturnSelf(); $mockPromise->shouldReceive('success')->with(Mockery::on(function ($callback) use ($mockResponse) { $callback($mockResponse); return true; }))->andReturnSelf(); $this->client->shouldReceive('check') ->withArgs(fn ($s, $m, $t) => $s === $store && $m === $model && $t instanceof TupleKey && $t->getUser() === $user && $t->getRelation() === $relation && $t->getObject() === $object) ->once() ->andReturn($mockPromise); $result = $this->relationshipTools->checkPermission($store, $model, $user, $relation, $object); expect($result)->toBe('✅ Permission allowed'); }); it('checks permission and returns denied', function (): void { $store = 'store-123'; $model = 'model-456'; $user = 'user:1'; $relation = 'writer'; $object = 'document:1'; $mockResponse = Mockery::mock(CheckResponseInterface::class); $mockResponse->shouldReceive('getAllowed')->andReturn(false); $mockPromise = Mockery::mock(SuccessInterface::class); $mockPromise->shouldReceive('failure')->andReturnSelf(); $mockPromise->shouldReceive('success')->with(Mockery::on(function ($callback) use ($mockResponse) { $callback($mockResponse); return true; }))->andReturnSelf(); $this->client->shouldReceive('check') ->once() ->andReturn($mockPromise); $result = $this->relationshipTools->checkPermission($store, $model, $user, $relation, $object); expect($result)->toBe('❌ Permission denied'); }); it('handles check permission failure', function (): void { $store = 'store-123'; $model = 'model-456'; $user = 'user:1'; $relation = 'reader'; $object = 'document:1'; $errorMessage = 'Network error'; $mockPromise = Mockery::mock(FailureInterface::class); $mockPromise->shouldReceive('failure')->with(Mockery::on(function ($callback) use ($errorMessage) { $callback(new Exception($errorMessage)); return true; }))->andReturnSelf(); $mockPromise->shouldReceive('success')->andReturnSelf(); $this->client->shouldReceive('check') ->once() ->andReturn($mockPromise); $result = $this->relationshipTools->checkPermission($store, $model, $user, $relation, $object); expect($result)->toContain('❌ Failed to check permission') ->and($result)->toContain($errorMessage); }); it('prevents checking permission with non-restricted store', function (): void { putenv('OPENFGA_MCP_API_RESTRICT=true'); putenv('OPENFGA_MCP_API_STORE=allowed-store'); $this->client->shouldReceive('check')->never(); $result = $this->relationshipTools->checkPermission('different-store', 'model-123', 'user:1', 'reader', 'doc:1'); expect($result)->toBe('❌ The MCP server is configured in restricted mode. You cannot query stores other than allowed-store in this mode.'); }); it('prevents checking permission with non-restricted model', function (): void { putenv('OPENFGA_MCP_API_RESTRICT=true'); putenv('OPENFGA_MCP_API_MODEL=allowed-model'); $this->client->shouldReceive('check')->never(); $result = $this->relationshipTools->checkPermission('store-123', 'different-model', 'user:1', 'reader', 'doc:1'); expect($result)->toBe('❌ The MCP server is configured in restricted mode. You cannot query using authorization models other than allowed-model in this mode.'); }); }); describe('grantPermission', function (): void { it('grants permission successfully', function (): void { $store = 'store-123'; $model = 'model-456'; $user = 'user:1'; $relation = 'writer'; $object = 'document:1'; $mockResponse = Mockery::mock(WriteTuplesResponseInterface::class); $mockPromise = Mockery::mock(SuccessInterface::class); $mockPromise->shouldReceive('failure')->andReturnSelf(); $mockPromise->shouldReceive('success')->with(Mockery::on(function ($callback) use ($mockResponse) { $callback($mockResponse); return true; }))->andReturnSelf(); $this->client->shouldReceive('writeTuples') ->withArgs(fn ($s, $m, $w, $d = null) => $s === $store && $m === $model && $w instanceof TupleKeys && 1 === $w->count()) ->once() ->andReturn($mockPromise); $result = $this->relationshipTools->grantPermission($store, $model, $user, $relation, $object); expect($result)->toBe('✅ Permission granted successfully'); }); it('handles grant permission failure', function (): void { $store = 'store-123'; $model = 'model-456'; $user = 'user:1'; $relation = 'writer'; $object = 'document:1'; $errorMessage = 'Invalid tuple'; $mockPromise = Mockery::mock(FailureInterface::class); $mockPromise->shouldReceive('failure')->with(Mockery::on(function ($callback) use ($errorMessage) { $callback(new Exception($errorMessage)); return true; }))->andReturnSelf(); $mockPromise->shouldReceive('success')->andReturnSelf(); $this->client->shouldReceive('writeTuples') ->once() ->andReturn($mockPromise); $result = $this->relationshipTools->grantPermission($store, $model, $user, $relation, $object); expect($result)->toContain('❌ Failed to grant permission') ->and($result)->toContain($errorMessage); }); it('prevents granting permission in read-only mode', function (): void { putenv('OPENFGA_MCP_API_WRITEABLE=false'); $this->client->shouldReceive('writeTuples')->never(); $result = $this->relationshipTools->grantPermission('store-123', 'model-456', 'user:1', 'writer', 'doc:1'); expect($result)->toBe('❌ Write operations are disabled for safety. To enable grant permissions, set OPENFGA_MCP_API_WRITEABLE=true.'); }); it('prevents granting permission with non-restricted store', function (): void { putenv('OPENFGA_MCP_API_RESTRICT=true'); putenv('OPENFGA_MCP_API_STORE=allowed-store'); $this->client->shouldReceive('writeTuples')->never(); $result = $this->relationshipTools->grantPermission('different-store', 'model-123', 'user:1', 'writer', 'doc:1'); expect($result)->toBe('❌ The MCP server is configured in restricted mode. You cannot query stores other than allowed-store in this mode.'); }); }); describe('revokePermission', function (): void { it('revokes permission successfully', function (): void { $store = 'store-123'; $model = 'model-456'; $user = 'user:1'; $relation = 'writer'; $object = 'document:1'; $mockResponse = Mockery::mock(WriteTuplesResponseInterface::class); $mockPromise = Mockery::mock(SuccessInterface::class); $mockPromise->shouldReceive('failure')->andReturnSelf(); $mockPromise->shouldReceive('success')->with(Mockery::on(function ($callback) use ($mockResponse) { $callback($mockResponse); return true; }))->andReturnSelf(); $this->client->shouldReceive('writeTuples') ->withArgs(fn ($s, $m, $w = null, $d = null) => $s === $store && $m === $model && $d instanceof TupleKeys && 1 === $d->count()) ->once() ->andReturn($mockPromise); $result = $this->relationshipTools->revokePermission($store, $model, $user, $relation, $object); expect($result)->toBe('✅ Permission revoked successfully'); }); it('prevents revoking permission in read-only mode', function (): void { putenv('OPENFGA_MCP_API_WRITEABLE=false'); $this->client->shouldReceive('writeTuples')->never(); $result = $this->relationshipTools->revokePermission('store-123', 'model-456', 'user:1', 'writer', 'doc:1'); expect($result)->toBe('❌ Write operations are disabled for safety. To enable revoke permissions, set OPENFGA_MCP_API_WRITEABLE=true.'); }); }); describe('listObjects', function (): void { it('lists objects successfully', function (): void { $store = 'store-123'; $model = 'model-456'; $type = 'document'; $user = 'user:1'; $relation = 'reader'; $objects = ['document:1', 'document:2', 'document:3']; $mockResponse = Mockery::mock(ListObjectsResponseInterface::class); $mockResponse->shouldReceive('getObjects')->andReturn($objects); $mockPromise = Mockery::mock(SuccessInterface::class); $mockPromise->shouldReceive('failure')->andReturnSelf(); $mockPromise->shouldReceive('success')->with(Mockery::on(function ($callback) use ($mockResponse) { $callback($mockResponse); return true; }))->andReturnSelf(); $this->client->shouldReceive('listObjects') ->withArgs(fn ($s, $m, $t, $r, $u) => $s === $store && $m === $model && $t === $type && $r === $relation && $u === $user) ->once() ->andReturn($mockPromise); $result = $this->relationshipTools->listObjects($store, $model, $type, $user, $relation); expect($result)->toBeArray() ->and($result)->toHaveCount(3) ->and($result)->toBe($objects); }); it('handles list objects failure', function (): void { $store = 'store-123'; $model = 'model-456'; $type = 'document'; $user = 'user:1'; $relation = 'reader'; $errorMessage = 'Network error'; $mockPromise = Mockery::mock(FailureInterface::class); $mockPromise->shouldReceive('failure')->with(Mockery::on(function ($callback) use ($errorMessage) { $callback(new Exception($errorMessage)); return true; }))->andReturnSelf(); $mockPromise->shouldReceive('success')->andReturnSelf(); $this->client->shouldReceive('listObjects') ->once() ->andReturn($mockPromise); $result = $this->relationshipTools->listObjects($store, $model, $type, $user, $relation); expect($result)->toContain('❌ Failed to list objects') ->and($result)->toContain($errorMessage); }); it('prevents listing objects with non-restricted store', function (): void { putenv('OPENFGA_MCP_API_RESTRICT=true'); putenv('OPENFGA_MCP_API_STORE=allowed-store'); $this->client->shouldReceive('listObjects')->never(); $result = $this->relationshipTools->listObjects('different-store', 'model-123', 'document', 'user:1', 'reader'); expect($result)->toBe('❌ The MCP server is configured in restricted mode. You cannot query stores other than allowed-store in this mode.'); }); }); describe('listUsers', function (): void { it('handles list users failure', function (): void { $store = 'store-123'; $model = 'model-456'; $object = 'document:1'; $relation = 'reader'; $errorMessage = 'Network error'; $mockPromise = Mockery::mock(FailureInterface::class); $mockPromise->shouldReceive('failure')->with(Mockery::on(function ($callback) use ($errorMessage) { $callback(new Exception($errorMessage)); return true; }))->andReturnSelf(); $mockPromise->shouldReceive('success')->andReturnSelf(); $this->client->shouldReceive('listUsers') ->once() ->andReturn($mockPromise); $result = $this->relationshipTools->listUsers($store, $model, $object, $relation); expect($result)->toContain('❌ Failed to list users') ->and($result)->toContain($errorMessage); }); it('prevents listing users with non-restricted model', function (): void { putenv('OPENFGA_MCP_API_RESTRICT=true'); putenv('OPENFGA_MCP_API_MODEL=allowed-model'); $this->client->shouldReceive('listUsers')->never(); $result = $this->relationshipTools->listUsers('store-123', 'different-model', 'document:1', 'reader'); expect($result)->toBe('❌ The MCP server is configured in restricted mode. You cannot query using authorization models other than allowed-model in this mode.'); }); it('prevents listing users with non-restricted store', function (): void { putenv('OPENFGA_MCP_API_RESTRICT=true'); putenv('OPENFGA_MCP_API_STORE=allowed-store'); $this->client->shouldReceive('listUsers')->never(); $result = $this->relationshipTools->listUsers('different-store', 'model-123', 'document:1', 'reader'); expect($result)->toBe('❌ The MCP server is configured in restricted mode. You cannot query stores other than allowed-store in this mode.'); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/evansims/openfga-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server