From be38c6aa6a092060e636334b52cf73e9e3c88c5f Mon Sep 17 00:00:00 2001 From: Geist5000 Date: Tue, 8 Mar 2022 13:08:09 +0100 Subject: [PATCH 1/4] basic white and blacklist of emails --- app/Http/Controllers/UserController.php | 14 ++++---- app/Rules/EmailBlockList.php | 44 +++++++++++++++++++++++++ app/Services/EmailService.php | 38 ++++++++++++++++++++- app/Services/UserService.php | 18 +++++++--- config/accounts.php | 25 ++++++++++++++ resources/lang/en/validation.php | 3 ++ 6 files changed, 130 insertions(+), 12 deletions(-) create mode 100644 app/Rules/EmailBlockList.php create mode 100644 config/accounts.php diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 24e8855..28b1510 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -8,6 +8,7 @@ use App\Models\EmailVerification; use App\Models\User; use App\Policies\UserPolicy; +use App\Rules\EmailBlockList; use App\Services\EmailService; use App\Services\UserService; use Carbon\Carbon; @@ -32,7 +33,7 @@ class UserController extends Controller { /** - * Das regex, welches Benutzt wird um sicherzustellen, dass das User Password + * Das regex, welches Benutzt wird um sicherzustellen, dass das User Password valide ist * @var string */ public static $passwordRegex = "/^(?=.*[a-zäöüß])(?=.*[A-ZÄÖÜ])(?=.*\d)(?=.*[$&§+,:;=?@#|'<>.^*()%!_-])[A-Za-zäöüßÄÖÜ\d$&§+,:;=?@#|'<>.^*()%!_-].+$/"; @@ -66,18 +67,17 @@ public function store(Request $request, EmailService $emailService, UserService $validated = Validator::validate($request->all(), [ "username" => ["required", "string", "unique:users"], "password" => ["required", "string", "min:8", "max:120", "regex:" . UserController::$passwordRegex], - "email" => ["required", "email", "unique:users,email", "unique:" . EmailVerification::class . ",email"], + "email" => ["required", "email", new EmailBlockList($emailService), "unique:users,email", "unique:" . EmailVerification::class . ",email"], "anonymous_id" => ["integer", "exists:users,id"], ], [ "password.regex" => __("passwords.invalid_regex") ]); - $u = null; if (array_key_exists("anonymous_id", $validated)) { $u = User::find($validated["anonymous_id"]); - if($u->anonymous){ - $userService->upgradeAnonymousUser($u,$validated,$emailService); + if ($u->anonymous) { + $userService->upgradeAnonymousUser($u, $validated, $emailService); } } else { @@ -142,7 +142,7 @@ public function update(Request $request, User $user, EmailService $emailService, $validated = Validator::validate($request->all(), [ "username" => ["string", "unique:users"], "password" => ["string", "min:8", "max:120", "regex:" . UserController::$passwordRegex], - // "email" => ["email", "unique:users,email", "unique:" . EmailVerification::class . ",email"] + // "email" => ["email", "unique:users,email", new EmailBlockList($emailService), "unique:" . EmailVerification::class . ",email"] ], [ "password.regex" => __("passwords.invalid_regex") ]); @@ -190,7 +190,7 @@ public function checkUsername(Request $request, UserService $userService): JsonR * @param UserService $userService Dependency Injection * @return JsonResponse Body enthält available attribut, welches angibt, ob die E-Mail bereits benutzt wird */ - public function checkEmail(Request $request, UserService $userService): JsonResponse + public function checkEmail(Request $request, UserService $userService, EmailService $emailService): JsonResponse { $validated = $request->validate([ "email" => ["string", "required"] diff --git a/app/Rules/EmailBlockList.php b/app/Rules/EmailBlockList.php new file mode 100644 index 0000000..38cdadf --- /dev/null +++ b/app/Rules/EmailBlockList.php @@ -0,0 +1,44 @@ +emailService = $emailService; + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + public function passes($attribute, $value) + { + return $this->emailService->checkBlockLists($value); + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return trans('validation.email_block_list'); + } +} diff --git a/app/Services/EmailService.php b/app/Services/EmailService.php index 23abe53..ab7b01d 100644 --- a/app/Services/EmailService.php +++ b/app/Services/EmailService.php @@ -43,6 +43,42 @@ public function requestEmailChangeOfUser(User $user, string $email) // is queued because of the ShouldQueue interface of EmailVerificationEmail - Mail::to($emailVerification->email)->send(new EmailVerificationEmail($emailVerification->token,$user->username)); + Mail::to($emailVerification->email)->send(new EmailVerificationEmail($emailVerification->token, $user->username)); + } + + /** + * + * Prüft die gegebene E-Mail und gibt an, ob sie durch die white oder blacklist verboten wird. + * + * @param string $email die zu prüfende email + * @return bool true wenn die email erlaubt ist, sonst false + */ + public function checkBlockLists(string $email): bool + { + $whitelist = config("accounts.email_whitelist"); + $blacklist = config("accounts.email_blacklist"); + foreach ($whitelist as $allowedRegex) { + $result = preg_match($this->regexAddDelimiter($allowedRegex), $email); + if ($result === false || $result === 0) { + return false; + } + } + + foreach ($blacklist as $permittedRegex) { + $result = preg_match($this->regexAddDelimiter($permittedRegex), $email); + + if ($result === 1) { + return false; + } + } + + return true; + } + + private function regexAddDelimiter(string $regex) + { + $needsDelimiter = !(str_starts_with($regex, "/") && str_ends_with($regex, "/")); + + return ($needsDelimiter ? "/" : "") . $regex . ($needsDelimiter ? "/" : ""); } } diff --git a/app/Services/UserService.php b/app/Services/UserService.php index 5f6169d..6a22158 100644 --- a/app/Services/UserService.php +++ b/app/Services/UserService.php @@ -12,6 +12,13 @@ class UserService { + private EmailService $emailService; + + public function __construct(EmailService $emailService) + { + $this->emailService = $emailService; + } + /** Überprüft ob der Username bereits verwendet wird * @param string $username Der zu überprüfende Username @@ -23,7 +30,7 @@ public function checkUsername(string $username): bool } /** - * Überprüft ob die E-Mail bereits verwendet wird + * Überprüft ob die E-Mail bereits verwendet wird und ob sie von der white oder blacklist geblockt wird * * zu verifizierende E-Mail-Adressen gelten auch als verwendet * @param string $email Zu überprüfende E-Mail @@ -31,8 +38,11 @@ public function checkUsername(string $username): bool */ public function checkEmail(string $email): bool { - return !(User::whereEmail($email)->exists() || - EmailVerification::whereEmail($email)->exists()); + return $this->emailService->checkBlockLists($email) && + !( + User::whereEmail($email)->exists() || + EmailVerification::whereEmail($email)->exists() + ); } /** @@ -98,7 +108,7 @@ public function upgradeAnonymousUser(User $u, array $data, EmailService $emailSe $emailService->requestEmailChangeOfUser($u, $data["email"]); $u->save(); return true; - }else{ + } else { return false; } } diff --git a/config/accounts.php b/config/accounts.php new file mode 100644 index 0000000..48ccb61 --- /dev/null +++ b/config/accounts.php @@ -0,0 +1,25 @@ + [ + "^.*jade-hs.de$" + ], + + /* + * defines which email addresses are permitted. + * + * The given strings are checked against the email a user chooses to register with and are allowed by the whitelist. + * Regex is supported. + */ + "email_blacklist" => [ + ] +]; diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index 7a15f43..56bca4f 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -123,6 +123,9 @@ 'url' => 'The :attribute format is invalid.', 'uuid' => 'The :attribute must be a valid UUID.', + // custom + 'email_block_list' => 'The :attribute :value is blocked by a white and/or blacklist', + /* |-------------------------------------------------------------------------- | Custom Validation Language Lines From 26c132588cc0d22b94e282de4180d12b10e3fb04 Mon Sep 17 00:00:00 2001 From: Geist5000 Date: Tue, 8 Mar 2022 13:10:55 +0100 Subject: [PATCH 2/4] added rejection reason to checkEmail route --- app/Http/Controllers/UserController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 28b1510..2f2ac19 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -193,7 +193,7 @@ public function checkUsername(Request $request, UserService $userService): JsonR public function checkEmail(Request $request, UserService $userService, EmailService $emailService): JsonResponse { $validated = $request->validate([ - "email" => ["string", "required"] + "email" => ["string", "required", new EmailBlockList($emailService)] ]); return response()->json(["data" => [ "available" => $userService->checkEmail($validated["email"]) From ed3bce9bf96c512bb1cc495e3793e03602e54d28 Mon Sep 17 00:00:00 2001 From: Geist5000 Date: Tue, 8 Mar 2022 14:06:20 +0100 Subject: [PATCH 3/4] added reason to availability response --- app/Http/Controllers/UserController.php | 22 +++++++++++++++++++--- app/Services/UserService.php | 3 +-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 2f2ac19..5dbb97b 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -176,8 +176,10 @@ public function checkUsername(Request $request, UserService $userService): JsonR $validated = $request->validate([ "username" => ["string", "required"] ]); + $available = $userService->checkUsername($validated["username"]); return response()->json(["data" => [ - "available" => $userService->checkUsername($validated["username"]) + "available" => $available, + "reason" => $available? "": "taken" ]]); } @@ -193,10 +195,24 @@ public function checkUsername(Request $request, UserService $userService): JsonR public function checkEmail(Request $request, UserService $userService, EmailService $emailService): JsonResponse { $validated = $request->validate([ - "email" => ["string", "required", new EmailBlockList($emailService)] + "email" => ["string", "required"] ]); + $reason = ""; + + $allowed = $emailService->checkBlockLists($validated["email"]); + $available = false; + if ($allowed) { + $available = $userService->checkEmail($validated["email"]); + if (!$available) { + $reason = "taken"; + } + } else { + $reason = "blocked"; + } + return response()->json(["data" => [ - "available" => $userService->checkEmail($validated["email"]) + "available" => $available, + "reason" => $reason, ]]); } diff --git a/app/Services/UserService.php b/app/Services/UserService.php index 6a22158..6b5086f 100644 --- a/app/Services/UserService.php +++ b/app/Services/UserService.php @@ -38,8 +38,7 @@ public function checkUsername(string $username): bool */ public function checkEmail(string $email): bool { - return $this->emailService->checkBlockLists($email) && - !( + return !( User::whereEmail($email)->exists() || EmailVerification::whereEmail($email)->exists() ); From 11326025fb6d1a6100611a5166ee0b1364416bbe Mon Sep 17 00:00:00 2001 From: Geist5000 Date: Tue, 8 Mar 2022 18:17:54 +0100 Subject: [PATCH 4/4] added email validation to emailCheck --- app/Http/Controllers/UserController.php | 28 ++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 5dbb97b..971e0e2 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -11,7 +11,7 @@ use App\Rules\EmailBlockList; use App\Services\EmailService; use App\Services\UserService; -use Carbon\Carbon; +use Egulias\EmailValidator\EmailValidator; use Exception; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\JsonResponse; @@ -20,9 +20,6 @@ use Illuminate\Http\Response; use Illuminate\Support\Facades\Mail; use Illuminate\Validation\ValidationException; -use Laravel\Passport\Bridge\AccessTokenRepository; -use Laravel\Passport\Bridge\ClientRepository; -use League\OAuth2\Server\AuthorizationServer; use Validator; /** @@ -179,7 +176,7 @@ public function checkUsername(Request $request, UserService $userService): JsonR $available = $userService->checkUsername($validated["username"]); return response()->json(["data" => [ "available" => $available, - "reason" => $available? "": "taken" + "reason" => $available ? "" : "taken" ]]); } @@ -199,17 +196,24 @@ public function checkEmail(Request $request, UserService $userService, EmailServ ]); $reason = ""; - $allowed = $emailService->checkBlockLists($validated["email"]); - $available = false; - if ($allowed) { - $available = $userService->checkEmail($validated["email"]); - if (!$available) { - $reason = "taken"; + $validEmail = filter_var($validated["email"], FILTER_VALIDATE_EMAIL); + if ($validEmail !== false) { + $allowed = $emailService->checkBlockLists($validated["email"]); + $available = false; + if ($allowed) { + $available = $userService->checkEmail($validated["email"]); + if (!$available) { + $reason = "taken"; + } + } else { + $reason = "blocked"; } } else { - $reason = "blocked"; + $available = false; + $reason = "invalid"; } + return response()->json(["data" => [ "available" => $available, "reason" => $reason,