mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-nostr.git
synced 2026-05-20 10:04:53 +00:00
🚀 Refactor: Centralize isBoardMember logic in EinundzwanzigPleb model and replace redundant checks
This commit is contained in:
@@ -9,7 +9,7 @@ class NostrUser implements Authenticatable
|
||||
{
|
||||
protected string $pubkey;
|
||||
|
||||
protected ?object $pleb;
|
||||
protected ?EinundzwanzigPleb $pleb;
|
||||
|
||||
public function __construct(string $pubkey)
|
||||
{
|
||||
@@ -63,4 +63,9 @@ class NostrUser implements Authenticatable
|
||||
{
|
||||
return $this->pleb;
|
||||
}
|
||||
|
||||
public function isBoardMember(): bool
|
||||
{
|
||||
return $this->pleb?->isBoardMember() ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ trait WithNostrAuth
|
||||
->where('pubkey', $pubkey)
|
||||
->first();
|
||||
|
||||
if ($this->currentPleb && in_array($this->currentPleb->npub, config('einundzwanzig.config.current_board'), true)) {
|
||||
if ($this->currentPleb && $this->currentPleb->isBoardMember()) {
|
||||
$this->canEdit = true;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ trait WithNostrAuth
|
||||
$this->currentPleb = $user->getPleb();
|
||||
$this->isAllowed = true;
|
||||
|
||||
if ($this->currentPleb && in_array($this->currentPleb->npub, config('einundzwanzig.config.current_board'), true)) {
|
||||
if ($this->currentPleb && $this->currentPleb->isBoardMember()) {
|
||||
$this->canEdit = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,4 +50,18 @@ class EinundzwanzigPleb extends Authenticatable implements CipherSweetEncrypted
|
||||
{
|
||||
return $this->hasMany(PaymentEvent::class);
|
||||
}
|
||||
|
||||
public function isBoardMember(): bool
|
||||
{
|
||||
return in_array($this->npub, config('einundzwanzig.config.current_board', []), true);
|
||||
}
|
||||
|
||||
public function hasPaidMembership(?int $year = null): bool
|
||||
{
|
||||
return $this->association_status->value > 1
|
||||
&& $this->paymentEvents()
|
||||
->where('year', $year ?? (int) date('Y'))
|
||||
->where('paid', true)
|
||||
->exists();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class ElectionPolicy
|
||||
*/
|
||||
public function create(NostrUser $user): bool
|
||||
{
|
||||
return $this->isBoardMember($user);
|
||||
return $user->isBoardMember();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,7 +38,7 @@ class ElectionPolicy
|
||||
*/
|
||||
public function update(NostrUser $user, Election $election): bool
|
||||
{
|
||||
return $this->isBoardMember($user);
|
||||
return $user->isBoardMember();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +47,7 @@ class ElectionPolicy
|
||||
*/
|
||||
public function delete(NostrUser $user, Election $election): bool
|
||||
{
|
||||
return $this->isBoardMember($user);
|
||||
return $user->isBoardMember();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,15 +64,4 @@ class ElectionPolicy
|
||||
|
||||
return $pleb->association_status->value >= 3;
|
||||
}
|
||||
|
||||
private function isBoardMember(NostrUser $user): bool
|
||||
{
|
||||
$pleb = $user->getPleb();
|
||||
|
||||
if (! $pleb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array($pleb->npub, config('einundzwanzig.config.current_board'), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Auth\NostrUser;
|
||||
use App\Models\EinundzwanzigPleb;
|
||||
use App\Models\ProjectProposal;
|
||||
|
||||
class ProjectProposalPolicy
|
||||
@@ -26,7 +25,7 @@ class ProjectProposalPolicy
|
||||
|
||||
/**
|
||||
* Determine whether the user can create project proposals.
|
||||
* Requires: authenticated, association_status > 1, paid membership for current year.
|
||||
* Allowed for: board members (always) OR active members with paid membership for the current year.
|
||||
*/
|
||||
public function create(NostrUser $user): bool
|
||||
{
|
||||
@@ -36,8 +35,7 @@ class ProjectProposalPolicy
|
||||
return false;
|
||||
}
|
||||
|
||||
return $pleb->association_status->value > 1
|
||||
&& $pleb->paymentEvents()->where('year', date('Y'))->where('paid', true)->exists();
|
||||
return $pleb->isBoardMember() || $pleb->hasPaidMembership();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,7 +51,7 @@ class ProjectProposalPolicy
|
||||
}
|
||||
|
||||
return $pleb->id === $projectProposal->einundzwanzig_pleb_id
|
||||
|| $this->isBoardMember($pleb);
|
||||
|| $pleb->isBoardMember();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +67,7 @@ class ProjectProposalPolicy
|
||||
}
|
||||
|
||||
return $pleb->id === $projectProposal->einundzwanzig_pleb_id
|
||||
|| $this->isBoardMember($pleb);
|
||||
|| $pleb->isBoardMember();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,14 +82,6 @@ class ProjectProposalPolicy
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->isBoardMember($pleb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EinundzwanzigPleb $pleb
|
||||
*/
|
||||
private function isBoardMember(object $pleb): bool
|
||||
{
|
||||
return in_array($pleb->npub, config('einundzwanzig.config.current_board'), true);
|
||||
return $pleb->isBoardMember();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,5 +7,7 @@ return [
|
||||
'npub10t8npnmqhpwx9w8k232kess7gqtdlr6kqjemdzf8jnughwqd0gwsez0924',
|
||||
'npub1r8343wqpra05l3jnc4jud4xz7vlnyeslf7gfsty7ahpf92rhfmpsmqwym8',
|
||||
'npub17fqtu2mgf7zueq2kdusgzwr2lqwhgfl2scjsez77ddag2qx8vxaq3vnr8y',
|
||||
'npub1v4lgwjv7qfn3t7qjscpsgz9vqvspf6hecdp2ckgp0dz89uqn5slsgrhw3p',
|
||||
'npub14r770s5wrqpm8jmzur5arnm9aum9x0wasaxwczael54xhjggl7ws5lygc6',
|
||||
],
|
||||
];
|
||||
|
||||
+2
-1
@@ -22,7 +22,8 @@
|
||||
<env name="APP_MAINTENANCE_DRIVER" value="file"/>
|
||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||
<env name="CACHE_STORE" value="array"/>
|
||||
<env name="DB_DATABASE" value="einundzwanzig_verein_testing"/>
|
||||
<env name="DB_CONNECTION" value="sqlite"/>
|
||||
<env name="DB_DATABASE" value=":memory:"/>
|
||||
<env name="MAIL_MAILER" value="array"/>
|
||||
<env name="PULSE_ENABLED" value="false"/>
|
||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@php
|
||||
$boardVotes = $project->votes->filter(function ($vote) {
|
||||
return in_array($vote->einundzwanzigPleb->npub, config('einundzwanzig.config.current_board'));
|
||||
return $vote->einundzwanzigPleb->isBoardMember();
|
||||
});
|
||||
$approveCount = $boardVotes->where('value', 1)->count();
|
||||
$disapproveCount = $boardVotes->where('value', 0)->count();
|
||||
|
||||
@@ -38,28 +38,27 @@ class extends Component {
|
||||
#[Locked]
|
||||
public bool $canEdit = false;
|
||||
|
||||
#[Locked]
|
||||
public ?\App\Models\EinundzwanzigPleb $currentPleb = null;
|
||||
|
||||
public ?int $confirmDeleteId = null;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
if (NostrAuth::check()) {
|
||||
$currentPubkey = NostrAuth::pubkey();
|
||||
$currentPleb = \App\Models\EinundzwanzigPleb::query()->where('pubkey', $currentPubkey)->first();
|
||||
$this->currentPleb = NostrAuth::user()?->getPleb();
|
||||
|
||||
if (
|
||||
$currentPleb
|
||||
&& $currentPleb->association_status->value > 1
|
||||
&& $currentPleb->paymentEvents()->where('year', date('Y'))->where('paid', true)->exists()
|
||||
) {
|
||||
$this->isAllowed = true;
|
||||
}
|
||||
|
||||
if ($currentPleb && in_array($currentPleb->npub, config('einundzwanzig.config.current_board'))) {
|
||||
$this->canEdit = true;
|
||||
}
|
||||
|
||||
$this->loadNews();
|
||||
if (! $this->currentPleb) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->currentPleb->isBoardMember()) {
|
||||
$this->isAllowed = true;
|
||||
$this->canEdit = true;
|
||||
} elseif ($this->currentPleb->hasPaidMembership()) {
|
||||
$this->isAllowed = true;
|
||||
}
|
||||
|
||||
$this->loadNews();
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
@@ -101,13 +100,11 @@ class extends Component {
|
||||
'form.description' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$currentPleb = \App\Models\EinundzwanzigPleb::query()->where('pubkey', NostrAuth::pubkey())->first();
|
||||
|
||||
$news = new Notification;
|
||||
$news->name = $this->form['name'];
|
||||
$news->description = $this->form['description'] ?? null;
|
||||
$news->category = $this->form['category'];
|
||||
$news->einundzwanzig_pleb_id = $currentPleb->id;
|
||||
$news->einundzwanzig_pleb_id = $this->currentPleb->id;
|
||||
$news->save();
|
||||
|
||||
if ($this->file) {
|
||||
@@ -340,20 +337,15 @@ class extends Component {
|
||||
</button>
|
||||
|
||||
<!-- User badge -->
|
||||
@if(NostrAuth::check())
|
||||
@php
|
||||
$currentPleb = \App\Models\EinundzwanzigPleb::query()->where('pubkey', NostrAuth::pubkey())->first();
|
||||
@endphp
|
||||
@if($currentPleb)
|
||||
<div class="flex items-center gap-2.5 rounded-xl bg-bg-surface border border-border-subtle px-4 py-2.5">
|
||||
<img
|
||||
src="{{ $currentPleb->profile?->picture ?? asset('einundzwanzig-alpha.jpg') }}"
|
||||
alt="{{ $currentPleb->profile?->name ?? 'Anonym' }}"
|
||||
class="w-8 h-8 rounded-full bg-bg-elevated object-cover shrink-0"
|
||||
/>
|
||||
<span class="text-[13px] font-medium text-text-primary">{{ $currentPleb->profile?->name ?? str($currentPleb->npub)->limit(32) }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if($currentPleb)
|
||||
<div class="flex items-center gap-2.5 rounded-xl bg-bg-surface border border-border-subtle px-4 py-2.5">
|
||||
<img
|
||||
src="{{ $currentPleb->profile?->picture ?? asset('einundzwanzig-alpha.jpg') }}"
|
||||
alt="{{ $currentPleb->profile?->name ?? 'Anonym' }}"
|
||||
class="w-8 h-8 rounded-full bg-bg-elevated object-cover shrink-0"
|
||||
/>
|
||||
<span class="text-[13px] font-medium text-text-primary">{{ $currentPleb->profile?->name ?? str($currentPleb->npub)->limit(32) }}</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -43,11 +43,8 @@ class extends Component
|
||||
$this->isAllowed = true;
|
||||
}
|
||||
|
||||
if ($nostrUser) {
|
||||
$pleb = $nostrUser->getPleb();
|
||||
if ($pleb && in_array($pleb->npub, config('einundzwanzig.config.current_board'), true)) {
|
||||
$this->isAdmin = true;
|
||||
}
|
||||
if ($nostrUser && $nostrUser->isBoardMember()) {
|
||||
$this->isAdmin = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ new class extends Component {
|
||||
$this->projects = ProjectProposal::query()
|
||||
->with([
|
||||
'einundzwanzigPleb.profile',
|
||||
'votes',
|
||||
'votes.einundzwanzigPleb',
|
||||
])
|
||||
->where(function ($query) {
|
||||
$query
|
||||
|
||||
@@ -45,7 +45,7 @@ new class extends Component {
|
||||
{
|
||||
return Vote::query()
|
||||
->where('project_proposal_id', $this->projectProposal->id)
|
||||
->whereHas('einundzwanzigPleb', fn($q) => $q->whereIn('npub', config('einundzwanzig.config.current_board')))
|
||||
->whereHas('einundzwanzigPleb', fn($q) => $q->whereIn('npub', config('einundzwanzig.config.current_board', [])))
|
||||
->get();
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ new class extends Component {
|
||||
->where('project_proposal_id', $this->projectProposal->id)
|
||||
->whereDoesntHave(
|
||||
'einundzwanzigPleb',
|
||||
fn($q) => $q->whereIn('npub', config('einundzwanzig.config.current_board'))
|
||||
fn($q) => $q->whereIn('npub', config('einundzwanzig.config.current_board', []))
|
||||
)
|
||||
->get();
|
||||
}
|
||||
|
||||
@@ -54,6 +54,18 @@ it('allows board member to edit news', function () {
|
||||
->assertSet('canEdit', true);
|
||||
});
|
||||
|
||||
it('grants board member access even without active membership or paid year', function () {
|
||||
$pleb = EinundzwanzigPleb::factory()->boardMember()->create([
|
||||
'association_status' => AssociationStatus::DEFAULT,
|
||||
]);
|
||||
|
||||
NostrAuth::login($pleb->pubkey);
|
||||
|
||||
Livewire::test('association.news')
|
||||
->assertSet('isAllowed', true)
|
||||
->assertSet('canEdit', true);
|
||||
});
|
||||
|
||||
it('can create news entry with pdf', function () {
|
||||
$pleb = EinundzwanzigPleb::factory()->boardMember()->withPaidCurrentYear()->create();
|
||||
|
||||
|
||||
@@ -69,6 +69,15 @@ it('denies creation for unauthenticated users', function () {
|
||||
expect(Gate::forUser(null)->allows('create', ProjectProposal::class))->toBeFalse();
|
||||
});
|
||||
|
||||
it('allows board member to create project proposals without active membership or paid year', function () {
|
||||
$pleb = EinundzwanzigPleb::factory()->boardMember()->create([
|
||||
'association_status' => AssociationStatus::DEFAULT,
|
||||
]);
|
||||
$nostrUser = new NostrUser($pleb->pubkey);
|
||||
|
||||
expect(Gate::forUser($nostrUser)->allows('create', ProjectProposal::class))->toBeTrue();
|
||||
});
|
||||
|
||||
// update
|
||||
it('allows project creator to update their project proposal', function () {
|
||||
$pleb = EinundzwanzigPleb::factory()->create();
|
||||
|
||||
Reference in New Issue
Block a user