Skip to content

Commit

Permalink
Merge pull request #3 from area17/twill-login
Browse files Browse the repository at this point in the history
Allow features to be available publicly for Twill users
  • Loading branch information
antonioribeiro authored Jul 10, 2024
2 parents 4f7e715 + 670f771 commit 1880fb5
Show file tree
Hide file tree
Showing 12 changed files with 179 additions and 19 deletions.
1 change: 1 addition & 0 deletions .github/workflows/prettier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ jobs:
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "Prettify code"
commit_options: '--no-verify'
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,15 @@ Or in Blade:
```

Don't forget to add the feature flags to your navigation too.

## Allow users logged in Twill option
You can allow your users to have access to a feature in public available domains if they are logged in on Twill. But there are some caveats for it to work:

- Twill must be int he same domain of the web application, meaning that `ADMIN_APP_URL` must be empty or have the same domain as the frontend; or
- The Laravel session domain should set in order for the apps to share the session cookie:

```dotenv
SESSION_DOMAIN=.laravel-twill-project.test
```

Also, make sure your sessions are working fine, when you switch to a shared domain they might break and a browser cookie clear might be needed.
Binary file modified docs/screenshot02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 2 additions & 4 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ parameters:

excludePaths:

checkMissingIterableValueType: false

universalObjectCratesClasses:

ignoreErrors:
- identifier: missingType.generics
- identifier: missingType.iterableValue

reportUnmatchedIgnoredErrors: false

checkGenericClassInNonGenericObjectType: false
6 changes: 6 additions & 0 deletions src/Http/Controllers/TwillFeatureFlagController.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ class TwillFeatureFlagController extends ModuleController
'sort' => true,
],

'publicly_available_twill_users' => [
'title' => 'Publicly available for Twill users',
'field' => 'publicly_available_twill_users_yes_no',
'sort' => true,
],

'publicly_available_ips' => [
'title' => 'Publicly available to (IPs)',
'field' => 'publicly_available_ips',
Expand Down
45 changes: 44 additions & 1 deletion src/Models/TwillFeatureFlag.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

use A17\Twill\Models\Model;
use Illuminate\Support\Str;
use A17\Twill\Models\Behaviors\HasRelated;
use A17\Twill\Models\Behaviors\HasRevisions;
use Illuminate\Foundation\Auth\User as AuthenticatableContract;

/**
* @property string $code
Expand All @@ -14,12 +16,22 @@
* @property string $publicly_available_yes_no
* @property string|null $publicly_available_ips
* @property bool $published
* @property bool $publicly_available_twill_users
*/
class TwillFeatureFlag extends Model
{
use HasRelated;
use HasRevisions;

protected $fillable = ['published', 'title', 'description', 'code', 'publicly_available', 'ip_addresses'];
protected $fillable = [
'published',
'title',
'description',
'code',
'publicly_available',
'ip_addresses',
'publicly_available_twill_users',
];

/**
* Save the model to the database.
Expand All @@ -39,8 +51,39 @@ public function getPubliclyAvailableYesNoAttribute(): string
return $this->publicly_available ? 'Yes' : '';
}

public function getPubliclyAvailableTwillUsersYesNoAttribute(): string
{
return $this->publicly_available_twill_users ? 'Yes' : '';
}

public function getPubliclyAvailableIpsAttribute(): string|null
{
return $this->ip_addresses ?? null;
}

public function userIsPubliclyAllowed(AuthenticatableContract|null $user): bool
{
if ($user === null) {
return false;
}

/** @phpstan-ignore-next-line */
if ($user->published === false) {
return false;
}

/** @phpstan-ignore-next-line */
if ($user->isSuperAdmin()) {
return true;
}

$allowedUsers = $this->getRelated('allowed_twill_users');

if ($allowedUsers->isEmpty()) {
return true;
}

/** @phpstan-ignore-next-line */
return $allowedUsers->pluck('email')->contains($user->email);
}
}
40 changes: 33 additions & 7 deletions src/Repositories/TwillFeatureFlagRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ class TwillFeatureFlagRepository extends ModuleRepository
{
use HandleRevisions;

protected $relatedBrowsers = [
'allowed_twill_users' => ['moduleName' => 'users', 'relation' => 'allowed_twill_users'],
];

public function __construct(TwillFeatureFlag $model = null)
{
$this->bootCache();
Expand All @@ -40,7 +44,7 @@ public function getFeature(string $code): bool
return false;
}

if (blank($featureFlag) || blank($featureFlag?->published) || $featureFlag?->published === false) {
if (blank($featureFlag) || blank($featureFlag->published) || $featureFlag->published === false) {
return false;
}

Expand All @@ -53,7 +57,8 @@ public function getFeature(string $code): bool

private function isRealProduction(): bool
{
return (new Collection(config('app.domains.publicly_available')))->contains(request()->getHost());
return app()->environment('production') &&
(new Collection(config('app.domains.publicly_available')))->contains(request()->getHost());
}

public function featureList(bool $all = false): array
Expand All @@ -73,11 +78,8 @@ public function featureList(bool $all = false): array

private function isPubliclyAvailableToCurrentUser(TwillFeatureFlag $featureFlag): bool
{
return (new GeolocationService())->currentIpAddressIsOnList(
collect(explode(',', $featureFlag->ip_addresses))
->map(fn($ip) => trim($ip))
->toArray(),
);
return $this->isPubliclyAvailableToIpAddresses($featureFlag) ||
$this->isPubliclyAvailableToTwillUsers($featureFlag);
}

private function bootCache(): void
Expand Down Expand Up @@ -132,4 +134,28 @@ protected function url(array|bool $parsed, string $attribute): string|null

return $parsed[$attribute];
}

public function isPubliclyAvailableToIpAddresses(TwillFeatureFlag $featureFlag): bool
{
if (blank($featureFlag->ip_addresses)) {
return false;
}

return (new GeolocationService())->currentIpAddressIsOnList(
collect(explode(',', $featureFlag->ip_addresses))
->map(fn($ip) => trim($ip))
->toArray(),
);
}

public function isPubliclyAvailableToTwillUsers(TwillFeatureFlag $featureFlag): bool
{
if (!$featureFlag->publicly_available_twill_users) {
return false;
}

$auth = auth('twill_users');

return $auth->check() && $featureFlag->userIsPubliclyAllowed($auth->user());
}
}
3 changes: 3 additions & 0 deletions src/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Support\Str;
use A17\Twill\Facades\TwillCapsules;
use A17\Twill\TwillPackageServiceProvider;
use A17\TwillFeatureFlags\Services\Helpers;

class ServiceProvider extends TwillPackageServiceProvider
{
Expand All @@ -15,6 +16,8 @@ public function boot(): void
{
$this->registerThisCapsule();

Helpers::load();

parent::boot();
}

Expand Down
2 changes: 1 addition & 1 deletion src/Services/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ class Helpers
{
public static function load(): void
{
require __DIR__ . '/../Support/helpers.php';
require_once __DIR__ . '/../Support/helpers.php';
}
}
24 changes: 24 additions & 0 deletions src/Support/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,27 @@ function feature_list(bool $all = false): array
return (new TwillFeatureFlagRepository())->featureList($all);
}
}

if (!function_exists('features_can_be_public_on_twill')) {
function features_can_be_public_on_twill(): bool
{
$sessionDomain = config('session.domain');

$twillAdminAppHost = parse_url(config('twill.admin_app_url') ?? '')['host'] ?? null;

$appDomainHost = parse_url(config('app.url') ?? '')['host'] ?? null;

// If the admin app is not set it probably means Twill is in the same domain of the frontend
if (blank($twillAdminAppHost)) {
return true;
}

// Otherwise the domain must be the same
if ($twillAdminAppHost === $appDomainHost) {
return true;
}

// Otherwise the session domain must be set
return filled($sessionDomain);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('twill_feature_flags', function (Blueprint $table) {
$table->boolean('publicly_available_twill_users')->default(false);
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('twill_feature_flags', function (Blueprint $table) {
$table->dropColumn('publicly_available_twill_users');
});
}
};
32 changes: 26 additions & 6 deletions src/resources/views/admin/form.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
'type' => 'text'
])

@formField('input', [
'label' => 'Description',
'name' => 'description',
'rows' => 4,
'type' => 'textarea'
])

@formField('checkbox', [
'name' => 'publicly_available',
'label' => 'Publicly available',
Expand All @@ -22,10 +29,23 @@
'translated' => false,
])

@formField('input', [
'label' => 'Description',
'name' => 'description',
'rows' => 4,
'type' => 'textarea'
])
@if (features_can_be_public_on_twill())
@formField('checkbox', [
'name' => 'publicly_available_twill_users',
'label' => 'Publicly available for users logged in Twill',
])

@formConnectedFields([
'fieldName' => 'publicly_available_twill_users',
'fieldValues' => true,
])
@formField('browser', [
'moduleName' => 'users',
'name' => 'allowed_twill_users',
'label' => 'Allowed users',
'note' => 'If no users are selected, all users will be allowed',
'max' => 999,
])
@endformConnectedFields
@endif
@stop

0 comments on commit 1880fb5

Please sign in to comment.