Compare commits
933 commits
sdk-2.12.x
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
2ea69f2cde | ||
|
f4abaf2474 | ||
|
7983c9c6a9 | ||
|
bfefacda30 | ||
|
67d5f183c1 | ||
|
3865bd41df | ||
|
b71744869d | ||
|
496d05c05d | ||
|
9ffe9d6b09 | ||
|
b92f821745 | ||
|
432cf59cf8 | ||
|
218e4735dd | ||
|
d18bcef2ca | ||
|
28e355510b | ||
|
562bb5a433 | ||
|
b03a7d74c2 | ||
|
8d2f32b3e8 | ||
|
2153827523 | ||
|
1334de4cf4 | ||
|
46824efee0 | ||
|
7dfa47cfe9 | ||
|
f4f36206b6 | ||
|
37c2720969 | ||
|
802de34a33 | ||
|
00f68a57cc | ||
|
b82a2db9a7 | ||
|
61928f5b12 | ||
|
f636c29fdf | ||
|
b2794f4f0b | ||
|
6c8ca004ba | ||
|
c01e96ddc9 | ||
|
42596c1026 | ||
|
5e25d7729d | ||
|
96984ea7da | ||
|
3ab3a4261c | ||
|
3d04bbb561 | ||
|
a67b75ca52 | ||
|
931695b22e | ||
|
ae41dbf55d | ||
|
2e4e153b4f | ||
|
fadcffdc25 | ||
|
ee9d512c1f | ||
|
dcddc2992a | ||
|
775bae4a61 | ||
|
2d351c1319 | ||
|
4b112cd54b | ||
|
b3f3a263fd | ||
|
5687c693d2 | ||
|
7afe56844e | ||
|
80a8b135c7 | ||
|
0915c48054 | ||
|
11c12fd590 | ||
|
c194fadbc2 | ||
|
62fc87e619 | ||
|
1bd7ecd19b | ||
|
9aab0837cb | ||
|
5833e9df6b | ||
|
2ac0157b4a | ||
|
8d016a5b56 | ||
|
14a3f4027d | ||
|
9157754f72 | ||
|
75688821a7 | ||
|
eab5bca3ec | ||
|
6194185e87 | ||
|
e4775d1ea9 | ||
|
78671dd621 | ||
|
8d6f66abcb | ||
|
84f205845a | ||
|
9bb742dcad | ||
|
c8167753b2 | ||
|
9e34773962 | ||
|
6bf4ad4b9e | ||
|
ea94d33b87 | ||
|
7cbdcb0255 | ||
|
d3df89c0a3 | ||
|
19a7f8316d | ||
|
5ed443ae33 | ||
|
7e42a7e317 | ||
|
cb25da3077 | ||
|
2510b76e80 | ||
|
b5b9412814 | ||
|
4f14264d23 | ||
|
6c71ee34a7 | ||
|
3803fd0ace | ||
|
d9e9fe1c38 | ||
|
b71f350d29 | ||
|
bc5ad58aa5 | ||
|
f482e4dc58 | ||
|
5c19a002b6 | ||
|
6224d359e0 | ||
|
564fc5953c | ||
|
bb093a03f6 | ||
|
3f049465d1 | ||
|
184fd15fba | ||
|
07ea5fe889 | ||
|
5daf5390e5 | ||
|
85eb443f88 | ||
|
3411e16418 | ||
|
c18e0dabb5 | ||
|
094fe740c0 | ||
|
a0ade9d3a3 | ||
|
5a97dc51fc | ||
|
ed0b275479 | ||
|
4ceaf7a338 | ||
|
d57dbae6f9 | ||
|
b39795b67b | ||
|
7e6dc485e9 | ||
|
0b109cadfe | ||
|
d31a07bbab | ||
|
06855d127a | ||
|
9b164c8740 | ||
|
6e48fedf5c | ||
|
ba83f62524 | ||
|
c308e5193a | ||
|
529ab75d94 | ||
|
7aad3e615c | ||
|
205743ed9b | ||
|
3c6d2c2e87 | ||
|
2139ae4422 | ||
|
ab36e7ccac | ||
|
23a8d94cd0 | ||
|
32a7e88d2d | ||
|
2698c4d753 | ||
|
e802de14f1 | ||
|
9ba0092b4f | ||
|
e2c0230900 | ||
|
b76ff8195c | ||
|
d56ae5b139 | ||
|
dff56a5034 | ||
|
6a9eb9e5d6 | ||
|
f00f24899d | ||
|
1727700763 | ||
|
1e3ead968a | ||
|
1362002e8c | ||
|
e1679a5115 | ||
|
5c5aad6da0 | ||
|
5c3120f058 | ||
|
64771fb470 | ||
|
c2cef6c68e | ||
|
630f7c16ec | ||
|
bee328e1c1 | ||
|
196ae6a68c | ||
|
d1bbad88d9 | ||
|
d26c50ae70 | ||
|
2a7be6a886 | ||
|
fe44b0f60b | ||
|
ae3544396c | ||
|
b9b59523ef | ||
|
cc362fb704 | ||
|
ea528abc3d | ||
|
1580cc98e6 | ||
|
613a24c89a | ||
|
c1dbc6ed8a | ||
|
124376e22c | ||
|
8f96e89b41 | ||
|
e9fe8dfe26 | ||
|
6fd8f56b52 | ||
|
d4794dbbd1 | ||
|
cfe1ef8189 | ||
|
4fdd0133a3 | ||
|
cf11cc2d3c | ||
|
52c36cdcc2 | ||
|
a1c65849b9 | ||
|
94d1c5affd | ||
|
85a5c45a13 | ||
|
26c39effec | ||
|
46f5bfb600 | ||
|
0ce4741a49 | ||
|
ae60a4f3e2 | ||
|
84a06843e0 | ||
|
e657b2070d | ||
|
1496d61a1c | ||
|
834406de2d | ||
|
eb6d4e6d10 | ||
|
0ca3eb7e01 | ||
|
3afc91edfa | ||
|
6506a3e0de | ||
|
e78f68487d | ||
|
21bc260ec3 | ||
|
a51d09eab0 | ||
|
4cca844b89 | ||
|
65ce31a8cf | ||
|
0285cdbd74 | ||
|
fc5fb9aea7 | ||
|
811e68de70 | ||
|
799f94bb60 | ||
|
c29c7b7049 | ||
|
630c527381 | ||
|
4dcf6883d0 | ||
|
bfd05b6854 | ||
|
393281c903 | ||
|
131a60467b | ||
|
dc5600a9cb | ||
|
df3f3dfd02 | ||
|
113c1b7cd4 | ||
|
d6960acfbf | ||
|
b83adcd51a | ||
|
6772ebbf31 | ||
|
d0363fd6fe | ||
|
ab66012ef1 | ||
|
048f06bc42 | ||
|
b52e5aed39 | ||
|
f59b309327 | ||
|
a806d7c6db | ||
|
8e251e553a | ||
|
1e616f3f5c | ||
|
4ebb63c5f7 | ||
|
1d00088d89 | ||
|
764829b01a | ||
|
adf380dc54 | ||
|
e5743b95e2 | ||
|
7f3eba8538 | ||
|
f2c9ab23eb | ||
|
82b78fdaef | ||
|
d440ebd649 | ||
|
dad2106021 | ||
|
79b3417fcc | ||
|
435fbe4e1a | ||
|
a1f0512f5a | ||
|
97e9c6caf1 | ||
|
7a9362fc56 | ||
|
981397791a | ||
|
85e7463afc | ||
|
f3a1ede6fb | ||
|
2933dbc191 | ||
|
3f77318e80 | ||
|
f89d15b1f4 | ||
|
b8475b01b8 | ||
|
b2618316ef | ||
|
1301b492f7 | ||
|
fb99b0b4ec | ||
|
7ed8589754 | ||
|
c2191a8c9c | ||
|
16bf2ddfc6 | ||
|
9626cc93a4 | ||
|
1f0e61958f | ||
|
193fa6c94a | ||
|
2058e4b2f1 | ||
|
5ce990ccbf | ||
|
c96454fab1 | ||
|
22d3c29857 | ||
|
ae5a714d9c | ||
|
9d8c5ae2f1 | ||
|
c6ca7b4e65 | ||
|
cc7e3a255e | ||
|
71b7d54763 | ||
|
d034fcebe8 | ||
|
83873d0fb7 | ||
|
168c51074b | ||
|
bdf7d33c54 | ||
|
7575a561bb | ||
|
c307147780 | ||
|
0d969aa7f9 | ||
|
966622ed2f | ||
|
95f425021a | ||
|
fcadbd4ccb | ||
|
fd5f726e2f | ||
|
5634447337 | ||
|
f51171faa8 | ||
|
0d4c90a129 | ||
|
9290bce457 | ||
|
a2fafe6d8e | ||
|
08ba4f0609 | ||
|
0905afecbd | ||
|
05cbd71248 | ||
|
342a5a4ac9 | ||
|
ec68a78c1d | ||
|
9b85007072 | ||
|
bddd9fbfb8 | ||
|
87910959fa | ||
|
55ecadce60 | ||
|
f5735ad919 | ||
|
ac1aa898f0 | ||
|
86538f7af1 | ||
|
7809c72f03 | ||
|
14a7b82616 | ||
|
959078fa95 | ||
|
2c74a1d158 | ||
|
f777776df0 | ||
|
c83ba22439 | ||
|
359ed6bf47 | ||
|
4a3b354c92 | ||
|
fecead4a90 | ||
|
a7c750e70f | ||
|
cf8d9814e8 | ||
|
56e0e7a05a | ||
|
d3c672823a | ||
|
0fa22e65e4 | ||
|
bfc94d94eb | ||
|
529d2d20ad | ||
|
1f5e12a8cf | ||
|
8c48be9f9f | ||
|
b9b0e49fd0 | ||
|
5e658703fa | ||
|
6a33545037 | ||
|
6a7e251fee | ||
|
681e91d024 | ||
|
b0d792602a | ||
|
87d446c495 | ||
|
47b658ee3a | ||
|
9a5201b6f0 | ||
|
41167e01bd | ||
|
71b50452ec | ||
|
272363052b | ||
|
5fd3f4d5d2 | ||
|
1708c1f870 | ||
|
308d6df606 | ||
|
6416811dea | ||
|
06d4d4659d | ||
|
43ea7ae60a | ||
|
3fad3ee34b | ||
|
c52bc61e49 | ||
|
52a3a3a3ab | ||
|
ba247bd388 | ||
|
0301b300ad | ||
|
cec05d8c19 | ||
|
729a7740db | ||
|
45218176c7 | ||
|
3fe1778c27 | ||
|
e219b91424 | ||
|
6a34b51f04 | ||
|
2c52e4a5d5 | ||
|
eabe3b3810 | ||
|
1540394586 | ||
|
0043114180 | ||
|
33ba0c69ca | ||
|
7d8677e308 | ||
|
f00d3916f6 | ||
|
5402fa55a9 | ||
|
ba26c88d6b | ||
|
4a48000bc3 | ||
|
5bea55d2c0 | ||
|
3f4ff925dc | ||
|
1560e147cc | ||
|
d1d63ca834 | ||
|
f93f921162 | ||
|
c30609f955 | ||
|
3db9c53fdd | ||
|
98dfef8e33 | ||
|
aff68b2f07 | ||
|
572a134c9c | ||
|
793a8ac115 | ||
|
f0a5e7c6fc | ||
|
a5f4e4ff7a | ||
|
a6681dbdab | ||
|
6c51e7a231 | ||
|
224d2be026 | ||
|
da5b87043d | ||
|
a714059cb9 | ||
|
b185baec7b | ||
|
c96980a887 | ||
|
a226d29514 | ||
|
c89983cb96 | ||
|
a75182565f | ||
|
55c4199b6a | ||
|
1f3dc36604 | ||
|
d28930d3c1 | ||
|
f4ba4fb25c | ||
|
03885543c2 | ||
|
178bc924c1 | ||
|
ae0120398c | ||
|
88643bd6f2 | ||
|
da1faa3ce3 | ||
|
4ba42fca73 | ||
|
d845259edb | ||
|
da49c74f7b | ||
|
1717897b9a | ||
|
2ed19c08cc | ||
|
3cbba10c36 | ||
|
bddc53e0c1 | ||
|
c1755cf156 | ||
|
579cbbfe26 | ||
|
ab2c8d0ad2 | ||
|
38073e5b61 | ||
|
3375d50e68 | ||
|
2ee12efede | ||
|
47cbaacef2 | ||
|
9382529ad7 | ||
|
d33faeacce | ||
|
5f07290573 | ||
|
de299e9c99 | ||
|
28aa31a89b | ||
|
5616303535 | ||
|
99cf7790a4 | ||
|
174388e075 | ||
|
0dc71df45a | ||
|
f5c2598cbd | ||
|
f0255b3cd6 | ||
|
4466926d31 | ||
|
e409e19563 | ||
|
510594d96e | ||
|
49da5f76b1 | ||
|
48a441cf77 | ||
|
6db69d0fb1 | ||
|
ff65e13c75 | ||
|
6d2be10536 | ||
|
8469a05ff0 | ||
|
35bf04b168 | ||
|
dfd974c106 | ||
|
0235039719 | ||
|
f9b07b2477 | ||
|
f28ba22bb4 | ||
|
cd31c040ba | ||
|
bab4e5eaf6 | ||
|
b55489974f | ||
|
2518e36e08 | ||
|
1c1a9de7f5 | ||
|
9637c25a97 | ||
|
abc1153076 | ||
|
d8dcfe8bef | ||
|
8a698f79a7 | ||
|
63b7c53d7f | ||
|
97855f4bf4 | ||
|
9d161b91d4 | ||
|
1c5f0a9d08 | ||
|
7245a45fbe | ||
|
9fa24a64c7 | ||
|
2f6b964807 | ||
|
df52801c66 | ||
|
cad8ae2e60 | ||
|
ac91cde03d | ||
|
339fc08e15 | ||
|
44fc43a9e2 | ||
|
c2faa16768 | ||
|
699e009899 | ||
|
8efc47630e | ||
|
27c79d601e | ||
|
7fbe1c4b7a | ||
|
f7b8d93a23 | ||
|
8adb18662d | ||
|
40b058e0e0 | ||
|
90178066be | ||
|
40542c35cc | ||
|
06d53d6f99 | ||
|
3ffe640033 | ||
|
7d6e869999 | ||
|
a34ce2393c | ||
|
7f82d8b941 | ||
|
399eebc67c | ||
|
a21b5a35e3 | ||
|
3bdee0bb7d | ||
|
1b306aeffe | ||
|
6325185f4e | ||
|
09acfe8a4c | ||
|
5b84a8a124 | ||
|
2c76702e44 | ||
|
9d0fbf9c81 | ||
|
262043861d | ||
|
9db875329d | ||
|
57f4c0d618 | ||
|
03ef2705bd | ||
|
8666997832 | ||
|
af933c3845 | ||
|
c5286ea623 | ||
|
630b301555 | ||
|
342bdb322e | ||
|
e470730990 | ||
|
77a364c446 | ||
|
487d1e0bc7 | ||
|
75fd2fce2a | ||
|
97fb2edf87 | ||
|
127b14bd99 | ||
|
b8499023a2 | ||
|
d8336d26b5 | ||
|
ab6a4895d0 | ||
|
61410f8d9d | ||
|
9b000c768e | ||
|
70e6f1b6c1 | ||
|
2a6536c5c1 | ||
|
2327564d14 | ||
|
b481b6fdf6 | ||
|
9a78563dd6 | ||
|
16ed4bf9a4 | ||
|
72db767c49 | ||
|
2796c0a615 | ||
|
4bacd4d77e | ||
|
9dc5f60379 | ||
|
6ffbdcf442 | ||
|
1e7446af51 | ||
|
ca8e85e60c | ||
|
cf1eef865f | ||
|
8dfc804afb | ||
|
5b52133b55 | ||
|
c679ba2071 | ||
|
437c863aa5 | ||
|
eee6631d73 | ||
|
7c3aa37786 | ||
|
bc070ca798 | ||
|
e9ded9d6b4 | ||
|
33d113e455 | ||
|
9e3e88abe6 | ||
|
e8af46492c | ||
|
6c8789ce34 | ||
|
5221cd244d | ||
|
75890c0efd | ||
|
3500065b96 | ||
|
f1772b3996 | ||
|
d34c335090 | ||
|
f0c629ceb8 | ||
|
5e3abdad74 | ||
|
ad0e6ecf06 | ||
|
1d8fb252be | ||
|
8b2d9bf725 | ||
|
da207f2ddf | ||
|
3db5497480 | ||
|
a8cb77a972 | ||
|
c870b4bab6 | ||
|
9c2b307ab6 | ||
|
967018b353 | ||
|
1f4a29c5fa | ||
|
558260ec94 | ||
|
d0fc25c4e1 | ||
|
94713ddbd6 | ||
|
3b9092d4df | ||
|
3699be3452 | ||
|
dcb7ff0e85 | ||
|
37ec2f06aa | ||
|
7cbbc686ba | ||
|
7652c02183 | ||
|
0d4c3e0f85 | ||
|
d7f41f1ab0 | ||
|
26ddc9d39e | ||
|
ca050070ae | ||
|
a40f622f75 | ||
|
879f205ee3 | ||
|
93f07c8871 | ||
|
4a632451dc | ||
|
e6b31ece2a | ||
|
d877064e85 | ||
|
478bbff83f | ||
|
70d247c017 | ||
|
d641b21eac | ||
|
8f0a8155d6 | ||
|
f3541e23bc | ||
|
b7aff78a41 | ||
|
37a8681bf8 | ||
|
612f010912 | ||
|
a7f3c87b00 | ||
|
28de345422 | ||
|
0b0a99e780 | ||
|
dbd8bda565 | ||
|
dcfd5b3ab0 | ||
|
7a5e3ae1ab | ||
|
efca425bd0 | ||
|
9fdeb7149a | ||
|
0bdfcfb183 | ||
|
f7baedb06e | ||
|
0399a90320 | ||
|
f624f87f96 | ||
|
4c426b76d2 | ||
|
ae0b40079c | ||
|
242a70b7c9 | ||
|
039fbf928b | ||
|
4199da654b | ||
|
9865ed93d2 | ||
|
c0457f0204 | ||
|
21455a327c | ||
|
ad3bde2e70 | ||
|
49ccccabf2 | ||
|
03a2a9385c | ||
|
6371c69bfe | ||
|
e985327fe3 | ||
|
d788b83ab7 | ||
|
fa42ce7294 | ||
|
b463568988 | ||
|
96ea1b1611 | ||
|
3b42fcae46 | ||
|
507e1a3710 | ||
|
715051e416 | ||
|
e88219da70 | ||
|
d09d08caad | ||
|
d528e5d2a4 | ||
|
d810e27673 | ||
|
3d8778db5a | ||
|
c099750755 | ||
|
a22e0fd752 | ||
|
68e410b94b | ||
|
5bfc268884 | ||
|
ee3c087ef5 | ||
|
d440590c2c | ||
|
c989df244c | ||
|
d2c6750225 | ||
|
b8024e8120 | ||
|
6f15e76d65 | ||
|
1f57b79c6d | ||
|
4f36170ead | ||
|
ab347f1b3e | ||
|
ae4e82f55b | ||
|
b65af20843 | ||
|
4726b2d8c1 | ||
|
c734d91dfd | ||
|
1bd40f28b4 | ||
|
d5c625d9f9 | ||
|
2643436d62 | ||
|
de18c82e66 | ||
|
c24b0095da | ||
|
9184dce6ab | ||
|
8885a6b1c3 | ||
|
3fde227c94 | ||
|
bdb7ce0163 | ||
|
3ba446fdce | ||
|
08f47a2743 | ||
|
b670a08ae3 | ||
|
3eeefa7916 | ||
|
adcbdb3824 | ||
|
bf797c7dfe | ||
|
0b2fc542f8 | ||
|
c9f8d5d479 | ||
|
5a6992bb16 | ||
|
7f74187e22 | ||
|
cd2e0f8095 | ||
|
e81b44af8e | ||
|
9c4df63b8f | ||
|
8603d22d97 | ||
|
516d366043 | ||
|
68d2541f03 | ||
|
468513304c | ||
|
91f405811b | ||
|
28c4705898 | ||
|
7d815a2aa0 | ||
|
cd5c120f8f | ||
|
0f15b1932a | ||
|
a02e8025bd | ||
|
50bc5d2c51 | ||
|
82a8c9caed | ||
|
9da4eeeb46 | ||
|
f2eb12551c | ||
|
d3f65b12fa | ||
|
a6f24189e4 | ||
|
38cc17e381 | ||
|
6dbc1f5b9a | ||
|
fdc909b536 | ||
|
62336e0d53 | ||
|
0d294adb20 | ||
|
335e1a9907 | ||
|
b620bf853c | ||
|
346389f1ef | ||
|
340d8a926d | ||
|
f70f090e7b | ||
|
88016ec045 | ||
|
f3ca65976c | ||
|
3b37f604a7 | ||
|
b5de3450c7 | ||
|
c16b369de8 | ||
|
d9d6ef1fcb | ||
|
29ec277dee | ||
|
01040973b7 | ||
|
6272987ccd | ||
|
5a014bf1d0 | ||
|
c4f26ad4ca | ||
|
fdfd859ff2 | ||
|
7ead38f5c8 | ||
|
437849bbd1 | ||
|
0d1601980a | ||
|
aa3ce41a9a | ||
|
de3f8683e2 | ||
|
e5201fb6e9 | ||
|
2146b390fb | ||
|
cadbc1c4f7 | ||
|
89d710fe28 | ||
|
eb52952eaa | ||
|
d5dba913af | ||
|
7287bf12c1 | ||
|
16be2e8f94 | ||
|
5dbc9b5044 | ||
|
92d3000033 | ||
|
fa25803e50 | ||
|
87d6a6bb58 | ||
|
4cd63aa41a | ||
|
44f519e9d1 | ||
|
c9c431b3e5 | ||
|
0a456954b4 | ||
|
32db2c2b46 | ||
|
92b3f0b74f | ||
|
1dbe9e0bf7 | ||
|
75138eb416 | ||
|
546abbc49f | ||
|
96fb385631 | ||
|
c68e1c2216 | ||
|
3906c64fc6 | ||
|
0cccae9cd4 | ||
|
ec2190162d | ||
|
685c7fc6a5 | ||
|
d95cf1ef63 | ||
|
51b7e553ea | ||
|
f5cd469981 | ||
|
4617eab0e7 | ||
|
92df34f375 | ||
|
1a63e8f6bd | ||
|
3dda3cf844 | ||
|
62f2235a6d | ||
|
3adf9d62bc | ||
|
bba3948a2b | ||
|
4141c60c50 | ||
|
286ad100f0 | ||
|
0b507461a7 | ||
|
2deddf2761 | ||
|
1b28887608 | ||
|
19e060800e | ||
|
70c4882727 | ||
|
e25b67d41f | ||
|
740b78ba00 | ||
|
7f9e3c58cc | ||
|
2a0f32c89e | ||
|
05a45bee4f | ||
|
a30201ebb1 | ||
|
3f42d1ff5f | ||
|
72029bd6e7 | ||
|
c8cc239e1a | ||
|
db49c82fc0 | ||
|
e79826bad0 | ||
|
ef0e625707 | ||
|
9c2f1dd0cb | ||
|
16fc8c3436 | ||
|
6890155712 | ||
|
4b3ba0ba2b | ||
|
3af80f80b2 | ||
|
e5036e734f | ||
|
88bdabc5c9 | ||
|
d2eb602b2d | ||
|
94d3adb3f1 | ||
|
03857aba7c | ||
|
d2006816cb | ||
|
9dfc847840 | ||
|
26d9936a3f | ||
|
082b7876b4 | ||
|
38e38c4a84 | ||
|
bb6fa4bec1 | ||
|
27d739864c | ||
|
b7e0d01844 | ||
|
63bc91c203 | ||
|
80ec2e10a8 | ||
|
718064ad66 | ||
|
93ae90f68b | ||
|
e050003554 | ||
|
3146e42531 | ||
|
e88faa03ae | ||
|
ebdb634a95 | ||
|
eb9f79ab85 | ||
|
8e4be01565 | ||
|
ea03a12a24 | ||
|
4be81470f9 | ||
|
b4b8031faf | ||
|
361ca79ae4 | ||
|
ed801da440 | ||
|
c9c5699cdd | ||
|
37f4f6b329 | ||
|
57b45decd1 | ||
|
4afe73ca98 | ||
|
3df9010af0 | ||
|
515c813900 | ||
|
9bf1fbdafb | ||
|
203f99f2f6 | ||
|
61bae79233 | ||
|
37c586a7e5 | ||
|
17ceb3d91d | ||
|
5dd45c73dc | ||
|
43640a56f1 | ||
|
3f55c1df67 | ||
|
4603bfcc69 | ||
|
7a4d31ad7d | ||
|
0f4e4df04e | ||
|
0b2dd96a75 | ||
|
048acafa31 | ||
|
091d7c93a3 | ||
|
b643cfc274 | ||
|
eabc1ba828 | ||
|
19f7a273d9 | ||
|
f88936f03c | ||
|
e6a7496d66 | ||
|
8a30a91a27 | ||
|
3ac245db46 | ||
|
cf208bf673 | ||
|
6092d028b5 | ||
|
e5acc33350 | ||
|
b1d580fdd2 | ||
|
b68af3789a | ||
|
9dd44d21fc | ||
|
50088f4160 | ||
|
143e4841f8 | ||
|
6939441543 | ||
|
42060f7b90 | ||
|
440e8bd899 | ||
|
8a04fc0344 | ||
|
62224b73da | ||
|
9d6b630c42 | ||
|
6bd1f07308 | ||
|
b3a32dfeb6 | ||
|
3467f63fa6 | ||
|
5837a32840 | ||
|
d4b8dc64cd | ||
|
3a5040d26c | ||
|
ab5b32dcc4 | ||
|
537a45e3d2 | ||
|
cdec62a914 | ||
|
cfd34cbce0 | ||
|
0a15214e53 | ||
|
d6dac4b35b | ||
|
8473e93774 | ||
|
7153eba111 | ||
|
c4b39d94ae | ||
|
36874a97ec | ||
|
f0077caaa5 | ||
|
419b9de7f3 | ||
|
f713c1f001 | ||
|
4d021e9f95 | ||
|
19858b3f6e | ||
|
5db88c8a92 | ||
|
82c6c9866e | ||
|
7c37497860 | ||
|
b0682383da | ||
|
dd752ad11b | ||
|
33f51d837b | ||
|
b56724aab5 | ||
|
b6dbeb2ce7 | ||
|
01bab10c02 | ||
|
5a4dd0fb2b | ||
|
d561b97fd5 | ||
|
b3196f9d9c | ||
|
95e13b15c1 | ||
|
7d5e75e440 | ||
|
25af334f4d | ||
|
15926ff45d | ||
|
5b3c589c8e | ||
|
f749264fbe | ||
|
c2c9e40836 | ||
|
7adeff9a63 | ||
|
05bea04db7 | ||
|
bdefa92f91 | ||
|
6c8decd77c | ||
|
35ea05bdef | ||
|
64bddb1ff7 | ||
|
291c852f65 | ||
|
370df62fb6 | ||
|
0addba99d3 | ||
|
6e8f6aea9e | ||
|
50f8ba23da | ||
|
005e819fa9 | ||
|
41569aa53f | ||
|
975c42000d | ||
|
bd6fe44cc0 | ||
|
6dba84d69a | ||
|
b952d10d4b | ||
|
922eaa102b | ||
|
5feecc2275 | ||
|
1c4ff192a5 | ||
|
8d3c8b9121 | ||
|
0204977281 | ||
|
e11f8f77e1 | ||
|
a4ff96a831 | ||
|
2d6fdfbe8a | ||
|
f27fe7381b | ||
|
d241d7ca77 | ||
|
7644d8c2d2 | ||
|
522f5215e0 | ||
|
81b2e2e6b2 | ||
|
106528a93b | ||
|
ad4558cd0f | ||
|
af36c3b339 | ||
|
d2af68779e | ||
|
aa68d7f011 | ||
|
32f3eaa5c4 | ||
|
2deaf5d299 | ||
|
69b7483794 | ||
|
ed87d48c96 | ||
|
1d3d10fb4f | ||
|
31e52b6ad3 | ||
|
19e37b330e | ||
|
75ee10d9f0 | ||
|
a1e65f830f | ||
|
7e5feb4500 | ||
|
1bc68956de | ||
|
3f30f8a56e | ||
|
3b2fc97da1 | ||
|
1e92db46a6 | ||
|
2a69ecf91b | ||
|
b091b435e4 | ||
|
5994b1f405 | ||
|
d80798d535 | ||
|
926ab3fabc | ||
|
9553e41a2b | ||
|
750dd63309 | ||
|
e83d56c9af | ||
|
ea6d566607 | ||
|
436f797982 | ||
|
ce510e0394 | ||
|
e546ee2bd7 | ||
|
99c0bd9ada | ||
|
7bd8e5a57b | ||
|
41188b3df8 | ||
|
ee2ac157eb | ||
|
64d6cb8951 | ||
|
7186fa7990 | ||
|
037e82d699 | ||
|
5e9172aca9 | ||
|
d211fc2c3d | ||
|
526c761846 | ||
|
d65a0e7c83 | ||
|
22e073eb3f | ||
|
ddbf7f33b6 | ||
|
01171d8954 | ||
|
cb56b31d10 | ||
|
a7c2a0d3dc | ||
|
f280c8b9bd | ||
|
64d11cc86d | ||
|
7f257d173f | ||
|
de6521158e | ||
|
0bf4134792 | ||
|
9c2f0cfa28 | ||
|
3dadedaf20 | ||
|
1ccc78e3a2 | ||
|
5302f635ac | ||
|
fdf1532074 | ||
|
0fe0a5a56b | ||
|
554fbebe03 | ||
|
27ff8d9352 | ||
|
f8ec10d4e2 | ||
|
aefe1f1ab8 | ||
|
5a1782efba | ||
|
5d722a6041 | ||
|
d62a4aa7ac | ||
|
55beadd737 | ||
|
5b6f6bdf97 | ||
|
4cf7409c1d | ||
|
942ffed1ae | ||
|
2217804391 | ||
|
39fe2fd89e | ||
|
7ff5021491 | ||
|
71b55f9548 | ||
|
ffa68e94ea | ||
|
0670c12c69 | ||
|
5920212d04 | ||
|
1db6b35fb3 |
1707 changed files with 51315 additions and 59661 deletions
224
.cursorrules
Normal file
224
.cursorrules
Normal file
|
@ -0,0 +1,224 @@
|
|||
The name of our company is Protevus
|
||||
The name of our project is Platform
|
||||
Protevus = Laravel and Platform = Illuminate
|
||||
Our Development Langauge is Dart
|
||||
Our cross-platform UI Kit is Flutter
|
||||
Our Implementation is based on Angel3
|
||||
Our Inspiration is based on Laravel
|
||||
Our Deployment Target are Windows, MacOS, Linux, iOS, Android, Web, IoT and Edge Devices
|
||||
|
||||
Protevus Aims to bring a complete Laravel experience to Dart, and Flutter with a truely Unified Full Stack Experience. We aim to mature the platform to support cross-platform development of complex government, military, financial, medical, supply-chain, enterprise and idustrial applications.
|
||||
|
||||
Our 10 Step Development Lifecycle is as follows: We use a hybrid mix of Interface Driven Development, Test Driven Development and AI Software Engineer Agents to assist in generating efficient and reliable code while also providing help with other task like code reviews, documentation, and testing. We call this approach IDD-AI or Individual Driven Developemt AI.
|
||||
|
||||
1. Research - Research the requirements and specifications of the project.
|
||||
2. Identify - Identify the key components and technologies that are needed to build the project. (use opensource)
|
||||
3. Transform - Transform the requirements and specifications from the code to a langauge agnostic abstract contract (YAML)
|
||||
4. Inform - Use the abstract contracts to inform the design of the system and the components.
|
||||
5. Generate - Use AI to generate the initial code based on the abstract contracts.
|
||||
6. Implement - Implement the code in the codebase.
|
||||
7. Test - Test the code to ensure it meets the requirements and specifications.
|
||||
8. Iterate - Iterate the code to improve the design and implementation.
|
||||
9. Review - Review the code to ensure it meets the requirements and specifications.
|
||||
10. Release - Release the code to the public.
|
||||
|
||||
Or RITIGITIRR pronounced Rih-tih-gih-tirr
|
||||
|
||||
The we wash rinse and repeat for each project. When we are done we have a fully functional and robust implementation of the requirements and specifications we will still follow this pattern for enhancements, bug fixes, and new features.
|
||||
|
||||
The API Specifications are in inbox/spec_packagename
|
||||
The Concrete Implementations are in inbox/src_packagename
|
||||
|
||||
The directory structure for the project is as follows:
|
||||
|
||||
config/ = configuration files
|
||||
devops/ = docker and kubernetes configuration
|
||||
docs/ = documentation
|
||||
examples/ = example applications demonstrating using the Platform API
|
||||
packages/ = platform packages
|
||||
resources = images, icons, and other resources
|
||||
scripts/ = utility scripts
|
||||
templates/ = starter templates
|
||||
tests/ = repository wide integration tests
|
||||
tools/ = project wide tools
|
||||
utils/ = cli, console, and other utilities
|
||||
|
||||
Protevus Platform is a hard-fork of Angel3 that is being refactored and rebranded to be Protevus Platform
|
||||
For now all references to Angel3 remain the same, but in the future we will be changing them to Protevus or Platform
|
||||
You must always refer to the project as Platform, not Protevus or Angel3
|
||||
|
||||
You must always prefer leveraging existing sanctioned angel3 packages as dependencies where possible as most of them will be replace and can't be relied on initially.
|
||||
|
||||
You must always prefer leveraging existing standard dart packages as dependencies where possible
|
||||
You will always prefer leveraging any packages that we can from pub.dev or github.com dart/flutter ecosystem
|
||||
|
||||
Our goal is to reimplement the laravel illuminate packages as angel3 packages in short bringing Laravel to Dart
|
||||
Our goal is to reach feature parity with the illuminate api so we must adhere to the specifications of laravel's api as we reimplement the illuminate packages
|
||||
|
||||
We will not reimplement any packages that are not part of laravel, such as symfony packages, or any other packages that are not part of the laravel framework as our goal is to have pure dart implementations so we must find packages in the dart ecosystem where possible or create the feature in dart.
|
||||
|
||||
We will not be re-implementing some features of laravel that would require reinventing wheels, for example angel3's server system is more then sufficiant and consist of the following angel3 packages which we will be initally keeping. Container, Framework, Route Http_Exception, Mock_Request, Modal. All other Illuminate packages will be reimplemented in dart. This list of keepers may change as we begin our phases of development.
|
||||
|
||||
You must always take the following points into consderation when planning and executing your task.
|
||||
|
||||
1. Dart and Flutter ecosystem awareness:
|
||||
- Stay updated with the latest Dart language features and best practices.
|
||||
- Consider Flutter compatibility for components that might be used in mobile/web applications.
|
||||
- Utilize popular Dart packages when appropriate, but be mindful of adding unnecessary dependencies.
|
||||
|
||||
2. Performance considerations:
|
||||
- Implement benchmarking tools to compare performance with both Laravel and the original Angel3.
|
||||
- Use Dart's profiling tools to identify and optimize bottlenecks.
|
||||
- Consider implementing lazy loading and deferred initialization where appropriate.
|
||||
|
||||
3. Asynchronous programming:
|
||||
- Leverage Dart's async/await and Future-based programming extensively.
|
||||
- Implement proper error handling and cancellation for asynchronous operations.
|
||||
- Consider using Streams for reactive programming where appropriate.
|
||||
|
||||
4. Code style and structure:
|
||||
- Follow Dart's official style guide and linting rules.
|
||||
- Implement consistent error handling and logging practices across all packages.
|
||||
- Use Dart's strong typing system to its full potential, including generics and type inference.
|
||||
|
||||
5. Testing strategy:
|
||||
- Implement both unit and integration tests for all components.
|
||||
- Use mock objects and dependency injection to facilitate easier testing.
|
||||
- Implement property-based testing for complex algorithms or data structures.
|
||||
|
||||
6. Documentation:
|
||||
- Use Dart's documentation comments extensively.
|
||||
- Provide examples in documentation for all public APIs.
|
||||
- Create architecture decision records (ADRs) for significant design choices.
|
||||
|
||||
7. Internationalization and localization:
|
||||
- Design with i18n in mind from the start.
|
||||
- Use Dart's intl package for formatting dates, numbers, and plurals.
|
||||
|
||||
8. Error handling and logging:
|
||||
- Implement a consistent error handling strategy across all packages.
|
||||
- Create custom exception classes where appropriate.
|
||||
- Implement structured logging with different log levels.
|
||||
|
||||
9. Security:
|
||||
- Implement security best practices, including input validation, output encoding, and CSRF protection.
|
||||
- Use secure random number generation for cryptographic operations.
|
||||
- Implement rate limiting and throttling mechanisms.
|
||||
|
||||
10. Dependency management:
|
||||
- Use Dart's pub tool effectively for package management.
|
||||
- Consider implementing a monorepo structure for managing multiple packages.
|
||||
|
||||
11. Compatibility layers:
|
||||
- Implement compatibility layers or wrappers to ease migration from Angel3 to the new framework.
|
||||
- Provide migration scripts or tools where possible.
|
||||
|
||||
12. Extensibility:
|
||||
- Design components with extensibility in mind, allowing for easy customization and overriding of default behaviors.
|
||||
- Implement plugin systems where appropriate.
|
||||
|
||||
13. Configuration:
|
||||
- Implement a flexible configuration system that supports different environments (development, testing, production).
|
||||
- Support both file-based and environment variable-based configuration.
|
||||
|
||||
14. CLI tools:
|
||||
- Develop CLI tools to assist in common development tasks (e.g., generating boilerplate code, running migrations).
|
||||
- Ensure CLI tools are cross-platform compatible.
|
||||
|
||||
15. Database abstraction:
|
||||
- Implement database migrations and seeders.
|
||||
- Support multiple database systems, including NoSQL databases.
|
||||
|
||||
16. API design:
|
||||
- Follow RESTful principles when designing APIs.
|
||||
- Consider implementing GraphQL support.
|
||||
|
||||
17. Caching strategies:
|
||||
- Implement various caching mechanisms (in-memory, file-based, distributed).
|
||||
- Provide cache invalidation strategies.
|
||||
|
||||
18. Scalability:
|
||||
- Design components with horizontal scalability in mind.
|
||||
- Implement support for distributed systems and microservices architecture.
|
||||
|
||||
19. Metrics and monitoring:
|
||||
- Implement instrumentation for gathering metrics.
|
||||
- Provide hooks for integrating with monitoring systems.
|
||||
|
||||
20. Dependency injection:
|
||||
- Implement a robust dependency injection system.
|
||||
- Support both constructor and property injection.
|
||||
|
||||
21. Reflection and metadata:
|
||||
- Utilize Dart's reflection capabilities where appropriate.
|
||||
- Implement custom metadata annotations for declarative programming.
|
||||
|
||||
22. Error reporting:
|
||||
- Implement integration with error reporting services (e.g., Sentry).
|
||||
- Provide detailed stack traces and context for errors.
|
||||
|
||||
23. Code generation:
|
||||
- Utilize Dart's build system for code generation where appropriate.
|
||||
- Implement source code generators for repetitive tasks.
|
||||
|
||||
24. Versioning:
|
||||
- Follow semantic versioning principles.
|
||||
- Maintain a detailed changelog.
|
||||
|
||||
25. Community engagement:
|
||||
- Set up issue templates and contribution guidelines on GitHub.
|
||||
- Implement a code of conduct for the project.
|
||||
|
||||
26. Null Safety
|
||||
- Implement sound null safety throughout the entire codebase.
|
||||
- Utilize Dart's null safety features, including:
|
||||
- Nullable and non-nullable types
|
||||
- Late variables
|
||||
- Null-aware operators
|
||||
- Perform null checks where necessary and provide clear documentation on nullability for all public APIs.
|
||||
- Use the `required` keyword for named parameters that must not be null.
|
||||
- Leverage the `?`, `!`, and `??` operators appropriately to handle potential null values.
|
||||
- Implement proper error handling for cases where null values are unexpected.
|
||||
- When interfacing with external code or APIs that are not null-safe:
|
||||
- Use the `dynamic` type or appropriate cast operations cautiously
|
||||
- Utilize the `Never` type for functions that never return normally (i.e., always throw an exception or loop forever).
|
||||
- Consider using the `late` keyword for variables that are initialized after their declaration but before they're used.
|
||||
- When working with collections:
|
||||
- Prefer using nullable item types (e.g., `List<String?>`) over nullable collections (e.g., `List<String>?`) where appropriate
|
||||
- Use static analysis tools to catch potential null safety issues early in the development process.
|
||||
- When migrating existing code:
|
||||
- Use the Dart migration tool
|
||||
- Carefully review all changes
|
||||
- Educate contributors on null safety best practices and include null safety checks in code review processes.
|
||||
|
||||
Here are some notes that you should always keep in mind when working with the project
|
||||
|
||||
Here's the content formatted in Markdown:
|
||||
|
||||
## General Guidelines
|
||||
|
||||
- Always consider the idiomatic Dart approach when implementing Laravel features.
|
||||
- Maintain compatibility with existing Angel3 applications where possible.
|
||||
- Prioritize performance and scalability in all implementations.
|
||||
- Write comprehensive tests for each component, aiming for high code coverage.
|
||||
- Document all public APIs and provide usage examples.
|
||||
- Consider cross-platform compatibility (server, web, mobile) where applicable.
|
||||
- Keep security as a top priority, especially when implementing authentication and encryption features.
|
||||
- Regularly refactor and optimize code as the project progresses.
|
||||
- Engage with the Angel3 community for feedback and contributions throughout the development process.
|
||||
|
||||
## Advanced Implementation Considerations
|
||||
|
||||
- When implementing advanced features like Telescope or Dusk, consider how they can be adapted to work well in a Dart ecosystem.
|
||||
- Pay special attention to how Facades and Macroable traits can be implemented in Dart, as these are core to Laravel's flexibility but may not have direct equivalents in Dart.
|
||||
- For packages like Scout and Passport, research Dart-specific best practices for implementing search and OAuth2 functionalities.
|
||||
- When working on Blade-like templating, consider how to balance between staying close to Blade's syntax and leveraging Dart's language features.
|
||||
|
||||
## Dart-Specific Considerations
|
||||
|
||||
- Always consider the implications of Dart's strong typing when implementing dynamic features from Laravel.
|
||||
- Look for opportunities to leverage Dart's unique features, such as isolates for concurrency, where appropriate.
|
||||
|
||||
The Project Road Map is in @Roadmap Book
|
||||
|
||||
You now have access to all the information that all team members need to start working on the project.
|
6
.devcontainer/devcontainer.json
Normal file
6
.devcontainer/devcontainer.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"image": "dart:3.4",
|
||||
"forwardPorts": [3000,5000],
|
||||
"features": {
|
||||
}
|
||||
}
|
1
.fork
Normal file
1
.fork
Normal file
|
@ -0,0 +1 @@
|
|||
Hard-Fork of Angel3 Master Repo 2024-10-01 Advance to Version 9.x
|
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
|
@ -1,3 +0,0 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: [thosakwe]
|
56
.gitignore
vendored
56
.gitignore
vendored
|
@ -14,7 +14,6 @@
|
|||
.metals/
|
||||
build/
|
||||
#**/packages/
|
||||
packages/hubbub/
|
||||
|
||||
# Files created by dart2js
|
||||
# (Most Dart developers will use pub build to compile Dart, use/modify these
|
||||
|
@ -37,38 +36,34 @@ pubspec.lock
|
|||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/dictionaries
|
||||
.idea/vcs.xml
|
||||
.idea/jsLibraryMappings.xml
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
#pubspec.lock
|
||||
pubspec_overrides.yaml
|
||||
pubspec.yaml.bak
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/dataSources.ids
|
||||
.idea/dataSources.xml
|
||||
.idea/dataSources.local.xml
|
||||
.idea/sqlDataSources.xml
|
||||
.idea/dynamic.xml
|
||||
.idea/uiDesigner.xml
|
||||
# User-specific stuff:
|
||||
|
||||
## VsCode
|
||||
.vscode/
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
/out/
|
||||
.idea_modules/
|
||||
|
||||
# Gradle:
|
||||
.idea/gradle.xml
|
||||
.idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
@ -79,14 +74,15 @@ crashlytics.properties
|
|||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
### VSCode template
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# Others
|
||||
logs/
|
||||
*.pem
|
||||
.DS_Store
|
||||
server_log.txt
|
||||
backup/
|
||||
|
||||
# ignore protevus platform
|
||||
#wspace/
|
||||
|
||||
# temp
|
||||
#inbox/
|
||||
|
||||
|
|
2
.pubignore
Normal file
2
.pubignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
.DS_Store
|
||||
tasks.json
|
15
.vscode/settings.json
vendored
15
.vscode/settings.json
vendored
|
@ -1,5 +1,18 @@
|
|||
{
|
||||
"files.watcherExclude": {
|
||||
"**/target": true
|
||||
}
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"beautify.language": {
|
||||
"html": ["html"],
|
||||
"css": [],
|
||||
"js": []
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.markdownlint": "explicit"
|
||||
},
|
||||
"cmake.configureOnOpen": false
|
||||
}
|
36
AUTHORS.md
Normal file
36
AUTHORS.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
# Protevus Platform Authors
|
||||
|
||||
## Core Team
|
||||
|
||||
- Patrick Stewart <p.stewart@protevus.com> (Project Lead)
|
||||
- Vacant Spot <jane.smith@example.com> (Lead Developer)
|
||||
- Vacant Spot <bob.johnson@example.com> (Developer)
|
||||
|
||||
## AI Team
|
||||
- Cody <cody@protevus.com> (AI Coding Assistant)
|
||||
- Claude <claude@protevus.com> (AI Coding Assistant)
|
||||
- Code Copilot <c.copilot@protevus.com> (AI Coding Assistant)
|
||||
- Keymate <keymate@protevus.com> (AI Coding Assistant)
|
||||
|
||||
## Contributors
|
||||
|
||||
- Alice Williams <alice.williams@example.com>
|
||||
- Implemented the ORM module
|
||||
- Contributed to the database abstraction layer
|
||||
|
||||
- Charlie Brown <charlie.brown@example.com>
|
||||
- Implemented the authentication and authorization system
|
||||
- Contributed to the event broadcasting and queueing system
|
||||
|
||||
- Eve Green <eve.green@example.com>
|
||||
- Improved the routing system
|
||||
- Contributed to the middleware implementation
|
||||
|
||||
- Michael Davis <michael.davis@example.com>
|
||||
- Worked on the caching and performance optimization features
|
||||
- Contributed to the documentation
|
||||
|
||||
# Additional contributors (in alphabetical order)
|
||||
|
||||
- Vacant Spot
|
||||
|
56
CHANGELOG.md
56
CHANGELOG.md
|
@ -1,56 +0,0 @@
|
|||
# 3.0.1 (NNBD)
|
||||
* Changed Dart SDK requirements for all packages to ">=2.12.0 <3.0.0" to support NNBD.
|
||||
* Updated pretty_logging to 2.0.0
|
||||
* Updated angel_http_exception to 2.0.0
|
||||
* Updated angel_cli to 3.0.0. (Rename not working)
|
||||
|
||||
# 3.0.0 (Non NNBD)
|
||||
* Changed Dart SDK requirements for all packages to ">=2.10.0 <3.0.0"
|
||||
* Updated pretty_logging to 2.0.0
|
||||
* Updated angel_http_exception to 2.0.0
|
||||
* Updated angel_cli to 3.0.0. (Rename not working)
|
||||
* Updated angel_route to 4.0.0
|
||||
* Updated angel_model to 2.0.0
|
||||
* Updated angel_container to 2.0.0
|
||||
* Updated angel_framework to 3.0.0
|
||||
* Updated angel_auth to 3.0.0
|
||||
* Updated angel_configuration to 3.0.0
|
||||
* Updated jael to 3.0.0
|
||||
* Updated jael_preprocessor to 3.0.0
|
||||
* Updated validate to 3.0.0
|
||||
* Added and updated json_god to 3.0.0
|
||||
* Updated angel_client to 3.0.0
|
||||
* Updated angel_websocket to 3.0.0 (3/3 tests passed)
|
||||
* Updated test to 3.0.0
|
||||
* Updated angel_jael to 3.0.0 (Issue with 2 dependencies)
|
||||
* Added pub_sub and updated to 3.0.0
|
||||
* Updated production to 2.0.0
|
||||
* Updated hot to 3.0.0
|
||||
* Updated static to 3.0.0
|
||||
* Update basic-sdk-2.12.x boilerplate
|
||||
* Updated angel_serialize to 3.0.0
|
||||
* Updated angel_serialize_generator to 3.0.0
|
||||
* Updated angel_orm to 3.0.0
|
||||
* Updated angel_migration to 3.0.0
|
||||
* Updated angel_orm_generator to 3.0.0 (use a fork of postgres)
|
||||
* Updated angel_migration_runner to 3.0.0
|
||||
* Updated angel_orm_test to 1.0.0
|
||||
* Updated angel_orm_postgres to 2.0.0
|
||||
* Update orm-sdk-2.12.x boilerplate
|
||||
* Updated angel_auth_oauth2 to 3.0.0
|
||||
* Updated angel_auth_cache to 3.0.0
|
||||
* Updated angel_auth_cors to 3.0.0
|
||||
* Updated angel_container_generator to 2.0.0
|
||||
* Updated angel_file_service to 3.0.0
|
||||
* Updated angel_eventsource to 2.0.0 (use a fork of eventsource)
|
||||
* Updated angel_auth_twitter to 3.0.0 (use a fork of twitter and oauth)
|
||||
|
||||
# 2.2.0
|
||||
* Changed Dart SDK requirements for all packages to ">=2.10.0 <2.12.0"
|
||||
* Upgraded 3rd party libraries to the latest version prior to dart 2.12
|
||||
* Fixed broken code due to 3rd party libraries update
|
||||
* Revert packages/validate from version 3.0 to version 2.2
|
||||
|
||||
# 2.1.x and below
|
||||
* Refer to the orginal repo before the fork
|
||||
|
39
CODE_OF_CONDUCT.md
Normal file
39
CODE_OF_CONDUCT.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Protevus Platform Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We, as members, contributors, and leaders of the Protevus Platform community, pledge to make participation in our project and community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our community include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or advances of any kind
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned with this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [insert email
|
71
CONTRIBUTING.md
Normal file
71
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
# Contributing to the Protevus Platform
|
||||
|
||||
Welcome to the Protevus Platform project! We appreciate your interest in contributing to our open-source application server platform. This document outlines the guidelines and best practices for contributing to the project.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
By participating in this project, you are expected to uphold our [Code of Conduct](CODE_OF_CONDUCT.md). Please review it to understand the behavior standards expected of all contributors.
|
||||
|
||||
## Ways to Contribute
|
||||
|
||||
There are many ways to contribute to the Protevus Platform project, including:
|
||||
|
||||
- Reporting bugs or issues
|
||||
- Suggesting new features or improvements
|
||||
- Submitting pull requests with bug fixes or new features
|
||||
- Improving documentation
|
||||
- Participating in discussions and providing feedback
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Fork the repository and create a new branch for your contribution.
|
||||
2. Follow the project's coding standards and conventions.
|
||||
3. Write clear and descriptive commit messages.
|
||||
4. Test your changes locally before submitting a pull request.
|
||||
5. Submit a pull request with a detailed description of your changes.
|
||||
|
||||
## Branching Conventions
|
||||
|
||||
When creating a new branch for your contribution, please follow these naming conventions:
|
||||
|
||||
topic names.
|
||||
```
|
||||
docs/<description>
|
||||
fix/<username>-<description>
|
||||
feature/<username>-<description>
|
||||
refactor/<username>-<description>
|
||||
```
|
||||
|
||||
If the scope of the issue changes for any reason, please create a new branch with the appropriate naming convention.
|
||||
|
||||
## Local Testing
|
||||
|
||||
While we provide CI/CD through GitHub Actions, it is recommended to set up your local testing environment to run tests before pushing commits. Follow the instructions in the project's documentation or the CI configuration files to set up your local testing environment.
|
||||
|
||||
### Running Tests
|
||||
|
||||
Currently, there are three sets of tests that need to be run:
|
||||
|
||||
```bash
|
||||
melos test-unit
|
||||
# These two need to be run inside packages/conduit
|
||||
dart test -j1 test/* # use dart test -j1 for Windows and macOS
|
||||
dart tool/generated_test_runner.dart
|
||||
```
|
||||
The first command will run all the unit tests in the Conduit package and its dependencies. The last two commands test CLI components and string-compiled code, respectively.
|
||||
|
||||
## Pull Request Requirements
|
||||
|
||||
Document the intent and purpose of the pull request.
|
||||
All non-documentation pull requests must include automated tests that cover the new code, including failure cases.
|
||||
If tests work locally but not on the CI, please mention @j4qfrost on the pull request or reach out on the Discord server.
|
||||
|
||||
## Commits and Versioning
|
||||
|
||||
The project uses melos for tooling, which provides autoversioning based on conventional commits. Commits to the master branch will usually be squashed from pull requests, so ensure that the pull request title uses conventional commits to trigger versioning and publishing CI. You do not need to use conventional commits on each commit to your branch.
|
||||
|
||||
## Licensing
|
||||
|
||||
Protevus Platform is released under the [MIT License](LICENSE).
|
||||
|
||||
Thank you for your interest in contributing to the Protevus Platform project! We look forward to your contributions and appreciate your efforts to make this project better.
|
4
LICENSE
4
LICENSE
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 angel-dart
|
||||
Copyright (c) 2024 Protevus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
144
README.md
144
README.md
|
@ -1,69 +1,113 @@
|
|||
[![The Angel Framework](https://angel-dart.github.io/assets/images/logo.png)](https://angel-dart.dev)
|
||||
<p align="center"><a href="https://protevus.com" target="_blank"><img src="https://git.protevus.com/protevus/branding/raw/branch/main/protevus-logo-bg.png"></a></p>
|
||||
|
||||
[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/angel_dart/discussion)
|
||||
[![Pub](https://img.shields.io/pub/v/angel_framework.svg)](https://pub.dartlang.org/packages/angel_framework)
|
||||
[![Build status](https://travis-ci.org/angel-dart/framework.svg?branch=master)](https://travis-ci.org/angel-dart/framework)
|
||||
![License](https://img.shields.io/github/license/angel-dart/framework.svg)
|
||||
# Protevus Platform
|
||||
|
||||
**A polished, production-ready backend framework in Dart.**
|
||||
Protevus Platform is a highly versatile and extensible application server platform for the Dart programming language. It is a hard fork of the Angel3 framework, inspired by Express.js and Laravel, aiming to provide a familiar and Laravel-compatible API while leveraging the power of Dart.
|
||||
|
||||
-----
|
||||
## About
|
||||
Angel is a full-stack Web framework in Dart. It aims to
|
||||
streamline development by providing many common features
|
||||
out-of-the-box in a consistent manner.
|
||||
> **Note:** This repository contains the core code of the Protevus Platform. If you want to build an application using Protevus, visit the main [Protevus repository](https://github.com/protevus/protevus).
|
||||
|
||||
With features like the following, Angel is the all-in-one framework you should choose to build your next project:
|
||||
* GraphQL Support
|
||||
* PostgreSQL ORM
|
||||
* Dependency Injection
|
||||
* Static File Handling
|
||||
* And much more...
|
||||
## About Protevus
|
||||
|
||||
See all the packages in the `packages/` directory.
|
||||
Protevus Platform allows developers to leverage their existing Laravel knowledge and experience in the Dart ecosystem. It combines the best features of Angel3 with Laravel-inspired design patterns and APIs, creating a powerful and familiar environment for web application development.
|
||||
|
||||
## Installation & Setup
|
||||
## AI Assistance
|
||||
|
||||
Once you have [Dart](https://www.dartlang.org/) installed, bootstrapping a project is as simple as running a few shell commands:
|
||||
The Protevus Platform project utilizes AI assistance in various aspects of its development process. We believe in leveraging the capabilities of AI to enhance productivity, code quality, and overall project progress while maintaining transparency and adhering to ethical practices.
|
||||
|
||||
Install the [Angel CLI](https://github.com/angel-dart/cli):
|
||||
### AI Tools and Models
|
||||
|
||||
```bash
|
||||
pub global activate angel_cli
|
||||
The following AI tools and models have been primarily employed in the development of the Protevus Platform:
|
||||
|
||||
- **Continue** (continue.dev)
|
||||
- **OpenRouter** (openrouter.ai)
|
||||
- **Claude** (claude.ai)
|
||||
- **Codestral** (mistral.ai)
|
||||
- **Voyage** (voyage.ai)
|
||||
- Other tools and LLMs
|
||||
|
||||
### Guidelines and Limitations
|
||||
|
||||
While AI assistance has been invaluable in accelerating certain aspects of development, we adhere to strict guidelines to ensure quality, security, and ethical use of AI in our development process.
|
||||
|
||||
## Features
|
||||
|
||||
- **Laravel API Compatibility**: Familiar API for Laravel developers
|
||||
- **Modular Architecture**: Separating core components and libraries
|
||||
- **High Performance**: Leverages Dart's efficient event-driven model and isolates for concurrent processing.
|
||||
- **Asynchronous Processing**: Built on Dart's async-await paradigm for non-blocking operations.
|
||||
- **Extensibility**: Support for custom extensions
|
||||
- **Community-Driven**: Open-source principles and community contributions
|
||||
- **Modular Packages**: Standalone Dart packages for each component
|
||||
- **Comprehensive Routing**: Powerful routing capabilities
|
||||
- **Dependency Injection**: Built-in support
|
||||
- **Middleware Support**: For filtering HTTP requests
|
||||
- **Authentication & Authorization**: Robust tools
|
||||
- **Database Abstraction**: Query builder and ORM
|
||||
- **Queueing System**: Manage background tasks
|
||||
- **Event Broadcasting**: Real-time event capabilities
|
||||
- **Full-Stack Experience**: Server-side views and Flutter support for frontends
|
||||
- **WebSocket Support**: Real-time communication
|
||||
- **ORM and Database Integration**: Work with various database systems
|
||||
- **Templating Engine**: For server-side rendering
|
||||
- **Static File Serving**: Built-in middleware
|
||||
- **Scalability**: Designed to handle multiple concurrent connections efficiently.
|
||||
- **Testing Utilities**: Comprehensive testing support
|
||||
|
||||
## Getting Started
|
||||
|
||||
To get started with Protevus Platform, follow these steps:
|
||||
|
||||
1. **Install Dart**: Ensure you have the Dart SDK installed on your system.
|
||||
|
||||
2. **Create a new project**:
|
||||
- dart create -t console my_protevus_app cd
|
||||
- my_protevus_app
|
||||
|
||||
3. **Add Protevus dependencies**: Add the following to your `pubspec.yaml`:
|
||||
```yaml
|
||||
dependencies:
|
||||
protevus_framework: ^1.0.0
|
||||
protevus_configuration: ^1.0.0
|
||||
```
|
||||
|
||||
Bootstrap a project:
|
||||
|
||||
```bash
|
||||
angel init hello
|
||||
4. **Run pub get**:
|
||||
```shell
|
||||
dart pub get
|
||||
```
|
||||
5. **Create your first Protevus application**: Replace the contents of bin/my_protevus_app.dart with:
|
||||
```dart
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'package:protevus_framework/http.dart';
|
||||
|
||||
You can even have your server run and be *hot-reloaded* on file changes:
|
||||
void main() async {
|
||||
var app = Protevus();
|
||||
var http = ProtevusHttp(app);
|
||||
|
||||
```bash
|
||||
dart --observe bin/dev.dart
|
||||
app.get('/', (req, res) => res.write('Hello, Protevus!'));
|
||||
|
||||
await http.startServer('localhost', 3000);
|
||||
print('Server listening at http://localhost:3000');
|
||||
}
|
||||
```
|
||||
6. **Run your application**:
|
||||
```shell
|
||||
dart run bin/my_protevus_app.dart
|
||||
```
|
||||
Visit http://localhost:3000 in your browser to see your Protevus app in action!
|
||||
|
||||
Next, check out the [detailed documentation](https://docs.angel-dart.dev/v/2.x) to learn to flesh out your project.
|
||||
## Documentation
|
||||
Comprehensive documentation for Protevus Platform is available at protevus.com/docs/platform. The documentation covers installation, configuration, usage, and advanced topics, including guides and examples.
|
||||
|
||||
## Development
|
||||
## Plugins and Packages
|
||||
Protevus Platform offers a wide range of official plugins and packages to extend its functionality, building upon the Angel3 ecosystem and introducing new Laravel-inspired components.
|
||||
|
||||
* Install development version of Angel CLI
|
||||
`dart pub global activate --source path ./packages/cli`
|
||||
`dart pub global activate --source git https://github.com/dukefirehawk/angel/packages/cli`
|
||||
## Community and Support
|
||||
GitHub Discussions: github.com/protevus/platform/discussions
|
||||
Twitter: @Protevus
|
||||
Contributing
|
||||
We welcome contributions from the community! Please read our CONTRIBUTING.md for guidelines on how to contribute to Protevus Platform.
|
||||
|
||||
## Examples and Documentation
|
||||
Visit the [documentation](https://docs.angel-dart.dev/v/2.x)
|
||||
for dozens of guides and resources, including video tutorials,
|
||||
to get up and running as quickly as possible with Angel.
|
||||
## License
|
||||
Protevus Platform is open-source software licensed under the MIT license.
|
||||
|
||||
Examples and complete projects can be found
|
||||
[here](https://github.com/angel-dart/examples-v2).
|
||||
|
||||
|
||||
You can also view the [API Documentation](http://www.dartdocs.org/documentation/angel_framework/latest).
|
||||
|
||||
There is also an [Awesome Angel :fire:](https://github.com/angel-dart/awesome-angel) list.
|
||||
|
||||
## Contributing
|
||||
Interested in contributing to Angel? Start by reading the contribution guide [here](CONTRIBUTING.md).
|
||||
## Acknowledgements
|
||||
Protevus Platform is built upon the foundation of Angel3 and inspired by Laravel. We'd like to thank the creators and contributors of both these frameworks for their invaluable work in the web development ecosystem.
|
49
SECURITY.md
Normal file
49
SECURITY.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
# Protevus Platform Security
|
||||
|
||||
The Protevus Platform team takes security seriously and is committed to ensuring the security and integrity of the project. This document outlines the security practices, policies, and guidelines followed by the project.
|
||||
|
||||
## Reporting Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within the Protevus Platform, we appreciate your help in disclosing it responsibly. Please follow these steps:
|
||||
|
||||
1. **Do not** create a public issue or disclose the vulnerability publicly.
|
||||
2. Send an email to the Protevus Platform security team at [security@protevus.com](mailto:security@protevus.com) with details about the vulnerability, including:
|
||||
- A brief description of the vulnerability
|
||||
- Steps to reproduce the issue
|
||||
- Any potential impact or consequences
|
||||
- Your contact information (optional)
|
||||
3. The security team will acknowledge your report and work with you to investigate and address the vulnerability.
|
||||
4. Once the vulnerability has been addressed, you will be credited in the release notes and the security advisory.
|
||||
|
||||
We appreciate your cooperation in responsibly disclosing security vulnerabilities, as it helps us maintain the integrity and security of the Protevus Platform.
|
||||
|
||||
## Security Practices
|
||||
|
||||
The Protevus Platform team follows industry-standard security practices to ensure the security and integrity of the project:
|
||||
|
||||
- **Code Reviews**: All code contributions undergo thorough code reviews by the core team to identify and mitigate potential security risks.
|
||||
- **Secure Coding Practices**: The project adheres to secure coding practices, including input validation, output encoding, and protection against common web application vulnerabilities (e.g., XSS, CSRF, SQL injection).
|
||||
- **Dependency Management**: Third-party dependencies are regularly monitored and updated to address known vulnerabilities.
|
||||
- **Security Testing**: The project undergoes regular security testing, including static code analysis, dynamic application security testing (DAST), and penetration testing.
|
||||
- **Secure Development Lifecycle**: The project follows a secure development lifecycle, incorporating security considerations throughout the development process, from design to deployment.
|
||||
|
||||
## Security Advisories
|
||||
|
||||
In the event of a security vulnerability being discovered and addressed, the Protevus Platform team will release a security advisory containing the following information:
|
||||
|
||||
- A description of the vulnerability
|
||||
- Affected versions
|
||||
- Mitigation steps or patches
|
||||
- Credit to the individuals who reported the vulnerability (if desired)
|
||||
|
||||
Security advisories will be published on the project's website and communicated to the community through appropriate channels.
|
||||
|
||||
## Responsible Disclosure
|
||||
|
||||
The Protevus Platform team believes in responsible disclosure of security vulnerabilities. We will work with researchers and security professionals to address vulnerabilities in a timely and responsible manner, ensuring that the necessary fixes and mitigations are in place before publicly disclosing the details of the vulnerability.
|
||||
|
||||
## Conclusion
|
||||
|
||||
The security and integrity of the Protevus Platform are of utmost importance to the project team. We are committed to following industry-standard security practices, responsibly disclosing and addressing vulnerabilities, and maintaining open communication with the community regarding security matters.
|
||||
|
||||
If you have any questions or concerns regarding the security of the Protevus Platform, please contact the security team at [security@protevus.com](mailto:security@protevus.com).
|
9
TODO.md
9
TODO.md
|
@ -1,9 +0,0 @@
|
|||
# Todo
|
||||
|
||||
### Container/angel_container_generator
|
||||
|
||||
* test/reflector_test.reflectab.dart - Changed ImplicitGetterMirrorImpl() from 5 to 3 parameters (revisit later)
|
||||
* A user forum
|
||||
* Updated User Guide
|
||||
|
||||
|
30
analysis_options.yaml
Normal file
30
analysis_options.yaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
75
devops/README.md
Normal file
75
devops/README.md
Normal file
|
@ -0,0 +1,75 @@
|
|||
# Protevus Platform DevOps
|
||||
|
||||
This directory contains Docker and Kubernetes configurations for the Protevus Platform. It is organized to support our containerization and orchestration needs across different environments.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
devops/
|
||||
├── docker/
|
||||
│ ├── Dockerfile
|
||||
│ ├── docker-compose.yml
|
||||
│ └── .dockerignore
|
||||
├── kubernetes/
|
||||
│ ├── deployment.yaml
|
||||
│ ├── service.yaml
|
||||
│ ├── ingress.yaml
|
||||
│ └── configmap.yaml
|
||||
├── scripts/
|
||||
│ ├── docker-build.sh
|
||||
│ ├── docker-push.sh
|
||||
│ ├── k8s-deploy.sh
|
||||
│ └── k8s-rollback.sh
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### docker/
|
||||
|
||||
This directory contains Docker-related files for building and running the Protevus Platform in containers.
|
||||
|
||||
- `Dockerfile`: Defines the container image for the Protevus Platform.
|
||||
- `docker-compose.yml`: Configures multi-container Docker applications for local development.
|
||||
- `.dockerignore`: Specifies which files and directories should be excluded when building Docker images.
|
||||
|
||||
### kubernetes/
|
||||
|
||||
The kubernetes/ directory houses Kubernetes manifests for deploying and managing the Protevus Platform in a Kubernetes cluster.
|
||||
|
||||
- `deployment.yaml`: Defines the deployment configuration for the Protevus Platform.
|
||||
- `service.yaml`: Specifies the service configuration for exposing the platform.
|
||||
- `ingress.yaml`: Configures ingress rules for routing external traffic to the service.
|
||||
- `configmap.yaml`: Stores configuration data that can be consumed by pods.
|
||||
|
||||
### scripts/
|
||||
|
||||
This directory contains utility scripts for Docker and Kubernetes operations.
|
||||
|
||||
- `docker-build.sh`: Script for building Docker images.
|
||||
- `docker-push.sh`: Script for pushing Docker images to a registry.
|
||||
- `k8s-deploy.sh`: Script for deploying the application to a Kubernetes cluster.
|
||||
- `k8s-rollback.sh`: Script for rolling back a Kubernetes deployment.
|
||||
|
||||
## Usage Guidelines
|
||||
|
||||
1. Use the provided scripts in the `scripts/` directory for common Docker and Kubernetes operations.
|
||||
2. Ensure all configuration files are properly parameterized for different environments (dev, staging, production).
|
||||
3. Keep sensitive information (like passwords and API keys) out of these files and use Kubernetes secrets instead.
|
||||
4. Regularly update and test these configurations as the Protevus Platform evolves.
|
||||
|
||||
## Deployment Process
|
||||
|
||||
1. Build the Docker image using `scripts/docker-build.sh`.
|
||||
2. Push the image to the container registry with `scripts/docker-push.sh`.
|
||||
3. Deploy to Kubernetes using `scripts/k8s-deploy.sh`.
|
||||
4. If needed, rollback the deployment using `scripts/k8s-rollback.sh`.
|
||||
|
||||
## Contributing
|
||||
|
||||
When contributing to the DevOps configurations:
|
||||
|
||||
1. Test all changes thoroughly in a non-production environment before applying to production.
|
||||
2. Document any new scripts or significant changes to existing configurations.
|
||||
3. Follow Kubernetes and Docker best practices for security and efficiency.
|
||||
4. Submit a pull request with a clear description of the changes and their purpose.
|
||||
|
||||
For any questions or suggestions regarding the DevOps setup, please contact the Protevus Platform infrastructure team.
|
172
devops/docker/README.md
Normal file
172
devops/docker/README.md
Normal file
|
@ -0,0 +1,172 @@
|
|||
# Docker Services
|
||||
|
||||
The required applications by the framework can be run using the docker compose files provided in this folder.
|
||||
|
||||
## PostreSQL
|
||||
|
||||
### Starting the PostreSQL container
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-pg.yml -p pg up -d
|
||||
```
|
||||
|
||||
### Stopping the PostreSQL container
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-pg.yml -p pg stop
|
||||
docker compose -f docker-compose-pg.yml -p pg down
|
||||
```
|
||||
|
||||
### Checking the PostreSQL container log
|
||||
|
||||
```bash
|
||||
docker logs docker-pg-1 -f
|
||||
```
|
||||
|
||||
### Running psql
|
||||
|
||||
```bash
|
||||
docker exec -it <container id> /bin/bash
|
||||
psql --username postgres
|
||||
```
|
||||
|
||||
### Create PostgreSQL database, user and grant access
|
||||
|
||||
```sql
|
||||
create database orm_test;
|
||||
create user test with encrypted password 'test123';
|
||||
grant all privileges on database orm_test to test;
|
||||
```
|
||||
|
||||
## MariaDB
|
||||
|
||||
### Starting the MariaDB container
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-mariadb.yml -p maria up -d
|
||||
```
|
||||
|
||||
### Stopping the MariaDB container
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-mariadb.yml -p maria stop
|
||||
docker compose -f docker-compose-mariadb.yml -p maria down
|
||||
```
|
||||
|
||||
### Checking the MariaDB container log
|
||||
|
||||
```bash
|
||||
docker logs maria-mariadb-1 -f
|
||||
```
|
||||
|
||||
### Create MariaDB database, user and grant access
|
||||
|
||||
```sql
|
||||
create database orm_test;
|
||||
|
||||
-- Granting localhost access only
|
||||
create user 'test'@'localhost' identified by 'test123';
|
||||
grant all privileges on orm_test.* to 'test'@'localhost';
|
||||
|
||||
-- Granting localhost and remote access
|
||||
create user 'test'@'%' identified by 'test123';
|
||||
grant all privileges on orm_test.* to 'test'@'%';
|
||||
```
|
||||
|
||||
## MySQL
|
||||
|
||||
### Starting the MySQL container
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-mysql.yml -p mysql up -d
|
||||
```
|
||||
|
||||
### Stopping the MySQL container
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-mysql.yml -p mysql stop
|
||||
docker compose -f docker-compose-mysql.yml -p mysql down
|
||||
```
|
||||
|
||||
### Checking the MySQL container log
|
||||
|
||||
```bash
|
||||
docker logs mysql-mysql-1 -f
|
||||
```
|
||||
|
||||
### Create MySQL database, user and grant access
|
||||
|
||||
```sql
|
||||
create database orm_test;
|
||||
|
||||
-- Granting localhost access only
|
||||
create user 'test'@'localhost' identified by 'test123';
|
||||
grant all privileges on orm_test.* to 'test'@'localhost';
|
||||
|
||||
-- Granting localhost and remote access
|
||||
create user 'test'@'%' identified by 'test123';
|
||||
grant all privileges on orm_test.* to 'test'@'%';
|
||||
```
|
||||
|
||||
## MongoDB
|
||||
|
||||
### Starting the MongoDB container
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-mongo.yml -p mongo up -d
|
||||
```
|
||||
|
||||
### Stopping the MongoDB container
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-mongo.yml -p mongo stop
|
||||
docker compose -f docker-compose-mongo.yml -p mongo down
|
||||
```
|
||||
|
||||
### Checking the MongoDB container log
|
||||
|
||||
```bash
|
||||
docker logs mongo-mongo-1 -f
|
||||
```
|
||||
|
||||
## rethinkDB
|
||||
|
||||
### Starting the rethinkDB container
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-rethinkdb.yml -p rethink up -d
|
||||
```
|
||||
|
||||
### Stopping the rethinkDB container
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-rethinkdb.yml -p rethink stop
|
||||
docker compose -f docker-compose-rethinkdb.yml -p rethink down
|
||||
```
|
||||
|
||||
### Checking the rethinkDB container log
|
||||
|
||||
```bash
|
||||
docker logs rethink-rethinkdb-1 -f
|
||||
```
|
||||
|
||||
## Redis
|
||||
|
||||
### Starting the Redis container
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-redis.yml -p redis up -d
|
||||
```
|
||||
|
||||
### Stopping the Redis container
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-redis.yml -p redis stop
|
||||
docker compose -f docker-compose-redis.yml -p redis down
|
||||
```
|
||||
|
||||
### Checking the Redis container log
|
||||
|
||||
```bash
|
||||
docker logs redis-redis-1 -f
|
||||
```
|
19
devops/docker/services/docker-compose-mariadb.yml
Normal file
19
devops/docker/services/docker-compose-mariadb.yml
Normal file
|
@ -0,0 +1,19 @@
|
|||
services:
|
||||
mariadb:
|
||||
image: mariadb:latest
|
||||
restart: "no"
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
- MARIADB_ROOT_PASSWORD=Qwerty
|
||||
volumes:
|
||||
- "mariadb:/var/lib/mysql"
|
||||
networks:
|
||||
- appnet
|
||||
|
||||
volumes:
|
||||
mariadb:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
appnet:
|
37
devops/docker/services/docker-compose-mongo.yml
Normal file
37
devops/docker/services/docker-compose-mongo.yml
Normal file
|
@ -0,0 +1,37 @@
|
|||
services:
|
||||
|
||||
mongo:
|
||||
image: mongo
|
||||
restart: no
|
||||
ports:
|
||||
- 27017:27017
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: root
|
||||
MONGO_INITDB_ROOT_PASSWORD: Qwerty
|
||||
MONGO_INITDB_DATABASE: local
|
||||
volumes:
|
||||
- "mongo:/data/db"
|
||||
networks:
|
||||
- appnet
|
||||
|
||||
mongo-express:
|
||||
image: mongo-express
|
||||
restart: no
|
||||
depends_on:
|
||||
- mongo
|
||||
ports:
|
||||
- 8081:8081
|
||||
environment:
|
||||
ME_CONFIG_MONGODB_ADMINUSERNAME: root
|
||||
ME_CONFIG_MONGODB_ADMINPASSWORD: Qwerty
|
||||
ME_CONFIG_MONGODB_URL: mongodb://root:Qwerty@mongo:27017/
|
||||
ME_CONFIG_BASICAUTH: false
|
||||
networks:
|
||||
- webnet
|
||||
|
||||
volumes:
|
||||
mongo:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
appnet:
|
19
devops/docker/services/docker-compose-mysql.yml
Normal file
19
devops/docker/services/docker-compose-mysql.yml
Normal file
|
@ -0,0 +1,19 @@
|
|||
services:
|
||||
mysql:
|
||||
image: mysql:latest
|
||||
restart: "no"
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=Qwerty
|
||||
volumes:
|
||||
- "mysql:/var/lib/mysql"
|
||||
networks:
|
||||
- appnet
|
||||
|
||||
volumes:
|
||||
mysql:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
appnet:
|
31
devops/docker/services/docker-compose-pg.yml
Normal file
31
devops/docker/services/docker-compose-pg.yml
Normal file
|
@ -0,0 +1,31 @@
|
|||
services:
|
||||
pgdb:
|
||||
image: postgres:latest
|
||||
restart: "no"
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
volumes:
|
||||
- "db:/var/lib/postgresql/data"
|
||||
networks:
|
||||
- appnet
|
||||
|
||||
pgadmin4:
|
||||
image: dpage/pgadmin4:latest
|
||||
restart: "no"
|
||||
ports:
|
||||
- "5050:80"
|
||||
environment:
|
||||
- PGADMIN_DEFAULT_EMAIL=admin@mydomain.com
|
||||
- PGADMIN_DEFAULT_PASSWORD=Qwerty
|
||||
networks:
|
||||
- appnet
|
||||
|
||||
volumes:
|
||||
db:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
appnet:
|
20
devops/docker/services/docker-compose-redis.yml
Normal file
20
devops/docker/services/docker-compose-redis.yml
Normal file
|
@ -0,0 +1,20 @@
|
|||
services:
|
||||
redis:
|
||||
image: redis:latest
|
||||
restart: "no"
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
volumes:
|
||||
- "redis:/data"
|
||||
networks:
|
||||
- appnet
|
||||
|
||||
volumes:
|
||||
redis:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
appnet:
|
19
devops/docker/services/docker-compose-rethinkdb.yml
Normal file
19
devops/docker/services/docker-compose-rethinkdb.yml
Normal file
|
@ -0,0 +1,19 @@
|
|||
services:
|
||||
rethinkdb:
|
||||
image: rethinkdb:latest
|
||||
restart: "no"
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "28015:28015"
|
||||
- "29015:29015"
|
||||
volumes:
|
||||
- "rethinkdb:/data"
|
||||
networks:
|
||||
- appnet
|
||||
|
||||
volumes:
|
||||
rethinkdb:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
appnet:
|
10
docs/Testing.md
Normal file
10
docs/Testing.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Performance Testing
|
||||
|
||||
The performance test can be run with the following tools.
|
||||
|
||||
## WRT
|
||||
|
||||
```bash
|
||||
wrk -t12 -c400 -d30s http://localhost:8080/query?queries=20
|
||||
```
|
||||
This runs a benchmark for 30 seconds, using 12 threads, and keeping 400 HTTP connections open.
|
68
helpers/README.md
Normal file
68
helpers/README.md
Normal file
|
@ -0,0 +1,68 @@
|
|||
|
||||
# Protevus Platform Helpers
|
||||
|
||||
This directory contains various helper functionalities, tools, and utilities for the Protevus Platform. It is organized into subdirectories to maintain a clear structure and separation of concerns.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
helpers/
|
||||
├── cli/
|
||||
├── console/
|
||||
├── tools/
|
||||
└── utilities/
|
||||
```
|
||||
|
||||
### cli/
|
||||
|
||||
This directory contains command-line interface tools and scripts specific to the Protevus Platform. These are typically used for development, deployment, or maintenance tasks that are run directly from the command line.
|
||||
|
||||
Examples:
|
||||
- Database migration scripts
|
||||
- Code generation tools
|
||||
- Deployment scripts
|
||||
|
||||
### console/
|
||||
|
||||
The console/ directory houses console commands and utilities, similar to Laravel's Artisan commands. These are interactive tools that provide a user-friendly interface for various platform operations.
|
||||
|
||||
Examples:
|
||||
- REPL (Read-Eval-Print Loop) for the Protevus Platform
|
||||
- Interactive configuration tools
|
||||
- Database seeding commands
|
||||
|
||||
### tools/
|
||||
|
||||
This directory is for larger, more complex helper applications or scripts used in development, testing, or deployment of the Protevus Platform. These tools often combine multiple functionalities or interact with external services.
|
||||
|
||||
Examples:
|
||||
- Automated testing suites
|
||||
- Performance profiling tools
|
||||
- Documentation generators
|
||||
|
||||
### utilities/
|
||||
|
||||
The utilities/ directory contains general-purpose utility functions and smaller helper scripts. These are typically reusable across different parts of the platform and provide common functionality.
|
||||
|
||||
Examples:
|
||||
- String manipulation functions
|
||||
- Date and time helpers
|
||||
- File system operations
|
||||
|
||||
## Usage Guidelines
|
||||
|
||||
1. Place new helpers in the appropriate subdirectory based on their purpose and complexity.
|
||||
2. Maintain consistency in naming conventions and file structures within each subdirectory.
|
||||
3. Document each helper, tool, or utility with clear comments and usage examples.
|
||||
4. Update this README when adding new significant helpers or changing the structure.
|
||||
|
||||
## Contributing
|
||||
|
||||
When contributing new helpers:
|
||||
|
||||
1. Ensure your code follows the Protevus Platform coding standards.
|
||||
2. Write tests for your helpers when applicable.
|
||||
3. Update or create documentation for new functionalities.
|
||||
4. Submit a pull request with a clear description of the new helper and its purpose.
|
||||
|
||||
For any questions or suggestions regarding the helpers structure, please contact the Protevus Platform core development team.
|
20
melos.yaml
Normal file
20
melos.yaml
Normal file
|
@ -0,0 +1,20 @@
|
|||
name: protevus_platform
|
||||
repository: https://github.com/protevus/platform
|
||||
packages:
|
||||
- core/**
|
||||
- packages/**
|
||||
- sandbox/**
|
||||
- wspace/**
|
||||
- examples/**
|
||||
|
||||
command:
|
||||
version:
|
||||
# Generate commit links in package changelogs.
|
||||
linkToCommits: true
|
||||
# Only allow versioning to happen on main branch.
|
||||
branch: master
|
||||
workspaceChangelog: true
|
||||
|
||||
ide:
|
||||
intellij:
|
||||
enabled: false
|
56
packages/auth/.gitignore
vendored
56
packages/auth/.gitignore
vendored
|
@ -1,15 +1,32 @@
|
|||
# Created by .ignore support plugin (hsz.mobi)
|
||||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.dart_tool
|
||||
.packages
|
||||
.pub/
|
||||
build/
|
||||
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
|
||||
### Dart template
|
||||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.buildlog
|
||||
.packages
|
||||
|
||||
# SDK 1.20 and later (no longer creates packages directories)
|
||||
|
||||
# Older SDK versions
|
||||
# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20)
|
||||
.project
|
||||
.pub/
|
||||
build/
|
||||
.buildlog
|
||||
**/packages/
|
||||
|
||||
|
||||
# Files created by dart2js
|
||||
# (Most Dart developers will use pub build to compile Dart, use/modify these
|
||||
# rules if you intend to use dart2js directly
|
||||
|
@ -22,36 +39,17 @@ build/
|
|||
*.info.json
|
||||
|
||||
# Directory created by dartdoc
|
||||
doc/api/
|
||||
|
||||
# Don't commit pubspec lock file
|
||||
# (Library packages only! Remove pattern if developing an application package)
|
||||
pubspec.lock
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/dictionaries
|
||||
.idea/vcs.xml
|
||||
.idea/jsLibraryMappings.xml
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/dataSources.ids
|
||||
.idea/dataSources.xml
|
||||
.idea/dataSources.local.xml
|
||||
.idea/sqlDataSources.xml
|
||||
.idea/dynamic.xml
|
||||
.idea/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/gradle.xml
|
||||
.idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/mongoSettings.xml
|
||||
## VsCode
|
||||
.vscode/
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
@ -59,9 +57,8 @@ pubspec.lock
|
|||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
|
@ -72,5 +69,4 @@ com_crashlytics_export_strings.xml
|
|||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
.dart_tool
|
||||
.DS_Store
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
</component>
|
||||
</module>
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
<component name="ProjectInspectionProfilesVisibleTreeState">
|
||||
<entry key="Project Default">
|
||||
<profile-state>
|
||||
<expanded-state>
|
||||
<State>
|
||||
<id />
|
||||
</State>
|
||||
<State>
|
||||
<id>General</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>XPath</id>
|
||||
</State>
|
||||
</expanded-state>
|
||||
<selected-state>
|
||||
<State>
|
||||
<id>AngularJS</id>
|
||||
</State>
|
||||
</selected-state>
|
||||
</profile-state>
|
||||
</entry>
|
||||
</component>
|
||||
</project>
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/angel_auth.iml" filepath="$PROJECT_DIR$/.idea/angel_auth.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -1,8 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/test" />
|
||||
<option name="scope" value="FOLDER" />
|
||||
<option name="testRunnerOptions" value="-j 4" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,6 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Auth Token Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/test/auth_token_test.dart" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,6 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Callback Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/test/callback_test.dart" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,6 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Local Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/test/local_test.dart" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,8 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="preserve existing user in callback_test.dart" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true" nameIsGenerated="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/test/callback_test.dart" />
|
||||
<option name="scope" value="GROUP_OR_TEST_BY_NAME" />
|
||||
<option name="testName" value="preserve existing user" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,7 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="tests in protect_cookie_test.dart" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true" nameIsGenerated="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/test/protect_cookie_test.dart" />
|
||||
<option name="testRunnerOptions" value="-j4" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,4 +0,0 @@
|
|||
language: dart
|
||||
dart:
|
||||
- dev
|
||||
- stable
|
12
packages/auth/AUTHORS.md
Normal file
12
packages/auth/AUTHORS.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
Primary Authors
|
||||
===============
|
||||
|
||||
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
|
||||
|
||||
Thomas is the current maintainer of the code base. He has refactored and migrated the
|
||||
code base to support NNBD.
|
||||
|
||||
* __[Tobe O](thosakwe@gmail.com)__
|
||||
|
||||
Tobe has written much of the original code prior to NNBD migration. He has moved on and
|
||||
is no longer involved with the project.
|
|
@ -1,83 +1,193 @@
|
|||
# 2.1.5+1
|
||||
# Change Log
|
||||
|
||||
## 8.2.0
|
||||
|
||||
* Require Dart >= 3.3
|
||||
* Updated `lints` to 4.0.0
|
||||
|
||||
## 8.1.1
|
||||
|
||||
* Updated repository link
|
||||
|
||||
## 8.1.0
|
||||
|
||||
* Updated `lints` to 3.0.0
|
||||
|
||||
## 8.0.0
|
||||
|
||||
* Require Dart >= 3.0
|
||||
* Upgraded `http` to 1.0.0
|
||||
* Fixed failed `successRedirect` test case
|
||||
* Fixed failed `failureRedirect` test case
|
||||
* Fixed failed `login` test case
|
||||
* Fixed failed `force basic` test case
|
||||
* Added `example1` and `example2`
|
||||
|
||||
## 7.0.1
|
||||
|
||||
* Fixed linter warnings
|
||||
|
||||
## 7.0.0
|
||||
|
||||
* Require Dart >= 2.17
|
||||
|
||||
## 6.0.0
|
||||
|
||||
* Require Dart >= 2.16
|
||||
|
||||
## 5.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 4.1.2
|
||||
|
||||
* Fixed `requireAuthentication` to work correctly with null-safety type
|
||||
|
||||
## 4.1.1
|
||||
|
||||
* Changed `userId` field of `AuthToken` to String type
|
||||
* Changed `serializer` return value to String type
|
||||
* Changed `deserializer` input parameter to String type
|
||||
|
||||
## 4.1.0
|
||||
|
||||
* Updated linter to `package:lints`
|
||||
|
||||
## 4.0.5
|
||||
|
||||
* Added support for verifier function to return an empty Map instead of null
|
||||
* Fixed `canRespondWithJson` option to return data in the response body when set to true
|
||||
|
||||
## 4.0.4
|
||||
|
||||
* Changed `serializer` and `deserializer` parameters to be required
|
||||
* Fixed HTTP basic authentication
|
||||
* All 31 unit tests passed
|
||||
|
||||
## 4.0.3
|
||||
|
||||
* Fixed "failureRedirect" unit test
|
||||
|
||||
## 4.0.2
|
||||
|
||||
* Added MirrorsReflector to unit test
|
||||
|
||||
## 4.0.1
|
||||
|
||||
* Updated README
|
||||
|
||||
## 4.0.0
|
||||
|
||||
* Migrated to support Dart >= 2.12 NNBD
|
||||
|
||||
## 3.0.0
|
||||
|
||||
* Migrated to work with Dart >= 2.12 Non NNBD
|
||||
|
||||
## 2.1.5+1
|
||||
|
||||
* Fix error in popup page.
|
||||
|
||||
# 2.1.5
|
||||
## 2.1.5
|
||||
|
||||
* Modify `_apply` to honor an existing `User` over `Future<User>`.
|
||||
|
||||
# 2.1.4
|
||||
## 2.1.4
|
||||
|
||||
* Deprecate `decodeJwt`, in favor of asynchronous injections.
|
||||
|
||||
# 2.1.3
|
||||
## 2.1.3
|
||||
|
||||
* Use `await` on redirects, etc.
|
||||
|
||||
# 2.1.2
|
||||
## 2.1.2
|
||||
|
||||
* Change empty cookie string to have double quotes (thanks @korsvanloon).
|
||||
|
||||
# 2.1.1
|
||||
## 2.1.1
|
||||
|
||||
* Added `scopes` to `ExternalAuthOptions`.
|
||||
|
||||
# 2.1.0
|
||||
## 2.1.0
|
||||
|
||||
* Added `ExternalAuthOptions`.
|
||||
|
||||
# 2.0.4
|
||||
## 2.0.4
|
||||
|
||||
* `successRedirect` was previously explicitly returning a `200`; remove this and allow the default `302`.
|
||||
|
||||
# 2.0.3
|
||||
## 2.0.3
|
||||
|
||||
* Updates for streaming parse of request bodies.
|
||||
|
||||
# 2.0.2
|
||||
## 2.0.2
|
||||
|
||||
* Handle `null` return in `authenticate` + `failureRedirect`.
|
||||
|
||||
# 2.0.1
|
||||
## 2.0.1
|
||||
|
||||
* Add generic parameter to `options` on `AuthStrategy.authenticate`.
|
||||
|
||||
# 2.0.0+1
|
||||
## 2.0.0+1
|
||||
|
||||
* Meta update to improve Pub score.
|
||||
|
||||
# 2.0.0
|
||||
* Made `AuthStrategy` generic.
|
||||
* `AngelAuth.strategies` is now a `Map<String, AuthStrategy<User>>`.
|
||||
* Removed `AuthStrategy.canLogout`.
|
||||
* Made `AngelAuthTokenCallback` generic.
|
||||
## 2.0.0
|
||||
|
||||
# 2.0.0-alpha
|
||||
* Depend on Dart 2 and Angel 2.
|
||||
* Made `AuthStrategy` generic.
|
||||
* `ProtevusAuth.strategies` is now a `Map<String, AuthStrategy<User>>`.
|
||||
* Removed `AuthStrategy.canLogout`.
|
||||
* Made `ProtevusAuthTokenCallback` generic.
|
||||
|
||||
## 2.0.0-alpha
|
||||
|
||||
* Depend on Dart 2 and Protevus 2.
|
||||
* Remove `dart2_constant`.
|
||||
* Remove `requireAuth`.
|
||||
* Remove `userKey`, instead favoring generic parameters.
|
||||
|
||||
# 1.2.0
|
||||
## 1.2.0
|
||||
|
||||
* Deprecate `requireAuth`, in favor of `requireAuthentication`.
|
||||
* Allow configuring of the `userKey`.
|
||||
* Deprecate `middlewareName`.
|
||||
|
||||
# 1.1.1+6
|
||||
## 1.1.1+6
|
||||
|
||||
* Fix a small logic bug that prevented `LocalAuthStrategy`
|
||||
from correctly propagating the authenticated user when
|
||||
using `Basic` auth.
|
||||
|
||||
# 1.1.1+5
|
||||
## 1.1.1+5
|
||||
|
||||
* Prevent duplication of cookies.
|
||||
* Regenerate the JWT if `tokenCallback` is called.
|
||||
|
||||
# 1.1.1+4
|
||||
## 1.1.1+4
|
||||
|
||||
* Patched `logout` to properly erase cookies
|
||||
* Fixed checking of expired tokens.
|
||||
|
||||
# 1.1.1+3
|
||||
## 1.1.1+3
|
||||
|
||||
* `authenticate` returns the current user, if one is present.
|
||||
|
||||
# 1.1.1+2
|
||||
## 1.1.1+2
|
||||
|
||||
* `_apply` now always sends a `token` cookie.
|
||||
|
||||
# 1.1.1+1
|
||||
## 1.1.1+1
|
||||
|
||||
* Update `protectCookie` to only send `maxAge` when it is not `-1`.
|
||||
|
||||
# 1.1.1
|
||||
## 1.1.1
|
||||
|
||||
* Added `protectCookie`, to better protect data sent in cookies.
|
||||
|
||||
# 1.1.0+2
|
||||
## 1.1.0+2
|
||||
|
||||
* `LocalAuthStrategy` returns `true` on `Basic` authentication.
|
||||
|
||||
# 1.1.0+1
|
||||
## 1.1.0+1
|
||||
|
||||
* Modified `LocalAuthStrategy`'s handling of `Basic` authentication.
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
The MIT License (MIT)
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2016 angel-dart
|
||||
Copyright (c) 2021, dukefirehawk.com
|
||||
All rights reserved.
|
||||
|
||||
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:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
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.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
|
@ -1,31 +1,33 @@
|
|||
# angel_auth
|
||||
# Protevus Anthentication
|
||||
|
||||
[![Pub](https://img.shields.io/pub/v/angel_auth.svg)](https://pub.dartlang.org/packages/angel_auth)
|
||||
[![build status](https://travis-ci.org/angel-dart/auth.svg?branch=master)](https://travis-ci.org/angel-dart/auth)
|
||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/protevus_auth?include_prereleases)
|
||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||
[![Discord](https://img.shields.io/discord/1060322353214660698)](https://discord.gg/3X6bxTUdCM)
|
||||
[![License](https://img.shields.io/github/license/dart-backend/protevus)](https://github.com/dart-backend/protevus/tree/master/packages/auth/LICENSE)
|
||||
|
||||
A complete authentication plugin for Angel. Inspired by Passport.
|
||||
A complete authentication plugin for Protevus. Inspired by Passport. More details in the [User Guide](https://protevus-docs.dukefirehawk.com/guides/authentication).
|
||||
|
||||
# Wiki
|
||||
[Click here](https://github.com/angel-dart/auth/wiki).
|
||||
## Bundled Strategies
|
||||
|
||||
# Bundled Strategies
|
||||
* Local (with and without Basic Auth)
|
||||
* Find other strategies (Twitter, Google, OAuth2, etc.) on Pub!!!
|
||||
* Find other strategies (Twitter, Google, OAuth2, etc.) on pub
|
||||
|
||||
# Example
|
||||
Ensure you have read the [wiki](https://github.com/angel-dart/auth/wiki).
|
||||
## Example
|
||||
|
||||
Ensure you have read the [User Guide](https://protevus-docs.dukefirehawk.com/guides/authentication).
|
||||
|
||||
```dart
|
||||
configureServer(Angel app) async {
|
||||
var auth = AngelAuth<User>();
|
||||
auth.serializer = ...;
|
||||
auth.deserializer = ...;
|
||||
configureServer(Protevus app) async {
|
||||
var auth = ProtevusAuth<User>(
|
||||
serializer: (user) => user.id ?? '',
|
||||
deserializer: (id) => fetchAUserByIdSomehow(id
|
||||
);
|
||||
auth.strategies['local'] = LocalAuthStrategy(...);
|
||||
|
||||
// POST route to handle username+password
|
||||
app.post('/local', auth.authenticate('local'));
|
||||
|
||||
// Using Angel's asynchronous injections, we can parse the JWT
|
||||
// Using Protevus's asynchronous injections, we can parse the JWT
|
||||
// on demand. It won't be parsed until we check.
|
||||
app.get('/profile', ioc((User user) {
|
||||
print(user.description);
|
||||
|
@ -48,17 +50,15 @@ configureServer(Angel app) async {
|
|||
}
|
||||
```
|
||||
|
||||
# Default Authentication Callback
|
||||
A frequent use case within SPA's is opening OAuth login endpoints in a separate window.
|
||||
[`angel_client`](https://github.com/angel-dart/client)
|
||||
provides a facility for this, which works perfectly with the default callback provided
|
||||
in this package.
|
||||
## Default Authentication Callback
|
||||
|
||||
A frequent use case within SPA's is opening OAuth login endpoints in a separate window. [`protevus_client`](https://pub.dev/packages/protevus_client) provides a facility for this, which works perfectly with the default callback provided in this package.
|
||||
|
||||
```dart
|
||||
configureServer(Angel app) async {
|
||||
configureServer(Protevus app) async {
|
||||
var handler = auth.authenticate(
|
||||
'facebook',
|
||||
AngelAuthOptions(callback: confirmPopupAuthentication()));
|
||||
ProtevusAuthOptions(callback: confirmPopupAuthentication()));
|
||||
app.get('/auth/facebook', handler);
|
||||
|
||||
// Use a comma to try multiple strategies!!!
|
||||
|
@ -75,8 +75,7 @@ configureServer(Angel app) async {
|
|||
}
|
||||
```
|
||||
|
||||
This renders a simple HTML page that fires the user's JWT as a `token` event in `window.opener`.
|
||||
`angel_client` [exposes this as a Stream](https://github.com/angel-dart/client#authentication):
|
||||
This renders a simple HTML page that fires the user's JWT as a `token` event in `window.opener`. `protevus_client` [exposes this as a Stream](https://pub.dev/documentation/protevus_client/latest/):
|
||||
|
||||
```dart
|
||||
app.authenticateViaPopup('/auth/google').listen((jwt) {
|
||||
|
|
|
@ -1,4 +1 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
||||
include: package:lints/recommended.yaml
|
22
packages/auth/example/client/example_client.http
Normal file
22
packages/auth/example/client/example_client.http
Normal file
|
@ -0,0 +1,22 @@
|
|||
### Load landing page
|
||||
GET http://localhost:3000/ HTTP/1.1
|
||||
|
||||
### login (call_back)
|
||||
POST http://localhost:3000/login HTTP/1.1
|
||||
Content-Type: application/json
|
||||
Authorization: Basic jdoe1:password
|
||||
|
||||
### Success redirect (local)
|
||||
POST http://localhost:3000/login HTTP/1.1
|
||||
Content-Type: application/json
|
||||
Authorization: Basic username:password
|
||||
|
||||
### Failure redirect (local)
|
||||
POST http://localhost:3000/login HTTP/1.1
|
||||
Content-Type: application/json
|
||||
Authorization: Basic password:username
|
||||
|
||||
### Force basic
|
||||
GET http://localhost:3000/hello HTTP/1.1
|
||||
Content-Type: application/json
|
||||
Accept:application/json
|
|
@ -1,15 +1,13 @@
|
|||
import 'dart:async';
|
||||
import 'package:angel_auth/angel_auth.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_framework/http.dart';
|
||||
import 'package:protevus_auth/protevus_auth.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'package:protevus_framework/http.dart';
|
||||
|
||||
main() async {
|
||||
var app = Angel();
|
||||
var auth = AngelAuth<User>();
|
||||
|
||||
auth.serializer = (user) => user.id;
|
||||
|
||||
auth.deserializer = (id) => fetchAUserByIdSomehow(id);
|
||||
void main() async {
|
||||
var app = Protevus();
|
||||
var auth = ProtevusAuth<User>(
|
||||
serializer: (user) => user.id ?? '',
|
||||
deserializer: (id) => fetchAUserByIdSomehow(id));
|
||||
|
||||
// Middleware to decode JWT's and inject a user object...
|
||||
await app.configure(auth.configureServer);
|
||||
|
@ -19,21 +17,22 @@ main() async {
|
|||
// If authentication succeeds, return a User object.
|
||||
//
|
||||
// Otherwise, return `null`.
|
||||
return null;
|
||||
});
|
||||
|
||||
app.post('/auth/local', auth.authenticate('local'));
|
||||
|
||||
var http = AngelHttp(app);
|
||||
var http = ProtevusHttp(app);
|
||||
await http.startServer('127.0.0.1', 3000);
|
||||
|
||||
print('Listening at http://127.0.0.1:3000');
|
||||
}
|
||||
|
||||
class User {
|
||||
String id, username, password;
|
||||
String? id, username, password;
|
||||
}
|
||||
|
||||
Future<User> fetchAUserByIdSomehow(id) async {
|
||||
Future<User> fetchAUserByIdSomehow(String id) async {
|
||||
// Fetch a user somehow...
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
|
114
packages/auth/example/example1.dart
Normal file
114
packages/auth/example/example1.dart
Normal file
|
@ -0,0 +1,114 @@
|
|||
import 'package:protevus_auth/protevus_auth.dart';
|
||||
import 'package:protevus_container/mirrors.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'package:protevus_framework/http.dart';
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:io/ansi.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
class User extends Model {
|
||||
String? username, password;
|
||||
|
||||
User({this.username, this.password});
|
||||
|
||||
static User parse(Map<String, dynamic> map) {
|
||||
return User(
|
||||
username: map['username'],
|
||||
password: map['password'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'username': username,
|
||||
'password': password,
|
||||
'created_at': createdAt?.toIso8601String(),
|
||||
'updated_at': updatedAt?.toIso8601String()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Backend for callback test cases
|
||||
*/
|
||||
void main() async {
|
||||
hierarchicalLoggingEnabled = true;
|
||||
|
||||
Protevus app = Protevus(reflector: MirrorsReflector());
|
||||
ProtevusHttp angelHttp = ProtevusHttp(app);
|
||||
app.use('/users', MapService());
|
||||
|
||||
var oldErrorHandler = app.errorHandler;
|
||||
app.errorHandler = (e, req, res) {
|
||||
app.logger.severe(e.message, e, e.stackTrace ?? StackTrace.current);
|
||||
return oldErrorHandler(e, req, res);
|
||||
};
|
||||
|
||||
app.logger = Logger('protevus_auth')
|
||||
..level = Level.FINEST
|
||||
..onRecord.listen((rec) {
|
||||
print(rec);
|
||||
|
||||
if (rec.error != null) {
|
||||
print(yellow.wrap(rec.error.toString()));
|
||||
}
|
||||
|
||||
if (rec.stackTrace != null) {
|
||||
print(yellow.wrap(rec.stackTrace.toString()));
|
||||
}
|
||||
});
|
||||
|
||||
await app
|
||||
.findService('users')
|
||||
?.create({'username': 'jdoe1', 'password': 'password'});
|
||||
|
||||
var auth = ProtevusAuth<User>(
|
||||
serializer: (u) => u.id ?? '',
|
||||
deserializer: (id) async =>
|
||||
await app.findService('users')?.read(id) as User);
|
||||
//auth.serializer = (u) => u.id;
|
||||
//auth.deserializer =
|
||||
// (id) async => await app.findService('users')!.read(id) as User;
|
||||
|
||||
await app.configure(auth.configureServer);
|
||||
|
||||
auth.strategies['local'] = LocalAuthStrategy((username, password) async {
|
||||
var users = await app
|
||||
.findService('users')
|
||||
?.index()
|
||||
.then((it) => it.map<User>((m) => User.parse(m)).toList());
|
||||
|
||||
var result = users?.firstWhereOrNull(
|
||||
(user) => user.username == username && user.password == password);
|
||||
|
||||
return Future.value(result);
|
||||
}, allowBasic: true);
|
||||
|
||||
app.post(
|
||||
'/login',
|
||||
auth.authenticate('local',
|
||||
ProtevusAuthOptions(callback: (req, res, token) {
|
||||
res
|
||||
..write('Hello!')
|
||||
..close();
|
||||
})));
|
||||
|
||||
app.get('/', (req, res) => res.write("Hello"));
|
||||
|
||||
app.chain([
|
||||
(req, res) {
|
||||
if (!req.container!.has<User>()) {
|
||||
req.container!.registerSingleton<User>(
|
||||
User(username: req.params['name']?.toString()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
]).post(
|
||||
'/existing/:name',
|
||||
auth.authenticate('local'),
|
||||
);
|
||||
|
||||
await angelHttp.startServer('127.0.0.1', 3000);
|
||||
}
|
69
packages/auth/example/example2.dart
Normal file
69
packages/auth/example/example2.dart
Normal file
|
@ -0,0 +1,69 @@
|
|||
import 'dart:async';
|
||||
import 'package:protevus_auth/protevus_auth.dart';
|
||||
import 'package:protevus_container/mirrors.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'package:protevus_framework/http.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
final Map<String, String> sampleUser = {'hello': 'world'};
|
||||
|
||||
final ProtevusAuth<Map<String, String>> auth =
|
||||
ProtevusAuth<Map<String, String>>(
|
||||
serializer: (user) async => '1337',
|
||||
deserializer: (id) async => sampleUser);
|
||||
//var headers = <String, String>{'accept': 'application/json'};
|
||||
var localOpts = ProtevusAuthOptions<Map<String, String>>(
|
||||
failureRedirect: '/failure', successRedirect: '/success');
|
||||
var localOpts2 =
|
||||
ProtevusAuthOptions<Map<String, String>>(canRespondWithJson: false);
|
||||
|
||||
Future<Map<String, String>> verifier(String? username, String? password) async {
|
||||
if (username == 'username' && password == 'password') {
|
||||
return sampleUser;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Future wireAuth(Protevus app) async {
|
||||
//auth.strategies['local'] = LocalAuthStrategy(verifier);
|
||||
auth.strategies['local'] =
|
||||
LocalAuthStrategy(verifier, forceBasic: true, realm: 'test');
|
||||
await app.configure(auth.configureServer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Backend for local test cases
|
||||
*/
|
||||
void main() async {
|
||||
Protevus app = Protevus(reflector: MirrorsReflector());
|
||||
ProtevusHttp angelHttp = ProtevusHttp(app, useZone: false);
|
||||
await app.configure(wireAuth);
|
||||
|
||||
app.get('/hello', (req, res) {
|
||||
// => 'Woo auth'
|
||||
return 'Woo auth';
|
||||
}, middleware: [auth.authenticate('local', localOpts2)]);
|
||||
|
||||
app.post('/login', (req, res) => 'This should not be shown',
|
||||
middleware: [auth.authenticate('local', localOpts)]);
|
||||
|
||||
app.get('/success', (req, res) => 'yep', middleware: [
|
||||
requireAuthentication<Map<String, String>>(),
|
||||
]);
|
||||
|
||||
app.get('/failure', (req, res) => 'nope');
|
||||
|
||||
app.logger = Logger('local_test')
|
||||
..onRecord.listen((rec) {
|
||||
print(
|
||||
'${rec.time}: ${rec.level.name}: ${rec.loggerName}: ${rec.message}');
|
||||
|
||||
if (rec.error != null) {
|
||||
print(rec.error);
|
||||
print(rec.stackTrace);
|
||||
}
|
||||
});
|
||||
|
||||
await angelHttp.startServer('127.0.0.1', 3000);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
/// Stand-alone JWT library.
|
||||
library angel_auth.auth_token;
|
||||
library protevus_auth.auth_token;
|
||||
|
||||
export 'src/auth_token.dart';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
library angel_auth;
|
||||
library protevus_auth;
|
||||
|
||||
export 'src/middleware/require_auth.dart';
|
||||
export 'src/strategies/strategies.dart';
|
|
@ -1,7 +1,8 @@
|
|||
import 'dart:collection';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
/// Calls [BASE64URL], but also works for strings with lengths
|
||||
/// that are *not* multiples of 4.
|
||||
|
@ -25,88 +26,100 @@ String decodeBase64(String str) {
|
|||
}
|
||||
|
||||
class AuthToken {
|
||||
final SplayTreeMap<String, String> _header =
|
||||
SplayTreeMap.from({"alg": "HS256", "typ": "JWT"});
|
||||
static final _log = Logger('AuthToken');
|
||||
|
||||
String ipAddress;
|
||||
DateTime issuedAt;
|
||||
final SplayTreeMap<String, String> _header =
|
||||
SplayTreeMap.from({'alg': 'HS256', 'typ': 'JWT'});
|
||||
|
||||
String? ipAddress;
|
||||
num lifeSpan;
|
||||
var userId;
|
||||
String userId;
|
||||
late DateTime issuedAt;
|
||||
Map<String, dynamic> payload = {};
|
||||
|
||||
AuthToken(
|
||||
{this.ipAddress,
|
||||
this.lifeSpan = -1,
|
||||
this.userId,
|
||||
DateTime issuedAt,
|
||||
Map payload = const {}}) {
|
||||
required this.userId,
|
||||
DateTime? issuedAt,
|
||||
Map<String, dynamic>? payload}) {
|
||||
this.issuedAt = issuedAt ?? DateTime.now();
|
||||
this.payload.addAll(
|
||||
payload?.keys?.fold({}, (out, k) => out..[k.toString()] = payload[k]) ??
|
||||
if (payload != null) {
|
||||
this.payload.addAll(payload.keys
|
||||
.fold({}, ((out, k) => out?..[k.toString()] = payload[k])) ??
|
||||
{});
|
||||
}
|
||||
}
|
||||
|
||||
factory AuthToken.fromJson(String jsons) =>
|
||||
AuthToken.fromMap(json.decode(jsons) as Map);
|
||||
AuthToken.fromMap(json.decode(jsons) as Map<String, dynamic>);
|
||||
|
||||
factory AuthToken.fromMap(Map data) {
|
||||
factory AuthToken.fromMap(Map<String, dynamic> data) {
|
||||
return AuthToken(
|
||||
ipAddress: data["aud"].toString(),
|
||||
lifeSpan: data["exp"] as num,
|
||||
issuedAt: DateTime.parse(data["iat"].toString()),
|
||||
userId: data["sub"],
|
||||
payload: data["pld"] as Map ?? {});
|
||||
ipAddress: data['aud'].toString(),
|
||||
lifeSpan: data['exp'] as num,
|
||||
issuedAt: DateTime.parse(data['iat'].toString()),
|
||||
userId: data['sub'],
|
||||
payload: data['pld']);
|
||||
}
|
||||
|
||||
factory AuthToken.parse(String jwt) {
|
||||
var split = jwt.split(".");
|
||||
var split = jwt.split('.');
|
||||
|
||||
if (split.length != 3)
|
||||
throw AngelHttpException.notAuthenticated(message: "Invalid JWT.");
|
||||
if (split.length != 3) {
|
||||
_log.warning('Invalid JWT');
|
||||
throw ProtevusHttpException.notAuthenticated(message: 'Invalid JWT.');
|
||||
}
|
||||
|
||||
var payloadString = decodeBase64(split[1]);
|
||||
return AuthToken.fromMap(json.decode(payloadString) as Map);
|
||||
return AuthToken.fromMap(
|
||||
json.decode(payloadString) as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
factory AuthToken.validate(String jwt, Hmac hmac) {
|
||||
var split = jwt.split(".");
|
||||
var split = jwt.split('.');
|
||||
|
||||
if (split.length != 3)
|
||||
throw AngelHttpException.notAuthenticated(message: "Invalid JWT.");
|
||||
if (split.length != 3) {
|
||||
_log.warning('Invalid JWT');
|
||||
throw ProtevusHttpException.notAuthenticated(message: 'Invalid JWT.');
|
||||
}
|
||||
|
||||
// var headerString = decodeBase64(split[0]);
|
||||
var payloadString = decodeBase64(split[1]);
|
||||
var data = split[0] + "." + split[1];
|
||||
var data = '${split[0]}.${split[1]}';
|
||||
var signature = base64Url.encode(hmac.convert(data.codeUnits).bytes);
|
||||
|
||||
if (signature != split[2])
|
||||
throw AngelHttpException.notAuthenticated(
|
||||
message: "JWT payload does not match hashed version.");
|
||||
if (signature != split[2]) {
|
||||
_log.warning('JWT payload does not match hashed version');
|
||||
throw ProtevusHttpException.notAuthenticated(
|
||||
message: 'JWT payload does not match hashed version.');
|
||||
}
|
||||
|
||||
return AuthToken.fromMap(json.decode(payloadString) as Map);
|
||||
return AuthToken.fromMap(
|
||||
json.decode(payloadString) as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
String serialize(Hmac hmac) {
|
||||
var headerString = base64Url.encode(json.encode(_header).codeUnits);
|
||||
var payloadString = base64Url.encode(json.encode(toJson()).codeUnits);
|
||||
var data = headerString + "." + payloadString;
|
||||
var data = '$headerString.$payloadString';
|
||||
var signature = hmac.convert(data.codeUnits).bytes;
|
||||
return data + "." + base64Url.encode(signature);
|
||||
return '$data.${base64Url.encode(signature)}';
|
||||
}
|
||||
|
||||
Map toJson() {
|
||||
Map<String, dynamic> toJson() {
|
||||
return _splayify({
|
||||
"iss": "angel_auth",
|
||||
"aud": ipAddress,
|
||||
"exp": lifeSpan,
|
||||
"iat": issuedAt.toIso8601String(),
|
||||
"sub": userId,
|
||||
"pld": _splayify(payload)
|
||||
'iss': 'angel_auth',
|
||||
'aud': ipAddress,
|
||||
'exp': lifeSpan,
|
||||
'iat': issuedAt.toIso8601String(),
|
||||
'sub': userId,
|
||||
'pld': _splayify(payload)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
SplayTreeMap _splayify(Map map) {
|
||||
Map<String, dynamic> _splayify(Map<String, dynamic> map) {
|
||||
var data = {};
|
||||
map.forEach((k, v) {
|
||||
data[k] = _splay(v);
|
||||
|
@ -114,11 +127,12 @@ SplayTreeMap _splayify(Map map) {
|
|||
return SplayTreeMap.from(data);
|
||||
}
|
||||
|
||||
_splay(value) {
|
||||
dynamic _splay(dynamic value) {
|
||||
if (value is Iterable) {
|
||||
return value.map(_splay).toList();
|
||||
} else if (value is Map)
|
||||
return _splayify(value);
|
||||
else
|
||||
} else if (value is Map) {
|
||||
return _splayify(value as Map<String, dynamic>);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import 'package:charcode/ascii.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:quiver_hashcode/hashcode.dart';
|
||||
import 'package:quiver/core.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
/// A common class containing parsing and validation logic for third-party authentication configuration.
|
||||
class ExternalAuthOptions {
|
||||
static final _log = Logger('VirtualDirectory');
|
||||
|
||||
/// The user's identifier, otherwise known as an "application id".
|
||||
final String clientId;
|
||||
|
||||
|
@ -18,18 +20,12 @@ class ExternalAuthOptions {
|
|||
final Set<String> scopes;
|
||||
|
||||
ExternalAuthOptions._(
|
||||
this.clientId, this.clientSecret, this.redirectUri, this.scopes) {
|
||||
if (clientId == null) {
|
||||
throw ArgumentError.notNull('clientId');
|
||||
} else if (clientSecret == null) {
|
||||
throw ArgumentError.notNull('clientSecret');
|
||||
}
|
||||
}
|
||||
this.clientId, this.clientSecret, this.redirectUri, this.scopes);
|
||||
|
||||
factory ExternalAuthOptions(
|
||||
{@required String clientId,
|
||||
@required String clientSecret,
|
||||
@required redirectUri,
|
||||
{required String clientId,
|
||||
required String clientSecret,
|
||||
required redirectUri,
|
||||
Iterable<String> scopes = const []}) {
|
||||
if (redirectUri is String) {
|
||||
return ExternalAuthOptions._(
|
||||
|
@ -38,6 +34,7 @@ class ExternalAuthOptions {
|
|||
return ExternalAuthOptions._(
|
||||
clientId, clientSecret, redirectUri, scopes.toSet());
|
||||
} else {
|
||||
_log.severe('RedirectUri is not valid');
|
||||
throw ArgumentError.value(
|
||||
redirectUri, 'redirectUri', 'must be a String or Uri');
|
||||
}
|
||||
|
@ -49,10 +46,17 @@ class ExternalAuthOptions {
|
|||
/// * `client_id`
|
||||
/// * `client_secret`
|
||||
/// * `redirect_uri`
|
||||
factory ExternalAuthOptions.fromMap(Map map) {
|
||||
factory ExternalAuthOptions.fromMap(Map<String, dynamic> map) {
|
||||
var clientId = map['client_id'];
|
||||
var clientSecret = map['client_secret'];
|
||||
if (clientId == null || clientSecret == null) {
|
||||
_log.severe('clientId or clientSecret is null');
|
||||
throw ArgumentError('Invalid clientId and/or clientSecret');
|
||||
}
|
||||
|
||||
return ExternalAuthOptions(
|
||||
clientId: map['client_id'] as String,
|
||||
clientSecret: map['client_secret'] as String,
|
||||
clientId: clientId,
|
||||
clientSecret: clientSecret,
|
||||
redirectUri: map['redirect_uri'],
|
||||
scopes: map['scopes'] is Iterable
|
||||
? ((map['scopes'] as Iterable).map((x) => x.toString()))
|
||||
|
@ -73,15 +77,15 @@ class ExternalAuthOptions {
|
|||
|
||||
/// Creates a copy of this object, with the specified changes.
|
||||
ExternalAuthOptions copyWith(
|
||||
{String clientId,
|
||||
String clientSecret,
|
||||
{String? clientId,
|
||||
String? clientSecret,
|
||||
redirectUri,
|
||||
Iterable<String> scopes}) {
|
||||
Iterable<String> scopes = const []}) {
|
||||
return ExternalAuthOptions(
|
||||
clientId: clientId ?? this.clientId,
|
||||
clientSecret: clientSecret ?? this.clientSecret,
|
||||
redirectUri: redirectUri ?? this.redirectUri,
|
||||
scopes: (scopes ??= []).followedBy(this.scopes),
|
||||
scopes: (scopes).followedBy(this.scopes),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -111,8 +115,8 @@ class ExternalAuthOptions {
|
|||
/// If no [asteriskCount] is given, then the number of asterisks will equal the length of
|
||||
/// the actual [clientSecret].
|
||||
@override
|
||||
String toString({bool obscureSecret = true, int asteriskCount}) {
|
||||
String secret;
|
||||
String toString({bool obscureSecret = true, int? asteriskCount}) {
|
||||
String? secret;
|
||||
|
||||
if (!obscureSecret) {
|
||||
secret = clientSecret;
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
import 'dart:async';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
|
||||
/// Forces Basic authentication over the requested resource, with the given [realm] name, if no JWT is present.
|
||||
///
|
||||
/// [realm] defaults to `'angel_auth'`.
|
||||
RequestHandler forceBasicAuth<User>({String realm}) {
|
||||
/// [realm] defaults to `'protevus_auth'`.
|
||||
RequestHandler forceBasicAuth<User>({String? realm}) {
|
||||
return (RequestContext req, ResponseContext res) async {
|
||||
if (req.container.has<User>())
|
||||
if (req.container != null) {
|
||||
var reqContainer = req.container!;
|
||||
if (reqContainer.has<User>()) {
|
||||
return true;
|
||||
else if (req.container.has<Future<User>>()) {
|
||||
await req.container.makeAsync<User>();
|
||||
} else if (reqContainer.has<Future<User>>()) {
|
||||
await reqContainer.makeAsync<User>();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
res.headers['www-authenticate'] = 'Basic realm="${realm ?? 'angel_auth'}"';
|
||||
throw AngelHttpException.notAuthenticated();
|
||||
throw ProtevusHttpException.notAuthenticated();
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -22,20 +25,27 @@ RequestHandler forceBasicAuth<User>({String realm}) {
|
|||
RequestHandler requireAuthentication<User>() {
|
||||
return (RequestContext req, ResponseContext res,
|
||||
{bool throwError = true}) async {
|
||||
bool _reject(ResponseContext res) {
|
||||
bool reject(ResponseContext res) {
|
||||
if (throwError) {
|
||||
res.statusCode = 403;
|
||||
throw AngelHttpException.forbidden();
|
||||
} else
|
||||
throw ProtevusHttpException.forbidden();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.container.has<User>() || req.method == 'OPTIONS')
|
||||
if (req.container != null) {
|
||||
var reqContainer = req.container!;
|
||||
if (reqContainer.has<User>() || req.method == 'OPTIONS') {
|
||||
return true;
|
||||
else if (req.container.has<Future<User>>()) {
|
||||
await req.container.makeAsync<User>();
|
||||
} else if (reqContainer.has<Future<User>>()) {
|
||||
await reqContainer.makeAsync<User>();
|
||||
return true;
|
||||
} else
|
||||
return _reject(res);
|
||||
} else {
|
||||
return reject(res);
|
||||
}
|
||||
} else {
|
||||
return reject(res);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'auth_token.dart';
|
||||
|
||||
typedef FutureOr AngelAuthCallback(
|
||||
typedef ProtevusAuthCallback = FutureOr Function(
|
||||
RequestContext req, ResponseContext res, String token);
|
||||
|
||||
typedef FutureOr AngelAuthTokenCallback<User>(
|
||||
typedef ProtevusAuthTokenCallback<User> = FutureOr Function(
|
||||
RequestContext req, ResponseContext res, AuthToken token, User user);
|
||||
|
||||
class AngelAuthOptions<User> {
|
||||
AngelAuthCallback callback;
|
||||
AngelAuthTokenCallback<User> tokenCallback;
|
||||
String successRedirect;
|
||||
String failureRedirect;
|
||||
class ProtevusAuthOptions<User> {
|
||||
ProtevusAuthCallback? callback;
|
||||
ProtevusAuthTokenCallback<User>? tokenCallback;
|
||||
String? successRedirect;
|
||||
String? failureRedirect;
|
||||
|
||||
/// If `false` (default: `true`), then successful authentication will return `true` and allow the
|
||||
/// execution of subsequent handlers, just like any other middleware.
|
||||
|
@ -21,10 +21,10 @@ class AngelAuthOptions<User> {
|
|||
/// Works well with `Basic` authentication.
|
||||
bool canRespondWithJson;
|
||||
|
||||
AngelAuthOptions(
|
||||
ProtevusAuthOptions(
|
||||
{this.callback,
|
||||
this.tokenCallback,
|
||||
this.canRespondWithJson = true,
|
||||
this.successRedirect,
|
||||
String this.failureRedirect});
|
||||
this.failureRedirect});
|
||||
}
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math' as Math;
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'dart:math';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'auth_token.dart';
|
||||
import 'options.dart';
|
||||
import 'strategy.dart';
|
||||
|
||||
/// Handles authentication within an Angel application.
|
||||
class AngelAuth<User> {
|
||||
Hmac _hs256;
|
||||
int _jwtLifeSpan;
|
||||
/// Handles authentication within an Protevus application.
|
||||
class ProtevusAuth<User> {
|
||||
final _log = Logger('ProtevusAuth');
|
||||
|
||||
late Hmac _hs256;
|
||||
late int _jwtLifeSpan;
|
||||
final StreamController<User> _onLogin = StreamController<User>(),
|
||||
_onLogout = StreamController<User>();
|
||||
Math.Random _random = Math.Random.secure();
|
||||
final RegExp _rgxBearer = RegExp(r"^Bearer");
|
||||
final Random _random = Random.secure();
|
||||
final RegExp _rgxBearer = RegExp(r'^Bearer');
|
||||
|
||||
/// If `true` (default), then JWT's will be stored and retrieved from a `token` cookie.
|
||||
final bool allowCookie;
|
||||
|
@ -29,7 +33,7 @@ class AngelAuth<User> {
|
|||
/// A domain to restrict emitted cookies to.
|
||||
///
|
||||
/// Only applies if [allowCookie] is `true`.
|
||||
final String cookieDomain;
|
||||
final String? cookieDomain;
|
||||
|
||||
/// A path to restrict emitted cookies to.
|
||||
///
|
||||
|
@ -48,10 +52,10 @@ class AngelAuth<User> {
|
|||
Map<String, AuthStrategy<User>> strategies = {};
|
||||
|
||||
/// Serializes a user into a unique identifier associated only with one identity.
|
||||
FutureOr Function(User) serializer;
|
||||
FutureOr<String> Function(User) serializer;
|
||||
|
||||
/// Deserializes a unique identifier into its associated identity. In most cases, this is a user object or model instance.
|
||||
FutureOr<User> Function(Object) deserializer;
|
||||
FutureOr<User> Function(String) deserializer;
|
||||
|
||||
/// Fires the result of [deserializer] whenever a user signs in to the application.
|
||||
Stream<User> get onLogin => _onLogin.stream;
|
||||
|
@ -65,71 +69,89 @@ class AngelAuth<User> {
|
|||
String _randomString(
|
||||
{int length = 32,
|
||||
String validChars =
|
||||
"ABCDEFHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"}) {
|
||||
'ABCDEFHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_'}) {
|
||||
var chars = <int>[];
|
||||
while (chars.length < length) chars.add(_random.nextInt(validChars.length));
|
||||
while (chars.length < length) {
|
||||
chars.add(_random.nextInt(validChars.length));
|
||||
}
|
||||
return String.fromCharCodes(chars);
|
||||
}
|
||||
|
||||
/// `jwtLifeSpan` - should be in *milliseconds*.
|
||||
AngelAuth(
|
||||
{String jwtKey,
|
||||
this.serializer,
|
||||
this.deserializer,
|
||||
num jwtLifeSpan,
|
||||
ProtevusAuth(
|
||||
{String? jwtKey,
|
||||
required this.serializer,
|
||||
required this.deserializer,
|
||||
num jwtLifeSpan = -1,
|
||||
this.allowCookie = true,
|
||||
this.allowTokenInQuery = true,
|
||||
this.enforceIp = true,
|
||||
this.cookieDomain,
|
||||
this.cookiePath = '/',
|
||||
this.secureCookies = true,
|
||||
this.reviveTokenEndpoint = "/auth/token"})
|
||||
this.reviveTokenEndpoint = '/auth/token'})
|
||||
: super() {
|
||||
_hs256 = Hmac(sha256, (jwtKey ?? _randomString()).codeUnits);
|
||||
_jwtLifeSpan = jwtLifeSpan?.toInt() ?? -1;
|
||||
_jwtLifeSpan = jwtLifeSpan.toInt();
|
||||
}
|
||||
|
||||
/// Configures an Angel server to decode and validate JSON Web tokens on demand,
|
||||
/// Configures an Protevus server to decode and validate JSON Web tokens on demand,
|
||||
/// whenever an instance of [User] is injected.
|
||||
Future<void> configureServer(Angel app) async {
|
||||
if (serializer == null)
|
||||
Future<void> configureServer(Protevus app) async {
|
||||
/*
|
||||
if (serializer == null) {
|
||||
throw StateError(
|
||||
'An `AngelAuth` plug-in was called without its `serializer` being set. All authentication will fail.');
|
||||
if (deserializer == null)
|
||||
'An `ProtevusAuth` plug-in was called without its `serializer` being set. All authentication will fail.');
|
||||
}
|
||||
if (deserializer == null) {
|
||||
throw StateError(
|
||||
'An `AngelAuth` plug-in was called without its `deserializer` being set. All authentication will fail.');
|
||||
'An `ProtevusAuth` plug-in was called without its `deserializer` being set. All authentication will fail.');
|
||||
}
|
||||
|
||||
app.container.registerSingleton(this);
|
||||
if (runtimeType != AngelAuth)
|
||||
app.container.registerSingleton(this, as: AngelAuth);
|
||||
if (app.container == null) {
|
||||
_log.severe('Protevus container is null');
|
||||
throw StateError(
|
||||
'Protevus.container is null. All authentication will fail.');
|
||||
}
|
||||
*/
|
||||
var appContainer = app.container;
|
||||
|
||||
if (!app.container.has<_AuthResult<User>>()) {
|
||||
app.container
|
||||
appContainer.registerSingleton(this);
|
||||
if (runtimeType != ProtevusAuth) {
|
||||
appContainer.registerSingleton(this, as: ProtevusAuth);
|
||||
}
|
||||
|
||||
if (!appContainer.has<_AuthResult<User>>()) {
|
||||
appContainer
|
||||
.registerLazySingleton<Future<_AuthResult<User>>>((container) async {
|
||||
var req = container.make<RequestContext>();
|
||||
var res = container.make<ResponseContext>();
|
||||
//if (req == null || res == null) {
|
||||
// _log.warning('RequestContext or responseContext is null');
|
||||
// throw ProtevusHttpException.forbidden();
|
||||
//}
|
||||
|
||||
var result = await _decodeJwt(req, res);
|
||||
if (result != null) {
|
||||
return result;
|
||||
} else {
|
||||
throw AngelHttpException.forbidden();
|
||||
_log.warning('JWT is null');
|
||||
throw ProtevusHttpException.forbidden();
|
||||
}
|
||||
});
|
||||
|
||||
app.container.registerLazySingleton<Future<User>>((container) async {
|
||||
appContainer.registerLazySingleton<Future<User>>((container) async {
|
||||
var result = await container.makeAsync<_AuthResult<User>>();
|
||||
return result.user;
|
||||
});
|
||||
|
||||
app.container.registerLazySingleton<Future<AuthToken>>((container) async {
|
||||
appContainer.registerLazySingleton<Future<AuthToken>>((container) async {
|
||||
var result = await container.makeAsync<_AuthResult<User>>();
|
||||
return result.token;
|
||||
});
|
||||
}
|
||||
|
||||
if (reviveTokenEndpoint != null) {
|
||||
app.post(reviveTokenEndpoint, reviveJwt);
|
||||
}
|
||||
app.post(reviveTokenEndpoint, _reviveJwt);
|
||||
|
||||
app.shutdownHooks.add((_) {
|
||||
_onLogin.close();
|
||||
|
@ -138,15 +160,22 @@ class AngelAuth<User> {
|
|||
|
||||
void _apply(
|
||||
RequestContext req, ResponseContext res, AuthToken token, User user) {
|
||||
if (!req.container.has<User>()) {
|
||||
req.container.registerSingleton<User>(user);
|
||||
if (req.container == null) {
|
||||
_log.severe('RequestContext.container is null');
|
||||
throw StateError(
|
||||
'RequestContext.container is not set. All authentication will fail.');
|
||||
}
|
||||
|
||||
if (!req.container.has<AuthToken>()) {
|
||||
req.container.registerSingleton<AuthToken>(token);
|
||||
var reqContainer = req.container!;
|
||||
if (!reqContainer.has<User>()) {
|
||||
reqContainer.registerSingleton<User>(user);
|
||||
}
|
||||
|
||||
if (allowCookie == true) {
|
||||
if (!reqContainer.has<AuthToken>()) {
|
||||
reqContainer.registerSingleton<AuthToken>(token);
|
||||
}
|
||||
|
||||
if (allowCookie) {
|
||||
_addProtectedCookie(res, 'token', token.serialize(_hs256));
|
||||
}
|
||||
}
|
||||
|
@ -155,10 +184,10 @@ class AngelAuth<User> {
|
|||
///
|
||||
/// Now that `package:angel_framework` supports asynchronous injections, this middleware
|
||||
/// is no longer directly necessary. Instead, call [configureServer]. You can then use
|
||||
/// `makeAsync<User>`, or Angel's injections directly:
|
||||
/// `makeAsync<User>`, or Protevus's injections directly:
|
||||
///
|
||||
/// ```dart
|
||||
/// var auth = AngelAuth<User>(...);
|
||||
/// var auth = ProtevusAuth<User>(...);
|
||||
/// await app.configure(auth.configureServer);
|
||||
///
|
||||
/// app.get('/hmm', (User user) async {
|
||||
|
@ -172,35 +201,41 @@ class AngelAuth<User> {
|
|||
/// String getUsername(User user) => user.name
|
||||
/// }
|
||||
/// ```
|
||||
/*
|
||||
@deprecated
|
||||
Future decodeJwt(RequestContext req, ResponseContext res) async {
|
||||
if (req.method == "POST" && req.path == reviveTokenEndpoint) {
|
||||
return await reviveJwt(req, res);
|
||||
if (req.method == 'POST' && req.path == reviveTokenEndpoint) {
|
||||
return await _reviveJwt(req, res);
|
||||
} else {
|
||||
await _decodeJwt(req, res);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Future<_AuthResult<User>> _decodeJwt(
|
||||
Future<_AuthResult<User>?> _decodeJwt(
|
||||
RequestContext req, ResponseContext res) async {
|
||||
String jwt = getJwt(req);
|
||||
var jwt = getJwt(req);
|
||||
|
||||
if (jwt != null) {
|
||||
var token = AuthToken.validate(jwt, _hs256);
|
||||
|
||||
if (enforceIp) {
|
||||
if (req.ip != null && req.ip != token.ipAddress)
|
||||
throw AngelHttpException.forbidden(
|
||||
message: "JWT cannot be accessed from this IP address.");
|
||||
if (req.ip != token.ipAddress) {
|
||||
_log.warning('JWT cannot be accessed from this IP address');
|
||||
throw ProtevusHttpException.forbidden(
|
||||
message: 'JWT cannot be accessed from this IP address.');
|
||||
}
|
||||
}
|
||||
|
||||
if (token.lifeSpan > -1) {
|
||||
var expiry =
|
||||
token.issuedAt.add(Duration(milliseconds: token.lifeSpan.toInt()));
|
||||
|
||||
if (!expiry.isAfter(DateTime.now()))
|
||||
throw AngelHttpException.forbidden(message: "Expired JWT.");
|
||||
if (!expiry.isAfter(DateTime.now())) {
|
||||
_log.warning('Expired JWT');
|
||||
throw ProtevusHttpException.forbidden(message: 'Expired JWT.');
|
||||
}
|
||||
}
|
||||
|
||||
var user = await deserializer(token.userId);
|
||||
|
@ -212,19 +247,25 @@ class AngelAuth<User> {
|
|||
}
|
||||
|
||||
/// Retrieves a JWT from a request, if any was sent at all.
|
||||
String getJwt(RequestContext req) {
|
||||
if (req.headers.value("Authorization") != null) {
|
||||
final authHeader = req.headers.value("Authorization");
|
||||
|
||||
String? getJwt(RequestContext req) {
|
||||
if (req.headers?.value('Authorization') != null) {
|
||||
final authHeader = req.headers?.value('Authorization');
|
||||
if (authHeader != null) {
|
||||
// Allow Basic auth to fall through
|
||||
if (_rgxBearer.hasMatch(authHeader))
|
||||
return authHeader.replaceAll(_rgxBearer, "").trim();
|
||||
if (_rgxBearer.hasMatch(authHeader)) {
|
||||
return authHeader.replaceAll(_rgxBearer, '').trim();
|
||||
}
|
||||
}
|
||||
|
||||
_log.info('RequestContext.headers is null');
|
||||
} else if (allowCookie &&
|
||||
req.cookies.any((cookie) => cookie.name == "token")) {
|
||||
return req.cookies.firstWhere((cookie) => cookie.name == "token").value;
|
||||
} else if (allowTokenInQuery &&
|
||||
req.uri.queryParameters['token'] is String) {
|
||||
return req.uri.queryParameters['token']?.toString();
|
||||
req.cookies.any((cookie) => cookie.name == 'token')) {
|
||||
return req.cookies.firstWhere((cookie) => cookie.name == 'token').value;
|
||||
} else if (allowTokenInQuery) {
|
||||
//&& req.uri?.queryParameters['token'] is String) {
|
||||
if (req.uri != null) {
|
||||
return req.uri?.queryParameters['token']?.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -243,10 +284,10 @@ class AngelAuth<User> {
|
|||
cookie.secure = true;
|
||||
}
|
||||
|
||||
if (_jwtLifeSpan > 0) {
|
||||
cookie.maxAge ??= _jwtLifeSpan < 0 ? -1 : _jwtLifeSpan ~/ 1000;
|
||||
cookie.expires ??=
|
||||
DateTime.now().add(Duration(milliseconds: _jwtLifeSpan));
|
||||
var lifeSpan = _jwtLifeSpan;
|
||||
if (lifeSpan > 0) {
|
||||
cookie.maxAge ??= lifeSpan < 0 ? -1 : lifeSpan ~/ 1000;
|
||||
cookie.expires ??= DateTime.now().add(Duration(milliseconds: lifeSpan));
|
||||
}
|
||||
|
||||
cookie.domain ??= cookieDomain;
|
||||
|
@ -255,7 +296,7 @@ class AngelAuth<User> {
|
|||
}
|
||||
|
||||
/// Attempts to revive an expired (or still alive) JWT.
|
||||
Future<Map<String, dynamic>> reviveJwt(
|
||||
Future<Map<String, dynamic>> _reviveJwt(
|
||||
RequestContext req, ResponseContext res) async {
|
||||
try {
|
||||
var jwt = getJwt(req);
|
||||
|
@ -264,14 +305,18 @@ class AngelAuth<User> {
|
|||
var body = await req.parseBody().then((_) => req.bodyAsMap);
|
||||
jwt = body['token']?.toString();
|
||||
}
|
||||
|
||||
if (jwt == null) {
|
||||
throw AngelHttpException.forbidden(message: "No JWT provided");
|
||||
_log.warning('No JWT provided');
|
||||
throw ProtevusHttpException.forbidden(message: 'No JWT provided');
|
||||
} else {
|
||||
var token = AuthToken.validate(jwt, _hs256);
|
||||
if (enforceIp) {
|
||||
if (req.ip != token.ipAddress)
|
||||
throw AngelHttpException.forbidden(
|
||||
message: "JWT cannot be accessed from this IP address.");
|
||||
if (req.ip != token.ipAddress) {
|
||||
_log.warning('WT cannot be accessed from this IP address');
|
||||
throw ProtevusHttpException.forbidden(
|
||||
message: 'JWT cannot be accessed from this IP address.');
|
||||
}
|
||||
}
|
||||
|
||||
if (token.lifeSpan > -1) {
|
||||
|
@ -294,8 +339,11 @@ class AngelAuth<User> {
|
|||
return {'data': data, 'token': token.serialize(_hs256)};
|
||||
}
|
||||
} catch (e) {
|
||||
if (e is AngelHttpException) rethrow;
|
||||
throw AngelHttpException.badRequest(message: "Malformed JWT");
|
||||
if (e is ProtevusHttpException) {
|
||||
rethrow;
|
||||
}
|
||||
_log.warning('Malformed JWT');
|
||||
throw ProtevusHttpException.badRequest(message: 'Malformed JWT');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -307,14 +355,17 @@ class AngelAuth<User> {
|
|||
/// or a `401 Not Authenticated` is thrown, if it is the last one.
|
||||
///
|
||||
/// Any other result is considered an authenticated user, and terminates the loop.
|
||||
RequestHandler authenticate(type, [AngelAuthOptions<User> options]) {
|
||||
RequestHandler authenticate(type, [ProtevusAuthOptions<User>? opt]) {
|
||||
return (RequestContext req, ResponseContext res) async {
|
||||
List<String> names = [];
|
||||
var authOption = opt ?? ProtevusAuthOptions<User>();
|
||||
|
||||
var names = <String>[];
|
||||
|
||||
var arr = type is Iterable
|
||||
? type.map((x) => x.toString()).toList()
|
||||
: [type.toString()];
|
||||
|
||||
for (String t in arr) {
|
||||
for (var t in arr) {
|
||||
var n = t
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
|
@ -323,19 +374,30 @@ class AngelAuth<User> {
|
|||
names.addAll(n);
|
||||
}
|
||||
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
var name = names[i];
|
||||
|
||||
var strategy = strategies[name] ??=
|
||||
var strategy = strategies[name];
|
||||
if (strategy == null) {
|
||||
_log.severe('No strategy "$name" found.');
|
||||
throw ArgumentError('No strategy "$name" found.');
|
||||
}
|
||||
|
||||
var hasExisting = req.container.has<User>();
|
||||
var reqContainer = req.container;
|
||||
|
||||
if (reqContainer == null) {
|
||||
print('req.container is null');
|
||||
}
|
||||
|
||||
var hasExisting = reqContainer?.has<User>() ?? false;
|
||||
var result = hasExisting
|
||||
? req.container.make<User>()
|
||||
: await strategy.authenticate(req, res, options);
|
||||
if (result == true)
|
||||
? reqContainer?.make<User>()
|
||||
: await strategy.authenticate(req, res, authOption);
|
||||
|
||||
if (result == true) {
|
||||
return result;
|
||||
else if (result != false && result != null) {
|
||||
} else if (result != null && result != false) {
|
||||
//} else if (result != null && result is Map && result.isNotEmpty) {
|
||||
var userId = await serializer(result);
|
||||
|
||||
// Create JWT
|
||||
|
@ -343,12 +405,13 @@ class AngelAuth<User> {
|
|||
userId: userId, lifeSpan: _jwtLifeSpan, ipAddress: req.ip);
|
||||
var jwt = token.serialize(_hs256);
|
||||
|
||||
if (options?.tokenCallback != null) {
|
||||
if (!req.container.has<User>()) {
|
||||
req.container.registerSingleton<User>(result);
|
||||
if (authOption.tokenCallback != null) {
|
||||
var hasUser = reqContainer?.has<User>() ?? false;
|
||||
if (!hasUser) {
|
||||
reqContainer?.registerSingleton<User>(result);
|
||||
}
|
||||
|
||||
var r = await options.tokenCallback(req, res, token, result);
|
||||
var r = await authOption.tokenCallback!(req, res, token, result);
|
||||
if (r != null) return r;
|
||||
jwt = token.serialize(_hs256);
|
||||
}
|
||||
|
@ -359,20 +422,21 @@ class AngelAuth<User> {
|
|||
_addProtectedCookie(res, 'token', jwt);
|
||||
}
|
||||
|
||||
if (options?.callback != null) {
|
||||
return await options.callback(req, res, jwt);
|
||||
// Options is not null
|
||||
if (authOption.callback != null) {
|
||||
return await authOption.callback!(req, res, jwt);
|
||||
}
|
||||
|
||||
if (options?.successRedirect?.isNotEmpty == true) {
|
||||
await res.redirect(options.successRedirect);
|
||||
if (authOption.successRedirect?.isNotEmpty == true) {
|
||||
await res.redirect(authOption.successRedirect);
|
||||
return false;
|
||||
} else if (options?.canRespondWithJson != false &&
|
||||
} else if (authOption.canRespondWithJson &&
|
||||
req.accepts('application/json')) {
|
||||
var user = hasExisting
|
||||
? result
|
||||
: await deserializer(await serializer(result));
|
||||
_onLogin.add(user);
|
||||
return {"data": user, "token": jwt};
|
||||
return {'data': user, 'token': jwt};
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -381,13 +445,15 @@ class AngelAuth<User> {
|
|||
// Check if not redirect
|
||||
if (res.statusCode == 301 ||
|
||||
res.statusCode == 302 ||
|
||||
res.headers.containsKey('location'))
|
||||
res.headers.containsKey('location')) {
|
||||
return false;
|
||||
else if (options?.failureRedirect != null) {
|
||||
await res.redirect(options.failureRedirect);
|
||||
} else if (authOption.failureRedirect != null) {
|
||||
await res.redirect(authOption.failureRedirect);
|
||||
return false;
|
||||
} else
|
||||
throw AngelHttpException.notAuthenticated();
|
||||
} else {
|
||||
_log.warning('Not authenticated');
|
||||
throw ProtevusHttpException.notAuthenticated();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -405,7 +471,8 @@ class AngelAuth<User> {
|
|||
}
|
||||
|
||||
/// Log a user in on-demand.
|
||||
Future loginById(userId, RequestContext req, ResponseContext res) async {
|
||||
Future loginById(
|
||||
String userId, RequestContext req, ResponseContext res) async {
|
||||
var user = await deserializer(userId);
|
||||
var token =
|
||||
AuthToken(userId: userId, lifeSpan: _jwtLifeSpan, ipAddress: req.ip);
|
||||
|
@ -418,21 +485,23 @@ class AngelAuth<User> {
|
|||
}
|
||||
|
||||
/// Log an authenticated user out.
|
||||
RequestHandler logout([AngelAuthOptions<User> options]) {
|
||||
RequestHandler logout([ProtevusAuthOptions<User>? options]) {
|
||||
return (RequestContext req, ResponseContext res) async {
|
||||
if (req.container.has<User>()) {
|
||||
var user = req.container.make<User>();
|
||||
if (req.container?.has<User>() == true) {
|
||||
var user = req.container?.make<User>();
|
||||
if (user != null) {
|
||||
_onLogout.add(user);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowCookie == true) {
|
||||
res.cookies.removeWhere((cookie) => cookie.name == "token");
|
||||
res.cookies.removeWhere((cookie) => cookie.name == 'token');
|
||||
_addProtectedCookie(res, 'token', '""');
|
||||
}
|
||||
|
||||
if (options != null &&
|
||||
options.successRedirect != null &&
|
||||
options.successRedirect.isNotEmpty) {
|
||||
options.successRedirect!.isNotEmpty) {
|
||||
await res.redirect(options.successRedirect);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import 'dart:convert';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'options.dart';
|
||||
|
||||
/// Displays a default callback page to confirm authentication via popups.
|
||||
AngelAuthCallback confirmPopupAuthentication({String eventName = 'token'}) {
|
||||
ProtevusAuthCallback confirmPopupAuthentication({String eventName = 'token'}) {
|
||||
return (req, ResponseContext res, String jwt) {
|
||||
var evt = json.encode(eventName);
|
||||
var detail = json.encode({'detail': jwt});
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import '../options.dart';
|
||||
import '../strategy.dart';
|
||||
|
||||
bool _validateString(String str) => str != null && str.isNotEmpty;
|
||||
|
||||
/// Determines the validity of an incoming username and password.
|
||||
typedef FutureOr<User> LocalAuthVerifier<User>(
|
||||
String username, String password);
|
||||
// typedef FutureOr<User> LocalAuthVerifier<User>(String? username, String? password);
|
||||
typedef LocalAuthVerifier<User> = FutureOr<User?> Function(
|
||||
String? username, String? password);
|
||||
|
||||
class LocalAuthStrategy<User> extends AuthStrategy<User> {
|
||||
RegExp _rgxBasic = RegExp(r'^Basic (.+)$', caseSensitive: false);
|
||||
RegExp _rgxUsrPass = RegExp(r'^([^:]+):(.+)$');
|
||||
final _log = Logger('LocalAuthStrategy');
|
||||
|
||||
final RegExp _rgxBasic = RegExp(r'^Basic (.+)$', caseSensitive: false);
|
||||
final RegExp _rgxUsrPass = RegExp(r'^([^:]+):(.+)$');
|
||||
|
||||
LocalAuthVerifier<User> verifier;
|
||||
String usernameField;
|
||||
|
@ -23,35 +25,40 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
|
|||
String realm;
|
||||
|
||||
LocalAuthStrategy(this.verifier,
|
||||
{String this.usernameField = 'username',
|
||||
String this.passwordField = 'password',
|
||||
String this.invalidMessage =
|
||||
'Please provide a valid username and password.',
|
||||
bool this.allowBasic = true,
|
||||
bool this.forceBasic = false,
|
||||
String this.realm = 'Authentication is required.'});
|
||||
{this.usernameField = 'username',
|
||||
this.passwordField = 'password',
|
||||
this.invalidMessage = 'Please provide a valid username and password.',
|
||||
this.allowBasic = false,
|
||||
this.forceBasic = false,
|
||||
this.realm = 'Authentication is required.'}) {
|
||||
_log.info('Using LocalAuthStrategy');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<User> authenticate(RequestContext req, ResponseContext res,
|
||||
[AngelAuthOptions options_]) async {
|
||||
AngelAuthOptions options = options_ ?? AngelAuthOptions();
|
||||
User verificationResult;
|
||||
Future<User?> authenticate(RequestContext req, ResponseContext res,
|
||||
[ProtevusAuthOptions? options]) async {
|
||||
var localOptions = options ?? ProtevusAuthOptions();
|
||||
User? verificationResult;
|
||||
|
||||
if (allowBasic) {
|
||||
String authHeader = req.headers.value('authorization') ?? "";
|
||||
var authHeader = req.headers?.value('authorization') ?? '';
|
||||
|
||||
if (_rgxBasic.hasMatch(authHeader)) {
|
||||
String base64AuthString = _rgxBasic.firstMatch(authHeader).group(1);
|
||||
String authString =
|
||||
String.fromCharCodes(base64.decode(base64AuthString));
|
||||
var base64AuthString = _rgxBasic.firstMatch(authHeader)?.group(1);
|
||||
if (base64AuthString == null) {
|
||||
return null;
|
||||
}
|
||||
var authString = String.fromCharCodes(base64.decode(base64AuthString));
|
||||
if (_rgxUsrPass.hasMatch(authString)) {
|
||||
Match usrPassMatch = _rgxUsrPass.firstMatch(authString);
|
||||
Match usrPassMatch = _rgxUsrPass.firstMatch(authString)!;
|
||||
verificationResult =
|
||||
await verifier(usrPassMatch.group(1), usrPassMatch.group(2));
|
||||
} else
|
||||
throw AngelHttpException.badRequest(errors: [invalidMessage]);
|
||||
} else {
|
||||
_log.warning('Bad request: $invalidMessage');
|
||||
throw ProtevusHttpException.badRequest(errors: [invalidMessage]);
|
||||
}
|
||||
|
||||
if (verificationResult == false || verificationResult == null) {
|
||||
if (verificationResult == null) {
|
||||
res
|
||||
..statusCode = 401
|
||||
..headers['www-authenticate'] = 'Basic realm="$realm"';
|
||||
|
@ -59,11 +66,10 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
|
|||
return null;
|
||||
}
|
||||
|
||||
return verificationResult;
|
||||
//Allow non-null to pass through
|
||||
//return verificationResult;
|
||||
}
|
||||
}
|
||||
|
||||
if (verificationResult == null) {
|
||||
} else {
|
||||
var body = await req
|
||||
.parseBody()
|
||||
.then((_) => req.bodyAsMap)
|
||||
|
@ -75,23 +81,58 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
|
|||
}
|
||||
}
|
||||
|
||||
if (verificationResult == false || verificationResult == null) {
|
||||
if (options.failureRedirect != null &&
|
||||
options.failureRedirect.isNotEmpty) {
|
||||
await res.redirect(options.failureRedirect, code: 401);
|
||||
// User authentication succeeded can return Map(one element), User(non null) or true
|
||||
if (verificationResult != null && verificationResult != false) {
|
||||
if (verificationResult is Map && verificationResult.isNotEmpty) {
|
||||
return verificationResult;
|
||||
} else if (verificationResult is! Map) {
|
||||
return verificationResult;
|
||||
}
|
||||
}
|
||||
|
||||
// Force basic if set
|
||||
if (forceBasic) {
|
||||
//res.headers['www-authenticate'] = 'Basic realm="$realm"';
|
||||
res
|
||||
..statusCode = 401
|
||||
..headers['www-authenticate'] = 'Basic realm="$realm"';
|
||||
await res.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
// Redirect failed authentication
|
||||
if (localOptions.failureRedirect != null &&
|
||||
localOptions.failureRedirect!.isNotEmpty) {
|
||||
await res.redirect(localOptions.failureRedirect, code: 401);
|
||||
return null;
|
||||
}
|
||||
|
||||
_log.info('Not authenticated');
|
||||
throw ProtevusHttpException.notAuthenticated();
|
||||
|
||||
/*
|
||||
if (verificationResult is Map && verificationResult.isEmpty) {
|
||||
if (localOptions.failureRedirect != null &&
|
||||
localOptions.failureRedirect!.isNotEmpty) {
|
||||
await res.redirect(localOptions.failureRedirect, code: 401);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (forceBasic) {
|
||||
res.headers['www-authenticate'] = 'Basic realm="$realm"';
|
||||
throw AngelHttpException.notAuthenticated();
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
} else if (verificationResult != null && verificationResult != false) {
|
||||
} else if (verificationResult != false ||
|
||||
(verificationResult is Map && verificationResult.isNotEmpty)) {
|
||||
return verificationResult;
|
||||
} else {
|
||||
throw AngelHttpException.notAuthenticated();
|
||||
_log.info('Not authenticated');
|
||||
throw ProtevusHttpException.notAuthenticated();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
bool _validateString(String? str) => str != null && str.isNotEmpty;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import 'dart:async';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'options.dart';
|
||||
|
||||
/// A function that handles login and signup for an Angel application.
|
||||
/// A function that handles login and signup for an Protevus application.
|
||||
abstract class AuthStrategy<User> {
|
||||
/// Authenticates or rejects an incoming user.
|
||||
FutureOr<User> authenticate(RequestContext req, ResponseContext res,
|
||||
[AngelAuthOptions<User> options]);
|
||||
FutureOr<User?> authenticate(RequestContext req, ResponseContext res,
|
||||
[ProtevusAuthOptions<User>? options]);
|
||||
}
|
||||
|
|
|
@ -1,26 +1,35 @@
|
|||
name: angel_auth
|
||||
description: A complete authentication plugin for Angel. Includes support for stateless JWT tokens, Basic Auth, and more.
|
||||
version: 3.0.0
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/angel_auth
|
||||
publish_to: none
|
||||
name: protevus_auth
|
||||
description: A complete authentication plugin for Protevus. Includes support for stateless JWT tokens, Basic Auth, and more.
|
||||
version: 8.2.0
|
||||
homepage: https://protevus-framework.web.app/
|
||||
repository: https://github.com/dart-backend/protevus/tree/master/packages/auth
|
||||
environment:
|
||||
sdk: ">=2.10.0 <3.0.0"
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
dependencies:
|
||||
angel_framework:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x
|
||||
path: packages/framework
|
||||
charcode: ^1.0.0
|
||||
collection: ^1.0.0
|
||||
protevus_framework: ^8.0.0
|
||||
charcode: ^1.3.0
|
||||
collection: ^1.17.0
|
||||
crypto: ^3.0.0
|
||||
http_parser: ^4.0.0
|
||||
meta: ^1.0.0
|
||||
quiver_hashcode: ^2.0.0
|
||||
meta: ^1.9.0
|
||||
quiver: ^3.2.0
|
||||
logging: ^1.2.0
|
||||
dev_dependencies:
|
||||
http: ^0.13.0
|
||||
protevus_container: ^8.0.0
|
||||
http: ^1.0.0
|
||||
io: ^1.0.0
|
||||
logging: ^1.0.0
|
||||
pedantic: ^1.0.0
|
||||
test: ^1.15.7
|
||||
test: ^1.24.0
|
||||
lints: ^4.0.0
|
||||
# dependency_overrides:
|
||||
# protevus_container:
|
||||
# path: ../container/angel_container
|
||||
# protevus_framework:
|
||||
# path: ../framework
|
||||
# protevus_http_exception:
|
||||
# path: ../http_exception
|
||||
# protevus_model:
|
||||
# path: ../model
|
||||
# protevus_route:
|
||||
# path: ../route
|
||||
# protevus_mock_request:
|
||||
# path: ../mock_request
|
|
@ -1,12 +1,12 @@
|
|||
import "package:angel_auth/src/auth_token.dart";
|
||||
import "package:crypto/crypto.dart";
|
||||
import "package:test/test.dart";
|
||||
import 'package:protevus_auth/src/auth_token.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
main() async {
|
||||
final Hmac hmac = Hmac(sha256, "angel_auth".codeUnits);
|
||||
void main() async {
|
||||
final hmac = Hmac(sha256, 'angel_auth'.codeUnits);
|
||||
|
||||
test("sample serialization", () {
|
||||
var token = AuthToken(ipAddress: "localhost", userId: "thosakwe");
|
||||
test('sample serialization', () {
|
||||
var token = AuthToken(ipAddress: 'localhost', userId: 'thosakwe');
|
||||
var jwt = token.serialize(hmac);
|
||||
print(jwt);
|
||||
|
||||
|
@ -17,11 +17,11 @@ main() async {
|
|||
});
|
||||
|
||||
test('custom payload', () {
|
||||
var token = AuthToken(ipAddress: "localhost", userId: "thosakwe", payload: {
|
||||
"foo": "bar",
|
||||
"baz": {
|
||||
"one": 1,
|
||||
"franken": ["stein"]
|
||||
var token = AuthToken(ipAddress: 'localhost', userId: 'thosakwe', payload: {
|
||||
'foo': 'bar',
|
||||
'baz': {
|
||||
'one': 1,
|
||||
'franken': ['stein']
|
||||
}
|
||||
});
|
||||
var jwt = token.serialize(hmac);
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
import 'dart:io';
|
||||
import 'package:angel_auth/angel_auth.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_framework/http.dart';
|
||||
import 'package:protevus_auth/protevus_auth.dart';
|
||||
import 'package:protevus_container/mirrors.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'package:protevus_framework/http.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:io/ansi.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
class User extends Model {
|
||||
String username, password;
|
||||
String? username, password;
|
||||
|
||||
User({this.username, this.password});
|
||||
|
||||
static User parse(Map map) {
|
||||
static User parse(Map<String, dynamic> map) {
|
||||
return User(
|
||||
username: map['username'] as String,
|
||||
password: map['password'] as String,
|
||||
username: map['username'] as String?,
|
||||
password: map['password'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -31,18 +34,19 @@ class User extends Model {
|
|||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
Angel app;
|
||||
AngelHttp angelHttp;
|
||||
AngelAuth<User> auth;
|
||||
http.Client client;
|
||||
void main() {
|
||||
late Protevus app;
|
||||
late ProtevusHttp angelHttp;
|
||||
ProtevusAuth<User> auth;
|
||||
http.Client? client;
|
||||
HttpServer server;
|
||||
String url;
|
||||
String? url;
|
||||
String? encodedAuth;
|
||||
|
||||
setUp(() async {
|
||||
hierarchicalLoggingEnabled = true;
|
||||
app = Angel();
|
||||
angelHttp = AngelHttp(app);
|
||||
app = Protevus(reflector: MirrorsReflector());
|
||||
angelHttp = ProtevusHttp(app);
|
||||
app.use('/users', MapService());
|
||||
|
||||
var oldErrorHandler = app.errorHandler;
|
||||
|
@ -51,7 +55,7 @@ main() {
|
|||
return oldErrorHandler(e, req, res);
|
||||
};
|
||||
|
||||
app.logger = Logger('angel_auth')
|
||||
app.logger = Logger('protevus_auth')
|
||||
..level = Level.FINEST
|
||||
..onRecord.listen((rec) {
|
||||
print(rec);
|
||||
|
@ -67,29 +71,34 @@ main() {
|
|||
|
||||
await app
|
||||
.findService('users')
|
||||
.create({'username': 'jdoe1', 'password': 'password'});
|
||||
?.create({'username': 'jdoe1', 'password': 'password'});
|
||||
|
||||
auth = AngelAuth<User>();
|
||||
auth.serializer = (u) => u.id;
|
||||
auth.deserializer =
|
||||
(id) async => await app.findService('users').read(id) as User;
|
||||
auth = ProtevusAuth<User>(
|
||||
serializer: (u) => u.id ?? '',
|
||||
deserializer: (id) async =>
|
||||
await app.findService('users')?.read(id) as User);
|
||||
//auth.serializer = (u) => u.id;
|
||||
//auth.deserializer =
|
||||
// (id) async => await app.findService('users')!.read(id) as User;
|
||||
|
||||
await app.configure(auth.configureServer);
|
||||
|
||||
auth.strategies['local'] = LocalAuthStrategy((username, password) async {
|
||||
var users = await app
|
||||
.findService('users')
|
||||
.index()
|
||||
.then((it) => it.map<User>((m) => User.parse(m as Map)).toList());
|
||||
return users.firstWhere(
|
||||
(user) => user.username == username && user.password == password,
|
||||
orElse: () => null);
|
||||
});
|
||||
?.index()
|
||||
.then((it) => it.map<User>((m) => User.parse(m)).toList());
|
||||
|
||||
var result = users?.firstWhereOrNull(
|
||||
(user) => user.username == username && user.password == password);
|
||||
|
||||
return Future.value(result);
|
||||
}, allowBasic: true);
|
||||
|
||||
app.post(
|
||||
'/login',
|
||||
auth.authenticate('local',
|
||||
AngelAuthOptions(callback: (req, res, token) {
|
||||
ProtevusAuthOptions(callback: (req, res, token) {
|
||||
res
|
||||
..write('Hello!')
|
||||
..close();
|
||||
|
@ -97,8 +106,8 @@ main() {
|
|||
|
||||
app.chain([
|
||||
(req, res) {
|
||||
if (!req.container.has<User>()) {
|
||||
req.container.registerSingleton<User>(
|
||||
if (!req.container!.has<User>()) {
|
||||
req.container!.registerSingleton<User>(
|
||||
User(username: req.params['name']?.toString()));
|
||||
}
|
||||
return true;
|
||||
|
@ -108,22 +117,24 @@ main() {
|
|||
auth.authenticate('local'),
|
||||
);
|
||||
|
||||
encodedAuth = base64.encode(utf8.encode('jdoe1:password'));
|
||||
|
||||
client = http.Client();
|
||||
server = await angelHttp.startServer();
|
||||
url = 'http://${server.address.address}:${server.port}';
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
client.close();
|
||||
client!.close();
|
||||
await angelHttp.close();
|
||||
app = null;
|
||||
//app = null;
|
||||
client = null;
|
||||
url = null;
|
||||
});
|
||||
|
||||
test('login', () async {
|
||||
final response = await client.post(Uri.parse('$url/login'),
|
||||
body: {'username': 'jdoe1', 'password': 'password'});
|
||||
final response = await client!.post(Uri.parse('$url/login'),
|
||||
headers: {'Authorization': 'Basic $encodedAuth'});
|
||||
print('Response: ${response.body}');
|
||||
expect(response.body, equals('Hello!'));
|
||||
},
|
||||
|
@ -132,10 +143,11 @@ main() {
|
|||
: null);
|
||||
|
||||
test('preserve existing user', () async {
|
||||
final response = await client.post(Uri.parse('$url/existing/foo'),
|
||||
final response = await client!.post(Uri.parse('$url/existing/foo'),
|
||||
body: {'username': 'jdoe1', 'password': 'password'},
|
||||
headers: {'accept': 'application/json'});
|
||||
print('Response: ${response.body}');
|
||||
print(response.headers);
|
||||
expect(json.decode(response.body)['data']['username'], equals('foo'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:angel_auth/angel_auth.dart';
|
||||
import 'package:protevus_auth/protevus_auth.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
|
@ -70,6 +70,7 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
/* Deprecated as clientId and clientSecret cannot be null
|
||||
test('ensures id not null', () {
|
||||
expect(
|
||||
() => ExternalAuthOptions(
|
||||
|
@ -89,6 +90,7 @@ void main() {
|
|||
throwsArgumentError,
|
||||
);
|
||||
});
|
||||
*/
|
||||
});
|
||||
|
||||
group('fromMap()', () {
|
||||
|
|
|
@ -1,48 +1,58 @@
|
|||
import 'dart:async';
|
||||
import 'package:angel_auth/angel_auth.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_framework/http.dart';
|
||||
import 'package:protevus_auth/protevus_auth.dart';
|
||||
import 'package:protevus_container/mirrors.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'package:protevus_framework/http.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
final AngelAuth<Map<String, String>> auth = AngelAuth<Map<String, String>>();
|
||||
var headers = <String, String>{'accept': 'application/json'};
|
||||
var localOpts = AngelAuthOptions<Map<String, String>>(
|
||||
final ProtevusAuth<Map<String, String>> auth =
|
||||
ProtevusAuth<Map<String, String>>(
|
||||
serializer: (user) async => '1337',
|
||||
deserializer: (id) async => sampleUser);
|
||||
//var headers = <String, String>{'accept': 'application/json'};
|
||||
var localOpts = ProtevusAuthOptions<Map<String, String>>(
|
||||
failureRedirect: '/failure', successRedirect: '/success');
|
||||
var localOpts2 =
|
||||
ProtevusAuthOptions<Map<String, String>>(canRespondWithJson: false);
|
||||
|
||||
Map<String, String> sampleUser = {'hello': 'world'};
|
||||
|
||||
Future<Map<String, String>> verifier(String username, String password) async {
|
||||
Future<Map<String, String>> verifier(String? username, String? password) async {
|
||||
if (username == 'username' && password == 'password') {
|
||||
return sampleUser;
|
||||
} else {
|
||||
return null;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Future wireAuth(Angel app) async {
|
||||
auth.serializer = (user) async => 1337;
|
||||
auth.deserializer = (id) async => sampleUser;
|
||||
Future wireAuth(Protevus app) async {
|
||||
//auth.serializer = (user) async => 1337;
|
||||
//auth.deserializer = (id) async => sampleUser;
|
||||
|
||||
auth.strategies['local'] = LocalAuthStrategy(verifier);
|
||||
auth.strategies['local'] = LocalAuthStrategy(verifier, allowBasic: true);
|
||||
await app.configure(auth.configureServer);
|
||||
}
|
||||
|
||||
void main() async {
|
||||
Angel app;
|
||||
AngelHttp angelHttp;
|
||||
http.Client client;
|
||||
String url;
|
||||
String basicAuthUrl;
|
||||
Protevus app;
|
||||
late ProtevusHttp angelHttp;
|
||||
late http.Client client;
|
||||
String? url;
|
||||
String? basicAuthUrl;
|
||||
|
||||
setUp(() async {
|
||||
client = http.Client();
|
||||
app = Angel();
|
||||
angelHttp = AngelHttp(app, useZone: false);
|
||||
app = Protevus(reflector: MirrorsReflector());
|
||||
angelHttp = ProtevusHttp(app, useZone: false);
|
||||
await app.configure(wireAuth);
|
||||
app.get('/hello', (req, res) => 'Woo auth',
|
||||
middleware: [auth.authenticate('local')]);
|
||||
|
||||
app.get('/hello', (req, res) {
|
||||
// => 'Woo auth'
|
||||
return 'Woo auth';
|
||||
}, middleware: [auth.authenticate('local', localOpts2)]);
|
||||
app.post('/login', (req, res) => 'This should not be shown',
|
||||
middleware: [auth.authenticate('local', localOpts)]);
|
||||
app.get('/success', (req, res) => 'yep', middleware: [
|
||||
|
@ -50,8 +60,11 @@ void main() async {
|
|||
]);
|
||||
app.get('/failure', (req, res) => 'nope');
|
||||
|
||||
app.logger = Logger('angel_auth')
|
||||
app.logger = Logger('local_test')
|
||||
..onRecord.listen((rec) {
|
||||
print(
|
||||
'${rec.time}: ${rec.level.name}: ${rec.loggerName}: ${rec.message}');
|
||||
|
||||
if (rec.error != null) {
|
||||
print(rec.error);
|
||||
print(rec.stackTrace);
|
||||
|
@ -66,7 +79,7 @@ void main() async {
|
|||
|
||||
tearDown(() async {
|
||||
await angelHttp.close();
|
||||
client = null;
|
||||
//client = null;
|
||||
url = null;
|
||||
basicAuthUrl = null;
|
||||
});
|
||||
|
@ -79,28 +92,43 @@ void main() async {
|
|||
});
|
||||
|
||||
test('successRedirect', () async {
|
||||
var postData = {'username': 'username', 'password': 'password'};
|
||||
//var postData = {'username': 'username', 'password': 'password'};
|
||||
var encodedAuth = base64.encode(utf8.encode('username:password'));
|
||||
|
||||
var response = await client.post(Uri.parse('$url/login'),
|
||||
body: json.encode(postData),
|
||||
headers: {'content-type': 'application/json'});
|
||||
headers: {'Authorization': 'Basic $encodedAuth'});
|
||||
expect(response.statusCode, equals(302));
|
||||
expect(response.headers['location'], equals('/success'));
|
||||
});
|
||||
|
||||
test('failureRedirect', () async {
|
||||
var postData = {'username': 'password', 'password': 'username'};
|
||||
//var postData = {'username': 'password', 'password': 'username'};
|
||||
var encodedAuth = base64.encode(utf8.encode('password:username'));
|
||||
|
||||
var response = await client.post(Uri.parse('$url/login'),
|
||||
body: json.encode(postData),
|
||||
headers: {'content-type': 'application/json'});
|
||||
print('Login response: ${response.body}');
|
||||
headers: {'Authorization': 'Basic $encodedAuth'});
|
||||
print('Status Code: ${response.statusCode}');
|
||||
print(response.headers);
|
||||
print(response.body);
|
||||
expect(response.headers['location'], equals('/failure'));
|
||||
expect(response.statusCode, equals(401));
|
||||
});
|
||||
|
||||
test('allow basic', () async {
|
||||
test('basic auth without authorization', () async {
|
||||
var response = await client.get(Uri.parse('$url/hello'));
|
||||
print('Status Code: ${response.statusCode}');
|
||||
print(response.headers);
|
||||
print(response.body);
|
||||
expect(response.statusCode, equals(401));
|
||||
});
|
||||
|
||||
//test('allow basic', () async {
|
||||
test('basic auth with authorization', () async {
|
||||
var authString = base64.encode('username:password'.runes.toList());
|
||||
var response = await client.get(Uri.parse('$url/hello'),
|
||||
headers: {'authorization': 'Basic $authString'});
|
||||
print(response.statusCode);
|
||||
print(response.body);
|
||||
expect(response.body, equals('"Woo auth"'));
|
||||
});
|
||||
|
||||
|
@ -117,8 +145,9 @@ void main() async {
|
|||
'accept': 'application/json',
|
||||
'content-type': 'application/json'
|
||||
});
|
||||
print(response.headers);
|
||||
print('Header = ${response.headers}');
|
||||
print('Body <${response.body}>');
|
||||
expect(response.headers['www-authenticate'], equals('Basic realm="test"'));
|
||||
var head = response.headers['www-authenticate'];
|
||||
expect(head, equals('Basic realm="test"'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:angel_auth/angel_auth.dart';
|
||||
import 'package:protevus_auth/protevus_auth.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
const Duration threeDays = const Duration(days: 3);
|
||||
const Duration threeDays = Duration(days: 3);
|
||||
|
||||
void main() {
|
||||
Cookie defaultCookie;
|
||||
var auth = AngelAuth(
|
||||
late Cookie defaultCookie;
|
||||
var auth = ProtevusAuth(
|
||||
secureCookies: true,
|
||||
cookieDomain: 'SECURE',
|
||||
jwtLifeSpan: threeDays.inMilliseconds,
|
||||
);
|
||||
serializer: (u) => u,
|
||||
deserializer: (u) => u);
|
||||
|
||||
setUp(() => defaultCookie = Cookie('a', 'b'));
|
||||
|
||||
|
@ -21,7 +22,7 @@ void main() {
|
|||
|
||||
test('sets expires', () {
|
||||
var now = DateTime.now();
|
||||
var expiry = auth.protectCookie(defaultCookie).expires;
|
||||
var expiry = auth.protectCookie(defaultCookie).expires!;
|
||||
var diff = expiry.difference(now);
|
||||
expect(diff.inSeconds, threeDays.inSeconds);
|
||||
});
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
</component>
|
||||
</module>
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/auth_oauth2.iml" filepath="$PROJECT_DIR$/.idea/auth_oauth2.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -1,7 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Github Auth Server" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/example/main.dart" />
|
||||
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
12
packages/auth_oauth2/AUTHORS.md
Normal file
12
packages/auth_oauth2/AUTHORS.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
Primary Authors
|
||||
===============
|
||||
|
||||
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
|
||||
|
||||
Thomas is the current maintainer of the code base. He has refactored and migrated the
|
||||
code base to support NNBD.
|
||||
|
||||
* __[Tobe O](thosakwe@gmail.com)__
|
||||
|
||||
Tobe has written much of the original code prior to NNBD migration. He has moved on and
|
||||
is no longer involved with the project.
|
|
@ -1,13 +1,74 @@
|
|||
# 2.1.0
|
||||
* Angel 2 + Dart 2 update
|
||||
# Change Log
|
||||
|
||||
## 8.2.0
|
||||
|
||||
* Require Dart >= 3.3
|
||||
* Updated `lints` to 4.0.0
|
||||
|
||||
## 8.1.1
|
||||
|
||||
* Updated repository link
|
||||
|
||||
## 8.1.0
|
||||
|
||||
* Updated `lints` to 3.0.0
|
||||
* Fixed linter warnings
|
||||
|
||||
## 8.0.0
|
||||
|
||||
* Require Dart >= 3.0
|
||||
* Issue: `oauth2` does not support `http` 1.0.0
|
||||
|
||||
## 7.0.1
|
||||
|
||||
* Updated example
|
||||
|
||||
## 7.0.0
|
||||
|
||||
* Require Dart >= 2.17
|
||||
|
||||
## 6.0.0
|
||||
|
||||
* Require Dart >= 2.16
|
||||
|
||||
## 5.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 4.1.0
|
||||
|
||||
* Updated linter to `package:lints`
|
||||
|
||||
## 4.0.2
|
||||
|
||||
* Updated example
|
||||
|
||||
## 4.0.1
|
||||
|
||||
* Updated README
|
||||
|
||||
## 4.0.0
|
||||
|
||||
* Migrated to support Dart >= 2.12 NNBD
|
||||
|
||||
## 3.0.0
|
||||
|
||||
* Migrated to work with Dart >= 2.12.x Non NNBD
|
||||
|
||||
## 2.1.0
|
||||
|
||||
* Protevus 2 + Dart 2 update
|
||||
* Support for handling errors + rejections.
|
||||
* Use `ExternalAuthOptions`.
|
||||
|
||||
# 2.0.0+1
|
||||
## 2.0.0+1
|
||||
|
||||
* Meta update to improve Pub score.
|
||||
|
||||
# 2.0.0
|
||||
* Angel 2 + Dart 2 updates.
|
||||
## 2.0.0
|
||||
|
||||
# 1.0.2
|
||||
Added `getParameters` to `AngelOAuth2Options`.
|
||||
* Protevus 2 + Dart 2 updates.
|
||||
|
||||
## 1.0.2
|
||||
|
||||
Added `getParameters` to `ProtevusOAuth2Options`.
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
MIT License
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2017 Tobe O
|
||||
Copyright (c) 2021, dukefirehawk.com
|
||||
All rights reserved.
|
||||
|
||||
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:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
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.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
# auth_oauth2
|
||||
# Protevus OAuth2 Handler
|
||||
|
||||
[![Pub](https://img.shields.io/pub/v/angel_auth_oauth2.svg)](https://pub.dartlang.org/packages/angel_auth_oauth2)
|
||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/protevus_auth_oauth2?include_prereleases)
|
||||
![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)(<https://dart.dev/null-safety>)
|
||||
[![Discord](https://img.shields.io/discord/1060322353214660698)](https://discord.gg/3X6bxTUdCM)
|
||||
[![License](https://img.shields.io/github/license/dart-backend/protevus)](https://github.com/dart-backend/protevus/tree/master/packages/auth_oauth2/LICENSE)
|
||||
|
||||
`package:angel_auth` strategy for OAuth2 login, i.e. Facebook or Github.
|
||||
Protevus library for authenticating users with remote identity providers via OAuth2, i.e. Facebook, Google, Azure AD, etc.
|
||||
|
||||
## Usage
|
||||
|
||||
# Usage
|
||||
First, create an options object:
|
||||
|
||||
```dart
|
||||
configureServer(Angel app) async {
|
||||
configureServer(Protevus app) async {
|
||||
// Load from a Map, i.e. app config:
|
||||
var opts = ExternalAuthOptions.fromMap(app.configuration['auth0'] as Map);
|
||||
|
||||
|
@ -20,11 +24,10 @@ configureServer(Angel app) async {
|
|||
}
|
||||
```
|
||||
|
||||
After getting authenticated against the remote server, we need to be able to identify
|
||||
users within our own application.
|
||||
After getting authenticated against the remote server, we need to be able to identify users within our own application.
|
||||
|
||||
```dart
|
||||
typedef FutureOr<User> OAuth2Verifier(oauth2.Client, RequestContext, ResponseContext);
|
||||
typedef OAuth2Verifier = FutureOr<User> Function(oauth2.Client, RequestContext, ResponseContext);
|
||||
|
||||
/// You might use a pure function to create a verifier that queries a
|
||||
/// given service.
|
||||
|
@ -49,12 +52,10 @@ OAuth2Verifier oauth2verifier(Service<User> userService) {
|
|||
}
|
||||
```
|
||||
|
||||
Now, initialize an `OAuth2Strategy`, using the options and verifier.
|
||||
You'll also need to provide a name for this instance of the strategy.
|
||||
Consider using the name of the remote authentication provider (ex. `facebook`).
|
||||
Now, initialize an `OAuth2Strategy`, using the options and verifier. You'll also need to provide a name for this instance of the strategy. Consider using the name of the remote authentication provider (ex. `facebook`).
|
||||
|
||||
```dart
|
||||
configureServer(Angel app) {
|
||||
configureServer(Protevus app) {
|
||||
auth.strategies['github'] = OAuth2Strategy(
|
||||
options,
|
||||
authorizationEndpoint,
|
||||
|
@ -70,19 +71,17 @@ configureServer(Angel app) {
|
|||
}
|
||||
```
|
||||
|
||||
Lastly, connect it to an `AngelAuth` instance, and wire it up to an `Angel` server.
|
||||
Set up two routes:
|
||||
Lastly, connect it to an `ProtevusAuth` instance, and wire it up to an `Protevus` server. Set up two routes:
|
||||
|
||||
1. Redirect users to the external provider
|
||||
2. Acts as a callback and handles an access code
|
||||
|
||||
In the case of the callback route, you may want to display an HTML page that closes
|
||||
a popup window. In this case, use `confirmPopupAuthentication`, which is bundled with
|
||||
`package:angel_auth`, as a `callback` function:
|
||||
In the case of the callback route, you may want to display an HTML page that closes a popup window. In this case, use `confirmPopupAuthentication`, which is bundled with `package:protevus_auth`, as a `callback` function:
|
||||
|
||||
```dart
|
||||
configureServer(Angel app) async {
|
||||
configureServer(Protevus app) async {
|
||||
// ...
|
||||
var auth = AngelAuth<User>();
|
||||
var auth = ProtevusAuth<User>();
|
||||
auth.strategies['github'] = oauth2Strategy;
|
||||
|
||||
// Redirect
|
||||
|
@ -91,7 +90,7 @@ configureServer(Angel app) async {
|
|||
// Callback
|
||||
app.get('/auth/github/callback', auth.authenticate(
|
||||
'github',
|
||||
AngelAuthOptions(callback: confirmPopupAuthentication())
|
||||
ProtevusAuthOptions(callback: confirmPopupAuthentication())
|
||||
));
|
||||
|
||||
// Connect the plug-in!!!
|
||||
|
@ -100,22 +99,20 @@ configureServer(Angel app) async {
|
|||
```
|
||||
|
||||
## Custom Scope Delimiter
|
||||
This package should work out-of-the-box for most OAuth2 providers, such as Github or Dropbox.
|
||||
However, if your OAuth2 scopes are separated by a delimiter other than the default (`' '`),
|
||||
you can add it in the `OAuth2Strategy` constructor:
|
||||
|
||||
This package should work out-of-the-box for most OAuth2 providers, such as Github or Dropbox. However, if your OAuth2 scopes are separated by a delimiter other than the default (`' '`), you can add it in the `OAuth2Strategy` constructor:
|
||||
|
||||
```dart
|
||||
configureServer(Angel app) async {
|
||||
configureServer(Protevus app) async {
|
||||
OAuth2Strategy(..., delimiter: ' ');
|
||||
}
|
||||
```
|
||||
|
||||
## Handling non-JSON responses
|
||||
Many OAuth2 providers do not follow the specification, and do not return
|
||||
`application/json` responses.
|
||||
|
||||
You can add a `getParameters` callback to parse the contents of any arbitrary
|
||||
response:
|
||||
Many OAuth2 providers do not follow the specification, and do not return `application/json` responses.
|
||||
|
||||
You can add a `getParameters` callback to parse the contents of any arbitrary response:
|
||||
|
||||
```dart
|
||||
OAuth2Strategy(
|
||||
|
|
|
@ -1,4 +1 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
||||
include: package:lints/recommended.yaml
|
|
@ -1,8 +1,8 @@
|
|||
import 'dart:convert';
|
||||
import 'package:angel_auth/angel_auth.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_framework/http.dart';
|
||||
import 'package:angel_auth_oauth2/angel_auth_oauth2.dart';
|
||||
import 'package:protevus_auth/protevus_auth.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'package:protevus_framework/http.dart';
|
||||
import 'package:protevus_auth_oauth2/protevus_auth_oauth2.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
|
@ -33,9 +33,9 @@ Map<String, dynamic> parseParamsFromGithub(MediaType contentType, String body) {
|
|||
|
||||
void main() async {
|
||||
// Create the server instance.
|
||||
var app = Angel();
|
||||
var http = AngelHttp(app);
|
||||
app.logger = Logger('angel')
|
||||
var app = Protevus();
|
||||
var http = ProtevusHttp(app);
|
||||
app.logger = Logger('protevus')
|
||||
..onRecord.listen((rec) {
|
||||
print(rec);
|
||||
if (rec.error != null) print(rec.error);
|
||||
|
@ -47,11 +47,12 @@ void main() async {
|
|||
var mappedUserService = userService.map(User.parse, User.serialize);
|
||||
|
||||
// Set up the authenticator plugin.
|
||||
var auth =
|
||||
AngelAuth<User>(jwtKey: 'oauth2 example secret', allowCookie: false);
|
||||
auth.serializer = (user) async => user.id;
|
||||
auth.deserializer = (id) => mappedUserService.read(id.toString());
|
||||
app.fallback(auth.decodeJwt);
|
||||
var auth = ProtevusAuth<User>(
|
||||
serializer: (user) async => user.id ?? '',
|
||||
deserializer: (id) => mappedUserService.read(id),
|
||||
jwtKey: 'oauth2 example secret',
|
||||
allowCookie: false);
|
||||
await app.configure(auth.configureServer);
|
||||
|
||||
/// Create an instance of the strategy class.
|
||||
auth.strategies['github'] = OAuth2Strategy(
|
||||
|
@ -63,7 +64,7 @@ void main() async {
|
|||
(client, req, res) async {
|
||||
var response = await client.get(Uri.parse('https://api.github.com/user'));
|
||||
var ghUser = json.decode(response.body);
|
||||
var id = ghUser['id'] as int;
|
||||
var id = ghUser['id'] as int?;
|
||||
|
||||
var matchingUsers = await mappedUserService.index({
|
||||
'query': {'github_id': id}
|
||||
|
@ -85,7 +86,7 @@ void main() async {
|
|||
},
|
||||
|
||||
// We have to pass this parser function when working with Github.
|
||||
getParameters: parseParamsFromGithub,
|
||||
//getParameters: parseParamsFromGithub,
|
||||
);
|
||||
|
||||
// Mount some routes
|
||||
|
@ -93,12 +94,12 @@ void main() async {
|
|||
app.get(
|
||||
'/auth/github/callback',
|
||||
auth.authenticate('github',
|
||||
AngelAuthOptions(callback: (req, res, jwt) async {
|
||||
ProtevusAuthOptions(callback: (req, res, jwt) async {
|
||||
// In real-life, you might include a pop-up callback script.
|
||||
//
|
||||
// Use `confirmPopupAuthentication`, which is bundled with
|
||||
// `package:angel_auth`.
|
||||
var user = req.container.make<User>();
|
||||
var user = req.container!.make<User>();
|
||||
res.write('Your user info: ${user.toJson()}\n\n');
|
||||
res.write('Your JWT: $jwt');
|
||||
await res.close();
|
||||
|
@ -112,15 +113,12 @@ void main() async {
|
|||
}
|
||||
|
||||
class User extends Model {
|
||||
@override
|
||||
String id;
|
||||
int? githubId;
|
||||
|
||||
int githubId;
|
||||
User({super.id, this.githubId});
|
||||
|
||||
User({this.id, this.githubId});
|
||||
|
||||
static User parse(Map map) =>
|
||||
User(id: map['id'] as String, githubId: map['github_id'] as int);
|
||||
static User parse(Map<String, dynamic> map) =>
|
||||
User(id: map['id'] as String?, githubId: map['github_id'] as int?);
|
||||
|
||||
static Map<String, dynamic> serialize(User user) => user.toJson();
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
library angel_auth_oauth2;
|
||||
library protevus_auth_oauth2;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:angel_auth/angel_auth.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:protevus_auth/protevus_auth.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:oauth2/oauth2.dart' as oauth2;
|
||||
|
||||
/// An Angel [AuthStrategy] that signs users in via a third-party service that speaks OAuth 2.0.
|
||||
/// An Protevus [AuthStrategy] that signs users in via a third-party service that speaks OAuth 2.0.
|
||||
class OAuth2Strategy<User> implements AuthStrategy<User> {
|
||||
/// A callback that uses the third-party service to authenticate a [User].
|
||||
///
|
||||
|
@ -28,34 +28,34 @@ class OAuth2Strategy<User> implements AuthStrategy<User> {
|
|||
final Uri tokenEndpoint;
|
||||
|
||||
/// An optional callback used to parse the response from a server who does not follow the OAuth 2.0 spec.
|
||||
final Map<String, dynamic> Function(MediaType, String) getParameters;
|
||||
final Map<String, dynamic> Function(MediaType?, String)? getParameters;
|
||||
|
||||
/// An optional delimiter used to send requests to server who does not follow the OAuth 2.0 spec.
|
||||
final String delimiter;
|
||||
|
||||
Uri _redirect;
|
||||
Uri? _redirect;
|
||||
|
||||
OAuth2Strategy(this.options, this.authorizationEndpoint, this.tokenEndpoint,
|
||||
this.verifier, this.onError,
|
||||
{this.getParameters, this.delimiter = ' '});
|
||||
|
||||
oauth2.AuthorizationCodeGrant _createGrant() =>
|
||||
new oauth2.AuthorizationCodeGrant(options.clientId, authorizationEndpoint,
|
||||
tokenEndpoint,
|
||||
oauth2.AuthorizationCodeGrant _createGrant() => oauth2.AuthorizationCodeGrant(
|
||||
options.clientId, authorizationEndpoint, tokenEndpoint,
|
||||
secret: options.clientSecret,
|
||||
delimiter: delimiter,
|
||||
getParameters: getParameters);
|
||||
|
||||
@override
|
||||
FutureOr<User> authenticate(RequestContext req, ResponseContext res,
|
||||
[AngelAuthOptions<User> options]) async {
|
||||
FutureOr<User?> authenticate(RequestContext req, ResponseContext res,
|
||||
[ProtevusAuthOptions<User>? options]) async {
|
||||
if (options != null) {
|
||||
var result = await authenticateCallback(req, res, options);
|
||||
if (result is User)
|
||||
if (result is User) {
|
||||
return result;
|
||||
else
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (_redirect == null) {
|
||||
var grant = _createGrant();
|
||||
|
@ -65,20 +65,20 @@ class OAuth2Strategy<User> implements AuthStrategy<User> {
|
|||
);
|
||||
}
|
||||
|
||||
res.redirect(_redirect);
|
||||
await res.redirect(_redirect);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// The endpoint that is invoked by the third-party after successful authentication.
|
||||
Future<dynamic> authenticateCallback(RequestContext req, ResponseContext res,
|
||||
[AngelAuthOptions options]) async {
|
||||
[ProtevusAuthOptions? options]) async {
|
||||
var grant = _createGrant();
|
||||
grant.getAuthorizationUrl(this.options.redirectUri,
|
||||
scopes: this.options.scopes);
|
||||
|
||||
try {
|
||||
var client =
|
||||
await grant.handleAuthorizationResponse(req.uri.queryParameters);
|
||||
await grant.handleAuthorizationResponse(req.uri!.queryParameters);
|
||||
return await verifier(client, req, res);
|
||||
} on oauth2.AuthorizationException catch (e) {
|
||||
return await onError(e, req, res);
|
|
@ -1,24 +1,30 @@
|
|||
name: angel_auth_oauth2
|
||||
description: angel_auth strategy for OAuth2 login, i.e. Facebook, Github, etc.
|
||||
version: 3.0.0
|
||||
#author: Tobe O <thosakwe@gmail.com>
|
||||
publish_to: none
|
||||
name: protevus_auth_oauth2
|
||||
version: 8.2.0
|
||||
description: Protevus library for authenticating users with external identity providers via OAuth2.
|
||||
homepage: https://protevus-framework.web.app/
|
||||
repository: https://github.com/dart-backend/protevus/tree/master/packages/auth_oauth2
|
||||
environment:
|
||||
sdk: ">=2.10.0 <3.0.0"
|
||||
homepage: https://github.com/angel-dart/auth_oauth2.git
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
dependencies:
|
||||
angel_auth:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x
|
||||
path: packages/auth
|
||||
angel_framework:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x
|
||||
path: packages/framework
|
||||
protevus_auth: ^8.0.0
|
||||
protevus_framework: ^8.0.0
|
||||
http_parser: ^4.0.0
|
||||
oauth2: ^2.0.0
|
||||
dev_dependencies:
|
||||
logging: ^1.0.0
|
||||
pedantic: ^1.0.0
|
||||
logging: ^1.2.0
|
||||
lints: ^4.0.0
|
||||
# dependency_overrides:
|
||||
# protevus_container:
|
||||
# path: ../container/angel_container
|
||||
# protevus_framework:
|
||||
# path: ../framework
|
||||
# protevus_http_exception:
|
||||
# path: ../http_exception
|
||||
# protevus_model:
|
||||
# path: ../model
|
||||
# protevus_route:
|
||||
# path: ../route
|
||||
# protevus_mock_request:
|
||||
# path: ../mock_request
|
||||
# protevus_auth:
|
||||
# path: ../auth
|
||||
|
|
|
@ -1,5 +1,36 @@
|
|||
# 2.0.0
|
||||
* Angel 2 + Dart 2 suppport.
|
||||
# Change Log
|
||||
|
||||
## 8.0.0
|
||||
|
||||
* Require Dart >= 3.3
|
||||
* Updated `oauth1` to `belatuk_oauth1`
|
||||
* Updated `lints` to 4.0.0
|
||||
* Updated repository link
|
||||
* Fixed linter warnings
|
||||
|
||||
## 7.0.0
|
||||
|
||||
* Require Dart >= 2.17
|
||||
|
||||
## 6.0.0
|
||||
|
||||
* Require Dart >= 2.16
|
||||
|
||||
## 5.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 4.0.0
|
||||
|
||||
* Migrated to support Dart >= 2.12 NNBD
|
||||
|
||||
## 3.0.0
|
||||
|
||||
* Migrated to work with Dart >= 2.12 Non NNBD
|
||||
|
||||
## 2.0.0
|
||||
|
||||
* Protevus 2 + Dart 2 suppport.
|
||||
* Use `package:twitter` instead of `package:twit`.
|
||||
* Add `TwitterAuthorizationException`.
|
||||
* Add `onError` callback.
|
|
@ -1,21 +1,29 @@
|
|||
MIT License
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2016 The Angel Framework
|
||||
Copyright (c) 2023, dukefirehawk.com
|
||||
All rights reserved.
|
||||
|
||||
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:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
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.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
# auth_twitter
|
||||
angel_auth strategy for Twitter login.
|
||||
# Protevus Twitter OAuth1
|
||||
|
||||
See the [example](example/server.dart);
|
||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/protevus_auth_twitter?include_prereleases)
|
||||
![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](<https://dart.dev/null-safety>)
|
||||
[![Discord](https://img.shields.io/discord/1060322353214660698)](https://discord.gg/3X6bxTUdCM)
|
||||
[![License](https://img.shields.io/github/license/dart-backend/protevus)](https://github.com/dart-backend/protevus/tree/master/packages/auth_twitter/LICENSE)
|
||||
|
||||
**Not ready for release**
|
||||
Protevus authentication strategy using Twitter OAuth 1.0a.
|
||||
|
||||
See the [example](example/example.dart);
|
||||
|
|
|
@ -1,4 +1 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
||||
include: package:lints/recommended.yaml
|
|
@ -1,9 +1,9 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:angel_auth/angel_auth.dart';
|
||||
import 'package:angel_auth_twitter/angel_auth_twitter.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_framework/http.dart';
|
||||
import 'package:protevus_auth/protevus_auth.dart';
|
||||
import 'package:protevus_auth_twitter/protevus_auth_twitter.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'package:protevus_framework/http.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class _User {
|
||||
|
@ -14,10 +14,10 @@ class _User {
|
|||
Map<String, dynamic> toJson() => {'handle': handle};
|
||||
}
|
||||
|
||||
main() async {
|
||||
var app = Angel();
|
||||
var http = AngelHttp(app);
|
||||
var auth = AngelAuth<_User>(
|
||||
void main() async {
|
||||
var app = Protevus();
|
||||
var http = ProtevusHttp(app);
|
||||
var auth = ProtevusAuth<_User>(
|
||||
jwtKey: 'AUTH_TWITTER_SECRET',
|
||||
allowCookie: false,
|
||||
serializer: (user) async => user.handle,
|
||||
|
@ -38,8 +38,8 @@ main() async {
|
|||
'http://localhost:3000/auth/twitter/callback',
|
||||
),
|
||||
(twit, req, res) async {
|
||||
var response = await twit.twitterClient
|
||||
.get('https://api.twitter.com/1.1/account/verify_credentials.json');
|
||||
var response = await twit.client.get(Uri.parse(
|
||||
'https://api.twitter.com/1.1/account/verify_credentials.json'));
|
||||
var userData = json.decode(response.body) as Map;
|
||||
return _User(userData['screen_name'] as String);
|
||||
},
|
||||
|
@ -48,21 +48,18 @@ main() async {
|
|||
if (e.isDenial) {
|
||||
res.write("Why'd you say no???");
|
||||
} else {
|
||||
res.write("oops: ${e.message}");
|
||||
res.write('oops: ${e.message}');
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
app
|
||||
..fallback(auth.decodeJwt)
|
||||
..get('/', auth.authenticate('twitter'));
|
||||
app.get('/', auth.authenticate('twitter'));
|
||||
|
||||
app
|
||||
..get(
|
||||
app.get(
|
||||
'/auth/twitter/callback',
|
||||
auth.authenticate(
|
||||
'twitter',
|
||||
AngelAuthOptions(
|
||||
ProtevusAuthOptions(
|
||||
callback: (req, res, jwt) {
|
||||
return res.redirect('/home?token=$jwt');
|
||||
},
|
||||
|
@ -75,7 +72,7 @@ main() async {
|
|||
chain([
|
||||
requireAuthentication<_User>(),
|
||||
(req, res) {
|
||||
var user = req.container.make<_User>();
|
||||
var user = req.container!.make<_User>();
|
||||
res.write('Your Twitter handle is ${user.handle}');
|
||||
return false;
|
||||
},
|
|
@ -1,11 +1,13 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:angel_auth/angel_auth.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'dart:io';
|
||||
import 'package:protevus_auth/protevus_auth.dart';
|
||||
import 'package:protevus_framework/protevus_framework.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:oauth/oauth.dart' as oauth;
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:twitter/twitter.dart';
|
||||
//import 'package:oauth1/oauth1.dart' as oauth;
|
||||
import 'package:belatuk_oauth1/belatuk_oauth1.dart' as oauth;
|
||||
import 'package:dart_twitter_api/twitter_api.dart';
|
||||
|
||||
/// Authenticates users by connecting to Twitter's API.
|
||||
class TwitterStrategy<User> extends AuthStrategy<User> {
|
||||
|
@ -15,7 +17,7 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
|
|||
/// A callback that uses Twitter to authenticate a [User].
|
||||
///
|
||||
/// As always, return `null` if authentication fails.
|
||||
final FutureOr<User> Function(Twitter, RequestContext, ResponseContext)
|
||||
final FutureOr<User> Function(TwitterApi, RequestContext, ResponseContext)
|
||||
verifier;
|
||||
|
||||
/// A callback that is triggered when an OAuth2 error occurs (i.e. the user declines to login);
|
||||
|
@ -25,17 +27,47 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
|
|||
/// The root of Twitter's API. Defaults to `'https://api.twitter.com'`.
|
||||
final Uri baseUrl;
|
||||
|
||||
oauth.Client _client;
|
||||
oauth.Client? _client;
|
||||
|
||||
/// The underlying [oauth.Client] used to query Twitter.
|
||||
oauth.Client get client => _client;
|
||||
oauth.Client get client => _client!;
|
||||
|
||||
TwitterStrategy(this.options, this.verifier, this.onError,
|
||||
{http.BaseClient client, Uri baseUrl})
|
||||
: this.baseUrl = baseUrl ?? Uri.parse('https://api.twitter.com') {
|
||||
var tokens = oauth.Tokens(
|
||||
consumerId: options.clientId, consumerKey: options.clientSecret);
|
||||
_client = oauth.Client(tokens, client: client);
|
||||
{http.BaseClient? client, Uri? baseUrl})
|
||||
: baseUrl = baseUrl ?? Uri.parse('https://api.twitter.com') {
|
||||
// define platform (server)
|
||||
final oauth.Platform platform = oauth.Platform(
|
||||
'$baseUrl/oauth/request_token', // temporary credentials request
|
||||
'$baseUrl/oauth/authorize', // resource owner authorization
|
||||
'$baseUrl/oauth/access_token', // token credentials request
|
||||
oauth.SignatureMethods.hmacSha1 // signature method
|
||||
);
|
||||
|
||||
// define client credentials (consumer keys)
|
||||
final oauth.ClientCredentials clientCredentials =
|
||||
oauth.ClientCredentials(options.clientId, options.clientSecret);
|
||||
|
||||
// create Authorization object with client credentials and platform definition
|
||||
final oauth.Authorization auth =
|
||||
oauth.Authorization(clientCredentials, platform);
|
||||
|
||||
// request temporary credentials (request tokens)
|
||||
auth.requestTemporaryCredentials('oob').then((res) {
|
||||
// redirect to authorization page
|
||||
print(
|
||||
"Open with your browser: ${auth.getResourceOwnerAuthorizationURI(res.credentials.token)}");
|
||||
|
||||
// get verifier (PIN)
|
||||
stdout.write("PIN: ");
|
||||
String verifier = stdin.readLineSync() ?? '';
|
||||
|
||||
// request token credentials (access tokens)
|
||||
return auth.requestTokenCredentials(res.credentials, verifier);
|
||||
}).then((res) {
|
||||
// create Client object
|
||||
_client = oauth.Client(
|
||||
platform.signatureMethod, clientCredentials, res.credentials);
|
||||
});
|
||||
}
|
||||
|
||||
/// Handle a response from Twitter.
|
||||
|
@ -60,7 +92,7 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
|
|||
|
||||
/// Get an access token.
|
||||
Future<Map<String, String>> getAccessToken(String token, String verifier) {
|
||||
return _client.post(
|
||||
return client.post(
|
||||
baseUrl.replace(path: p.join(baseUrl.path, 'oauth/access_token')),
|
||||
headers: {
|
||||
'accept': 'application/json'
|
||||
|
@ -75,45 +107,46 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
|
|||
|
||||
/// Get a request token.
|
||||
Future<Map<String, String>> getRequestToken() {
|
||||
return _client.post(
|
||||
return client.post(
|
||||
baseUrl.replace(path: p.join(baseUrl.path, 'oauth/request_token')),
|
||||
headers: {
|
||||
'accept': 'application/json'
|
||||
},
|
||||
body: {
|
||||
"oauth_callback": options.redirectUri.toString()
|
||||
'oauth_callback': options.redirectUri.toString()
|
||||
}).then(handleUrlEncodedResponse);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<User> authenticate(RequestContext req, ResponseContext res,
|
||||
[AngelAuthOptions options]) async {
|
||||
Future<User?> authenticate(RequestContext req, ResponseContext res,
|
||||
[ProtevusAuthOptions? options]) async {
|
||||
try {
|
||||
if (options != null) {
|
||||
var result = await authenticateCallback(req, res, options);
|
||||
if (result is User)
|
||||
if (result is User) {
|
||||
return result;
|
||||
else
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
var result = await getRequestToken();
|
||||
var token = result['oauth_token'];
|
||||
var url = baseUrl.replace(
|
||||
path: p.join(baseUrl.path, 'oauth/authorize'),
|
||||
queryParameters: {'oauth_token': token});
|
||||
res.redirect(url);
|
||||
await res.redirect(url);
|
||||
return null;
|
||||
}
|
||||
} on TwitterAuthorizationException catch (e) {
|
||||
var result = await onError(e, req, res);
|
||||
await req.app.executeHandler(result, req, res);
|
||||
await req.app?.executeHandler(result, req, res);
|
||||
await res.close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future authenticateCallback(
|
||||
RequestContext req, ResponseContext res, AngelAuthOptions options) async {
|
||||
Future authenticateCallback(RequestContext req, ResponseContext res,
|
||||
ProtevusAuthOptions options) async {
|
||||
try {
|
||||
if (req.queryParameters.containsKey('denied')) {
|
||||
throw TwitterAuthorizationException(
|
||||
|
@ -123,8 +156,13 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
|
|||
var token = req.queryParameters['oauth_token'] as String;
|
||||
var verifier = req.queryParameters['oauth_verifier'] as String;
|
||||
var loginData = await getAccessToken(token, verifier);
|
||||
var twitter = Twitter(this.options.clientId, this.options.clientSecret,
|
||||
loginData['oauth_token'], loginData['oauth_token_secret']);
|
||||
var twitter = TwitterApi(
|
||||
client: TwitterClient(
|
||||
consumerKey: this.options.clientId,
|
||||
consumerSecret: this.options.clientSecret,
|
||||
token: loginData['oauth_token'] ?? '',
|
||||
secret: loginData['oauth_token_secret'] ?? ''));
|
||||
|
||||
return await this.verifier(twitter, req, res);
|
||||
} on TwitterAuthorizationException catch (e) {
|
||||
return await onError(e, req, res);
|
|
@ -1,28 +1,34 @@
|
|||
name: "angel_auth_twitter"
|
||||
#author: "Tobe O <thosakwe@gmail.com>"
|
||||
description: "package:angel_auth strategy for Twitter login. Auto-signs requests."
|
||||
environment:
|
||||
sdk: ">=2.10.0 <3.0.0"
|
||||
homepage: "https://github.com/angel-dart/auth_twitter.git"
|
||||
version: 3.0.0
|
||||
name: "protevus_auth_twitter"
|
||||
description: Protevus authentication strategy for Twitter login. Auto-signs requests.
|
||||
version: 8.0.0
|
||||
homepage: https://protevus-framework.web.app/
|
||||
repository: https://github.com/dart-backend/protevus/tree/master/packages/auth_twitter
|
||||
publish_to: none
|
||||
environment:
|
||||
sdk: ">=3.3.0 <4.0.0"
|
||||
dependencies:
|
||||
angel_auth:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x
|
||||
path: packages/auth
|
||||
angel_framework:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x
|
||||
path: packages/framework
|
||||
http: ^0.13.0
|
||||
path: ^1.0.0
|
||||
twitter:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/twitter.dart.git
|
||||
ref: sdk-2.12.x
|
||||
protevus_auth: ^8.0.0
|
||||
protevus_framework: ^8.0.0
|
||||
http: ^1.0.0
|
||||
path: ^1.9.0
|
||||
belatuk_oauth1: ^3.0.0
|
||||
dart_twitter_api: ^0.5.6+1
|
||||
dev_dependencies:
|
||||
logging: ^1.0.0
|
||||
pedantic: ^1.11.0
|
||||
logging: ^1.2.0
|
||||
lints: ^4.0.0
|
||||
dependency_overrides:
|
||||
http: ^1.0.0
|
||||
# protevus_container:
|
||||
# path: ../container/angel_container
|
||||
# protevus_framework:
|
||||
# path: ../framework
|
||||
# protevus_http_exception:
|
||||
# path: ../http_exception
|
||||
# protevus_model:
|
||||
# path: ../model
|
||||
# protevus_route:
|
||||
# path: ../route
|
||||
# protevus_mock_request:
|
||||
# path: ../mock_request
|
||||
# protevus_auth:
|
||||
# path: ../auth
|
64
packages/body_parser/.gitignore
vendored
64
packages/body_parser/.gitignore
vendored
|
@ -1,64 +0,0 @@
|
|||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# CMake
|
||||
cmake-build-debug/
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
### Dart template
|
||||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.packages
|
||||
.pub/
|
||||
build/
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
</component>
|
||||
</module>
|
|
@ -1,421 +0,0 @@
|
|||
<component name="libraryTable">
|
||||
<library name="Dart Packages" type="DartPackagesLibraryType">
|
||||
<properties>
|
||||
<option name="packageNameToDirsMap">
|
||||
<entry key="analyzer">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/analyzer-0.32.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="args">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/args-1.5.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="async">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-2.0.8/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="boolean_selector">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-1.0.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="charcode">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.1.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="collection">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/collection-1.14.11/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="convert">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/convert-2.0.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="crypto">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/crypto-2.0.6/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="csslib">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/csslib-0.14.4+1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="dart2_constant">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/dart2_constant-1.0.2+dart2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="front_end">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/front_end-0.1.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="glob">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/glob-1.1.7/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="html">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/html-0.13.3+2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="http">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http-0.11.3+17/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="http_multi_server">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_multi_server-2.0.5/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="http_parser">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_parser-3.1.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="http_server">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_server-0.9.8/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="io">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/io-0.3.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="js">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/js-0.6.1+1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="json_rpc_2">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/json_rpc_2-2.0.9/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="kernel">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/kernel-0.3.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="logging">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/logging-0.11.3+2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="matcher">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.3+1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="meta">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/meta-1.1.6/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="mime">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/mime-0.9.6+2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="multi_server_socket">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/multi_server_socket-1.0.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="node_preamble">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/node_preamble-1.4.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="package_config">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/package_config-1.0.5/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="package_resolver">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/package_resolver-1.0.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="path">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path-1.6.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="plugin">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/plugin-0.2.0+3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="pool">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pool-1.3.6/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="pub_semver">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pub_semver-1.4.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="shelf">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf-0.7.3+3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="shelf_packages_handler">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_packages_handler-1.0.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="shelf_static">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_static-0.2.8/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="shelf_web_socket">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_web_socket-0.2.2+4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="source_map_stack_trace">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_map_stack_trace-1.1.5/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="source_maps">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_maps-0.10.7/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="source_span">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_span-1.4.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="stack_trace">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.9.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="stream_channel">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stream_channel-1.6.8/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="string_scanner">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.0.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="term_glyph">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.0.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="test">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test-1.3.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="typed_data">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/typed_data-1.1.6/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="utf">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/utf-0.9.0+5/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="vm_service_client">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vm_service_client-0.2.6/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="watcher">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/watcher-0.9.7+10/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="web_socket_channel">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/web_socket_channel-1.0.9/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="yaml">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/yaml-2.1.15/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
</option>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/analyzer-0.32.4/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/args-1.5.0/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-2.0.8/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-1.0.4/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.1.2/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/collection-1.14.11/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/convert-2.0.2/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/crypto-2.0.6/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/csslib-0.14.4+1/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/dart2_constant-1.0.2+dart2/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/front_end-0.1.4/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/glob-1.1.7/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/html-0.13.3+2/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http-0.11.3+17/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_multi_server-2.0.5/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_parser-3.1.3/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_server-0.9.8/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/io-0.3.3/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/js-0.6.1+1/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/json_rpc_2-2.0.9/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/kernel-0.3.4/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/logging-0.11.3+2/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.3+1/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/meta-1.1.6/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/mime-0.9.6+2/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/multi_server_socket-1.0.2/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/node_preamble-1.4.4/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/package_config-1.0.5/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/package_resolver-1.0.4/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path-1.6.2/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/plugin-0.2.0+3/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pool-1.3.6/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pub_semver-1.4.2/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf-0.7.3+3/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_packages_handler-1.0.4/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_static-0.2.8/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_web_socket-0.2.2+4/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_map_stack_trace-1.1.5/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_maps-0.10.7/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_span-1.4.1/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.9.3/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stream_channel-1.6.8/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.0.3/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.0.1/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test-1.3.0/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/typed_data-1.1.6/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/utf-0.9.0+5/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vm_service_client-0.2.6/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/watcher-0.9.7+10/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/web_socket_channel-1.0.9/lib" />
|
||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/yaml-2.1.15/lib" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<LIBRARY_FILE />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/body_parser.iml" filepath="$PROJECT_DIR$/.idea/body_parser.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -1,7 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="main.dart" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true" nameIsGenerated="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/example/main.dart" />
|
||||
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,4 +0,0 @@
|
|||
language: dart
|
||||
dart:
|
||||
- dev
|
||||
- stable
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue