Skip to content

Commit

Permalink
Merge pull request #28 from openfoodfoundation/feature/app-group-abil…
Browse files Browse the repository at this point in the history
…ities

Feature: App group abilities selection in PAT creation tool
  • Loading branch information
ok200paul authored Aug 19, 2024
2 parents f8559f0 + eb47989 commit 78f873c
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 90 deletions.
81 changes: 51 additions & 30 deletions app/Enums/PersonalAccessTokenAbility.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,52 @@ enum PersonalAccessTokenAbility: string
case MY_TEAM_VOUCHERS_READ = 'my-team-vouchers-read';
case MY_TEAM_VOUCHERS_UPDATE = 'my-team-vouchers-update';
case MY_TEAM_VOUCHERS_DELETE = 'my-team-vouchers-delete';
case REDEMPTIONS_CREATE = 'redemptions-create';
case REDEMPTIONS_READ = 'redemptions-read';
case REDEMPTIONS_UPDATE = 'redemptions-update';
case REDEMPTIONS_DELETE = 'redemptions-delete';
case SHOPS_CREATE = 'shops-create';
case SHOPS_READ = 'shops-read';
case SHOPS_UPDATE = 'shops-update';
case SHOPS_DELETE = 'shops-delete';
case SYSTEM_STATISTICS_CREATE = 'system-statistics-create';
case SYSTEM_STATISTICS_READ = 'system-statistics-read';
case SYSTEM_STATISTICS_UPDATE = 'system-statistics-update';
case SYSTEM_STATISTICS_DELETE = 'system-statistics-delete';
case TEAMS_CREATE = 'teams-create';
case TEAMS_READ = 'teams-read';
case TEAMS_UPDATE = 'teams-update';
case TEAMS_DELETE = 'teams-delete';


