Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inheriting permissions from LTI platform context #2868

Merged
merged 4 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions sourcecode/hub/app/Enums/ContentRole.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace App\Enums;

use function in_array;

enum ContentRole: string
{
case Owner = 'owner';
case Editor = 'editor';
case Reader = 'reader';

public function grants(self $role): bool
{
return in_array($role, match ($this) {
ContentRole::Owner => [ContentRole::Owner, ContentRole::Editor, ContentRole::Reader],
ContentRole::Editor => [ContentRole::Editor, ContentRole::Reader],
ContentRole::Reader => [ContentRole::Reader],
}, true);
}
}
12 changes: 0 additions & 12 deletions sourcecode/hub/app/Enums/ContentUserRole.php

This file was deleted.

14 changes: 14 additions & 0 deletions sourcecode/hub/app/Events/ContentSaving.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace App\Events;

use App\Models\Content;

final readonly class ContentSaving
{
public function __construct(public Content $content)
{
}
}
15 changes: 15 additions & 0 deletions sourcecode/hub/app/Events/LtiPlatformDeleting.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace App\Events;

use App\Models\LtiPlatform;

final readonly class LtiPlatformDeleting
{
public function __construct(
public LtiPlatform $ltiPlatform,
) {
}
}
34 changes: 34 additions & 0 deletions sourcecode/hub/app/Http/Controllers/Admin/ContextController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Http\Requests\StoreContextRequest;
use App\Models\Context;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Response;

use function redirect;

final readonly class ContextController
{
public function index(): Response
{
$contexts = Context::all();

return response()->view('admin.contexts.index', [
'contexts' => $contexts,
]);
}

public function add(StoreContextRequest $request): RedirectResponse
{
$context = new Context();
$context->fill($request->validated());
$context->save();

return redirect()->route('admin.contexts.index')
->with('alert', trans('messages.context-added'));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@

namespace App\Http\Controllers\Admin;

use App\Enums\ContentRole;
use App\Http\Requests\AddContextToLtiPlatformRequest;
use App\Http\Requests\StoreLtiPlatformRequest;
use App\Http\Requests\UpdateLtiPlatformRequest;
use App\Models\Context;
use App\Models\LtiPlatform;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

use function redirect;
use function route;
use function to_route;

Expand Down Expand Up @@ -71,4 +75,32 @@ public function destroy(LtiPlatform $platform, Request $request): Response

return to_route('admin.lti-platforms.index');
}

public function contexts(LtiPlatform $platform): View
{
// @phpstan-ignore larastan.noUnnecessaryCollectionCall
$availableContexts = Context::all()
->diff($platform->contexts)
->mapWithKeys(fn (Context $context) => [$context->id => $context->name]);

return view('admin.lti-platforms.contexts', [
'available_contexts' => $availableContexts,
'platform' => $platform,
]);
}

public function addContext(
LtiPlatform $platform,
AddContextToLtiPlatformRequest $request,
): RedirectResponse {
$context = Context::where('id', $request->validated('context'))
->firstOrFail();

$platform->contexts()->attach($context, [
'role' => ContentRole::from($request->validated('role')),
]);

return redirect()->back()
->with('alert', trans('messages.context-added-to-lti-platform'));
}
}
31 changes: 29 additions & 2 deletions sourcecode/hub/app/Http/Controllers/ContentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

namespace App\Http\Controllers;

use App\Enums\ContentUserRole;
use App\Enums\ContentRole;
use App\Enums\ContentViewSource;
use App\Enums\LtiToolEditMode;
use App\Http\Requests\AddContextToContentRequest;
use App\Http\Requests\ContentStatisticsRequest;
use App\Http\Requests\ContentStatusRequest;
use App\Http\Requests\DeepLinkingReturnRequest;
Expand All @@ -15,6 +16,7 @@
use App\Lti\LtiLaunchBuilder;
use App\Models\Content;
use App\Models\ContentVersion;
use App\Models\Context;
use App\Models\LtiPlatform;
use App\Models\LtiTool;
use App\Models\LtiToolExtra;
Expand Down Expand Up @@ -145,11 +147,36 @@ public function history(Content $content): View

public function roles(Content $content): View
{
// @phpstan-ignore larastan.noUnnecessaryCollectionCall
$availableContexts = Context::all()
->diff($content->contexts)
->mapWithKeys(fn (Context $context) => [$context->id => $context->name]);

return view('content.roles', [
'content' => $content,
'available_contexts' => $availableContexts,
]);
}

public function addContext(Content $content, AddContextToContentRequest $request): RedirectResponse
{
$context = Context::where('id', $request->validated('context'))
->firstOrFail();

$content->contexts()->attach($context);

return redirect()->back()
->with('alert', trans('messages.context-added-to-content'));
}

