mirror of
https://github.com/delight-im/PHP-Auth.git
synced 2025-08-07 00:26:28 +02:00
Compare commits
334 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a1ae66374b | ||
|
477164e8ec | ||
|
9478a43e9b | ||
|
1ba8e1ff21 | ||
|
1657102f75 | ||
|
d246248ab5 | ||
|
94531f24d3 | ||
|
2f29830ed9 | ||
|
42a8c1616c | ||
|
a2be4c61ee | ||
|
d9f9198b45 | ||
|
13b58abebc | ||
|
b0bf7647ce | ||
|
012577227a | ||
|
d834623954 | ||
|
d3594898cc | ||
|
7d44158c32 | ||
|
04edd9f88f | ||
|
cd2ac47912 | ||
|
7bcf201972 | ||
|
09247e7203 | ||
|
ab1c54fae2 | ||
|
23acb66cc7 | ||
|
a7a9d45302 | ||
|
ba4dc29ca5 | ||
|
0a97f67515 | ||
|
7a94c6acef | ||
|
dbbbf1b193 | ||
|
9637dfa60d | ||
|
aec738a9db | ||
|
382ee5bf93 | ||
|
47d1e303aa | ||
|
67443c122a | ||
|
24056e89a4 | ||
|
c06bc7da1a | ||
|
aedd2125fc | ||
|
425cf9b6f6 | ||
|
739fa7d574 | ||
|
302feb5da2 | ||
|
2ded232d8e | ||
|
70a905afd7 | ||
|
84f3ad10a9 | ||
|
81091df66b | ||
|
8926e7e708 | ||
|
eec450677f | ||
|
f1360dceba | ||
|
2cf7b27ba3 | ||
|
ecd8015acf | ||
|
1eedfd0e02 | ||
|
757579523c | ||
|
d695328a5a | ||
|
71506eaa05 | ||
|
ce8dbbc436 | ||
|
d181219e40 | ||
|
891cef2511 | ||
|
f70613b2b8 | ||
|
59816d1a40 | ||
|
1284f64f04 | ||
|
8165e8917b | ||
|
a4b68167a1 | ||
|
fc2fb4bb44 | ||
|
b2a3fde696 | ||
|
36880b87c9 | ||
|
4a66965994 | ||
|
e7b590dc80 | ||
|
33d2384c93 | ||
|
1169856217 | ||
|
fa75811679 | ||
|
fa8fa4887e | ||
|
8fecb86f15 | ||
|
04c466b309 | ||
|
61041cc6fd | ||
|
2ca835ac75 | ||
|
1e23e6de13 | ||
|
50220d463b | ||
|
f0bdd7b63e | ||
|
0473d59c39 | ||
|
f8f44a0286 | ||
|
ea91d8c92e | ||
|
7983bebd83 | ||
|
ddc5b50459 | ||
|
0b67f3d1e2 | ||
|
16bcfa85ef | ||
|
404739634d | ||
|
82a24fbbca | ||
|
1a195adf39 | ||
|
5e4d4fd072 | ||
|
6162092618 | ||
|
f142dd91dc | ||
|
05567acc7c | ||
|
3d8c583823 | ||
|
546a57cbf9 | ||
|
52ba03248d | ||
|
c5ed53898e | ||
|
a66312bbcf | ||
|
c1bb10f58d | ||
|
4fd37f079b | ||
|
8ff3776e75 | ||
|
b24979ae26 | ||
|
30b2f30aec | ||
|
b3d37ada86 | ||
|
27adc9fa91 | ||
|
c9a4e28c7b | ||
|
f83ac969d4 | ||
|
0bbf9d32b1 | ||
|
381e05f102 | ||
|
2839743c46 | ||
|
d86d7ffd25 | ||
|
e3873f2d15 | ||
|
b7a47fc707 | ||
|
91f50a80bb | ||
|
7272fbb9a8 | ||
|
62c5fab1ad | ||
|
1800525b51 | ||
|
e4f8673eab | ||
|
59cd626bd0 | ||
|
3809b9d5d5 | ||
|
3329c6a985 | ||
|
7b98993bf8 | ||
|
d5ae78a418 | ||
|
e925a73ef8 | ||
|
39f9b00b45 | ||
|
ea67c66bd1 | ||
|
7b4c4bf0e1 | ||
|
f13302b014 | ||
|
af5ce5a0b4 | ||
|
15f73567b6 | ||
|
90c621aeb0 | ||
|
28979925d7 | ||
|
b2e6f68a22 | ||
|
d14d929bc3 | ||
|
f962008fc4 | ||
|
ec8e9eab4e | ||
|
65b4f812c0 | ||
|
b8e04e3c6a | ||
|
5c92d026c9 | ||
|
2247c2781c | ||
|
72b2468aa3 | ||
|
7cc27b814e | ||
|
dbc463c95e | ||
|
4b6afc7c48 | ||
|
a3a28af2aa | ||
|
c842fa9792 | ||
|
a599771bd5 | ||
|
e73f29eec0 | ||
|
c118116a52 | ||
|
0e969ccd8d | ||
|
aae0bfb5ab | ||
|
fb982cee6a | ||
|
838c6edf66 | ||
|
ad5784364d | ||
|
d8f21a35fc | ||
|
79ecb85bb6 | ||
|
f56e7e6871 | ||
|
83f2ab0a9c | ||
|
5274dd5f8e | ||
|
b93d9616d0 | ||
|
0af55ad77c | ||
|
7b6287a7dc | ||
|
cf7493d87e | ||
|
f68d29000e | ||
|
cd3469c137 | ||
|
bc44a08b1b | ||
|
8ff4242f8f | ||
|
1a4041ea60 | ||
|
b7e6ca6dee | ||
|
f2074e1537 | ||
|
9c63c30cd9 | ||
|
8a1140a485 | ||
|
23b172055b | ||
|
c25b74d405 | ||
|
2278b86fba | ||
|
4eca6bb151 | ||
|
db4c99e729 | ||
|
d6bc8c6492 | ||
|
b577322939 | ||
|
6cf955ed52 | ||
|
8c2c32f9dc | ||
|
2d7ad74c44 | ||
|
a91cde706d | ||
|
8feda0ae58 | ||
|
78b7fb4169 | ||
|
499fbb6542 | ||
|
50b9c48f8d | ||
|
fcbace0aec | ||
|
c2ab825354 | ||
|
b1ac859fd2 | ||
|
0d9be76f8b | ||
|
64d15263ae | ||
|
854bc2b62b | ||
|
01a52b76bc | ||
|
ad88c1c6ab | ||
|
449e1c69ee | ||
|
63734fc5ee | ||
|
6e3728a918 | ||
|
0909291cf1 | ||
|
6aa3f58059 | ||
|
6156b1c135 | ||
|
829d5614ed | ||
|
47afa1c411 | ||
|
26cb41e992 | ||
|
ee485f99ab | ||
|
8fc0b98493 | ||
|
45553afaea | ||
|
7834455e16 | ||
|
e49adf0150 | ||
|
0fb653d6e0 | ||
|
dc233d9d46 | ||
|
7c842f903e | ||
|
0e2279ecda | ||
|
79db94f500 | ||
|
f38d7bd62c | ||
|
04a2e8ef4e | ||
|
59505479a5 | ||
|
fdcfd6f78c | ||
|
20606bc507 | ||
|
89a7af17fe | ||
|
4c084150c4 | ||
|
dd51d2c07d | ||
|
93477e4e7e | ||
|
d59ac83d13 | ||
|
9a0036b8a8 | ||
|
a05d277a2c | ||
|
0839beefcb | ||
|
bf5db38361 | ||
|
d9be7a4c22 | ||
|
e9bae4a346 | ||
|
2317423550 | ||
|
d9dccf8100 | ||
|
26ca48c3b9 | ||
|
9ec74b3b2d | ||
|
9c60acec0d | ||
|
94eeb9dbe0 | ||
|
4dca8439d1 | ||
|
81bdd79906 | ||
|
63144d4dc0 | ||
|
f06af42f87 | ||
|
6c6f34935c | ||
|
293c231003 | ||
|
05d72a849b | ||
|
cf41c9a105 | ||
|
da4bb583bf | ||
|
d99979f270 | ||
|
22872d55bd | ||
|
ff6d78942a | ||
|
d27005df10 | ||
|
ad2aa84e4a | ||
|
f7d50d53ea | ||
|
e916c3d07e | ||
|
fdeff8a792 | ||
|
43fa612d67 | ||
|
0b0258f29a | ||
|
9252bee030 | ||
|
6a15679238 | ||
|
8ab08f41e1 | ||
|
83464c0be7 | ||
|
b5c853388c | ||
|
5585623e08 | ||
|
a7d640154c | ||
|
8acd3a9779 | ||
|
374f27176b | ||
|
3cb2284870 | ||
|
690485ba6d | ||
|
495a87d499 | ||
|
784030139b | ||
|
fb6f3d31b8 | ||
|
370ecc4933 | ||
|
da2d282648 | ||
|
4aaf85e3cf | ||
|
f2561a1932 | ||
|
8cc54473e3 | ||
|
f26f2209cd | ||
|
188086f2e4 | ||
|
c6213a6081 | ||
|
c55250c572 | ||
|
dac2850aba | ||
|
4268e3fcd5 | ||
|
d579179494 | ||
|
bd02e08f83 | ||
|
d4fe11b844 | ||
|
09fabd4c91 | ||
|
4dcf491ad9 | ||
|
4f5ff151ef | ||
|
f5027c09e9 | ||
|
6db82d1f65 | ||
|
f944067aff | ||
|
a640e8a5ad | ||
|
2aee8a662e | ||
|
36ef710480 | ||
|
9187840767 | ||
|
6bfa298836 | ||
|
6be456a27a | ||
|
78a16d8f50 | ||
|
e669f6f017 | ||
|
5aafd0b009 | ||
|
d53a484c2e | ||
|
07732dcaa9 | ||
|
f486ab6763 | ||
|
5e331924f6 | ||
|
ac95be3714 | ||
|
e6c8ae056c | ||
|
5bac29065d | ||
|
36b590eb81 | ||
|
5c6a71d921 | ||
|
d94243f19d | ||
|
2a2d93f534 | ||
|
989c7940e5 | ||
|
51a5735295 | ||
|
e5e465782b | ||
|
83caa3e785 | ||
|
f2a1aedf7a | ||
|
5c87e877db | ||
|
70842b4320 | ||
|
d527a82bfa | ||
|
31ae135740 | ||
|
c5e3bd191d | ||
|
53e1a5c1fc | ||
|
f3ca69010f | ||
|
da8d22c599 | ||
|
c993657f20 | ||
|
cce172442d | ||
|
aef2672942 | ||
|
e0b69ee33c | ||
|
40a5518ba7 | ||
|
2441ea2dc1 | ||
|
07f60d6610 | ||
|
35cc941f20 | ||
|
f4b464a6f8 | ||
|
bfa5b5e6b1 | ||
|
9d2d764ced | ||
|
f45e0f1cb4 | ||
|
b9b6d46b4d | ||
|
ad2c338f6a | ||
|
1f8df61168 |
@@ -1,9 +1,21 @@
|
||||
-- PHP-Auth (https://github.com/delight-im/PHP-Auth)
|
||||
-- Copyright (c) delight.im (https://www.delight.im/)
|
||||
-- Licensed under the MIT License (https://opensource.org/licenses/MIT)
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `users` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`email` varchar(249) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`password` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
||||
`username` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`status` tinyint(2) unsigned NOT NULL DEFAULT '0',
|
||||
`verified` tinyint(1) unsigned NOT NULL DEFAULT '0',
|
||||
`resettable` tinyint(1) unsigned NOT NULL DEFAULT '1',
|
||||
`roles_mask` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`registered` int(10) unsigned NOT NULL,
|
||||
`last_login` int(10) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
@@ -12,13 +24,15 @@ CREATE TABLE IF NOT EXISTS `users` (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `users_confirmations` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(10) unsigned NOT NULL,
|
||||
`email` varchar(249) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`selector` varchar(16) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
||||
`token` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
||||
`expires` int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `selector` (`selector`),
|
||||
KEY `email_expires` (`email`,`expires`)
|
||||
KEY `email_expires` (`email`,`expires`),
|
||||
KEY `user_id` (`user_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `users_remembered` (
|
||||
@@ -35,20 +49,23 @@ CREATE TABLE IF NOT EXISTS `users_remembered` (
|
||||
CREATE TABLE IF NOT EXISTS `users_resets` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user` int(10) unsigned NOT NULL,
|
||||
`selector` varchar(24) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
||||
`selector` varchar(20) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
||||
`token` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
||||
`expires` int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `selector` (`selector`),
|
||||
KEY `user` (`user`)
|
||||
KEY `user_expires` (`user`,`expires`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `users_throttling` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`action_type` enum('login','register','confirm_email') COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`selector` varchar(44) CHARACTER SET latin1 COLLATE latin1_general_cs DEFAULT NULL,
|
||||
`time_bucket` int(10) unsigned NOT NULL,
|
||||
`attempts` mediumint(8) unsigned NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `action_type_selector_time_bucket` (`action_type`,`selector`,`time_bucket`)
|
||||
`bucket` varchar(44) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
||||
`tokens` float unsigned NOT NULL,
|
||||
`replenished_at` int(10) unsigned NOT NULL,
|
||||
`expires_at` int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (`bucket`),
|
||||
KEY `expires_at` (`expires_at`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
|
59
Database/SQLite.sql
Normal file
59
Database/SQLite.sql
Normal file
@@ -0,0 +1,59 @@
|
||||
-- PHP-Auth (https://github.com/delight-im/PHP-Auth)
|
||||
-- Copyright (c) delight.im (https://www.delight.im/)
|
||||
-- Licensed under the MIT License (https://opensource.org/licenses/MIT)
|
||||
|
||||
PRAGMA foreign_keys = OFF;
|
||||
|
||||
CREATE TABLE "users" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("id" >= 0),
|
||||
"email" VARCHAR(249) NOT NULL,
|
||||
"password" VARCHAR(255) NOT NULL,
|
||||
"username" VARCHAR(100) DEFAULT NULL,
|
||||
"status" INTEGER NOT NULL CHECK ("status" >= 0) DEFAULT "0",
|
||||
"verified" INTEGER NOT NULL CHECK ("verified" >= 0) DEFAULT "0",
|
||||
"resettable" INTEGER NOT NULL CHECK ("resettable" >= 0) DEFAULT "1",
|
||||
"roles_mask" INTEGER NOT NULL CHECK ("roles_mask" >= 0) DEFAULT "0",
|
||||
"registered" INTEGER NOT NULL CHECK ("registered" >= 0),
|
||||
"last_login" INTEGER CHECK ("last_login" >= 0) DEFAULT NULL,
|
||||
CONSTRAINT "email" UNIQUE ("email")
|
||||
);
|
||||
|
||||
CREATE TABLE "users_confirmations" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("id" >= 0),
|
||||
"user_id" INTEGER NOT NULL CHECK ("user_id" >= 0),
|
||||
"email" VARCHAR(249) NOT NULL,
|
||||
"selector" VARCHAR(16) NOT NULL,
|
||||
"token" VARCHAR(255) NOT NULL,
|
||||
"expires" INTEGER NOT NULL CHECK ("expires" >= 0),
|
||||
CONSTRAINT "selector" UNIQUE ("selector")
|
||||
);
|
||||
CREATE INDEX "users_confirmations.email_expires" ON "users_confirmations" ("email", "expires");
|
||||
CREATE INDEX "users_confirmations.user_id" ON "users_confirmations" ("user_id");
|
||||
|
||||
CREATE TABLE "users_remembered" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("id" >= 0),
|
||||
"user" INTEGER NOT NULL CHECK ("user" >= 0),
|
||||
"selector" VARCHAR(24) NOT NULL,
|
||||
"token" VARCHAR(255) NOT NULL,
|
||||
"expires" INTEGER NOT NULL CHECK ("expires" >= 0),
|
||||
CONSTRAINT "selector" UNIQUE ("selector")
|
||||
);
|
||||
CREATE INDEX "users_remembered.user" ON "users_remembered" ("user");
|
||||
|
||||
CREATE TABLE "users_resets" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("id" >= 0),
|
||||
"user" INTEGER NOT NULL CHECK ("user" >= 0),
|
||||
"selector" VARCHAR(20) NOT NULL,
|
||||
"token" VARCHAR(255) NOT NULL,
|
||||
"expires" INTEGER NOT NULL CHECK ("expires" >= 0),
|
||||
CONSTRAINT "selector" UNIQUE ("selector")
|
||||
);
|
||||
CREATE INDEX "users_resets.user_expires" ON "users_resets" ("user", "expires");
|
||||
|
||||
CREATE TABLE "users_throttling" (
|
||||
"bucket" VARCHAR(44) PRIMARY KEY NOT NULL,
|
||||
"tokens" REAL NOT NULL CHECK ("tokens" >= 0),
|
||||
"replenished_at" INTEGER NOT NULL CHECK ("replenished_at" >= 0),
|
||||
"expires_at" INTEGER NOT NULL CHECK ("expires_at" >= 0)
|
||||
);
|
||||
CREATE INDEX "users_throttling.expires_at" ON "users_throttling" ("expires_at");
|
214
LICENSE
214
LICENSE
@@ -1,201 +1,21 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
The MIT License (MIT)
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
Copyright (c) delight.im (https://www.delight.im/)
|
||||
|
||||
1. Definitions.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
203
Migration.md
203
Migration.md
@@ -1,35 +1,190 @@
|
||||
# Migration
|
||||
|
||||
* `v1.x.x` to `v2.x.x`
|
||||
* The MySQL schema has been changed from charset `utf8` to charset `utf8mb4` and from collation `utf8_general_ci` to collation `utf8mb4_unicode_ci`. Use the statements below to update the database schema:
|
||||
* [General](#general)
|
||||
* [From `v6.x.x` to `v7.x.x`](#from-v6xx-to-v7xx)
|
||||
* [From `v5.x.x` to `v6.x.x`](#from-v5xx-to-v6xx)
|
||||
* [From `v4.x.x` to `v5.x.x`](#from-v4xx-to-v5xx)
|
||||
* [From `v3.x.x` to `v4.x.x`](#from-v3xx-to-v4xx)
|
||||
* [From `v2.x.x` to `v3.x.x`](#from-v2xx-to-v3xx)
|
||||
* [From `v1.x.x` to `v2.x.x`](#from-v1xx-to-v2xx)
|
||||
|
||||
## General
|
||||
|
||||
Update your version of this library using Composer and its `composer update` or `composer require` commands [[?]](https://github.com/delight-im/Knowledge/blob/master/Composer%20(PHP).md#how-do-i-update-libraries-or-modules-within-my-application).
|
||||
|
||||
## From `v6.x.x` to `v7.x.x`
|
||||
|
||||
* The method `logOutButKeepSession` from class `Auth` is now simply called `logOut`. Therefore, the former method `logout` is now called `logOutAndDestroySession`.
|
||||
|
||||
* The second argument of the `Auth` constructor, which was named `$useHttps`, has been removed. If you previously had it set to `true`, make sure to set the value of the `session.cookie_secure` directive to `1` now. You may do so either directly in your [PHP configuration](http://php.net/manual/en/configuration.file.php) (`php.ini`), via the `\ini_set` method or via the `\session_set_cookie_params` method. Otherwise, make sure that directive is set to `0`.
|
||||
|
||||
* The third argument of the `Auth` constructor, which was named `$allowCookiesScriptAccess`, has been removed. If you previously had it set to `true`, make sure to set the value of the `session.cookie_httponly` directive to `0` now. You may do so either directly in your [PHP configuration](http://php.net/manual/en/configuration.file.php) (`php.ini`), via the `\ini_set` method or via the `\session_set_cookie_params` method. Otherwise, make sure that directive is set to `1`.
|
||||
|
||||
* Only if *both* of the following two conditions are met:
|
||||
|
||||
* The directive `session.cookie_domain` is set to an empty value. It may have been set directly in your [PHP configuration](http://php.net/manual/en/configuration.file.php) (`php.ini`), via the `\ini_set` method or via the `\session_set_cookie_params` method. You can check the value of that directive by executing the following statement somewhere in your application:
|
||||
|
||||
```php
|
||||
\var_dump(\ini_get('session.cookie_domain'));
|
||||
```
|
||||
|
||||
* Your application is accessed via a registered or registrable *domain name*, either by yourself during development and testing or by your visitors and users in production. That means your application is *not*, or *not only*, accessed via `localhost` or via an IP address.
|
||||
|
||||
Then the domain scope for the [two cookies](README.md#cookies) used by this library has changed. You can handle this change in one of two different ways:
|
||||
|
||||
* Restore the old behavior by placing the following statement as early as possible in your application, and before you create the `Auth` instance:
|
||||
|
||||
```php
|
||||
\ini_set('session.cookie_domain', \preg_replace('/^www\./', '', $_SERVER['HTTP_HOST']));
|
||||
```
|
||||
|
||||
You may also evaluate the complete second parameter and put its value directly into your [PHP configuration](http://php.net/manual/en/configuration.file.php) (`php.ini`).
|
||||
|
||||
* Use the new domain scope for your application. To do so, you only need to [rename the cookies](README.md#renaming-the-librarys-cookies) used by this library in order to prevent conflicts with old cookies that have been created previously. Renaming the cookies is critically important here. We recommend a versioned name such as `session_v1` for the session cookie.
|
||||
|
||||
* Only if *both* of the following two conditions are met:
|
||||
|
||||
* The directive `session.cookie_domain` is set to a value that starts with the `www` subdomain. It may have been set directly in your [PHP configuration](http://php.net/manual/en/configuration.file.php) (`php.ini`), via the `\ini_set` method or via the `\session_set_cookie_params` method. You can check the value of that directive by executing the following statement somewhere in your application:
|
||||
|
||||
```php
|
||||
\var_dump(\ini_get('session.cookie_domain'));
|
||||
```
|
||||
|
||||
* Your application is accessed via a registered or registrable *domain name*, either by yourself during development and testing or by your visitors and users in production. That means your application is *not*, or *not only*, accessed via `localhost` or via an IP address.
|
||||
|
||||
Then the domain scope for [one of the cookies](README.md#cookies) used by this library has changed. To make your application work correctly with the new scope, [rename the cookies](README.md#renaming-the-librarys-cookies) used by this library in order to prevent conflicts with old cookies that have been created previously. Renaming the cookies is critically important here. We recommend a versioned name such as `session_v1` for the session cookie.
|
||||
|
||||
* If the directive `session.cookie_path` is set to an empty value, then the path scope for [one of the cookies](README.md#cookies) used by this library has changed. To make your application work correctly with the new scope, [rename the cookies](README.md#renaming-the-librarys-cookies) used by this library in order to prevent conflicts with old cookies that have been created previously. Renaming the cookies is critically important here. We recommend a versioned name such as `session_v1` for the session cookie.
|
||||
|
||||
The directive may have been set directly in your [PHP configuration](http://php.net/manual/en/configuration.file.php) (`php.ini`), via the `\ini_set` method or via the `\session_set_cookie_params` method. You can check the value of that directive by executing the following statement somewhere in your application:
|
||||
|
||||
```php
|
||||
\var_dump(\ini_get('session.cookie_path'));
|
||||
```
|
||||
|
||||
## From `v5.x.x` to `v6.x.x`
|
||||
|
||||
* The database schema has changed.
|
||||
|
||||
* The MySQL database schema has changed. Use the statements below to update your database:
|
||||
|
||||
```sql
|
||||
ALTER TABLE `users` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
|
||||
ALTER TABLE `users_confirmations` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
|
||||
ALTER TABLE users
|
||||
ADD COLUMN roles_mask INT(10) UNSIGNED NOT NULL DEFAULT 0 AFTER verified,
|
||||
ADD COLUMN resettable TINYINT(1) UNSIGNED NOT NULL DEFAULT 1 AFTER verified;
|
||||
|
||||
-- ALTER DATABASE `<DATABASE_NAME>` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
ALTER TABLE users_confirmations
|
||||
ADD COLUMN user_id INT(10) UNSIGNED NULL DEFAULT NULL AFTER id;
|
||||
|
||||
ALTER TABLE `users` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
ALTER TABLE `users_confirmations` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
ALTER TABLE `users_remembered` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
ALTER TABLE `users_resets` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
ALTER TABLE `users_throttling` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
UPDATE users_confirmations SET user_id = (
|
||||
SELECT id FROM users WHERE email = users_confirmations.email
|
||||
) WHERE user_id IS NULL;
|
||||
|
||||
ALTER TABLE `users` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
|
||||
ALTER TABLE `users` CHANGE `username` `username` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL;
|
||||
ALTER TABLE users_confirmations
|
||||
CHANGE COLUMN user_id user_id INT(10) UNSIGNED NOT NULL;
|
||||
|
||||
ALTER TABLE `users_confirmations` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
|
||||
ALTER TABLE users_confirmations
|
||||
ADD INDEX user_id (user_id ASC);
|
||||
|
||||
ALTER TABLE `users_throttling` CHANGE `action_type` `action_type` ENUM('login','register','confirm_email') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
|
||||
DROP TABLE users_throttling;
|
||||
|
||||
REPAIR TABLE users;
|
||||
OPTIMIZE TABLE users;
|
||||
REPAIR TABLE users_confirmations;
|
||||
OPTIMIZE TABLE users_confirmations;
|
||||
REPAIR TABLE users_remembered;
|
||||
OPTIMIZE TABLE users_remembered;
|
||||
REPAIR TABLE users_resets;
|
||||
OPTIMIZE TABLE users_resets;
|
||||
REPAIR TABLE users_throttling;
|
||||
OPTIMIZE TABLE users_throttling;
|
||||
CREATE TABLE users_throttling (
|
||||
bucket varchar(44) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
||||
tokens float unsigned NOT NULL,
|
||||
replenished_at int(10) unsigned NOT NULL,
|
||||
expires_at int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (bucket),
|
||||
KEY expires_at (expires_at)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
```
|
||||
|
||||
* The SQLite database schema has changed. Use the statements below to update your database:
|
||||
|
||||
```sql
|
||||
ALTER TABLE users
|
||||
ADD COLUMN "roles_mask" INTEGER NOT NULL CHECK ("roles_mask" >= 0) DEFAULT "0",
|
||||
ADD COLUMN "resettable" INTEGER NOT NULL CHECK ("resettable" >= 0) DEFAULT "1";
|
||||
|
||||
ALTER TABLE users_confirmations
|
||||
ADD COLUMN "user_id" INTEGER CHECK ("user_id" >= 0);
|
||||
|
||||
UPDATE users_confirmations SET user_id = (
|
||||
SELECT id FROM users WHERE email = users_confirmations.email
|
||||
) WHERE user_id IS NULL;
|
||||
|
||||
CREATE INDEX "users_confirmations.user_id" ON "users_confirmations" ("user_id");
|
||||
|
||||
DROP TABLE users_throttling;
|
||||
|
||||
CREATE TABLE "users_throttling" (
|
||||
"bucket" VARCHAR(44) PRIMARY KEY NOT NULL,
|
||||
"tokens" REAL NOT NULL CHECK ("tokens" >= 0),
|
||||
"replenished_at" INTEGER NOT NULL CHECK ("replenished_at" >= 0),
|
||||
"expires_at" INTEGER NOT NULL CHECK ("expires_at" >= 0)
|
||||
);
|
||||
|
||||
CREATE INDEX "users_throttling.expires_at" ON "users_throttling" ("expires_at");
|
||||
```
|
||||
|
||||
* The method `setThrottlingOptions` has been removed.
|
||||
|
||||
* The method `changePassword` may now throw an additional `\Delight\Auth\TooManyRequestsException` if too many attempts have been made without the correct old password.
|
||||
|
||||
* The two methods `confirmEmail` and `confirmEmailAndSignIn` may now throw an additional `\Delight\Auth\UserAlreadyExistsException` if an attempt has been made to change the email address to an address that has become occupied in the meantime.
|
||||
|
||||
* The two methods `forgotPassword` and `resetPassword` may now throw an additional `\Delight\Auth\ResetDisabledException` if the user has disabled password resets for their account.
|
||||
|
||||
* The `Base64` class is now an external module and has been moved from the namespace `Delight\Auth` to the namespace `Delight\Base64`. The interface and the return values are not compatible with those from previous versions anymore.
|
||||
|
||||
## From `v4.x.x` to `v5.x.x`
|
||||
|
||||
* The MySQL database schema has changed. Use the statement below to update your database:
|
||||
|
||||
```sql
|
||||
ALTER TABLE `users` ADD COLUMN `status` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0 AFTER `username`;
|
||||
```
|
||||
|
||||
* The two classes `Auth` and `Base64` are now `final`, i.e. they can't be extended anymore, which has never been a good idea, anyway. If you still need to wrap your own methods around these classes, consider [object composition instead of class inheritance](https://en.wikipedia.org/wiki/Composition_over_inheritance).
|
||||
|
||||
## From `v3.x.x` to `v4.x.x`
|
||||
|
||||
* PHP 5.6.0 or higher is now required.
|
||||
|
||||
## From `v2.x.x` to `v3.x.x`
|
||||
|
||||
* The license has been changed from the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) to the [MIT License](https://opensource.org/licenses/MIT).
|
||||
|
||||
## From `v1.x.x` to `v2.x.x`
|
||||
|
||||
* The MySQL schema has been changed from charset `utf8` to charset `utf8mb4` and from collation `utf8_general_ci` to collation `utf8mb4_unicode_ci`. Use the statements below to update the database schema:
|
||||
|
||||
```sql
|
||||
ALTER TABLE `users` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
|
||||
ALTER TABLE `users_confirmations` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
|
||||
|
||||
-- ALTER DATABASE `<DATABASE_NAME>` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
ALTER TABLE `users` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
ALTER TABLE `users_confirmations` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
ALTER TABLE `users_remembered` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
ALTER TABLE `users_resets` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
ALTER TABLE `users_throttling` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
ALTER TABLE `users` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
|
||||
ALTER TABLE `users` CHANGE `username` `username` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL;
|
||||
|
||||
ALTER TABLE `users_confirmations` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
|
||||
|
||||
ALTER TABLE `users_throttling` CHANGE `action_type` `action_type` ENUM('login','register','confirm_email') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
|
||||
|
||||
REPAIR TABLE users;
|
||||
OPTIMIZE TABLE users;
|
||||
REPAIR TABLE users_confirmations;
|
||||
OPTIMIZE TABLE users_confirmations;
|
||||
REPAIR TABLE users_remembered;
|
||||
OPTIMIZE TABLE users_remembered;
|
||||
REPAIR TABLE users_resets;
|
||||
OPTIMIZE TABLE users_resets;
|
||||
REPAIR TABLE users_throttling;
|
||||
OPTIMIZE TABLE users_throttling;
|
||||
```
|
||||
|
5
NOTICE
5
NOTICE
@@ -1,5 +0,0 @@
|
||||
PHP-Auth
|
||||
Copyright (c) delight.im <info@delight.im>
|
||||
|
||||
This product includes software developed by
|
||||
delight.im (http://www.delight.im/).
|
@@ -2,14 +2,16 @@
|
||||
"name": "delight-im/auth",
|
||||
"description": "Authentication for PHP. Simple, lightweight and secure.",
|
||||
"require": {
|
||||
"php": ">=5.5.0",
|
||||
"php": ">=5.6.0",
|
||||
"ext-openssl": "*",
|
||||
"delight-im/cookie": "^1.3"
|
||||
"delight-im/base64": "^1.0",
|
||||
"delight-im/cookie": "^3.1",
|
||||
"delight-im/db": "^1.2"
|
||||
},
|
||||
"type": "library",
|
||||
"keywords": [ "auth", "authentication", "login", "security" ],
|
||||
"homepage": "https://github.com/delight-im/PHP-Auth",
|
||||
"license": "Apache-2.0",
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Delight\\Auth\\": "src/"
|
||||
|
121
composer.lock
generated
121
composer.lock
generated
@@ -4,28 +4,68 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "32e4912115e164a1a86227d49db9ac29",
|
||||
"content-hash": "1a5c0056d726ae6195da0faa38f37fdd",
|
||||
"content-hash": "54d541ae3c5ba25b0cc06688d2b65467",
|
||||
"packages": [
|
||||
{
|
||||
"name": "delight-im/cookie",
|
||||
"version": "v1.3.0",
|
||||
"name": "delight-im/base64",
|
||||
"version": "v1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/delight-im/PHP-Cookie.git",
|
||||
"reference": "481c569d6f4bcb0391f56203f078d425b3339001"
|
||||
"url": "https://github.com/delight-im/PHP-Base64.git",
|
||||
"reference": "687b2a49f663e162030a8d27b32838bbe7f91c78"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/delight-im/PHP-Cookie/zipball/481c569d6f4bcb0391f56203f078d425b3339001",
|
||||
"reference": "481c569d6f4bcb0391f56203f078d425b3339001",
|
||||
"url": "https://api.github.com/repos/delight-im/PHP-Base64/zipball/687b2a49f663e162030a8d27b32838bbe7f91c78",
|
||||
"reference": "687b2a49f663e162030a8d27b32838bbe7f91c78",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"delight-im/http": "^1.1",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Delight\\Base64\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Simple and convenient Base64 encoding and decoding for PHP",
|
||||
"homepage": "https://github.com/delight-im/PHP-Base64",
|
||||
"keywords": [
|
||||
"URL-safe",
|
||||
"base-64",
|
||||
"base64",
|
||||
"decode",
|
||||
"decoding",
|
||||
"encode",
|
||||
"encoding",
|
||||
"url"
|
||||
],
|
||||
"time": "2017-07-24T18:59:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "delight-im/cookie",
|
||||
"version": "v3.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/delight-im/PHP-Cookie.git",
|
||||
"reference": "76ef2a21817cf7a034f85fc3f4d4bfc60f873947"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/delight-im/PHP-Cookie/zipball/76ef2a21817cf7a034f85fc3f4d4bfc60f873947",
|
||||
"reference": "76ef2a21817cf7a034f85fc3f4d4bfc60f873947",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"delight-im/http": "^2.0",
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Delight\\Cookie\\": "src/"
|
||||
@@ -33,7 +73,7 @@
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
"MIT"
|
||||
],
|
||||
"description": "Modern cookie management for PHP",
|
||||
"homepage": "https://github.com/delight-im/PHP-Cookie",
|
||||
@@ -46,20 +86,61 @@
|
||||
"samesite",
|
||||
"xss"
|
||||
],
|
||||
"time": "2016-07-19 22:20:24"
|
||||
"time": "2017-10-18T19:48:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "delight-im/http",
|
||||
"version": "v1.1.0",
|
||||
"name": "delight-im/db",
|
||||
"version": "v1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/delight-im/PHP-HTTP.git",
|
||||
"reference": "2ca9001f047c8b4e1b7ca7281823a1a9437850f8"
|
||||
"url": "https://github.com/delight-im/PHP-DB.git",
|
||||
"reference": "df99ef7c2e86c7ce206647ffe8ba74447c075b57"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/delight-im/PHP-HTTP/zipball/2ca9001f047c8b4e1b7ca7281823a1a9437850f8",
|
||||
"reference": "2ca9001f047c8b4e1b7ca7281823a1a9437850f8",
|
||||
"url": "https://api.github.com/repos/delight-im/PHP-DB/zipball/df99ef7c2e86c7ce206647ffe8ba74447c075b57",
|
||||
"reference": "df99ef7c2e86c7ce206647ffe8ba74447c075b57",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pdo": "*",
|
||||
"php": ">=5.6.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Delight\\Db\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Safe and convenient SQL database access in a driver-agnostic way",
|
||||
"homepage": "https://github.com/delight-im/PHP-DB",
|
||||
"keywords": [
|
||||
"database",
|
||||
"mysql",
|
||||
"pdo",
|
||||
"pgsql",
|
||||
"postgresql",
|
||||
"sql",
|
||||
"sqlite"
|
||||
],
|
||||
"time": "2017-03-18T20:51:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "delight-im/http",
|
||||
"version": "v2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/delight-im/PHP-HTTP.git",
|
||||
"reference": "0a19a72a7eac8b1301aa972fb20cff494ac43e09"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/delight-im/PHP-HTTP/zipball/0a19a72a7eac8b1301aa972fb20cff494ac43e09",
|
||||
"reference": "0a19a72a7eac8b1301aa972fb20cff494ac43e09",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -73,7 +154,7 @@
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
"MIT"
|
||||
],
|
||||
"description": "Hypertext Transfer Protocol (HTTP) utilities for PHP",
|
||||
"homepage": "https://github.com/delight-im/PHP-HTTP",
|
||||
@@ -82,7 +163,7 @@
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"time": "2016-07-08 21:19:02"
|
||||
"time": "2016-07-21T15:05:01+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
@@ -92,7 +173,7 @@
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=5.5.0",
|
||||
"php": ">=5.6.0",
|
||||
"ext-openssl": "*"
|
||||
},
|
||||
"platform-dev": []
|
||||
|
529
src/Administration.php
Normal file
529
src/Administration.php
Normal file
@@ -0,0 +1,529 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* PHP-Auth (https://github.com/delight-im/PHP-Auth)
|
||||
* Copyright (c) delight.im (https://www.delight.im/)
|
||||
* Licensed under the MIT License (https://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
namespace Delight\Auth;
|
||||
|
||||
use Delight\Db\PdoDatabase;
|
||||
use Delight\Db\Throwable\Error;
|
||||
|
||||
require_once __DIR__ . '/Exceptions.php';
|
||||
|
||||
/** Component that can be used for administrative tasks by privileged and authorized users */
|
||||
final class Administration extends UserManager {
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @param PdoDatabase $databaseConnection the database connection to operate on
|
||||
* @param string|null $dbTablePrefix (optional) the prefix for the names of all database tables used by this component
|
||||
*/
|
||||
public function __construct(PdoDatabase $databaseConnection, $dbTablePrefix = null) {
|
||||
parent::__construct($databaseConnection, $dbTablePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new user
|
||||
*
|
||||
* @param string $email the email address to register
|
||||
* @param string $password the password for the new account
|
||||
* @param string|null $username (optional) the username that will be displayed
|
||||
* @return int the ID of the user that has been created (if any)
|
||||
* @throws InvalidEmailException if the email address was invalid
|
||||
* @throws InvalidPasswordException if the password was invalid
|
||||
* @throws UserAlreadyExistsException if a user with the specified email address already exists
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*/
|
||||
public function createUser($email, $password, $username = null) {
|
||||
return $this->createUserInternal(false, $email, $password, $username, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new user while ensuring that the username is unique
|
||||
*
|
||||
* @param string $email the email address to register
|
||||
* @param string $password the password for the new account
|
||||
* @param string|null $username (optional) the username that will be displayed
|
||||
* @return int the ID of the user that has been created (if any)
|
||||
* @throws InvalidEmailException if the email address was invalid
|
||||
* @throws InvalidPasswordException if the password was invalid
|
||||
* @throws UserAlreadyExistsException if a user with the specified email address already exists
|
||||
* @throws DuplicateUsernameException if the specified username wasn't unique
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*/
|
||||
public function createUserWithUniqueUsername($email, $password, $username = null) {
|
||||
return $this->createUserInternal(true, $email, $password, $username, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the user with the specified ID
|
||||
*
|
||||
* This action cannot be undone
|
||||
*
|
||||
* @param int $id the ID of the user to delete
|
||||
* @throws UnknownIdException if no user with the specified ID has been found
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*/
|
||||
public function deleteUserById($id) {
|
||||
$numberOfDeletedUsers = $this->deleteUsersByColumnValue('id', (int) $id);
|
||||
|
||||
if ($numberOfDeletedUsers === 0) {
|
||||
throw new UnknownIdException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the user with the specified email address
|
||||
*
|
||||
* This action cannot be undone
|
||||
*
|
||||
* @param string $email the email address of the user to delete
|
||||
* @throws InvalidEmailException if no user with the specified email address has been found
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*/
|
||||
public function deleteUserByEmail($email) {
|
||||
$email = self::validateEmailAddress($email);
|
||||
|
||||
$numberOfDeletedUsers = $this->deleteUsersByColumnValue('email', $email);
|
||||
|
||||
if ($numberOfDeletedUsers === 0) {
|
||||
throw new InvalidEmailException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the user with the specified username
|
||||
*
|
||||
* This action cannot be undone
|
||||
*
|
||||
* @param string $username the username of the user to delete
|
||||
* @throws UnknownUsernameException if no user with the specified username has been found
|
||||
* @throws AmbiguousUsernameException if multiple users with the specified username have been found
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*/
|
||||
public function deleteUserByUsername($username) {
|
||||
$userData = $this->getUserDataByUsername(
|
||||
\trim($username),
|
||||
[ 'id' ]
|
||||
);
|
||||
|
||||
$this->deleteUsersByColumnValue('id', (int) $userData['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified role to the user with the given ID
|
||||
*
|
||||
* A user may have any number of roles (i.e. no role at all, a single role, or any combination of roles)
|
||||
*
|
||||
* @param int $userId the ID of the user to assign the role to
|
||||
* @param int $role the role as one of the constants from the {@see Role} class
|
||||
* @throws UnknownIdException if no user with the specified ID has been found
|
||||
*
|
||||
* @see Role
|
||||
*/
|
||||
public function addRoleForUserById($userId, $role) {
|
||||
$userFound = $this->addRoleForUserByColumnValue(
|
||||
'id',
|
||||
(int) $userId,
|
||||
$role
|
||||
);
|
||||
|
||||
if ($userFound === false) {
|
||||
throw new UnknownIdException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified role to the user with the given email address
|
||||
*
|
||||
* A user may have any number of roles (i.e. no role at all, a single role, or any combination of roles)
|
||||
*
|
||||
* @param string $userEmail the email address of the user to assign the role to
|
||||
* @param int $role the role as one of the constants from the {@see Role} class
|
||||
* @throws InvalidEmailException if no user with the specified email address has been found
|
||||
*
|
||||
* @see Role
|
||||
*/
|
||||
public function addRoleForUserByEmail($userEmail, $role) {
|
||||
$userEmail = self::validateEmailAddress($userEmail);
|
||||
|
||||
$userFound = $this->addRoleForUserByColumnValue(
|
||||
'email',
|
||||
$userEmail,
|
||||
$role
|
||||
);
|
||||
|
||||
if ($userFound === false) {
|
||||
throw new InvalidEmailException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified role to the user with the given username
|
||||
*
|
||||
* A user may have any number of roles (i.e. no role at all, a single role, or any combination of roles)
|
||||
*
|
||||
* @param string $username the username of the user to assign the role to
|
||||
* @param int $role the role as one of the constants from the {@see Role} class
|
||||
* @throws UnknownUsernameException if no user with the specified username has been found
|
||||
* @throws AmbiguousUsernameException if multiple users with the specified username have been found
|
||||
*
|
||||
* @see Role
|
||||
*/
|
||||
public function addRoleForUserByUsername($username, $role) {
|
||||
$userData = $this->getUserDataByUsername(
|
||||
\trim($username),
|
||||
[ 'id' ]
|
||||
);
|
||||
|
||||
$this->addRoleForUserByColumnValue(
|
||||
'id',
|
||||
(int) $userData['id'],
|
||||
$role
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes away the specified role from the user with the given ID
|
||||
*
|
||||
* A user may have any number of roles (i.e. no role at all, a single role, or any combination of roles)
|
||||
*
|
||||
* @param int $userId the ID of the user to take the role away from
|
||||
* @param int $role the role as one of the constants from the {@see Role} class
|
||||
* @throws UnknownIdException if no user with the specified ID has been found
|
||||
*
|
||||
* @see Role
|
||||
*/
|
||||
public function removeRoleForUserById($userId, $role) {
|
||||
$userFound = $this->removeRoleForUserByColumnValue(
|
||||
'id',
|
||||
(int) $userId,
|
||||
$role
|
||||
);
|
||||
|
||||
if ($userFound === false) {
|
||||
throw new UnknownIdException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes away the specified role from the user with the given email address
|
||||
*
|
||||
* A user may have any number of roles (i.e. no role at all, a single role, or any combination of roles)
|
||||
*
|
||||
* @param string $userEmail the email address of the user to take the role away from
|
||||
* @param int $role the role as one of the constants from the {@see Role} class
|
||||
* @throws InvalidEmailException if no user with the specified email address has been found
|
||||
*
|
||||
* @see Role
|
||||
*/
|
||||
public function removeRoleForUserByEmail($userEmail, $role) {
|
||||
$userEmail = self::validateEmailAddress($userEmail);
|
||||
|
||||
$userFound = $this->removeRoleForUserByColumnValue(
|
||||
'email',
|
||||
$userEmail,
|
||||
$role
|
||||
);
|
||||
|
||||
if ($userFound === false) {
|
||||
throw new InvalidEmailException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes away the specified role from the user with the given username
|
||||
*
|
||||
* A user may have any number of roles (i.e. no role at all, a single role, or any combination of roles)
|
||||
*
|
||||
* @param string $username the username of the user to take the role away from
|
||||
* @param int $role the role as one of the constants from the {@see Role} class
|
||||
* @throws UnknownUsernameException if no user with the specified username has been found
|
||||
* @throws AmbiguousUsernameException if multiple users with the specified username have been found
|
||||
*
|
||||
* @see Role
|
||||
*/
|
||||
public function removeRoleForUserByUsername($username, $role) {
|
||||
$userData = $this->getUserDataByUsername(
|
||||
\trim($username),
|
||||
[ 'id' ]
|
||||
);
|
||||
|
||||
$this->removeRoleForUserByColumnValue(
|
||||
'id',
|
||||
(int) $userData['id'],
|
||||
$role
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the user with the given ID has the specified role
|
||||
*
|
||||
* @param int $userId the ID of the user to check the roles for
|
||||
* @param int $role the role as one of the constants from the {@see Role} class
|
||||
* @return bool
|
||||
* @throws UnknownIdException if no user with the specified ID has been found
|
||||
*
|
||||
* @see Role
|
||||
*/
|
||||
public function doesUserHaveRole($userId, $role) {
|
||||
$userId = (int) $userId;
|
||||
$role = (int) $role;
|
||||
|
||||
$rolesBitmask = $this->db->selectValue(
|
||||
'SELECT roles_mask FROM ' . $this->dbTablePrefix . 'users WHERE id = ?',
|
||||
[ $userId ]
|
||||
);
|
||||
|
||||
if ($rolesBitmask === null) {
|
||||
throw new UnknownIdException();
|
||||
}
|
||||
|
||||
return ($rolesBitmask & $role) === $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the roles of the user with the given ID, mapping the numerical values to their descriptive names
|
||||
*
|
||||
* @param int $userId the ID of the user to return the roles for
|
||||
* @return array
|
||||
* @throws UnknownIdException if no user with the specified ID has been found
|
||||
*
|
||||
* @see Role
|
||||
*/
|
||||
public function getRolesForUserById($userId) {
|
||||
$userId = (int) $userId;
|
||||
|
||||
$rolesBitmask = $this->db->selectValue(
|
||||
'SELECT roles_mask FROM ' . $this->dbTablePrefix . 'users WHERE id = ?',
|
||||
[ $userId ]
|
||||
);
|
||||
|
||||
if ($rolesBitmask === null) {
|
||||
throw new UnknownIdException();
|
||||
}
|
||||
|
||||
return \array_filter(
|
||||
Role::getMap(),
|
||||
function ($each) use ($rolesBitmask) {
|
||||
return ($rolesBitmask & $each) === $each;
|
||||
},
|
||||
\ARRAY_FILTER_USE_KEY
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs in as the user with the specified ID
|
||||
*
|
||||
* @param int $id the ID of the user to sign in as
|
||||
* @throws UnknownIdException if no user with the specified ID has been found
|
||||
* @throws EmailNotVerifiedException if the user has not verified their email address via a confirmation method yet
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*/
|
||||
public function logInAsUserById($id) {
|
||||
$numberOfMatchedUsers = $this->logInAsUserByColumnValue('id', (int) $id);
|
||||
|
||||
if ($numberOfMatchedUsers === 0) {
|
||||
throw new UnknownIdException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs in as the user with the specified email address
|
||||
*
|
||||
* @param string $email the email address of the user to sign in as
|
||||
* @throws InvalidEmailException if no user with the specified email address has been found
|
||||
* @throws EmailNotVerifiedException if the user has not verified their email address via a confirmation method yet
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*/
|
||||
public function logInAsUserByEmail($email) {
|
||||
$email = self::validateEmailAddress($email);
|
||||
|
||||
$numberOfMatchedUsers = $this->logInAsUserByColumnValue('email', $email);
|
||||
|
||||
if ($numberOfMatchedUsers === 0) {
|
||||
throw new InvalidEmailException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs in as the user with the specified display name
|
||||
*
|
||||
* @param string $username the display name of the user to sign in as
|
||||
* @throws UnknownUsernameException if no user with the specified username has been found
|
||||
* @throws AmbiguousUsernameException if multiple users with the specified username have been found
|
||||
* @throws EmailNotVerifiedException if the user has not verified their email address via a confirmation method yet
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*/
|
||||
public function logInAsUserByUsername($username) {
|
||||
$numberOfMatchedUsers = $this->logInAsUserByColumnValue('username', \trim($username));
|
||||
|
||||
if ($numberOfMatchedUsers === 0) {
|
||||
throw new UnknownUsernameException();
|
||||
}
|
||||
elseif ($numberOfMatchedUsers > 1) {
|
||||
throw new AmbiguousUsernameException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all existing users where the column with the specified name has the given value
|
||||
*
|
||||
* You must never pass untrusted input to the parameter that takes the column name
|
||||
*
|
||||
* @param string $columnName the name of the column to filter by
|
||||
* @param mixed $columnValue the value to look for in the selected column
|
||||
* @return int the number of deleted users
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*/
|
||||
private function deleteUsersByColumnValue($columnName, $columnValue) {
|
||||
try {
|
||||
return $this->db->delete(
|
||||
$this->dbTablePrefix . 'users',
|
||||
[
|
||||
$columnName => $columnValue
|
||||
]
|
||||
);
|
||||
}
|
||||
catch (Error $e) {
|
||||
throw new DatabaseError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the roles for the user where the column with the specified name has the given value
|
||||
*
|
||||
* You must never pass untrusted input to the parameter that takes the column name
|
||||
*
|
||||
* @param string $columnName the name of the column to filter by
|
||||
* @param mixed $columnValue the value to look for in the selected column
|
||||
* @param callable $modification the modification to apply to the existing bitmask of roles
|
||||
* @return bool whether any user with the given column constraints has been found
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*
|
||||
* @see Role
|
||||
*/
|
||||
private function modifyRolesForUserByColumnValue($columnName, $columnValue, callable $modification) {
|
||||
try {
|
||||
$userData = $this->db->selectRow(
|
||||
'SELECT id, roles_mask FROM ' . $this->dbTablePrefix . 'users WHERE ' . $columnName . ' = ?',
|
||||
[ $columnValue ]
|
||||
);
|
||||
}
|
||||
catch (Error $e) {
|
||||
throw new DatabaseError();
|
||||
}
|
||||
|
||||
if ($userData === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$newRolesBitmask = $modification($userData['roles_mask']);
|
||||
|
||||
try {
|
||||
$this->db->exec(
|
||||
'UPDATE ' . $this->dbTablePrefix . 'users SET roles_mask = ? WHERE id = ?',
|
||||
[
|
||||
$newRolesBitmask,
|
||||
(int) $userData['id']
|
||||
]
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Error $e) {
|
||||
throw new DatabaseError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified role to the user where the column with the specified name has the given value
|
||||
*
|
||||
* You must never pass untrusted input to the parameter that takes the column name
|
||||
*
|
||||
* @param string $columnName the name of the column to filter by
|
||||
* @param mixed $columnValue the value to look for in the selected column
|
||||
* @param int $role the role as one of the constants from the {@see Role} class
|
||||
* @return bool whether any user with the given column constraints has been found
|
||||
*
|
||||
* @see Role
|
||||
*/
|
||||
private function addRoleForUserByColumnValue($columnName, $columnValue, $role) {
|
||||
$role = (int) $role;
|
||||
|
||||
return $this->modifyRolesForUserByColumnValue(
|
||||
$columnName,
|
||||
$columnValue,
|
||||
function ($oldRolesBitmask) use ($role) {
|
||||
return $oldRolesBitmask | $role;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes away the specified role from the user where the column with the specified name has the given value
|
||||
*
|
||||
* You must never pass untrusted input to the parameter that takes the column name
|
||||
*
|
||||
* @param string $columnName the name of the column to filter by
|
||||
* @param mixed $columnValue the value to look for in the selected column
|
||||
* @param int $role the role as one of the constants from the {@see Role} class
|
||||
* @return bool whether any user with the given column constraints has been found
|
||||
*
|
||||
* @see Role
|
||||
*/
|
||||
private function removeRoleForUserByColumnValue($columnName, $columnValue, $role) {
|
||||
$role = (int) $role;
|
||||
|
||||
return $this->modifyRolesForUserByColumnValue(
|
||||
$columnName,
|
||||
$columnValue,
|
||||
function ($oldRolesBitmask) use ($role) {
|
||||
return $oldRolesBitmask & ~$role;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs in as the user for which the column with the specified name has the given value
|
||||
*
|
||||
* You must never pass untrusted input to the parameter that takes the column name
|
||||
*
|
||||
* @param string $columnName the name of the column to filter by
|
||||
* @param mixed $columnValue the value to look for in the selected column
|
||||
* @return int the number of matched users (where only a value of one means that the login may have been successful)
|
||||
* @throws EmailNotVerifiedException if the user has not verified their email address via a confirmation method yet
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*/
|
||||
private function logInAsUserByColumnValue($columnName, $columnValue) {
|
||||
try {
|
||||
$users = $this->db->select(
|
||||
'SELECT verified, id, email, username, status, roles_mask FROM ' . $this->dbTablePrefix . 'users WHERE ' . $columnName . ' = ? LIMIT 2 OFFSET 0',
|
||||
[ $columnValue ]
|
||||
);
|
||||
}
|
||||
catch (Error $e) {
|
||||
throw new DatabaseError();
|
||||
}
|
||||
|
||||
$numberOfMatchingUsers = \count($users);
|
||||
|
||||
if ($numberOfMatchingUsers === 1) {
|
||||
$user = $users[0];
|
||||
|
||||
if ((int) $user['verified'] === 1) {
|
||||
$this->onLoginSuccessful($user['id'], $user['email'], $user['username'], $user['status'], $user['roles_mask'], false);
|
||||
}
|
||||
else {
|
||||
throw new EmailNotVerifiedException();
|
||||
}
|
||||
}
|
||||
|
||||
return $numberOfMatchingUsers;
|
||||
}
|
||||
|
||||
}
|
1926
src/Auth.php
1926
src/Auth.php
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) delight.im <info@delight.im>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Delight\Auth;
|
||||
|
||||
class Base64 {
|
||||
|
||||
const SPECIAL_CHARS_ORIGINAL = '+/=';
|
||||
const SPECIAL_CHARS_SAFE = '._-';
|
||||
|
||||
public static function encode($data, $safeChars = false) {
|
||||
$result = base64_encode($data);
|
||||
|
||||
if ($safeChars) {
|
||||
$result = strtr($result, self::SPECIAL_CHARS_ORIGINAL, self::SPECIAL_CHARS_SAFE);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function decode($data) {
|
||||
$data = strtr($data, self::SPECIAL_CHARS_SAFE, self::SPECIAL_CHARS_ORIGINAL);
|
||||
|
||||
$result = base64_decode($data, true);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
@@ -1,27 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) delight.im <info@delight.im>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* PHP-Auth (https://github.com/delight-im/PHP-Auth)
|
||||
* Copyright (c) delight.im (https://www.delight.im/)
|
||||
* Licensed under the MIT License (https://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
namespace Delight\Auth;
|
||||
|
||||
class AuthException extends \Exception {}
|
||||
|
||||
class UnknownIdException extends AuthException {}
|
||||
|
||||
class InvalidEmailException extends AuthException {}
|
||||
|
||||
class UnknownUsernameException extends AuthException {}
|
||||
|
||||
class InvalidPasswordException extends AuthException {}
|
||||
|
||||
class EmailNotVerifiedException extends AuthException {}
|
||||
@@ -36,10 +30,24 @@ class TokenExpiredException extends AuthException {}
|
||||
|
||||
class TooManyRequestsException extends AuthException {}
|
||||
|
||||
class DuplicateUsernameException extends AuthException {}
|
||||
|
||||
class AmbiguousUsernameException extends AuthException {}
|
||||
|
||||
class AttemptCancelledException extends AuthException {}
|
||||
|
||||
class ResetDisabledException extends AuthException {}
|
||||
|
||||
class ConfirmationRequestNotFound extends AuthException {}
|
||||
|
||||
class AuthError extends \Exception {}
|
||||
|
||||
class DatabaseError extends AuthError {}
|
||||
|
||||
class DatabaseDriverError extends DatabaseError {}
|
||||
|
||||
class MissingCallbackError extends AuthError {}
|
||||
|
||||
class HeadersAlreadySentError extends AuthError {}
|
||||
|
||||
class EmailOrUsernameRequiredError extends AuthError {}
|
||||
|
79
src/Role.php
Normal file
79
src/Role.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* PHP-Auth (https://github.com/delight-im/PHP-Auth)
|
||||
* Copyright (c) delight.im (https://www.delight.im/)
|
||||
* Licensed under the MIT License (https://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
namespace Delight\Auth;
|
||||
|
||||
final class Role {
|
||||
|
||||
const ADMIN = 1;
|
||||
const AUTHOR = 2;
|
||||
const COLLABORATOR = 4;
|
||||
const CONSULTANT = 8;
|
||||
const CONSUMER = 16;
|
||||
const CONTRIBUTOR = 32;
|
||||
const COORDINATOR = 64;
|
||||
const CREATOR = 128;
|
||||
const DEVELOPER = 256;
|
||||
const DIRECTOR = 512;
|
||||
const EDITOR = 1024;
|
||||
const EMPLOYEE = 2048;
|
||||
const MAINTAINER = 4096;
|
||||
const MANAGER = 8192;
|
||||
const MODERATOR = 16384;
|
||||
const PUBLISHER = 32768;
|
||||
const REVIEWER = 65536;
|
||||
const SUBSCRIBER = 131072;
|
||||
const SUPER_ADMIN = 262144;
|
||||
const SUPER_EDITOR = 524288;
|
||||
const SUPER_MODERATOR = 1048576;
|
||||
const TRANSLATOR = 2097152;
|
||||
// const XYZ = 4194304;
|
||||
// const XYZ = 8388608;
|
||||
// const XYZ = 16777216;
|
||||
// const XYZ = 33554432;
|
||||
// const XYZ = 67108864;
|
||||
// const XYZ = 134217728;
|
||||
// const XYZ = 268435456;
|
||||
// const XYZ = 536870912;
|
||||
|
||||
/**
|
||||
* Returns an array mapping the numerical role values to their descriptive names
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getMap() {
|
||||
$reflectionClass = new \ReflectionClass(static::class);
|
||||
|
||||
return \array_flip($reflectionClass->getConstants());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the descriptive role names
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getNames() {
|
||||
$reflectionClass = new \ReflectionClass(static::class);
|
||||
|
||||
return \array_keys($reflectionClass->getConstants());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the numerical role values
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function getValues() {
|
||||
$reflectionClass = new \ReflectionClass(static::class);
|
||||
|
||||
return \array_values($reflectionClass->getConstants());
|
||||
}
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
}
|
20
src/Status.php
Normal file
20
src/Status.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* PHP-Auth (https://github.com/delight-im/PHP-Auth)
|
||||
* Copyright (c) delight.im (https://www.delight.im/)
|
||||
* Licensed under the MIT License (https://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
namespace Delight\Auth;
|
||||
|
||||
final class Status {
|
||||
|
||||
const NORMAL = 0;
|
||||
const ARCHIVED = 1;
|
||||
const BANNED = 2;
|
||||
const LOCKED = 3;
|
||||
const PENDING_REVIEW = 4;
|
||||
const SUSPENDED = 5;
|
||||
|
||||
}
|
336
src/UserManager.php
Normal file
336
src/UserManager.php
Normal file
@@ -0,0 +1,336 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* PHP-Auth (https://github.com/delight-im/PHP-Auth)
|
||||
* Copyright (c) delight.im (https://www.delight.im/)
|
||||
* Licensed under the MIT License (https://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
namespace Delight\Auth;
|
||||
|
||||
use Delight\Base64\Base64;
|
||||
use Delight\Cookie\Session;
|
||||
use Delight\Db\PdoDatabase;
|
||||
use Delight\Db\PdoDsn;
|
||||
use Delight\Db\Throwable\Error;
|
||||
use Delight\Db\Throwable\IntegrityConstraintViolationException;
|
||||
|
||||
require_once __DIR__ . '/Exceptions.php';
|
||||
|
||||
/**
|
||||
* Abstract base class for components implementing user management
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class UserManager {
|
||||
|
||||
/** @var string session field for whether the client is currently signed in */
|
||||
const SESSION_FIELD_LOGGED_IN = 'auth_logged_in';
|
||||
/** @var string session field for the ID of the user who is currently signed in (if any) */
|
||||
const SESSION_FIELD_USER_ID = 'auth_user_id';
|
||||
/** @var string session field for the email address of the user who is currently signed in (if any) */
|
||||
const SESSION_FIELD_EMAIL = 'auth_email';
|
||||
/** @var string session field for the display name (if any) of the user who is currently signed in (if any) */
|
||||
const SESSION_FIELD_USERNAME = 'auth_username';
|
||||
/** @var string session field for the status of the user who is currently signed in (if any) as one of the constants from the {@see Status} class */
|
||||
const SESSION_FIELD_STATUS = 'auth_status';
|
||||
/** @var string session field for the roles of the user who is currently signed in (if any) as a bitmask using constants from the {@see Role} class */
|
||||
const SESSION_FIELD_ROLES = 'auth_roles';
|
||||
/** @var string session field for whether the user who is currently signed in (if any) has been remembered (instead of them having authenticated actively) */
|
||||
const SESSION_FIELD_REMEMBERED = 'auth_remembered';
|
||||
|
||||
/** @var PdoDatabase the database connection to operate on */
|
||||
protected $db;
|
||||
/** @var string the prefix for the names of all database tables used by this component */
|
||||
protected $dbTablePrefix;
|
||||
|
||||
/**
|
||||
* Creates a random string with the given maximum length
|
||||
*
|
||||
* With the default parameter, the output should contain at least as much randomness as a UUID
|
||||
*
|
||||
* @param int $maxLength the maximum length of the output string (integer multiple of 4)
|
||||
* @return string the new random string
|
||||
*/
|
||||
public static function createRandomString($maxLength = 24) {
|
||||
// calculate how many bytes of randomness we need for the specified string length
|
||||
$bytes = \floor((int) $maxLength / 4) * 3;
|
||||
|
||||
// get random data
|
||||
$data = \openssl_random_pseudo_bytes($bytes);
|
||||
|
||||
// return the Base64-encoded result
|
||||
return Base64::encodeUrlSafe($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PdoDatabase|PdoDsn|\PDO $databaseConnection the database connection to operate on
|
||||
* @param string|null $dbTablePrefix (optional) the prefix for the names of all database tables used by this component
|
||||
*/
|
||||
protected function __construct($databaseConnection, $dbTablePrefix = null) {
|
||||
if ($databaseConnection instanceof PdoDatabase) {
|
||||
$this->db = $databaseConnection;
|
||||
}
|
||||
elseif ($databaseConnection instanceof PdoDsn) {
|
||||
$this->db = PdoDatabase::fromDsn($databaseConnection);
|
||||
}
|
||||
elseif ($databaseConnection instanceof \PDO) {
|
||||
$this->db = PdoDatabase::fromPdo($databaseConnection, true);
|
||||
}
|
||||
else {
|
||||
$this->db = null;
|
||||
|
||||
throw new \InvalidArgumentException('The database connection must be an instance of either `PdoDatabase`, `PdoDsn` or `PDO`');
|
||||
}
|
||||
|
||||
$this->dbTablePrefix = (string) $dbTablePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new user
|
||||
*
|
||||
* If you want the user's account to be activated by default, pass `null` as the callback
|
||||
*
|
||||
* If you want to make the user verify their email address first, pass an anonymous function as the callback
|
||||
*
|
||||
* The callback function must have the following signature:
|
||||
*
|
||||
* `function ($selector, $token)`
|
||||
*
|
||||
* Both pieces of information must be sent to the user, usually embedded in a link
|
||||
*
|
||||
* When the user wants to verify their email address as a next step, both pieces will be required again
|
||||
*
|
||||
* @param bool $requireUniqueUsername whether it must be ensured that the username is unique
|
||||
* @param string $email the email address to register
|
||||
* @param string $password the password for the new account
|
||||
* @param string|null $username (optional) the username that will be displayed
|
||||
* @param callable|null $callback (optional) the function that sends the confirmation email to the user
|
||||
* @return int the ID of the user that has been created (if any)
|
||||
* @throws InvalidEmailException if the email address has been invalid
|
||||
* @throws InvalidPasswordException if the password has been invalid
|
||||
* @throws UserAlreadyExistsException if a user with the specified email address already exists
|
||||
* @throws DuplicateUsernameException if it was specified that the username must be unique while it was *not*
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*
|
||||
* @see confirmEmail
|
||||
* @see confirmEmailAndSignIn
|
||||
*/
|
||||
protected function createUserInternal($requireUniqueUsername, $email, $password, $username = null, callable $callback = null) {
|
||||
\ignore_user_abort(true);
|
||||
|
||||
$email = self::validateEmailAddress($email);
|
||||
$password = self::validatePassword($password);
|
||||
|
||||
$username = isset($username) ? \trim($username) : null;
|
||||
|
||||
// if the supplied username is the empty string or has consisted of whitespace only
|
||||
if ($username === '') {
|
||||
// this actually means that there is no username
|
||||
$username = null;
|
||||
}
|
||||
|
||||
// if the uniqueness of the username is to be ensured
|
||||
if ($requireUniqueUsername) {
|
||||
// if a username has actually been provided
|
||||
if ($username !== null) {
|
||||
// count the number of users who do already have that specified username
|
||||
$occurrencesOfUsername = $this->db->selectValue(
|
||||
'SELECT COUNT(*) FROM ' . $this->dbTablePrefix . 'users WHERE username = ?',
|
||||
[ $username ]
|
||||
);
|
||||
|
||||
// if any user with that username does already exist
|
||||
if ($occurrencesOfUsername > 0) {
|
||||
// cancel the operation and report the violation of this requirement
|
||||
throw new DuplicateUsernameException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$password = \password_hash($password, \PASSWORD_DEFAULT);
|
||||
$verified = \is_callable($callback) ? 0 : 1;
|
||||
|
||||
try {
|
||||
$this->db->insert(
|
||||
$this->dbTablePrefix . 'users',
|
||||
[
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'username' => $username,
|
||||
'verified' => $verified,
|
||||
'registered' => \time()
|
||||
]
|
||||
);
|
||||
}
|
||||
// if we have a duplicate entry
|
||||
catch (IntegrityConstraintViolationException $e) {
|
||||
throw new UserAlreadyExistsException();
|
||||
}
|
||||
catch (Error $e) {
|
||||
throw new DatabaseError();
|
||||
}
|
||||
|
||||
$newUserId = (int) $this->db->getLastInsertId();
|
||||
|
||||
if ($verified === 0) {
|
||||
$this->createConfirmationRequest($newUserId, $email, $callback);
|
||||
}
|
||||
|
||||
return $newUserId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a user has successfully logged in
|
||||
*
|
||||
* This may happen via the standard login, via the "remember me" feature, or due to impersonation by administrators
|
||||
*
|
||||
* @param int $userId the ID of the user
|
||||
* @param string $email the email address of the user
|
||||
* @param string $username the display name (if any) of the user
|
||||
* @param int $status the status of the user as one of the constants from the {@see Status} class
|
||||
* @param int $roles the roles of the user as a bitmask using constants from the {@see Role} class
|
||||
* @param bool $remembered whether the user has been remembered (instead of them having authenticated actively)
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*/
|
||||
protected function onLoginSuccessful($userId, $email, $username, $status, $roles, $remembered) {
|
||||
// re-generate the session ID to prevent session fixation attacks (requests a cookie to be written on the client)
|
||||
Session::regenerate(true);
|
||||
|
||||
// save the user data in the session variables maintained by this library
|
||||
$_SESSION[self::SESSION_FIELD_LOGGED_IN] = true;
|
||||
$_SESSION[self::SESSION_FIELD_USER_ID] = (int) $userId;
|
||||
$_SESSION[self::SESSION_FIELD_EMAIL] = $email;
|
||||
$_SESSION[self::SESSION_FIELD_USERNAME] = $username;
|
||||
$_SESSION[self::SESSION_FIELD_STATUS] = (int) $status;
|
||||
$_SESSION[self::SESSION_FIELD_ROLES] = (int) $roles;
|
||||
$_SESSION[self::SESSION_FIELD_REMEMBERED] = $remembered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requested user data for the account with the specified username (if any)
|
||||
*
|
||||
* You must never pass untrusted input to the parameter that takes the column list
|
||||
*
|
||||
* @param string $username the username to look for
|
||||
* @param array $requestedColumns the columns to request from the user's record
|
||||
* @return array the user data (if an account was found unambiguously)
|
||||
* @throws UnknownUsernameException if no user with the specified username has been found
|
||||
* @throws AmbiguousUsernameException if multiple users with the specified username have been found
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*/
|
||||
protected function getUserDataByUsername($username, array $requestedColumns) {
|
||||
try {
|
||||
$projection = \implode(', ', $requestedColumns);
|
||||
|
||||
$users = $this->db->select(
|
||||
'SELECT ' . $projection . ' FROM ' . $this->dbTablePrefix . 'users WHERE username = ? LIMIT 2 OFFSET 0',
|
||||
[ $username ]
|
||||
);
|
||||
}
|
||||
catch (Error $e) {
|
||||
throw new DatabaseError();
|
||||
}
|
||||
|
||||
if (empty($users)) {
|
||||
throw new UnknownUsernameException();
|
||||
}
|
||||
else {
|
||||
if (\count($users) === 1) {
|
||||
return $users[0];
|
||||
}
|
||||
else {
|
||||
throw new AmbiguousUsernameException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an email address
|
||||
*
|
||||
* @param string $email the email address to validate
|
||||
* @return string the sanitized email address
|
||||
* @throws InvalidEmailException if the email address has been invalid
|
||||
*/
|
||||
protected static function validateEmailAddress($email) {
|
||||
if (empty($email)) {
|
||||
throw new InvalidEmailException();
|
||||
}
|
||||
|
||||
$email = \trim($email);
|
||||
|
||||
if (!\filter_var($email, \FILTER_VALIDATE_EMAIL)) {
|
||||
throw new InvalidEmailException();
|
||||
}
|
||||
|
||||
return $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a password
|
||||
*
|
||||
* @param string $password the password to validate
|
||||
* @return string the sanitized password
|
||||
* @throws InvalidPasswordException if the password has been invalid
|
||||
*/
|
||||
protected static function validatePassword($password) {
|
||||
if (empty($password)) {
|
||||
throw new InvalidPasswordException();
|
||||
}
|
||||
|
||||
$password = \trim($password);
|
||||
|
||||
if (\strlen($password) < 1) {
|
||||
throw new InvalidPasswordException();
|
||||
}
|
||||
|
||||
return $password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request for email confirmation
|
||||
*
|
||||
* The callback function must have the following signature:
|
||||
*
|
||||
* `function ($selector, $token)`
|
||||
*
|
||||
* Both pieces of information must be sent to the user, usually embedded in a link
|
||||
*
|
||||
* When the user wants to verify their email address as a next step, both pieces will be required again
|
||||
*
|
||||
* @param int $userId the user's ID
|
||||
* @param string $email the email address to verify
|
||||
* @param callable $callback the function that sends the confirmation email to the user
|
||||
* @throws AuthError if an internal problem occurred (do *not* catch)
|
||||
*/
|
||||
protected function createConfirmationRequest($userId, $email, callable $callback) {
|
||||
$selector = self::createRandomString(16);
|
||||
$token = self::createRandomString(16);
|
||||
$tokenHashed = \password_hash($token, \PASSWORD_DEFAULT);
|
||||
$expires = \time() + 60 * 60 * 24;
|
||||
|
||||
try {
|
||||
$this->db->insert(
|
||||
$this->dbTablePrefix . 'users_confirmations',
|
||||
[
|
||||
'user_id' => (int) $userId,
|
||||
'email' => $email,
|
||||
'selector' => $selector,
|
||||
'token' => $tokenHashed,
|
||||
'expires' => $expires
|
||||
]
|
||||
);
|
||||
}
|
||||
catch (Error $e) {
|
||||
throw new DatabaseError();
|
||||
}
|
||||
|
||||
if (\is_callable($callback)) {
|
||||
$callback($selector, $token);
|
||||
}
|
||||
else {
|
||||
throw new MissingCallbackError();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
875
tests/index.php
875
tests/index.php
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user