public static function abilityLabels(): array
{
$returnArray = [
self::SUPER_ADMIN->value => 'Super Admin',
self::MY_PROFILE_CREATE->value => 'My Profile Create',
self::MY_PROFILE_READ->value => 'My Profile Read',
self::MY_PROFILE_UPDATE->value => 'My Profile Update',
self::MY_PROFILE_DELETE->value => 'My Profile Delete',
self::MY_TEAM_CREATE->value => 'My Team Create',
self::MY_TEAM_READ->value => 'My Team Read',
self::MY_TEAM_UPDATE->value => 'My Team Update',
self::MY_TEAM_DELETE->value => 'My Team Delete',
self::TEAMS_READ->value => 'Teams Read',
self::TEAMS_CREATE->value => 'Teams Create',
self::SYSTEM_STATISTICS_READ->value => 'System Statistics Read',
// MUST MATCH FULL LIS
return [
self::SUPER_ADMIN->value => 'Super Admin: Perform most API actions',
self::MY_PROFILE_CREATE->value => 'My Profile Create',
self::MY_PROFILE_READ->value => 'My Profile Read',
self::MY_PROFILE_UPDATE->value => 'My Profile Update',
self::MY_PROFILE_DELETE->value => 'My Profile Delete',
self::MY_TEAM_CREATE->value => 'My Team Create',
self::MY_TEAM_READ->value => 'My Team Read',
self::MY_TEAM_UPDATE->value => 'My Team Update',
self::MY_TEAM_DELETE->value => 'My Team Delete',
self::MY_TEAM_AUDIT_ITEMS_CREATE->value => 'My Team Audit Items Create',
self::MY_TEAM_AUDIT_ITEMS_READ->value => 'My Team Audit Items Read',
self::MY_TEAM_AUDIT_ITEMS_UPDATE->value => 'My Team Audit Items Update',
self::MY_TEAM_AUDIT_ITEMS_DELETE->value => 'My Team Audit Items Delete',
self::MY_TEAM_VOUCHERS_CREATE->value => 'My Team Vouchers Create',
self::MY_TEAM_VOUCHERS_READ->value => 'My Team Vouchers Read',
self::MY_TEAM_VOUCHERS_UPDATE->value => 'My Team Vouchers Update',
self::MY_TEAM_VOUCHERS_DELETE->value => 'My Team Vouchers Delete',
self::REDEMPTIONS_CREATE->value => 'Redemptions Create: Perform a voucher redemption',
self::REDEMPTIONS_READ->value => 'Redemptions Read: Retrieve a redemption',
self::REDEMPTIONS_UPDATE->value => 'Redemptions Update: Update a redemption',
self::REDEMPTIONS_DELETE->value => 'Redemptions Delete: Delete a redemption',
self::SHOPS_CREATE->value => 'Shops Create: Create a shop that redeems vouchers',
self::SHOPS_READ->value => 'Shops Read: Retrieve shop details from the API',
self::SHOPS_UPDATE->value => 'Shops Update: Update a shop',
self::SHOPS_DELETE->value => 'Shops Delete: Delete a shop',
self::SYSTEM_STATISTICS_CREATE->value => 'System Statistics Create',
self::SYSTEM_STATISTICS_READ->value => 'System Statistics Read',
self::SYSTEM_STATISTICS_UPDATE->value => 'System Statistics Update',
self::SYSTEM_STATISTICS_DELETE->value => 'System Statistics Delete',
];

// Assert$returnArray
return $returnArray
}

/**
Expand All @@ -68,13 +84,12 @@ public static function abilityLabels(): array
public static function platformAppTokenAbilities(): array
{
return [
self::TEAMS_READ->value => self::abilityLabels()[self::TEAMS_READ->value],
self::TEAMS_CREATE->value => self::abilityLabels()[self::TEAMS_CREATE->value],
self::SHOPS_CREATE->value => self::abilityLabels()[self::SHOPS_CREATE->value],
self::SHOPS_READ->value => self::abilityLabels()[self::SHOPS_READ->value],
self::SYSTEM_STATISTICS_READ->value => self::abilityLabels()[self::SYSTEM_STATISTICS_READ->value],
];
}


/**
* The abilities that a "redemption" app API token should have.
*
Expand All @@ -83,7 +98,8 @@ public static function platformAppTokenAbilities(): array
public static function redemptionAppTokenAbilities(): array
{
return [

self::REDEMPTIONS_CREATE->value => self::abilityLabels()[self::REDEMPTIONS_CREATE->value],
self::REDEMPTIONS_READ->value => self::abilityLabels()[self::REDEMPTIONS_READ->value],
];
}

Expand All @@ -94,19 +110,24 @@ public static function groupsAbilityCasesWithDefinitions(): array
'name' => 'Super admin abilities',
'description' => 'A group of API abilities that allow an app to perform any / all actions on the API. Be careful assigning this ability!',
'abilities' => [
self::SUPER_ADMIN
]
self::SUPER_ADMIN->value => self::abilityLabels()[self::SUPER_ADMIN->value],
],
],
[
'name' => 'Platform App',
'description' => 'Perform administrative tasks for your OFN platform implementation.',
'abilities' => self::platformAppTokenAbilities()
'abilities' => self::platformAppTokenAbilities(),
],
[
'name' => 'Redemptions',
'description' => 'A group of API abilities that allow an app to perform redemptions on the system.',
'abilities' => self::redemptionAppTokenAbilities()
'abilities' => self::redemptionAppTokenAbilities(),
],
];
}

public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ public function store(): JsonResponse
],
];

$validator = Validator::make($this->request->all(), $validationArray);
$messages = [
'token_abilities' => 'Please ensure the token has at least 1 ability associated to it.',
];
$validator = Validator::make($this->request->all(), $validationArray, $messages);

if ($validator->fails()) {

Expand Down
4 changes: 2 additions & 2 deletions app/Http/Middleware/HandleInertiaRequests.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ public function share(Request $request): array
{
return [
...parent::share($request),
'auth' => [
'auth' => [
'user' => $request->user(),
'currentTeam' => Team::find($request->user()?->current_team_id),
],
'personalAccessTokenAbilities' => PersonalAccessTokenAbility::groupsAbilityCasesWithDefinitions(),
'platformAppTokenAbilities' => PersonalAccessTokenAbility::platformAppTokenAbilities(),
'platformAppTokenAbilities' => PersonalAccessTokenAbility::platformAppTokenAbilities(),
];
}
}
66 changes: 40 additions & 26 deletions resources/js/Pages/Admin/Users/User.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ const $props = defineProps({
const limit = ref(5)
const newPAT = ref({name: '', token_abilities: []})
const personalAccessTokenAbilities = usePage().props.personalAccessTokenAbilities
const platformAppTokenAbilities = usePage().props.platformAppTokenAbilities
const personalAccessTokenAbilities = usePage().props.personalAccessTokenAbilities;
const platformAppTokenAbilities = usePage().props.platformAppTokenAbilities;
const userTeams = ref({})
const user = ref({})
Expand All @@ -47,7 +47,11 @@ function createPAT() {
});
}).catch(error => {
console.log(error)
Swal.fire({
icon: "error",
title: "Oops..",
text: error.response.data.meta.message
})
})
}
Expand All @@ -63,7 +67,9 @@ function getUserTeams(page = 1) {
axios.get('/admin/team-users?cached=false&page=' + page + '&where[]=user_id,' + $props.id + '&relations=team&limit=' + limit.value + '&orderBy=id,desc').then(response => {
userTeams.value = response.data.data;
}).catch(error => {
console.log(error)
})
}
Expand All @@ -76,7 +82,7 @@ function textFormat(ability) {
}
function selectPlatformAppType(){
newPAT.value.token_abilities = platformAppTokenAbilities;
newPAT.value.token_abilities = Object.keys(platformAppTokenAbilities);
Swal.fire({
icon: "info",
Expand Down Expand Up @@ -169,30 +175,23 @@ function clearAbilitiesList(){

</div>

<div class="card">
<pre>{{personalAccessTokenAbilities}}</pre>
</div>

<div v-if="personalAccessTokenAbilities.length">
<div class="pb-4">
<InputLabel for="name" value="PAT name"/>
<TextInput
id="name"
type="text"
class="mt-1 block w-full"
v-model="newPAT.name"
required
/>
</div>


<div class="mt-8 mb-4">
<h2>Step 1: Select Token Abilities</h2>
</div>

<div class="flex justify-start space-x-4 pb-4 ">
<div class="flex justify-start items-center space-x-4 pb-4 ">
<SecondaryButton @click="clearAbilitiesList">
Clear Selected
</SecondaryButton>

<div class="pl-16">
Quick select:
</div>
<PrimaryButton @click="selectPlatformAppType()">
Master App
Platform App
</PrimaryButton>
</div>

Expand All @@ -208,9 +207,10 @@ function clearAbilitiesList(){
{{abilityGroup.description}}
</div>
<div class="mt-8">
<div v-for="ability in abilityGroup.abilities">
<div v-for="(ability, key) in abilityGroup.abilities">
<label :for="ability" class="cursor-pointer">
<input type="checkbox" :id="ability" class="mr-4" :value="ability"

<input type="checkbox" :id="ability" class="mr-4" :value="key"
v-model="newPAT.token_abilities"> {{ textFormat(ability) }}
</label>
</div>
Expand All @@ -219,12 +219,26 @@ function clearAbilitiesList(){
</div>
</div>


<div class="mt-8">
<h2>Step 2: Give the Token a name</h2>
</div>
<div class="pb-4">
<TextInput
id="name"
type="text"
class="mt-1 block w-full"
v-model="newPAT.name"
required
/>
</div>
<div>
Selected Abilities: {{newPAT.token_abilities.join(', ')}}
</div>

<div class="flex items-center justify-end mt-4">
<PrimaryButton @click.prevent="createPAT()" class="ms-4" :class="{ 'opacity-25': !newPAT.name }"
<PrimaryButton @click.prevent="createPAT()" class="" :class="{ 'opacity-25': !newPAT.name }"
:desabled="!newPAT.name">
Submit
Create New Token
</PrimaryButton>
</div>
</div>
Expand Down
Loading

0 comments on commit 78f873c

Please sign in to comment.