public function removeContext(Content $content, Context $context): RedirectResponse
{
$content->contexts()->detach($context->id);

return redirect()->back()
->with('alert', trans('messages.context-removed-from-content'));
}

public function create(): View
{
$tools = LtiTool::all();
Expand Down Expand Up @@ -287,7 +314,7 @@ public function ltiStore(
$content = new Content();
$content->saveQuietly();
$content->users()->save($this->getUser(), [
'role' => ContentUserRole::Owner,
'role' => ContentRole::Owner,
]);
$version = $content->createVersionFromLinkItem($item, $tool, $this->getUser());

Expand Down
22 changes: 22 additions & 0 deletions sourcecode/hub/app/Http/Requests/AddContextToContentRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace App\Http\Requests;

use App\Models\Context;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class AddContextToContentRequest extends FormRequest
{
/**
* @return array<mixed>
*/
public function rules(): array
{
return [
'context' => ['required', 'string', Rule::exists(Context::class, 'id')],
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace App\Http\Requests;

use App\Enums\ContentRole;
use App\Models\Context;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class AddContextToLtiPlatformRequest extends FormRequest
{
/**
* @return array<mixed>
*/
public function rules(): array
{
return [
'context' => ['required', 'string', Rule::exists(Context::class, 'id')],
'role' => ['required', Rule::enum(ContentRole::class)],
];
}
}
8 changes: 4 additions & 4 deletions sourcecode/hub/app/Http/Requests/Api/ContentRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace App\Http\Requests\Api;

use App\Enums\ContentUserRole;
use App\Enums\ContentRole;
use App\Models\User;
use Illuminate\Contracts\Auth\Access\Gate;
use Illuminate\Foundation\Http\FormRequest;
Expand Down Expand Up @@ -39,7 +39,7 @@ public function rules(Gate $gate): array

'roles.*.role' => [
Rule::prohibitedIf(fn () => $gate->denies('admin')),
Rule::enum(ContentUserRole::class),
Rule::enum(ContentRole::class),
'required_with:roles.*.user',
],

Expand All @@ -48,15 +48,15 @@ public function rules(Gate $gate): array
}

/**
* @return array<int, array{user: User, role: ContentUserRole}>
* @return array<int, array{user: User, role: ContentRole}>
*/
public function getRoles(): array
{
$roles = $this->validated('roles', []);

return array_map(fn (array $role) => [
'user' => User::where('id', $role['user'])->firstOrFail(),
'role' => ContentUserRole::from($role['role']),
'role' => ContentRole::from($role['role']),
], $roles);
}

Expand Down
22 changes: 22 additions & 0 deletions sourcecode/hub/app/Http/Requests/StoreContextRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace App\Http\Requests;

use App\Models\Context;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class StoreContextRequest extends FormRequest
{
/**
* @return array<string, mixed>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'regex:/^\w+$/', Rule::unique(Context::class, 'name')],
];
}
}
45 changes: 45 additions & 0 deletions sourcecode/hub/app/Listeners/AddContextsToContent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace App\Listeners;

use App\Events\ContentSaving;
use App\Models\LtiPlatform;
use Illuminate\Http\Request;

// TODO: only apply upon creation
final readonly class AddContextsToContent
{
public function __construct(private Request $request)
{
}

public function handleSaving(ContentSaving $event): void
{
$platform = $this->getLaunchingLtiPlatform();

if (!$platform) {
return;
}

foreach ($platform->contexts as $context) {
$event->content->contexts()->attach($context);
}
}

private function getLaunchingLtiPlatform(): LtiPlatform|null
{
if (!$this->request->hasPreviousSession()) {
return null;
}

$key = $this->request->session()->get('lti.oauth_consumer_key');

if ($key === null) {
return null;
}

return LtiPlatform::where('key', $key)->first();
}
}
2 changes: 2 additions & 0 deletions sourcecode/hub/app/Listeners/ContentListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public function handleForceDeleting(ContentForceDeleting $event): void
->lazy()
->each(fn (ContentVersion $version) => $version->delete());

$event->content->contexts()->detach();

$event->content->tags()->detach();

$event->content->users()->detach();
Expand Down
15 changes: 15 additions & 0 deletions sourcecode/hub/app/Listeners/LtiPlatformListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace App\Listeners;

use App\Events\LtiPlatformDeleting;

class LtiPlatformListener
{
public function handleDeleting(LtiPlatformDeleting $event): void
{
$event->ltiPlatform->contexts()->detach();
}
}
Loading
Loading