diff --git a/.env.testing b/.env.testing new file mode 100644 index 0000000..504a747 --- /dev/null +++ b/.env.testing @@ -0,0 +1,41 @@ +APP_NAME="Clearing Houz" +APP_ENV=testing +APP_KEY= +APP_DEBUG=true +#APP_URL=http://localhost + +LOG_CHANNEL=stderr +LOG_LEVEL=info + +DB_CONNECTION=pgsql +DB_HOST=database +DB_PORT=5432 +DB_DATABASE=postgres +DB_USERNAME=postgres +DB_PASSWORD=secret + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +QUEUE_CONNECTION=sync +SESSION_DRIVER=file +SESSION_LIFETIME=120 + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_DRIVER=smtp +MAIL_HOST=mail.leenooks.vpn +MAIL_PORT=25 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_AUTO_EMBED_METHOD=base64 + +PUSHER_APP_ID= +PUSHER_APP_KEY= +PUSHER_APP_SECRET= +PUSHER_APP_CLUSTER=mt1 + +MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" +MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2ea59a6..b4631f2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,4 +10,5 @@ cache: - vendor/ include: + - .gitlab-test.yml - .gitlab-docker-x86_64.yml diff --git a/.gitlab-test.yml b/.gitlab-test.yml new file mode 100644 index 0000000..886b9b1 --- /dev/null +++ b/.gitlab-test.yml @@ -0,0 +1,40 @@ +test: + image: ${CI_REGISTRY}/leenooks/php:8.0-fpm-ext-test + + stage: test + + # NOTE: This service is dependant on project file configuration, which is not there if the cache was deleted + # resulting in the testing to fail on the first run. + services: + - name: postgres:13-alpine + alias: database + + variables: + POSTGRES_PASSWORD: secret + + tags: + - php + only: + - master + + before_script: + - mv .env.testing .env + + # Install Composer and project dependencies. + - mkdir -p /root/.composer + - if [ -n "$GITHUB_TOKEN" ]; then cat $GITHUB_TOKEN |base64 -d > /root/.composer/auth.json ; fi + - composer install + + # Generate an application key. Re-cache. + - php artisan key:generate + - php artisan migrate + + script: + # run laravel tests + - XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage-text --colors=never + + # run frontend tests + # if you have any task for testing frontend + # set it in your package.json script + # comment this out if you don't have a frontend test + # npm test diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 51dc6e3..f9e3454 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -11,4 +11,14 @@ class HomeController extends Controller return view('domain.view') ->with('o',$o); } + + /** + * System Setup + * + * @note: Protected by Route + */ + public function setup() + { + + } } \ No newline at end of file diff --git a/app/Models/Domain.php b/app/Models/Domain.php index 2b6f160..ab16d40 100644 --- a/app/Models/Domain.php +++ b/app/Models/Domain.php @@ -2,13 +2,14 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use App\Traits\ScopeActive; class Domain extends Model { - use ScopeActive; + use HasFactory,ScopeActive; /* SCOPES */ diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 9784b1a..f1048e4 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -5,26 +5,30 @@ namespace App\Providers; use Illuminate\Support\Facades\Gate; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; +use App\Models\User; + class AuthServiceProvider extends ServiceProvider { - /** - * The policy mappings for the application. - * - * @var array - */ - protected $policies = [ - 'App\Model' => 'App\Policies\ModelPolicy', - ]; + /** + * The policy mappings for the application. + * + * @var array + */ + protected $policies = [ + 'App\Model' => 'App\Policies\ModelPolicy', + ]; - /** - * Register any authentication / authorization services. - * - * @return void - */ - public function boot() - { - $this->registerPolicies(); + /** + * Register any authentication / authorization services. + * + * @return void + */ + public function boot() + { + $this->registerPolicies(); - // - } + Gate::define('admin',function (User $o) { + return $o->admin == TRUE; + }); + } } diff --git a/composer.json b/composer.json index 11f34c2..8fe8349 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,9 @@ "app/helpers.php" ], "psr-4": { - "App\\": "app/" + "App\\": "app/", + "Database\\Factories\\": "database/factories/", + "Database\\Seeders\\": "database/seeders/" } }, "autoload-dev": { diff --git a/database/factories/DomainFactory.php b/database/factories/DomainFactory.php new file mode 100644 index 0000000..78246db --- /dev/null +++ b/database/factories/DomainFactory.php @@ -0,0 +1,52 @@ + $this->faker->name(), + 'dnsdomain' => $this->faker->unique()->safeEmail(), + 'notes' => $this->faker->text(), + 'homepage' => $this->faker->text(), + 'active' => FALSE, + 'public' => FALSE, + ]; + } + + public function active() + { + return $this->state(function (array $attributes) { + return [ + 'active' => TRUE, + ]; + }); + } + + public function public() + { + return $this->state(function (array $attributes) { + return [ + 'public' => TRUE, + ]; + }); + } +} diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index ec15e58..4b632c2 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -1,24 +1,42 @@ define(App\User::class, function (Faker $faker) { - return [ - 'name' => $faker->name, - 'email' => $faker->unique()->safeEmail, - 'email_verified_at' => now(), - 'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret - 'remember_token' => str_random(10), - ]; -}); +class UserFactory extends Factory +{ + /** + * The name of the factory's corresponding model. + * + * @var string + */ + protected $model = User::class; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + return [ + 'name' => $this->faker->name(), + 'email' => $this->faker->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password + 'remember_token' => Str::random(10), + ]; + } + + public function admin() + { + return $this->state(function (array $attributes) { + return [ + 'admin' => true, + ]; + }); + } +} diff --git a/database/migrations/2021_06_14_114942_add_admin_to_users.php b/database/migrations/2021_06_14_114942_add_admin_to_users.php new file mode 100644 index 0000000..fed48ab --- /dev/null +++ b/database/migrations/2021_06_14_114942_add_admin_to_users.php @@ -0,0 +1,42 @@ +boolean('admin')->nullable(); + }); + + Schema::create('domain_user', function (Blueprint $table) { + $table->integer('domain_id'); + $table->foreign('domain_id')->references('id')->on('domains'); + + $table->integer('user_id'); + $table->foreign('user_id')->references('id')->on('users'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('admin'); + }); + + Schema::dropIfExists('domain_user'); + } +} diff --git a/resources/views/domain/view.blade.php b/resources/views/domain/view.blade.php new file mode 100644 index 0000000..758d5ae --- /dev/null +++ b/resources/views/domain/view.blade.php @@ -0,0 +1,9 @@ +@extends('layouts.app') +@section('htmlheader_title') +{{ $o->name }} +@endsection + +@section('content') +
{!! \Illuminate\Mail\Markdown::parse($o->homepage) !!}
+@endsection \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 9fe5cad..f7988a2 100644 --- a/routes/web.php +++ b/routes/web.php @@ -42,8 +42,10 @@ Route::middleware(['verified'])->group(function () { Route::get('ftn/zone',[ZoneController::class,'home']); Route::match(['get','post'],'ftn/zone/addedit/{o?}',[ZoneController::class,'add_edit']) ->where('o','[0-9]+'); - - Route::get('ftn/network/{name}',[HomeController::class,'network']); }); -Route::get('network/{o}',[HomeController::class,'network']); \ No newline at end of file +Route::get('network/{o}',[HomeController::class,'network']); + +Route::middleware(['auth','can:admin'])->group(function () { + Route::get('setup',[HomeController::class,'setup']); +}); \ No newline at end of file diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php index f31e495..38856ad 100644 --- a/tests/Feature/ExampleTest.php +++ b/tests/Feature/ExampleTest.php @@ -14,7 +14,7 @@ class ExampleTest extends TestCase */ public function testBasicTest() { - $response = $this->get('/'); + $response = $this->get('/about'); $response->assertStatus(200); } diff --git a/tests/Feature/SiteAdminTest.php b/tests/Feature/SiteAdminTest.php new file mode 100644 index 0000000..4d6645f --- /dev/null +++ b/tests/Feature/SiteAdminTest.php @@ -0,0 +1,90 @@ +get('about') + ->assertOk(); + + $this->get('ftn/domain') + ->assertRedirect('login'); + $this->get('ftn/node') + ->assertRedirect('login'); + $this->get('ftn/zone') + ->assertRedirect('login'); + + $this->get('network/1') + ->assertNotFound(); + + Domain::factory()->create([ + 'id'=>1, + 'name'=>'test', + ]); + + $this->get('network/1') + ->assertOK(); + } + + /** + * Verify user who has registered by not verified their email + */ + public function test_unverified_user() + { + $user = User::factory()->make([ + 'name'=>'Site Visitor', + 'email_verified_at'=>NULL, + ]); + + // Use model in tests... + $this->actingAs($user); + + $this->get('ftn/domain') + ->assertRedirect('email/verify'); + $this->get('ftn/node') + ->assertRedirect('email/verify'); + $this->get('ftn/zone') + ->assertRedirect('email/verify'); + + Auth::logout(); + } + + public function test_site_admin() + { + // Site Visitor + $this->get('setup') + ->assertRedirect('login'); + + // Valid User + $user = User::factory()->make([ + 'name'=>'Site User', + ]); + $this->actingAs($user); + + $this->get('setup') + ->assertForbidden(); + + // Admin User + $user = User::factory()->admin()->make([ + 'name'=>'Site User', + ]); + $this->actingAs($user); + + $this->get('setup') + ->assertOK(); + } +}