Forms - v1.0.0-beta1

Policies

Authorization policies for ArtisanPack UI Forms.

Overview

The package includes two authorization policies:

  • FormPolicy - Controls access to forms
  • SubmissionPolicy - Controls access to submissions

FormPolicy

Controls who can view, create, update, and delete forms.

Policy Methods

Method Description
viewAny Can list all forms
view Can view a specific form
create Can create new forms
update Can update a form
delete Can delete a form
restore Can restore soft-deleted forms
forceDelete Can permanently delete forms

Default Behavior

By default, the package uses permissive authorization:

// config/artisanpack/forms.php
'authorization' => [
    'restrict_by_owner' => false, // Anyone can access any form
    'allow_admin_bypass' => true,
    'user_model' => 'App\\Models\\User',
],

Security Warning: With restrict_by_owner = false, any authenticated user can access all forms. Enable ownership enforcement for multi-user applications.

Ownership Enforcement

Enable ownership-based access:

// config/artisanpack/forms.php
'authorization' => [
    'restrict_by_owner' => true,
],

With this enabled:

// Users can only access their own forms
$user->can('view', $form); // true if $form->user_id === $user->id

// Admins bypass ownership checks
$admin->can('view', $form); // true if $admin->is_admin

Admin Bypass

Users with is_admin attribute can bypass ownership:

// config/artisanpack/forms.php
'authorization' => [
    'allow_admin_bypass' => true,
],

Ensure your User model has the is_admin attribute:

// User model
protected $casts = [
    'is_admin' => 'boolean',
];

Custom Policy

Override the default policy:

// app/Policies/CustomFormPolicy.php

namespace App\Policies;

use App\Models\User;
use ArtisanPackUI\Forms\Models\Form;
use ArtisanPackUI\Forms\Policies\FormPolicy;

class CustomFormPolicy extends FormPolicy
{
    public function update(User $user, Form $form): bool
    {
        // Add custom logic
        if ($user->hasRole('editor')) {
            return true;
        }

        return parent::update($user, $form);
    }

    public function delete(User $user, Form $form): bool
    {
        // Only allow deletion for admins
        return $user->is_admin;
    }
}

Register the custom policy:

// app/Providers/AuthServiceProvider.php

use ArtisanPackUI\Forms\Models\Form;
use App\Policies\CustomFormPolicy;

protected $policies = [
    Form::class => CustomFormPolicy::class,
];

SubmissionPolicy

Controls access to form submissions.

Policy Methods

Method Description
viewAny Can list submissions
view Can view a specific submission
delete Can delete a submission
export Can export submissions
downloadUpload Can download uploaded files

Default Behavior

Submission access follows form access:

// If user can view the form, they can view its submissions
$user->can('view', $submission); // Checks form access

Custom Policy

// app/Policies/CustomSubmissionPolicy.php

namespace App\Policies;

use App\Models\User;
use ArtisanPackUI\Forms\Models\FormSubmission;
use ArtisanPackUI\Forms\Policies\SubmissionPolicy;

class CustomSubmissionPolicy extends SubmissionPolicy
{
    public function view(User $user, FormSubmission $submission): bool
    {
        // Custom access rules
        if ($submission->form->is_public) {
            return true;
        }

        return parent::view($user, $submission);
    }

    public function export(User $user, FormSubmission $submission): bool
    {
        // Only managers can export
        return $user->hasRole('manager') || $user->is_admin;
    }
}

Using Policies

In Controllers

public function show(Form $form)
{
    $this->authorize('view', $form);

    return view('forms.show', compact('form'));
}

public function destroy(FormSubmission $submission)
{
    $this->authorize('delete', $submission);

    $submission->delete();

    return redirect()->back()->with('success', 'Submission deleted.');
}

In Blade Templates

@can('update', $form)
    <a href="{{ route('forms.edit', $form) }}">Edit</a>
@endcan

@can('delete', $submission)
    <button wire:click="delete({{ $submission->id }})">Delete</button>
@endcan

In Livewire Components

public function deleteSubmission(int $id): void
{
    $submission = FormSubmission::findOrFail($id);

    $this->authorize('delete', $submission);

    $submission->delete();
}

Using Gates

use Illuminate\Support\Facades\Gate;

if (Gate::allows('update', $form)) {
    // User can update the form
}

if (Gate::denies('delete', $submission)) {
    abort(403, 'You cannot delete this submission.');
}

Configuration Reference

// config/artisanpack/forms.php
'authorization' => [
    // Enable ownership-based access control
    // When true: users can only access their own forms
    // When false: any authenticated user can access any form
    'restrict_by_owner' => env('FORMS_RESTRICT_BY_OWNER', false),

    // Allow users with is_admin=true to bypass ownership checks
    'allow_admin_bypass' => env('FORMS_ALLOW_ADMIN_BYPASS', true),

    // User model class for ownership relationships
    'user_model' => env('FORMS_USER_MODEL', 'App\\Models\\User'),
],

Testing Policies

use ArtisanPackUI\Forms\Models\Form;
use App\Models\User;

test('owner can update their form', function () {
    $user = User::factory()->create();
    $form = Form::factory()->create(['user_id' => $user->id]);

    config(['artisanpack.forms.authorization.restrict_by_owner' => true]);

    $this->assertTrue($user->can('update', $form));
});

test('non-owner cannot update others form', function () {
    $owner = User::factory()->create();
    $other = User::factory()->create();
    $form = Form::factory()->create(['user_id' => $owner->id]);

    config(['artisanpack.forms.authorization.restrict_by_owner' => true]);

    $this->assertFalse($other->can('update', $form));
});

test('admin can update any form', function () {
    $admin = User::factory()->create(['is_admin' => true]);
    $form = Form::factory()->create();

    config(['artisanpack.forms.authorization.restrict_by_owner' => true]);
    config(['artisanpack.forms.authorization.allow_admin_bypass' => true]);

    $this->assertTrue($admin->can('update', $form));
});

Next Steps