Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117766303
D4606.1775232388.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
25 KB
Referenced Files
None
Subscribers
None
D4606.1775232388.diff
View Options
diff --git a/src/app/Console/Commands/Data/Import/SignupTokensCommand.php b/src/app/Console/Commands/Data/Import/SignupTokensCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/Data/Import/SignupTokensCommand.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace App\Console\Commands\Data\Import;
+
+use App\Plan;
+use App\SignupToken;
+use App\Console\Command;
+
+class SignupTokensCommand extends Command
+{
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'data:import:signup-tokens {plan} {file}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'Imports signup tokens from a file.';
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ $plan = $this->getObject(Plan::class, $this->argument('plan'), 'title', false);
+
+ if (!$plan) {
+ $this->error("Plan not found");
+ return 1;
+ }
+
+ if ($plan->mode != Plan::MODE_TOKEN) {
+ $this->error("The plan is not for tokens");
+ return 1;
+ }
+
+ $file = $this->argument('file');
+
+ if (!file_exists($file)) {
+ $this->error("File '$file' does not exist");
+ return 1;
+ }
+
+ $list = file($file);
+
+ if (empty($list)) {
+ $this->error("File '$file' is empty");
+ return 1;
+ }
+
+ $bar = $this->createProgressBar(count($list), "Validating tokens");
+
+ $list = array_map('trim', $list);
+ $list = array_map('strtoupper', $list);
+
+ // Validate tokens
+ foreach ($list as $idx => $token) {
+ if (!strlen($token)) {
+ // Skip empty lines
+ unset($list[$idx]);
+ } elseif (strlen($token) > 191) {
+ $bar->finish();
+ $this->error("Token '$token' is too long");
+ return 1;
+ } elseif (SignupToken::find($token)) {
+ // Skip existing tokens
+ unset($list[$idx]);
+ }
+
+ $bar->advance();
+ }
+
+ $bar->finish();
+
+ $this->info("DONE");
+
+ if (empty($list)) {
+ $this->info("Nothing to import");
+ return 0;
+ }
+
+ $list = array_unique($list); // remove duplicated tokens
+
+ $bar = $this->createProgressBar(count($list), "Importing tokens");
+
+ // Import tokens
+ foreach ($list as $token) {
+ $plan->signupTokens()->create([
+ 'id' => $token,
+ // This allows us to update counter when importing old tokens in migration.
+ // It can be removed later
+ 'counter' => \App\UserSetting::where('key', 'signup_token')
+ ->whereRaw('UPPER(value) = ?', [$token])->count(),
+ ]);
+
+ $bar->advance();
+ }
+
+ $bar->finish();
+
+ $this->info("DONE");
+ }
+}
diff --git a/src/app/Http/Controllers/API/SignupController.php b/src/app/Http/Controllers/API/SignupController.php
--- a/src/app/Http/Controllers/API/SignupController.php
+++ b/src/app/Http/Controllers/API/SignupController.php
@@ -9,7 +9,7 @@
use App\Plan;
use App\Providers\PaymentProvider;
use App\Rules\SignupExternalEmail;
-use App\Rules\SignupToken;
+use App\Rules\SignupToken as SignupTokenRule;
use App\Rules\Password;
use App\Rules\UserEmailDomain;
use App\Rules\UserEmailLocal;
@@ -95,7 +95,7 @@
$plan = $this->getPlan();
if ($plan->mode == Plan::MODE_TOKEN) {
- $rules['token'] = ['required', 'string', new SignupToken()];
+ $rules['token'] = ['required', 'string', new SignupTokenRule($plan)];
} else {
$rules['email'] = ['required', 'string', new SignupExternalEmail()];
}
@@ -241,7 +241,9 @@
// Direct signup by token
if ($request->token) {
- $rules['token'] = ['required', 'string', new SignupToken()];
+ // This will validate the token and the plan mode
+ $plan = $request->plan ? Plan::withEnvTenantContext()->where('title', $request->plan)->first() : null;
+ $rules['token'] = ['required', 'string', new SignupTokenRule($plan)];
}
// Validate input
@@ -254,13 +256,7 @@
$settings = [];
if (!empty($request->token)) {
- // Token mode, check the plan
- $plan = $request->plan ? Plan::withEnvTenantContext()->where('title', $request->plan)->first() : null;
-
- if (!$plan || $plan->mode != Plan::MODE_TOKEN) {
- $msg = self::trans('validation.exists', ['attribute' => 'plan']);
- return response()->json(['status' => 'error', 'errors' => ['plan' => $msg]], 422);
- }
+ $settings = ['signup_token' => strtoupper($request->token)];
} elseif (!empty($request->plan) && empty($request->code) && empty($request->invitation)) {
// Plan parameter is required/allowed in mandate mode
$plan = Plan::withEnvTenantContext()->where('title', $request->plan)->first();
@@ -315,7 +311,7 @@
];
if ($plan->mode == Plan::MODE_TOKEN) {
- $settings['signup_token'] = $code_data->email;
+ $settings['signup_token'] = strtoupper($code_data->email);
} else {
$settings['external_email'] = $code_data->email;
}
@@ -431,6 +427,11 @@
$request->code->save();
}
+ // Bump up counter on the signup token
+ if (!empty($request->settings['signup_token'])) {
+ \App\SignupToken::where('id', $request->settings['signup_token'])->increment('counter');
+ }
+
DB::commit();
$response = AuthController::logonResponse($user, $request->password);
diff --git a/src/app/Observers/SignupTokenObserver.php b/src/app/Observers/SignupTokenObserver.php
new file mode 100644
--- /dev/null
+++ b/src/app/Observers/SignupTokenObserver.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Observers;
+
+use App\SignupToken;
+
+class SignupTokenObserver
+{
+ /**
+ * Ensure the token is uppercased.
+ *
+ * @param \App\SignupToken $token The SignupToken object
+ */
+ public function creating(SignupToken $token): void
+ {
+ $token->id = strtoupper(trim($token->id));
+ }
+}
diff --git a/src/app/Plan.php b/src/app/Plan.php
--- a/src/app/Plan.php
+++ b/src/app/Plan.php
@@ -136,4 +136,14 @@
return false;
}
+
+ /**
+ * The relationship to signup tokens.
+ *
+ * @return \Illuminate\Database\Eloquent\Relations\HasMany
+ */
+ public function signupTokens()
+ {
+ return $this->hasMany(SignupToken::class);
+ }
}
diff --git a/src/app/Providers/AppServiceProvider.php b/src/app/Providers/AppServiceProvider.php
--- a/src/app/Providers/AppServiceProvider.php
+++ b/src/app/Providers/AppServiceProvider.php
@@ -56,6 +56,7 @@
\App\SharedFolderSetting::observe(\App\Observers\SharedFolderSettingObserver::class);
\App\SignupCode::observe(\App\Observers\SignupCodeObserver::class);
\App\SignupInvitation::observe(\App\Observers\SignupInvitationObserver::class);
+ \App\SignupToken::observe(\App\Observers\SignupTokenObserver::class);
\App\Transaction::observe(\App\Observers\TransactionObserver::class);
\App\User::observe(\App\Observers\UserObserver::class);
\App\UserAlias::observe(\App\Observers\UserAliasObserver::class);
diff --git a/src/app/Rules/SignupToken.php b/src/app/Rules/SignupToken.php
--- a/src/app/Rules/SignupToken.php
+++ b/src/app/Rules/SignupToken.php
@@ -2,11 +2,23 @@
namespace App\Rules;
+use App\Plan;
use Illuminate\Contracts\Validation\Rule;
class SignupToken implements Rule
{
protected $message;
+ protected $plan;
+
+ /**
+ * Class constructor.
+ *
+ * @param ?Plan $plan Signup plan
+ */
+ public function __construct($plan)
+ {
+ $this->plan = $plan;
+ }
/**
* Determine if the validation rule passes.
@@ -24,32 +36,14 @@
return false;
}
- // Check the list of tokens for token existence
- $file = storage_path('signup-tokens.txt');
- $list = [];
- $token = \strtoupper($token);
-
- if (file_exists($file)) {
- $list = file($file);
- $list = array_map('trim', $list);
- $list = array_map('strtoupper', $list);
- } else {
- \Log::error("Signup tokens file ({$file}) does not exist");
- }
-
- if (!in_array($token, $list)) {
+ // Sanity check on the plan
+ if (!$this->plan || $this->plan->mode != Plan::MODE_TOKEN) {
$this->message = \trans('validation.signuptokeninvalid');
return false;
}
- // Check if the token has been already used for registration (exclude deleted users)
- $used = \App\User::select()
- ->join('user_settings', 'users.id', '=', 'user_settings.user_id')
- ->where('user_settings.key', 'signup_token')
- ->where('user_settings.value', $token)
- ->exists();
-
- if ($used) {
+ // Check the token existence
+ if (!$this->plan->signupTokens()->find($token)) {
$this->message = \trans('validation.signuptokeninvalid');
return false;
}
diff --git a/src/app/SignupToken.php b/src/app/SignupToken.php
new file mode 100644
--- /dev/null
+++ b/src/app/SignupToken.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace App;
+
+use Carbon\Carbon;
+use Illuminate\Database\Eloquent\Model;
+
+/**
+ * The eloquent definition of a SignupToken.
+ *
+ * @property \Carbon\Carbon $created_at The creation timestamp
+ * @property int $counter Count of signups on this token
+ * @property ?string $id Token
+ * @property ?string $plan_id Plan identifier
+ */
+class SignupToken extends Model
+{
+ /** @var bool Indicates if the IDs are auto-incrementing */
+ public $incrementing = false;
+
+ /** @var string The "type" of the auto-incrementing ID */
+ protected $keyType = 'string';
+
+ /** @var array<int, string> The attributes that are mass assignable */
+ protected $fillable = [
+ 'plan_id',
+ 'id',
+ 'counter',
+ ];
+
+ /** @var array<string, string> The attributes that should be cast */
+ protected $casts = [
+ 'created_at' => 'datetime:Y-m-d H:i:s',
+ 'counter' => 'integer',
+ ];
+
+ /** @var bool Indicates if the model should be timestamped. */
+ public $timestamps = false;
+
+ /**
+ * The plan this token applies to
+ *
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+ */
+ public function plan()
+ {
+ return $this->belongsTo(Plan::class);
+ }
+}
diff --git a/src/database/migrations/2023_12_14_100000_create_signup_tokens_table.php b/src/database/migrations/2023_12_14_100000_create_signup_tokens_table.php
new file mode 100644
--- /dev/null
+++ b/src/database/migrations/2023_12_14_100000_create_signup_tokens_table.php
@@ -0,0 +1,38 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::create(
+ 'signup_tokens',
+ function (Blueprint $table) {
+ $table->string('id')->primary();
+ $table->string('plan_id', 36);
+ $table->integer('counter')->unsigned()->default(0);
+ $table->timestamp('created_at')->useCurrent();
+
+ $table->index('plan_id');
+ }
+ );
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('signup_tokens');
+ }
+};
diff --git a/src/tests/Browser/SignupTest.php b/src/tests/Browser/SignupTest.php
--- a/src/tests/Browser/SignupTest.php
+++ b/src/tests/Browser/SignupTest.php
@@ -7,6 +7,7 @@
use App\Plan;
use App\SignupCode;
use App\SignupInvitation;
+use App\SignupToken;
use App\User;
use Tests\Browser;
use Tests\Browser\Components\Menu;
@@ -32,6 +33,7 @@
$this->deleteTestDomain('user-domain-signup.com');
Plan::whereNot('mode', Plan::MODE_EMAIL)->update(['mode' => Plan::MODE_EMAIL]);
+ SignupToken::truncate();
}
/**
@@ -46,8 +48,7 @@
Plan::whereNot('mode', Plan::MODE_EMAIL)->update(['mode' => Plan::MODE_EMAIL]);
Discount::where('discount', 100)->update(['code' => null]);
-
- @unlink(storage_path('signup-tokens.txt'));
+ SignupToken::truncate();
parent::tearDown();
}
@@ -669,18 +670,18 @@
public function testSignupToken(): void
{
// Test the individual plan
- Plan::where('title', 'individual')->update(['mode' => Plan::MODE_TOKEN]);
+ $plan = Plan::withEnvTenantContext()->where('title', 'individual')->first();
+ $plan->update(['mode' => Plan::MODE_TOKEN]);
- // Register some valid tokens
- $tokens = ['1234567890', 'abcdefghijk'];
- file_put_contents(storage_path('signup-tokens.txt'), implode("\n", $tokens));
+ // Register a valid token
+ $plan->signupTokens()->create(['id' => '1234567890']);
- $this->browse(function (Browser $browser) use ($tokens) {
+ $this->browse(function (Browser $browser) {
$browser->visit(new Signup())
->waitFor('@step0 .plan-individual button')
->click('@step0 .plan-individual button')
// Step 1
- ->whenAvailable('@step1', function ($browser) use ($tokens) {
+ ->whenAvailable('@step1', function ($browser) {
$browser->assertSeeIn('.card-title', 'Sign Up - Step 1/2')
->type('#signup_first_name', 'Test')
->type('#signup_last_name', 'User')
@@ -693,7 +694,7 @@
->assertFocused('#signup_token')
->assertToast(Toast::TYPE_ERROR, 'Form validation error')
// valid token
- ->type('#signup_token', $tokens[0])
+ ->type('#signup_token', '1234567890')
->click('[type=submit]');
})
// Step 2
@@ -718,18 +719,21 @@
});
$user = User::where('email', 'signuptestdusk@' . \config('app.domain'))->first();
- $this->assertSame($tokens[0], $user->getSetting('signup_token'));
$this->assertSame(null, $user->getSetting('external_email'));
// Test the group plan
- Plan::where('title', 'group')->update(['mode' => Plan::MODE_TOKEN]);
+ $plan = Plan::withEnvTenantContext()->where('title', 'group')->first();
+ $plan->update(['mode' => Plan::MODE_TOKEN]);
- $this->browse(function (Browser $browser) use ($tokens) {
+ // Register a valid token
+ $plan->signupTokens()->create(['id' => 'abcdefghijk']);
+
+ $this->browse(function (Browser $browser) {
$browser->visit(new Signup())
->waitFor('@step0 .plan-group button')
->click('@step0 .plan-group button')
// Step 1
- ->whenAvailable('@step1', function ($browser) use ($tokens) {
+ ->whenAvailable('@step1', function ($browser) {
$browser->assertSeeIn('.card-title', 'Sign Up - Step 1/2')
->type('#signup_first_name', 'Test')
->type('#signup_last_name', 'User')
@@ -742,7 +746,7 @@
->assertFocused('#signup_token')
->assertToast(Toast::TYPE_ERROR, 'Form validation error')
// valid token
- ->type('#signup_token', $tokens[1])
+ ->type('#signup_token', 'abcdefghijk')
->click('[type=submit]');
})
// Step 2
@@ -762,7 +766,6 @@
});
$user = User::where('email', 'admin@user-domain-signup.com')->first();
- $this->assertSame($tokens[1], $user->getSetting('signup_token'));
$this->assertSame(null, $user->getSetting('external_email'));
}
diff --git a/src/tests/Feature/Console/Data/Import/SignupTokensTest.php b/src/tests/Feature/Console/Data/Import/SignupTokensTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/Data/Import/SignupTokensTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace Tests\Feature\Console\Data\Import;
+
+use App\Plan;
+use App\SignupToken;
+use Tests\TestCase;
+
+class SignupTokensTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ Plan::where('title', 'test')->delete();
+ SignupToken::truncate();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ Plan::where('title', 'test')->delete();
+ SignupToken::truncate();
+
+ @unlink(storage_path('test-tokens.txt'));
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test the command
+ */
+ public function testHandle(): void
+ {
+ $file = storage_path('test-tokens.txt');
+ file_put_contents($file, '');
+
+ // Unknown plan
+ $code = \Artisan::call("data:import:signup-tokens unknown {$file}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("Plan not found", $output);
+
+ // Plan not for tokens
+ $code = \Artisan::call("data:import:signup-tokens individual {$file}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("The plan is not for tokens", $output);
+
+ $plan = Plan::create([
+ 'title' => 'test',
+ 'name' => 'Test Account',
+ 'description' => 'Test',
+ 'mode' => Plan::MODE_TOKEN,
+ ]);
+
+ // Non-existent input file
+ $code = \Artisan::call("data:import:signup-tokens {$plan->title} nofile.txt");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("File 'nofile.txt' does not exist", $output);
+
+ // Empty input file
+ $code = \Artisan::call("data:import:signup-tokens {$plan->title} {$file}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("File '{$file}' is empty", $output);
+
+ // Valid tokens
+ file_put_contents($file, "12345\r\nabcde");
+ $code = \Artisan::call("data:import:signup-tokens {$plan->id} {$file}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertStringContainsString("Validating tokens... DONE", $output);
+ $this->assertStringContainsString("Importing tokens... DONE", $output);
+ $this->assertSame(['12345', 'ABCDE'], $plan->signupTokens()->orderBy('id')->pluck('id')->all());
+
+ // Attempt the same tokens again
+ $code = \Artisan::call("data:import:signup-tokens {$plan->id} {$file}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertStringContainsString("Validating tokens... DONE", $output);
+ $this->assertStringContainsString("Nothing to import", $output);
+ $this->assertStringNotContainsString("Importing tokens...", $output);
+ }
+}
diff --git a/src/tests/Feature/Controller/SignupTest.php b/src/tests/Feature/Controller/SignupTest.php
--- a/src/tests/Feature/Controller/SignupTest.php
+++ b/src/tests/Feature/Controller/SignupTest.php
@@ -10,6 +10,7 @@
use App\Package;
use App\SignupCode;
use App\SignupInvitation as SI;
+use App\SignupToken;
use App\User;
use App\VatRate;
use Illuminate\Support\Facades\Queue;
@@ -39,7 +40,9 @@
$this->deleteTestDomain('signup-domain.com');
$this->deleteTestGroup('group-test@kolabnow.com');
+
SI::truncate();
+ SignupToken::truncate();
Plan::where('title', 'test')->delete();
IP4Net::where('net_number', inet_pton('127.0.0.0'))->delete();
VatRate::query()->delete();
@@ -59,13 +62,13 @@
$this->deleteTestDomain('signup-domain.com');
$this->deleteTestGroup('group-test@kolabnow.com');
+
SI::truncate();
+ SignupToken::truncate();
Plan::where('title', 'test')->delete();
IP4Net::where('net_number', inet_pton('127.0.0.0'))->delete();
VatRate::query()->delete();
- @unlink(storage_path('signup-tokens.txt'));
-
parent::tearDown();
}
@@ -953,18 +956,8 @@
$this->assertSame('error', $json['status']);
$this->assertSame(['token' => ["The signup token is invalid."]], $json['errors']);
- file_put_contents(storage_path('signup-tokens.txt'), "abc\n");
-
- // Test invalid plan (existing plan with another mode)
- $post['plan'] = 'individual';
- $response = $this->post('/api/auth/signup', $post);
- $response->assertStatus(422);
- $json = $response->json();
-
- $this->assertSame('error', $json['status']);
- $this->assertSame(['plan' => "The selected plan is invalid."], $json['errors']);
-
- // Test valid input
+ // Test valid token
+ $plan->signupTokens()->create(['id' => 'abc']);
$post['plan'] = $plan->title;
$response = $this->post('/api/auth/signup', $post);
$response->assertStatus(200);
@@ -976,9 +969,13 @@
// Check if the user has been created
$user = User::where('email', 'test-inv@kolabnow.com')->first();
-
$this->assertNotEmpty($user);
$this->assertSame($plan->id, $user->getSetting('plan_id'));
+ $this->assertSame($plan->signupTokens()->first()->id, $user->getSetting('signup_token'));
+ $this->assertSame(null, $user->getSetting('external_email'));
+
+ // Token's counter bumped up
+ $this->assertSame(1, $plan->signupTokens()->first()->counter);
}
/**
diff --git a/src/tests/Unit/Rules/SignupTokenTest.php b/src/tests/Unit/Rules/SignupTokenTest.php
--- a/src/tests/Unit/Rules/SignupTokenTest.php
+++ b/src/tests/Unit/Rules/SignupTokenTest.php
@@ -2,7 +2,9 @@
namespace Tests\Unit\Rules;
-use App\Rules\SignupToken;
+use App\Plan;
+use App\Rules\SignupToken as SignupTokenRule;
+use App\SignupToken;
use Illuminate\Support\Facades\Validator;
use Tests\TestCase;
@@ -11,25 +13,44 @@
/**
* {@inheritDoc}
*/
- public function tearDown(): void
+ public function setUp(): void
{
- @unlink(storage_path('signup-tokens.txt'));
+ parent::setUp();
- $john = $this->getTestUser('john@kolab.org');
- $john->settings()->where('key', 'signup_token')->delete();
+ Plan::where('title', 'test-plan')->delete();
+ SignupToken::truncate();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ Plan::where('title', 'test-plan')->delete();
+ SignupToken::truncate();
parent::tearDown();
}
/**
- * Tests the resource name validator
+ * Tests the signup token validator
*/
public function testValidation(): void
{
- $tokens = ['1234567890', 'abcdefghijk'];
- file_put_contents(storage_path('signup-tokens.txt'), implode("\n", $tokens));
+ $tokens = ['abcdefghijk', 'T-abcdefghijk'];
+
+ $plan = Plan::where('title', 'individual')->first();
+ $tokenPlan = Plan::create([
+ 'title' => 'test-plan',
+ 'description' => 'test',
+ 'name' => 'Test',
+ 'mode' => Plan::MODE_TOKEN,
+ ]);
- $rules = ['token' => [new SignupToken()]];
+ $plan->signupTokens()->create(['id' => $tokens[0]]);
+ $tokenPlan->signupTokens()->create(['id' => $tokens[1]]);
+
+ $rules = ['token' => [new SignupTokenRule(null)]];
// Empty input
$v = Validator::make(['token' => null], $rules);
@@ -39,22 +60,28 @@
$v = Validator::make(['token' => str_repeat('a', 192)], $rules);
$this->assertSame(['token' => ["The signup token is invalid."]], $v->errors()->toArray());
+ // Valid token, but no plan
+ $v = Validator::make(['token' => $tokens[1]], $rules);
+ $this->assertSame(['token' => ["The signup token is invalid."]], $v->errors()->toArray());
+
+ $rules = ['token' => [new SignupTokenRule($plan)]];
+
+ // Plan that does not support tokens
+ $v = Validator::make(['token' => $tokens[0]], $rules);
+ $this->assertSame(['token' => ["The signup token is invalid."]], $v->errors()->toArray());
+
+ $rules = ['token' => [new SignupTokenRule($tokenPlan)]];
+
// Non-existing token
$v = Validator::make(['token' => '123'], $rules);
$this->assertSame(['token' => ["The signup token is invalid."]], $v->errors()->toArray());
- // Valid tokens
- $v = Validator::make(['token' => $tokens[0]], $rules);
+ // Valid token
+ $v = Validator::make(['token' => $tokens[1]], $rules);
$this->assertSame([], $v->errors()->toArray());
+ // Valid token (uppercase)
$v = Validator::make(['token' => strtoupper($tokens[1])], $rules);
$this->assertSame([], $v->errors()->toArray());
-
- // Tokens already used
- $john = $this->getTestUser('john@kolab.org');
- $john->setSetting('signup_token', $tokens[0]);
-
- $v = Validator::make(['token' => $tokens[0]], $rules);
- $this->assertSame(['token' => ["The signup token is invalid."]], $v->errors()->toArray());
}
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Apr 3, 4:06 PM (21 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18824638
Default Alt Text
D4606.1775232388.diff (25 KB)
Attached To
Mode
D4606: Signup tokens in database table
Attached
Detach File
Event Timeline