Compare commits
628 Commits
version/0.
...
version/0.
Author | SHA1 | Date | |
---|---|---|---|
7a98e6d92b | |||
49e915f98b | |||
3aa2f1e892 | |||
bc4b7ef44d | |||
9400b01a55 | |||
e57da71dcf | |||
7268afaaf9 | |||
205183445c | |||
a08bdfdbcd | |||
e6c47fee26 | |||
a5629c5155 | |||
41689fe3ce | |||
8e84208e2c | |||
32a48fa07a | |||
773a9c0692 | |||
8808e3afe0 | |||
ecea85f8ca | |||
5dfa141e35 | |||
447e81d0b8 | |||
e138076e1d | |||
721d133dc3 | |||
75b687ecbe | |||
bdd1863177 | |||
e5b85e8e6a | |||
d7481c9de7 | |||
571373866e | |||
e36d7928e4 | |||
2be026dd44 | |||
d5b9de3569 | |||
e22620b0ec | |||
ba74a3213d | |||
d9ecb7070d | |||
fc4a46bd9c | |||
78301b7bab | |||
7bf7bde856 | |||
9bdff14403 | |||
f124314eab | |||
684e4ffdcf | |||
d9ff5c69c8 | |||
8142e3df45 | |||
73920899de | |||
13666965a7 | |||
86f16e2781 | |||
2ed8e72c62 | |||
edeed18ae8 | |||
d24133d8a2 | |||
b9733e56aa | |||
cd34413914 | |||
c3a4a76d43 | |||
a59a29b256 | |||
dce1edbe53 | |||
264d43827a | |||
6207226bdf | |||
ebf33f39c9 | |||
696cd1f247 | |||
b7b3abc462 | |||
575739d07c | |||
2d7e70eebf | |||
387f3c981f | |||
865435fb25 | |||
b10c5306b9 | |||
7c706369cd | |||
20dd6355c1 | |||
ba8d5d6e27 | |||
c448f87027 | |||
2b8c70a61f | |||
9d7ed9a0ed | |||
ff69b4affe | |||
d77afd1ded | |||
c3909f9196 | |||
fa55ba5ef0 | |||
766518ee0e | |||
74b2b26a20 | |||
4ebbc6f065 | |||
3bd1eadd51 | |||
8eb3f0f708 | |||
31ea2e7139 | |||
323b4b4a5d | |||
7b8e1bea92 | |||
f986dc89ad | |||
b21fd10093 | |||
6f9c19b142 | |||
f45643ca87 | |||
85f8bea784 | |||
b428ec5237 | |||
92428529ad | |||
f6761b5b0b | |||
307b04f4ca | |||
6a520a5697 | |||
f22dbba931 | |||
82cf482fba | |||
a6afb99edd | |||
ac5f8465b9 | |||
218acb9e38 | |||
927c718fdd | |||
b7a6d6e739 | |||
0946d6a25d | |||
c1e98e2f0c | |||
807cbbeaaf | |||
6c358c4e0a | |||
74cd0bc08f | |||
b08ec0477e | |||
328c999cb9 | |||
c37e382c15 | |||
784dd0fdd6 | |||
e6256cb9c8 | |||
4520e3f8b8 | |||
23146de2bf | |||
e24f4fe3a8 | |||
8e6b69f96f | |||
979bea17ed | |||
30dba285d9 | |||
99fadf2e55 | |||
b606e3d0cb | |||
be642bc874 | |||
49a347b32f | |||
089b48aad1 | |||
2997cb83b1 | |||
08f0aca894 | |||
80ea7c40b7 | |||
019a0cb14d | |||
97290755e7 | |||
7f150c96b4 | |||
73558f30d1 | |||
dfcfd87644 | |||
2c0f0a68a8 | |||
3d73aac3ab | |||
e4fbcd3735 | |||
44c0eb37cf | |||
adc3dcc2c4 | |||
bac8227371 | |||
73d4d9dfe0 | |||
afdac5f3f8 | |||
dabce36667 | |||
3bd56ce522 | |||
540419d5c1 | |||
ed1fcc3930 | |||
c22ddc5394 | |||
0544864a3f | |||
0b9fc9e444 | |||
e862b97005 | |||
cffe09b02e | |||
846a86fb62 | |||
463c130351 | |||
ffca957838 | |||
543e949a48 | |||
feb80049aa | |||
5c59c8ccb6 | |||
1fadd82c65 | |||
7e7736126d | |||
5e0915afce | |||
bf6c9e8c4a | |||
3353aa0298 | |||
d4cb1a98c7 | |||
13f4ea0b8b | |||
261d57ad7b | |||
4086252979 | |||
8bdf12cff1 | |||
65a065c4ee | |||
a691ee529c | |||
f1c4a62612 | |||
358e39ced0 | |||
48c3f68cfc | |||
1849a7c383 | |||
82d14f37c3 | |||
a0261eafa3 | |||
2a27325dfd | |||
a6dee2e8ed | |||
2ff1635696 | |||
1cb6b5e984 | |||
1fe420fd80 | |||
50172e58d8 | |||
d7483d129f | |||
34ed0b3594 | |||
f008a3e20c | |||
9de950220f | |||
567c90b4c6 | |||
ae19236366 | |||
f9babe7089 | |||
78c74cd469 | |||
32abb27e61 | |||
8478b03892 | |||
e972f2b289 | |||
22c4fb1414 | |||
0154def916 | |||
fc69b6851d | |||
44a3c7fa5f | |||
37111fd07b | |||
4e6653e299 | |||
143a575369 | |||
c782585287 | |||
7718b3b3b8 | |||
8ff9e72972 | |||
ef6ef68a39 | |||
48a04744e0 | |||
6446ca8bb2 | |||
b9991465ee | |||
3d8242be06 | |||
344a8817c3 | |||
3afb0d4f6d | |||
c9714893bb | |||
3185a86b22 | |||
a53f7a49ac | |||
ca3bcc565d | |||
432176ea2f | |||
c1dae0b599 | |||
e70d3b6286 | |||
17e6bc921b | |||
46111e7cac | |||
3b7e47dbe2 | |||
fff99f0e3d | |||
2e15b24f0a | |||
088b9592cd | |||
b1e4e32b83 | |||
d91a852eda | |||
171c5b9759 | |||
64290b2a37 | |||
72769b8a0a | |||
1018309413 | |||
6d0ecd228e | |||
40a651e66c | |||
a390bb7b59 | |||
245ec65cbb | |||
17eea4a10c | |||
862fb0f5d2 | |||
ec73b53340 | |||
9110f7fee3 | |||
54cc1fdeef | |||
8f42a7f0b4 | |||
2c221ea819 | |||
93e0441b58 | |||
7f1455cb12 | |||
59fc223a85 | |||
0a6f555c23 | |||
6a4233d6fd | |||
15fa7e9652 | |||
f2acc154cd | |||
d21ec6c9a5 | |||
43dd858cd5 | |||
34cbf5f702 | |||
3c6e94b6a8 | |||
1cd149c815 | |||
4c6f562805 | |||
e59c4ec1c7 | |||
1169db7530 | |||
1453008796 | |||
2209b6d603 | |||
ccbc0384f9 | |||
a48924c896 | |||
dc8d8dd2b6 | |||
afca94ceb8 | |||
0b86231a36 | |||
c0df1f38b8 | |||
2b8fed8f4e | |||
c7322a32a0 | |||
64b75cab84 | |||
f58bc61999 | |||
fb8ccc0283 | |||
c38012f147 | |||
3676ff21c2 | |||
920e705d75 | |||
de0b137b1e | |||
d44ac6e2a3 | |||
71039a4012 | |||
8745ac7932 | |||
7f70048423 | |||
97dbfc8885 | |||
149ea22a93 | |||
404ed5406d | |||
b8656858ec | |||
6b0f0e8993 | |||
aec1ccd88d | |||
bee5c200b6 | |||
9d640efc88 | |||
f0907841dd | |||
2bffc12ef9 | |||
2ff9ec6522 | |||
43a54f5c54 | |||
7bff2734aa | |||
84768c0ec6 | |||
f4499a5459 | |||
b3aede5bba | |||
531ea1c039 | |||
c2c5ff6912 | |||
9cddab8fd5 | |||
06d15d8a27 | |||
b5c711854b | |||
4cf6c36f34 | |||
75a6f6c875 | |||
62abe3f256 | |||
9296c41650 | |||
7fb48fde6d | |||
174472bb45 | |||
17575ed921 | |||
b1b1a27444 | |||
f97a5eeefb | |||
10fd96981e | |||
67e3eb549c | |||
30a6d1f0b1 | |||
3d1fa9f048 | |||
1d2be6e68b | |||
c21e343986 | |||
ff37ed095c | |||
8623a2c3fc | |||
23d277eaf1 | |||
75ced59451 | |||
bccf424c5e | |||
2f9ae40d20 | |||
11e1eec3fb | |||
765c5633df | |||
6344b1aafb | |||
ed25801e6e | |||
4d0148193f | |||
804ae15c2e | |||
b35a9fad86 | |||
a4f83bd28a | |||
796f83c3d0 | |||
2099bbb713 | |||
67beba8f78 | |||
a798412e17 | |||
3b2c2d781f | |||
98c844f3d6 | |||
2645bd0132 | |||
2c4fc56b49 | |||
0ec1468058 | |||
5d1a3043b2 | |||
b46958d1f9 | |||
5daa8d5fe3 | |||
31846f1d05 | |||
1fac964b8b | |||
dfa6ed8ac2 | |||
66fe10299e | |||
e0a3ec033f | |||
7033ec0ab9 | |||
4004579905 | |||
9fe9e48a5c | |||
595a6c7fe6 | |||
11b5860d4a | |||
9bdbff4cda | |||
e0d597eeac | |||
f576985cc9 | |||
22a6aef60b | |||
ec0a6e7854 | |||
6904608e6f | |||
cb3732cb2b | |||
57de6cbafc | |||
b1dda764a9 | |||
5ec2102487 | |||
9f8fb7378a | |||
98cd646044 | |||
0cba1b4c45 | |||
53918462b6 | |||
8a7e74b523 | |||
4dc7065e97 | |||
3c93bb9f9f | |||
8143fae2d6 | |||
3cfe45d3cb | |||
8e5c3f2f31 | |||
5a3b2fdd49 | |||
e47b9f0d57 | |||
146dd747f1 | |||
f2ce56063b | |||
b26f378e4c | |||
9072b836c6 | |||
2fa57d064e | |||
146705c60a | |||
5029a99df6 | |||
e7129d18f6 | |||
d2bf9f81d6 | |||
30acf0660b | |||
dda41af5c8 | |||
9b5b03647b | |||
940b3eb943 | |||
16eb629b71 | |||
755045b226 | |||
61478db94e | |||
f69f959bdb | |||
146edb45d4 | |||
045a802365 | |||
c90d8ddcff | |||
3ff2ec929f | |||
a3ef26b7ad | |||
19cd1624c1 | |||
366ef352c6 | |||
a9031a6abc | |||
a1a5223b58 | |||
c723b0233f | |||
b369eb28f1 | |||
9b8f390e31 | |||
11630c9a74 | |||
c9ac10f6f6 | |||
04d613cb28 | |||
40866f9ecd | |||
d8585eb872 | |||
15aaeda475 | |||
8536ef9e23 | |||
35b6bb6b3f | |||
eaa573c715 | |||
660972e303 | |||
a21012bf0c | |||
8dbafa4bda | |||
80049413f0 | |||
2739442d4a | |||
c679f0a67c | |||
d9a952dd03 | |||
9a1a0f0aa8 | |||
4d6bb60134 | |||
80e6d59382 | |||
81ac951872 | |||
f33e553cfd | |||
9b0240dc26 | |||
c327310392 | |||
457375287c | |||
7e87bfef5b | |||
a7af5268de | |||
6d916029bb | |||
81fdcbadad | |||
ec1e25fe71 | |||
b5306e4a94 | |||
801b8a1e59 | |||
3a52059793 | |||
10b7d99b37 | |||
6be8d0cbb2 | |||
5b8e3689ec | |||
25a5d8f5da | |||
883d439544 | |||
1c3b5889e5 | |||
87012b65e1 | |||
29913773a7 | |||
0bc6a4fed4 | |||
4645d8353f | |||
260c5555fa | |||
6f7b917c38 | |||
1456ee6d3e | |||
ae3d3d0295 | |||
c23ceacd0b | |||
5155204283 | |||
5509ec9b0f | |||
d6f9b2e47d | |||
67aa4aef11 | |||
9e46c8bfec | |||
1eaa9b9733 | |||
ee05834b69 | |||
fccc8f4959 | |||
c721620f96 | |||
c9f73d718e | |||
bfa58be721 | |||
4bb602149e | |||
81ab9092fc | |||
29d5962c4c | |||
5c75339946 | |||
4774d9a46c | |||
dbe16ba4fd | |||
6972cf00a0 | |||
0445be9712 | |||
89dbdd9585 | |||
da88ce7150 | |||
5f50fcfcf5 | |||
96be087221 | |||
a53a269a8c | |||
59565a5286 | |||
ae3c092238 | |||
e98e5e4e3e | |||
d50c7ec8d4 | |||
c0fdf377d1 | |||
70c11c8988 | |||
67b19becc1 | |||
ae64024ef4 | |||
e6571826cb | |||
c621e61978 | |||
3626fa4b98 | |||
01b0eb159a | |||
63aa48d981 | |||
2e0ba05d55 | |||
b2ac57bb67 | |||
4c22e5c2c8 | |||
4a7b0ec8a9 | |||
330118249e | |||
8d4dabde02 | |||
cf7323c41b | |||
edd856df7d | |||
5e35859db6 | |||
acabb2df54 | |||
e6376a05f7 | |||
1f45aff7ad | |||
e1f1f617b6 | |||
2690675dca | |||
7529b51358 | |||
c394066d99 | |||
9c585032ef | |||
d408031304 | |||
c47bc11ec0 | |||
1deb094afe | |||
501fed1922 | |||
ad8125ac1c | |||
b42a551fb2 | |||
3256be23df | |||
f7c0c0146a | |||
e4baf8c21e | |||
364f040b36 | |||
2b8c2b2346 | |||
5f861189e4 | |||
5e11b6687e | |||
c4b429825d | |||
eebbae0677 | |||
42b30f4507 | |||
0e425418df | |||
7fe0300b86 | |||
c012c6be5c | |||
a5dc193cfd | |||
7507ad2620 | |||
f1291fec8d | |||
37aeeea239 | |||
0fa1fc86da | |||
c3034ab9ac | |||
76694e037a | |||
787db41cc3 | |||
74da3df7cd | |||
a6e435bd70 | |||
c313b496aa | |||
a7eaa74191 | |||
11ecdc4fcf | |||
2f7781b67a | |||
296d4f691a | |||
64033031b1 | |||
9daff7608d | |||
0a4af80b9b | |||
a54adb05c4 | |||
43a389e596 | |||
2d7e8f1b50 | |||
cf11f6b121 | |||
6dcdf7bcce | |||
56d872af15 | |||
ca663d16fc | |||
e05c18b19b | |||
a7b86e46bc | |||
84f56674c2 | |||
02ab177c6d | |||
1232c487e9 | |||
ef0a2bfbe8 | |||
05242a11ad | |||
4593ad7bcc | |||
d7fd5a7fa6 | |||
4439378fd4 | |||
acf65eafdd | |||
c2ebff55ef | |||
99c82676b6 | |||
4991e9b825 | |||
612f95c3ba | |||
cd91d5ca15 | |||
cbbbb5dc08 | |||
c1640b9411 | |||
a4842c1f95 | |||
a4707ddc54 | |||
fb82d56307 | |||
1a1005f80d | |||
e86cae6cac | |||
0b282f45e0 | |||
791e88ffc1 | |||
7bd3c4bccf | |||
722e2e4050 | |||
c7fc444c95 | |||
20ad062814 | |||
fcb5d36e07 | |||
9b131b619f | |||
54427f7c68 | |||
35eef9c28d | |||
e88a82553d | |||
01a9520140 | |||
46667615c3 | |||
c6721a83a4 | |||
46866e8ef0 | |||
4a49681127 | |||
4c3fced4e9 | |||
172347d90f | |||
f54520b5cf | |||
d7c4697625 | |||
5584f5bda8 | |||
2ce6f5a714 | |||
c66945623a | |||
cbae05c74c | |||
5b771da972 | |||
2db1738e4a | |||
95de6a14fd | |||
17132ebc19 | |||
289be46388 | |||
6c300b7b31 | |||
b726583084 | |||
48055d1cfd | |||
436070f5bd | |||
3ee79818db | |||
e7a02104db | |||
556740d7bc | |||
421f51770c | |||
96f7e70f9e | |||
ad96f7dbb8 | |||
e7fb48eba2 | |||
b19b5b644d | |||
250b6691d4 | |||
e3b02a6e78 | |||
e94ef34d8f | |||
49e945307a | |||
edfe0e5450 | |||
06b65a7882 | |||
ff9bc8aa70 | |||
28da67abe6 | |||
39d9fe9bf0 | |||
750117b0fd | |||
983462f80d | |||
4ae31d409b | |||
98b414f3e2 | |||
a0d42092e3 | |||
f2569b6424 | |||
9d344d887c | |||
7e9154a0ea | |||
e0ef061771 | |||
b8694a7ade | |||
10d6a30f2c | |||
8c94aef6d0 | |||
19bd3bfffb | |||
8611ac624c | |||
fa93b59a8c | |||
8b66b40f0d | |||
c2756f15fc | |||
408e205c5f | |||
5f3ab49535 | |||
33431ae013 | |||
b40ac6dc5d |
@ -1,44 +1,25 @@
|
||||
[bumpversion]
|
||||
current_version = 0.0.7-alpha
|
||||
current_version = 0.7.15-beta
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
|
||||
serialize = {major}.{minor}.{patch}-{release}
|
||||
message = bump version: {current_version} -> {new_version}
|
||||
message = new release: {new_version}
|
||||
tag_name = version/{new_version}
|
||||
|
||||
[bumpversion:part:release]
|
||||
optional_value = stable
|
||||
first_value = beta
|
||||
values =
|
||||
alpha
|
||||
beta
|
||||
stable
|
||||
|
||||
[bumpversion:file:helm/passbook/Chart.yaml]
|
||||
[bumpversion:file:helm/values.yaml]
|
||||
|
||||
[bumpversion:file:.gitlab-ci.yml]
|
||||
[bumpversion:file:helm/Chart.yaml]
|
||||
|
||||
[bumpversion:file:.github/workflows/release.yml]
|
||||
|
||||
[bumpversion:file:passbook/__init__.py]
|
||||
|
||||
[bumpversion:file:passbook/api/__init__.py]
|
||||
|
||||
[bumpversion:file:passbook/core/__init__.py]
|
||||
|
||||
[bumpversion:file:passbook/admin/__init__.py]
|
||||
|
||||
[bumpversion:file:passbook/captcha_factor/__init__.py]
|
||||
|
||||
[bumpversion:file:passbook/oauth_client/__init__.py]
|
||||
|
||||
[bumpversion:file:passbook/ldap/__init__.py]
|
||||
|
||||
[bumpversion:file:passbook/lib/__init__.py]
|
||||
|
||||
[bumpversion:file:passbook/saml_idp/__init__.py]
|
||||
|
||||
[bumpversion:file:passbook/audit/__init__.py]
|
||||
|
||||
[bumpversion:file:passbook/oauth_provider/__init__.py]
|
||||
|
||||
[bumpversion:file:passbook/otp/__init__.py]
|
||||
|
||||
|
@ -1,12 +1,10 @@
|
||||
[run]
|
||||
source = passbook
|
||||
omit =
|
||||
env/
|
||||
*/wsgi.py
|
||||
manage.py
|
||||
*/migrations/*
|
||||
*/apps.py
|
||||
passbook/management/commands/nexus_upload.py
|
||||
passbook/management/commands/web.py
|
||||
passbook/management/commands/worker.py
|
||||
docs/
|
||||
@ -23,6 +21,7 @@ exclude_lines =
|
||||
def __str__
|
||||
def __repr__
|
||||
if self\.debug
|
||||
if TYPE_CHECKING
|
||||
|
||||
# Don't complain if tests don't hit defensive assertion code:
|
||||
raise AssertionError
|
||||
|
@ -2,3 +2,4 @@ env
|
||||
helm
|
||||
passbook-ui
|
||||
static
|
||||
*.env.yml
|
||||
|
@ -9,3 +9,6 @@ insert_final_newline = true
|
||||
|
||||
[html]
|
||||
indent_size = 2
|
||||
|
||||
[yaml]
|
||||
indent_size = 2
|
||||
|
147
.github/workflows/ci.yml
vendored
Normal file
147
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
name: passbook-ci
|
||||
on:
|
||||
- push
|
||||
env:
|
||||
POSTGRES_DB: passbook
|
||||
POSTGRES_USER: passbook
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
|
||||
jobs:
|
||||
# Linting
|
||||
pylint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs/
|
||||
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pipenv-
|
||||
- name: Install dependencies
|
||||
run: pip install -U pip pipenv && pipenv install --dev
|
||||
- name: Lint with pylint
|
||||
run: pipenv run pylint passbook
|
||||
black:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs/
|
||||
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pipenv-
|
||||
- name: Install dependencies
|
||||
run: pip install -U pip pipenv && pipenv install --dev
|
||||
- name: Lint with black
|
||||
run: pipenv run black --check passbook
|
||||
prospector:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs/
|
||||
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pipenv-
|
||||
- name: Install dependencies
|
||||
run: pip install -U pip pipenv && pipenv install --dev && pipenv install --dev prospector --skip-lock
|
||||
- name: Lint with prospector
|
||||
run: pipenv run prospector
|
||||
bandit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs/
|
||||
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pipenv-
|
||||
- name: Install dependencies
|
||||
run: pip install -U pip pipenv && pipenv install --dev
|
||||
- name: Lint with bandit
|
||||
run: pipenv run bandit -r passbook
|
||||
# Actual CI tests
|
||||
migrations:
|
||||
needs:
|
||||
- pylint
|
||||
- black
|
||||
- prospector
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
env:
|
||||
POSTGRES_DB: passbook
|
||||
POSTGRES_USER: passbook
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
ports:
|
||||
- 5432:5432
|
||||
redis:
|
||||
image: redis:latest
|
||||
ports:
|
||||
- 6379:6379
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs/
|
||||
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pipenv-
|
||||
- name: Install dependencies
|
||||
run: pip install -U pip pipenv && pipenv install --dev
|
||||
- name: Run migrations
|
||||
run: pipenv run ./manage.py migrate
|
||||
coverage:
|
||||
needs:
|
||||
- pylint
|
||||
- black
|
||||
- prospector
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
env:
|
||||
POSTGRES_DB: passbook
|
||||
POSTGRES_USER: passbook
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
ports:
|
||||
- 5432:5432
|
||||
redis:
|
||||
image: redis:latest
|
||||
ports:
|
||||
- 6379:6379
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs/
|
||||
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pipenv-
|
||||
- name: Install dependencies
|
||||
run: pip install -U pip pipenv && pipenv install --dev
|
||||
- name: Run coverage
|
||||
run: pipenv run ./scripts/coverage.sh
|
89
.github/workflows/release.yml
vendored
Normal file
89
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
name: passbook-release
|
||||
on:
|
||||
release
|
||||
|
||||
jobs:
|
||||
# Build
|
||||
build-server:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Docker Login Registry
|
||||
env:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
run: docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
|
||||
- name: Building Docker Image
|
||||
run: docker build
|
||||
--no-cache
|
||||
-t beryju/passbook:0.7.15-beta
|
||||
-t beryju/passbook:latest
|
||||
-f Dockerfile .
|
||||
- name: Push Docker Container to Registry (versioned)
|
||||
run: docker push beryju/passbook:0.7.15-beta
|
||||
- name: Push Docker Container to Registry (latest)
|
||||
run: docker push beryju/passbook:latest
|
||||
build-gatekeeper:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Docker Login Registry
|
||||
env:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
run: docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
|
||||
- name: Building Docker Image
|
||||
run: |
|
||||
cd gatekeeper
|
||||
docker build \
|
||||
--no-cache \
|
||||
-t beryju/passbook-gatekeeper:0.7.15-beta \
|
||||
-t beryju/passbook-gatekeeper:latest \
|
||||
-f Dockerfile .
|
||||
- name: Push Docker Container to Registry (versioned)
|
||||
run: docker push beryju/passbook-gatekeeper:0.7.15-beta
|
||||
- name: Push Docker Container to Registry (latest)
|
||||
run: docker push beryju/passbook-gatekeeper:latest
|
||||
build-static:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
env:
|
||||
POSTGRES_DB: passbook
|
||||
POSTGRES_USER: passbook
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
redis:
|
||||
image: redis:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Docker Login Registry
|
||||
env:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
run: docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
|
||||
- name: Building Docker Image
|
||||
run: docker build
|
||||
--no-cache
|
||||
--network=$(docker network ls | grep github | awk '{print $1}')
|
||||
-t beryju/passbook-static:0.7.15-beta
|
||||
-t beryju/passbook-static:latest
|
||||
-f static.Dockerfile .
|
||||
- name: Push Docker Container to Registry (versioned)
|
||||
run: docker push beryju/passbook-static:0.7.15-beta
|
||||
- name: Push Docker Container to Registry (latest)
|
||||
run: docker push beryju/passbook-static:latest
|
||||
test-release:
|
||||
needs:
|
||||
- build-server
|
||||
- build-static
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Run test suite in final docker images
|
||||
run: |
|
||||
export PASSBOOK_DOMAIN=localhost
|
||||
docker-compose pull
|
||||
docker-compose up --no-start
|
||||
docker-compose start postgresql redis
|
||||
docker-compose run -u root server bash -c "pip install --no-cache -r requirements-dev.txt && ./manage.py test"
|
60
.github/workflows/tag.yml
vendored
Normal file
60
.github/workflows/tag.yml
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'version/*'
|
||||
|
||||
name: passbook-version-tag
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Create Release from Tag
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Pre-release test
|
||||
run: |
|
||||
export PASSBOOK_DOMAIN=localhost
|
||||
docker-compose pull
|
||||
docker build \
|
||||
--no-cache \
|
||||
-t beryju/passbook:latest \
|
||||
-f Dockerfile .
|
||||
docker-compose up --no-start
|
||||
docker-compose start postgresql redis
|
||||
docker-compose run -u root server bash -c "pip install --no-cache -r requirements-dev.txt && ./manage.py test"
|
||||
- name: Install Helm
|
||||
run: |
|
||||
apt update && apt install -y curl
|
||||
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
|
||||
- name: Helm package
|
||||
run: |
|
||||
helm dependency update helm/
|
||||
helm package helm/
|
||||
mv passbook-*.tgz passbook-chart.tgz
|
||||
- name: Extract verison number
|
||||
id: get_version
|
||||
uses: actions/github-script@0.2.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
return context.payload.ref.replace(/\/refs\/tags\/version\//, '');
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1.0.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ steps.get_version.outputs.result }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
- name: Upload packaged Helm Chart
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1.0.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./passbook-chart.tgz
|
||||
asset_name: passbook-chart.tgz
|
||||
asset_content_type: application/gzip
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -63,6 +63,7 @@ coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
unittest.xml
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
@ -184,10 +185,14 @@ dmypy.json
|
||||
[Ii]nclude
|
||||
[Ll]ib64
|
||||
[Ll]ocal
|
||||
[Ss]cripts
|
||||
pyvenv.cfg
|
||||
pip-selfcheck.json
|
||||
|
||||
# End of https://www.gitignore.io/api/python,django
|
||||
/static/
|
||||
local.env.yml
|
||||
.vscode/
|
||||
|
||||
### Helm ###
|
||||
# Chart dependencies
|
||||
**/charts/*.tgz
|
||||
|
140
.gitlab-ci.yml
140
.gitlab-ci.yml
@ -1,140 +0,0 @@
|
||||
# Global Variables
|
||||
before_script:
|
||||
- "python3 -m pip install -U virtualenv"
|
||||
- "virtualenv env"
|
||||
- "source env/bin/activate"
|
||||
- "pip3 install -U -r requirements-dev.txt"
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
- docs
|
||||
image: python:3.6
|
||||
services:
|
||||
- postgres:latest
|
||||
|
||||
variables:
|
||||
POSTGRES_DB: passbook
|
||||
POSTGRES_USER: passbook
|
||||
POSTGRES_PASSWORD: 'EK-5jnKfjrGRm<77'
|
||||
SUPERVISR_ENV: ci
|
||||
|
||||
include:
|
||||
- /allauth/.gitlab-ci.yml
|
||||
|
||||
isort:
|
||||
script:
|
||||
- isort -c -sg env
|
||||
stage: test
|
||||
migrations:
|
||||
script:
|
||||
- python manage.py migrate
|
||||
stage: test
|
||||
prospector:
|
||||
script:
|
||||
- prospector
|
||||
stage: test
|
||||
pylint:
|
||||
script:
|
||||
- pylint passbook
|
||||
stage: test
|
||||
coverage:
|
||||
script:
|
||||
- coverage run manage.py test
|
||||
- coverage report
|
||||
stage: test
|
||||
bandit:
|
||||
script:
|
||||
- bandit -r passbook
|
||||
stage: test
|
||||
|
||||
package-docker:
|
||||
image:
|
||||
name: gcr.io/kaniko-project/executor:debug
|
||||
entrypoint: [""]
|
||||
before_script:
|
||||
- echo "{\"auths\":{\"https://docker.$NEXUS_URL/\":{\"username\":\"$NEXUS_USER\",\"password\":\"$NEXUS_PASS\"}}}" > /kaniko/.docker/config.json
|
||||
script:
|
||||
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.0.7-alpha
|
||||
stage: build
|
||||
only:
|
||||
- tags
|
||||
- /^version/.*$/
|
||||
package-helm:
|
||||
stage: build
|
||||
script:
|
||||
- curl https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash
|
||||
- helm init --client-only
|
||||
- helm package helm/passbook
|
||||
- ./manage.py nexus_upload --method put --url $NEXUS_URL --user $NEXUS_USER --password $NEXUS_PASS --repo helm *.tgz
|
||||
only:
|
||||
- tags
|
||||
- /^version/.*$/
|
||||
# package-3.5:
|
||||
# before_script:
|
||||
# - apt update
|
||||
# - apt install -y build-essential debhelper devscripts equivs python3 python3-pip
|
||||
# - cp debian/control-3.5 debian/control
|
||||
# - mk-build-deps debian/control
|
||||
# - apt install ./*build-deps*deb -f -y
|
||||
# - "python3 -m pip install -U virtualenv"
|
||||
# - "virtualenv env"
|
||||
# - "source env/bin/activate"
|
||||
# - "pip3 install -U -r requirements.txt -r requirements-dev.txt"
|
||||
# image: debian
|
||||
# script:
|
||||
# - debuild -us -uc
|
||||
# - cp ../passbook*.deb .
|
||||
# - python manage.py nexus_upload
|
||||
# artifacts:
|
||||
# paths:
|
||||
# - passbook-python3.5*deb
|
||||
# expire_in: 2 days
|
||||
# stage: build
|
||||
# only:
|
||||
# - tags
|
||||
# - /^debian/.*$/
|
||||
# package-3.6:
|
||||
# before_script:
|
||||
# - apt update
|
||||
# - apt install -y build-essential debhelper devscripts equivs python3 python3-pip
|
||||
# - cp debian/control-3.6 debian/control
|
||||
# - mk-build-deps debian/control
|
||||
# - apt install ./*build-deps*deb -f -y
|
||||
# - "python3 -m pip install -U virtualenv"
|
||||
# - "virtualenv env"
|
||||
# - "source env/bin/activate"
|
||||
# - "pip3 install -U -r requirements.txt -r requirements-dev.txt"
|
||||
# image: debian:buster
|
||||
# script:
|
||||
# - debuild -us -uc
|
||||
# - cp ../passbook*.deb .
|
||||
# - python manage.py nexus_upload
|
||||
# artifacts:
|
||||
# paths:
|
||||
# - passbook-python3.6*deb
|
||||
# expire_in: 2 days
|
||||
# stage: build
|
||||
# only:
|
||||
# - tags
|
||||
# - /^debian/.*$r
|
||||
|
||||
# docs:
|
||||
# stage: docs
|
||||
# only:
|
||||
# - master
|
||||
# - tags
|
||||
# - /^debian/.*$/
|
||||
# environment:
|
||||
# name: docs
|
||||
# url: "https://passbook.beryju.org/docs/"
|
||||
# script:
|
||||
# - apt update
|
||||
# - apt install -y rsync
|
||||
# - "mkdir ~/.ssh"
|
||||
# - "cp .gitlab/known_hosts ~/.ssh/"
|
||||
# - "pip3 install -U -r requirements-docs.txt"
|
||||
# - "eval $(ssh-agent -s)"
|
||||
# - "echo \"${CI_SSH_PRIVATE}\" | ssh-add -"
|
||||
# - mkdocs build
|
||||
# - 'rsync -avh --delete web/* "beryjuorg@ory1-web-prod-1.ory1.beryju.org:passbook.beryju.org/"'
|
||||
# - 'rsync -avh --delete site/* "beryjuorg@ory1-web-prod-1.ory1.beryju.org:passbook.beryju.org/docs/"'
|
@ -3,7 +3,6 @@ test-warnings: true
|
||||
doc-warnings: false
|
||||
|
||||
ignore-paths:
|
||||
- env
|
||||
- migrations
|
||||
- docs
|
||||
- node_modules
|
||||
|
@ -1,10 +1,11 @@
|
||||
[MASTER]
|
||||
|
||||
disable=redefined-outer-name,arguments-differ,no-self-use,cyclic-import,fixme,locally-disabled,unpacking-non-sequence,too-many-ancestors,too-many-branches,too-few-public-methods
|
||||
disable=redefined-outer-name,arguments-differ,no-self-use,cyclic-import,fixme,locally-disabled,unpacking-non-sequence,too-many-ancestors,too-many-branches,too-few-public-methods,import-outside-toplevel,bad-continuation
|
||||
load-plugins=pylint_django,pylint.extensions.bad_builtin
|
||||
#,pylint.extensions.docparams
|
||||
extension-pkg-whitelist=lxml
|
||||
const-rgx=[a-zA-Z0-9_]{1,40}$
|
||||
ignored-modules=django-otp
|
||||
jobs=4
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
|
114
.vscode/.ropeproject/config.py
vendored
114
.vscode/.ropeproject/config.py
vendored
@ -1,114 +0,0 @@
|
||||
# The default ``config.py``
|
||||
# flake8: noqa
|
||||
|
||||
|
||||
def set_prefs(prefs):
|
||||
"""This function is called before opening the project"""
|
||||
|
||||
# Specify which files and folders to ignore in the project.
|
||||
# Changes to ignored resources are not added to the history and
|
||||
# VCSs. Also they are not returned in `Project.get_files()`.
|
||||
# Note that ``?`` and ``*`` match all characters but slashes.
|
||||
# '*.pyc': matches 'test.pyc' and 'pkg/test.pyc'
|
||||
# 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc'
|
||||
# '.svn': matches 'pkg/.svn' and all of its children
|
||||
# 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o'
|
||||
# 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o'
|
||||
prefs['ignored_resources'] = ['*.pyc', '*~', '.ropeproject',
|
||||
'.hg', '.svn', '_svn', '.git', '.tox']
|
||||
|
||||
# Specifies which files should be considered python files. It is
|
||||
# useful when you have scripts inside your project. Only files
|
||||
# ending with ``.py`` are considered to be python files by
|
||||
# default.
|
||||
# prefs['python_files'] = ['*.py']
|
||||
|
||||
# Custom source folders: By default rope searches the project
|
||||
# for finding source folders (folders that should be searched
|
||||
# for finding modules). You can add paths to that list. Note
|
||||
# that rope guesses project source folders correctly most of the
|
||||
# time; use this if you have any problems.
|
||||
# The folders should be relative to project root and use '/' for
|
||||
# separating folders regardless of the platform rope is running on.
|
||||
# 'src/my_source_folder' for instance.
|
||||
# prefs.add('source_folders', 'src')
|
||||
|
||||
# You can extend python path for looking up modules
|
||||
# prefs.add('python_path', '~/python/')
|
||||
|
||||
# Should rope save object information or not.
|
||||
prefs['save_objectdb'] = True
|
||||
prefs['compress_objectdb'] = False
|
||||
|
||||
# If `True`, rope analyzes each module when it is being saved.
|
||||
prefs['automatic_soa'] = True
|
||||
# The depth of calls to follow in static object analysis
|
||||
prefs['soa_followed_calls'] = 0
|
||||
|
||||
# If `False` when running modules or unit tests "dynamic object
|
||||
# analysis" is turned off. This makes them much faster.
|
||||
prefs['perform_doa'] = True
|
||||
|
||||
# Rope can check the validity of its object DB when running.
|
||||
prefs['validate_objectdb'] = True
|
||||
|
||||
# How many undos to hold?
|
||||
prefs['max_history_items'] = 32
|
||||
|
||||
# Shows whether to save history across sessions.
|
||||
prefs['save_history'] = True
|
||||
prefs['compress_history'] = False
|
||||
|
||||
# Set the number spaces used for indenting. According to
|
||||
# :PEP:`8`, it is best to use 4 spaces. Since most of rope's
|
||||
# unit-tests use 4 spaces it is more reliable, too.
|
||||
prefs['indent_size'] = 4
|
||||
|
||||
# Builtin and c-extension modules that are allowed to be imported
|
||||
# and inspected by rope.
|
||||
prefs['extension_modules'] = []
|
||||
|
||||
# Add all standard c-extensions to extension_modules list.
|
||||
prefs['import_dynload_stdmods'] = True
|
||||
|
||||
# If `True` modules with syntax errors are considered to be empty.
|
||||
# The default value is `False`; When `False` syntax errors raise
|
||||
# `rope.base.exceptions.ModuleSyntaxError` exception.
|
||||
prefs['ignore_syntax_errors'] = False
|
||||
|
||||
# If `True`, rope ignores unresolvable imports. Otherwise, they
|
||||
# appear in the importing namespace.
|
||||
prefs['ignore_bad_imports'] = False
|
||||
|
||||
# If `True`, rope will insert new module imports as
|
||||
# `from <package> import <module>` by default.
|
||||
prefs['prefer_module_from_imports'] = False
|
||||
|
||||
# If `True`, rope will transform a comma list of imports into
|
||||
# multiple separate import statements when organizing
|
||||
# imports.
|
||||
prefs['split_imports'] = False
|
||||
|
||||
# If `True`, rope will remove all top-level import statements and
|
||||
# reinsert them at the top of the module when making changes.
|
||||
prefs['pull_imports_to_top'] = True
|
||||
|
||||
# If `True`, rope will sort imports alphabetically by module name instead
|
||||
# of alphabetically by import statement, with from imports after normal
|
||||
# imports.
|
||||
prefs['sort_imports_alphabetically'] = False
|
||||
|
||||
# Location of implementation of
|
||||
# rope.base.oi.type_hinting.interfaces.ITypeHintingFactory In general
|
||||
# case, you don't have to change this value, unless you're an rope expert.
|
||||
# Change this value to inject you own implementations of interfaces
|
||||
# listed in module rope.base.oi.type_hinting.providers.interfaces
|
||||
# For example, you can add you own providers for Django Models, or disable
|
||||
# the search type-hinting in a class hierarchy, etc.
|
||||
prefs['type_hinting_factory'] = (
|
||||
'rope.base.oi.type_hinting.factory.default_type_hinting_factory')
|
||||
|
||||
|
||||
def project_opened(project):
|
||||
"""This function is called after opening the project"""
|
||||
# Do whatever you like here!
|
BIN
.vscode/.ropeproject/objectdb
vendored
BIN
.vscode/.ropeproject/objectdb
vendored
Binary file not shown.
11
.vscode/settings.json
vendored
11
.vscode/settings.json
vendored
@ -1,11 +0,0 @@
|
||||
{
|
||||
"python.pythonPath": "env/bin/python",
|
||||
"editor.tabSize": 4,
|
||||
"[html]": {
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"cSpell.words": [
|
||||
"SAML",
|
||||
"passbook"
|
||||
]
|
||||
}
|
37
Dockerfile
37
Dockerfile
@ -1,28 +1,31 @@
|
||||
FROM python:3.6-slim-stretch as build
|
||||
FROM python:3.8-slim-buster as locker
|
||||
|
||||
COPY ./passbook/ /app/passbook
|
||||
COPY ./manage.py /app/
|
||||
COPY ./requirements.txt /app/
|
||||
COPY ./Pipfile /app/
|
||||
COPY ./Pipfile.lock /app/
|
||||
|
||||
WORKDIR /app/
|
||||
|
||||
RUN mkdir /app/static/ && \
|
||||
pip install -r requirements.txt && \
|
||||
pip install psycopg2 && \
|
||||
./manage.py collectstatic --no-input
|
||||
RUN pip install pipenv && \
|
||||
pipenv lock -r > requirements.txt && \
|
||||
pipenv lock -rd > requirements-dev.txt
|
||||
|
||||
FROM python:3.6-slim-stretch
|
||||
FROM python:3.8-slim-buster
|
||||
|
||||
COPY ./passbook/ /app/passbook
|
||||
COPY ./manage.py /app/
|
||||
COPY ./requirements.txt /app/
|
||||
COPY --from=build /app/static /app/static/
|
||||
COPY --from=locker /app/requirements.txt /app/
|
||||
COPY --from=locker /app/requirements-dev.txt /app/
|
||||
|
||||
WORKDIR /app/
|
||||
|
||||
RUN pip install -r requirements.txt && \
|
||||
pip install psycopg2 && \
|
||||
adduser --system --home /app/ passbook && \
|
||||
chown -R passbook /app/
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends postgresql-client-11 && \
|
||||
rm -rf /var/lib/apt/ && \
|
||||
pip install -r requirements.txt --no-cache-dir && \
|
||||
adduser --system --no-create-home --uid 1000 --group --home /app passbook
|
||||
|
||||
COPY ./passbook/ /app/passbook
|
||||
COPY ./manage.py /app/
|
||||
COPY ./docker/uwsgi.ini /app/
|
||||
|
||||
WORKDIR /app/
|
||||
|
||||
USER passbook
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 BeryJu.org
|
||||
Copyright (c) 2019 BeryJu.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
61
Pipfile
Normal file
61
Pipfile
Normal file
@ -0,0 +1,61 @@
|
||||
[[source]]
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[packages]
|
||||
boto3 = "*"
|
||||
celery = "*"
|
||||
defusedxml = "*"
|
||||
django = "*"
|
||||
django-cors-middleware = "*"
|
||||
django-dbbackup = "*"
|
||||
django-filter = "*"
|
||||
django-guardian = "*"
|
||||
django-model-utils = "*"
|
||||
django-oauth-toolkit = "*"
|
||||
django-oidc-provider = "*"
|
||||
django-otp = "*"
|
||||
django-prometheus = "*"
|
||||
django-recaptcha = "*"
|
||||
django-redis = "*"
|
||||
django-rest-framework = "*"
|
||||
django-storages = "*"
|
||||
djangorestframework-guardian = "*"
|
||||
drf-yasg = "*"
|
||||
kombu = "*"
|
||||
ldap3 = "*"
|
||||
lxml = "*"
|
||||
oauthlib = "*"
|
||||
packaging = "*"
|
||||
psycopg2-binary = "*"
|
||||
pycryptodome = "*"
|
||||
pyuwsgi = "*"
|
||||
pyyaml = "*"
|
||||
qrcode = "*"
|
||||
requests-oauthlib = "*"
|
||||
sentry-sdk = "*"
|
||||
service_identity = "*"
|
||||
signxml = "*"
|
||||
structlog = "*"
|
||||
swagger-spec-validator = "*"
|
||||
urllib3 = {extras = ["secure"],version = "*"}
|
||||
jinja2 = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.8"
|
||||
|
||||
[dev-packages]
|
||||
autopep8 = "*"
|
||||
bandit = "*"
|
||||
bumpversion = "*"
|
||||
colorama = "*"
|
||||
coverage = "*"
|
||||
django-debug-toolbar = "*"
|
||||
pylint = "*"
|
||||
pylint-django = "*"
|
||||
unittest-xml-reporting = "*"
|
||||
black = "*"
|
||||
|
||||
[pipenv]
|
||||
allow_prereleases = true
|
1193
Pipfile.lock
generated
Normal file
1193
Pipfile.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
15
README.md
Normal file
15
README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# passbook
|
||||
|
||||

|
||||
|
||||
## Quick instance
|
||||
|
||||
```
|
||||
export PASSBOOK_DOMAIN=domain.tld
|
||||
# Optionally enable Error-reporting
|
||||
# export PASSBOOK_ERROR_REPORTING=true
|
||||
docker-compose pull
|
||||
docker-compose up -d
|
||||
docker-compose exec server ./manage.py migrate
|
||||
docker-compose exec server ./manage.py createsuperuser
|
||||
```
|
@ -1,27 +0,0 @@
|
||||
# Global Variables
|
||||
before_script:
|
||||
- cd allauth/
|
||||
- "python3 -m pip install -U virtualenv"
|
||||
- "virtualenv env"
|
||||
- "source env/bin/activate"
|
||||
- "pip3 install -U -r requirements-dev.txt"
|
||||
stages:
|
||||
- test-allauth
|
||||
image: python:3.6
|
||||
|
||||
isort:
|
||||
script:
|
||||
- isort -c -sg env
|
||||
stage: test-allauth
|
||||
prospector:
|
||||
script:
|
||||
- prospector
|
||||
stage: test-allauth
|
||||
pylint:
|
||||
script:
|
||||
- pylint passbook
|
||||
stage: test-allauth
|
||||
bandit:
|
||||
script:
|
||||
- bandit -r allauth_passbook
|
||||
stage: test-allauth
|
@ -1,35 +0,0 @@
|
||||
"""passbook provider"""
|
||||
from allauth.socialaccount.providers.base import ProviderAccount
|
||||
from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider
|
||||
|
||||
|
||||
class PassbookAccount(ProviderAccount):
|
||||
"""passbook account"""
|
||||
|
||||
def to_str(self):
|
||||
dflt = super().to_str()
|
||||
return self.account.extra_data.get('username', dflt)
|
||||
|
||||
|
||||
class PassbookProvider(OAuth2Provider):
|
||||
"""passbook provider"""
|
||||
|
||||
id = 'passbook'
|
||||
name = 'passbook'
|
||||
account_class = PassbookAccount
|
||||
|
||||
def extract_uid(self, data):
|
||||
return str(data['sub'])
|
||||
|
||||
def extract_common_fields(self, data):
|
||||
return {
|
||||
'email': data.get('email'),
|
||||
'username': data.get('preferred_username'),
|
||||
'name': data.get('name'),
|
||||
}
|
||||
|
||||
def get_default_scope(self):
|
||||
return ['openid:userinfo']
|
||||
|
||||
|
||||
provider_classes = [PassbookProvider] # noqa
|
@ -1,6 +0,0 @@
|
||||
"""passbook provider"""
|
||||
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
|
||||
|
||||
from allauth_passbook.provider import PassbookProvider
|
||||
|
||||
urlpatterns = default_urlpatterns(PassbookProvider)
|
@ -1,37 +0,0 @@
|
||||
"""passbook adapter"""
|
||||
import requests
|
||||
from allauth.socialaccount import app_settings
|
||||
from allauth.socialaccount.providers.oauth2.views import (OAuth2Adapter,
|
||||
OAuth2CallbackView,
|
||||
OAuth2LoginView)
|
||||
|
||||
from allauth_passbook.provider import PassbookProvider
|
||||
|
||||
|
||||
class PassbookOAuth2Adapter(OAuth2Adapter):
|
||||
"""passbook OAuth2 Adapter"""
|
||||
provider_id = PassbookProvider.id
|
||||
# pylint: disable=no-member
|
||||
settings = app_settings.PROVIDERS.get(provider_id, {}) # noqa
|
||||
provider_base_url = settings.get("PASSBOOK_URL", 'https://id.beryju.org')
|
||||
|
||||
access_token_url = '{0}/application/oauth/token/'.format(provider_base_url)
|
||||
authorize_url = '{0}/application/oauth/authorize/'.format(provider_base_url)
|
||||
profile_url = '{0}/api/v1/openid/'.format(
|
||||
provider_base_url)
|
||||
|
||||
def complete_login(self, request, app, access_token, **kwargs):
|
||||
headers = {
|
||||
'Authorization': 'Bearer {0}'.format(access_token.token),
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
extra_data = requests.get(self.profile_url, headers=headers)
|
||||
|
||||
return self.get_provider().sociallogin_from_response(
|
||||
request,
|
||||
extra_data.json()
|
||||
)
|
||||
|
||||
|
||||
oauth2_login = OAuth2LoginView.adapter_view(PassbookOAuth2Adapter) # noqa
|
||||
oauth2_callback = OAuth2CallbackView.adapter_view(PassbookOAuth2Adapter) # noqa
|
@ -1 +0,0 @@
|
||||
django-allauth
|
@ -1,33 +0,0 @@
|
||||
"""passbook allauth setup.py"""
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='django-allauth-passbook',
|
||||
version='1.0.0',
|
||||
description='passbook support for django-allauth',
|
||||
# long_description='\n'.join(read_simple('docs/index.md')[2:]),
|
||||
long_description_content_type='text/markdown',
|
||||
author='BeryJu.org',
|
||||
author_email='hello@beryju.org',
|
||||
packages=['allauth_passbook'],
|
||||
include_package_data=True,
|
||||
install_requires=['django-allauth'],
|
||||
keywords='django allauth passbook',
|
||||
license='MIT',
|
||||
classifiers=[
|
||||
'Intended Audience :: Developers',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
'Environment :: Web Environment',
|
||||
'Topic :: Internet',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Framework :: Django',
|
||||
'Framework :: Django :: 1.11',
|
||||
'Framework :: Django :: 2.0',
|
||||
'Framework :: Django :: 2.1',
|
||||
],
|
||||
)
|
87
docker-compose.yml
Normal file
87
docker-compose.yml
Normal file
@ -0,0 +1,87 @@
|
||||
---
|
||||
version: '3.2'
|
||||
|
||||
services:
|
||||
postgresql:
|
||||
image: postgres
|
||||
volumes:
|
||||
- database:/var/lib/postgresql/data
|
||||
networks:
|
||||
- internal
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=${PG_PASS:-thisisnotagoodpassword}
|
||||
- POSTGRES_USER=passbook
|
||||
- POSTGRES_DB=passbook
|
||||
labels:
|
||||
- traefik.enable=false
|
||||
redis:
|
||||
image: redis
|
||||
networks:
|
||||
- internal
|
||||
labels:
|
||||
- traefik.enable=false
|
||||
server:
|
||||
image: beryju/passbook:${SERVER_TAG:-latest}
|
||||
command:
|
||||
- uwsgi
|
||||
- uwsgi.ini
|
||||
environment:
|
||||
- PASSBOOK_DOMAIN=${PASSBOOK_DOMAIN}
|
||||
- PASSBOOK_REDIS__HOST=redis
|
||||
- PASSBOOK_ERROR_REPORTING=${PASSBOOK_ERROR_REPORTING:-false}
|
||||
- PASSBOOK_POSTGRESQL__HOST=postgresql
|
||||
- PASSBOOK_POSTGRESQL__PASSWORD=${PG_PASS:-thisisnotagoodpassword}
|
||||
ports:
|
||||
- 8000
|
||||
networks:
|
||||
- internal
|
||||
labels:
|
||||
- traefik.port=8000
|
||||
- traefik.docker.network=internal
|
||||
- traefik.frontend.rule=PathPrefix:/
|
||||
worker:
|
||||
image: beryju/passbook:${SERVER_TAG:-latest}
|
||||
command:
|
||||
- celery
|
||||
- worker
|
||||
- --autoscale=10,3
|
||||
- -E
|
||||
- -B
|
||||
- -A=passbook.root.celery
|
||||
- -s=/tmp/celerybeat-schedule
|
||||
networks:
|
||||
- internal
|
||||
labels:
|
||||
- traefik.enable=false
|
||||
environment:
|
||||
- PASSBOOK_DOMAIN=${PASSBOOK_DOMAIN}
|
||||
- PASSBOOK_REDIS__HOST=redis
|
||||
- PASSBOOK_ERROR_REPORTING=${PASSBOOK_ERROR_REPORTING:-false}
|
||||
- PASSBOOK_POSTGRESQL__HOST=postgresql
|
||||
- PASSBOOK_POSTGRESQL__PASSWORD=${PG_PASS:-thisisnotagoodpassword}
|
||||
static:
|
||||
image: beryju/passbook-static:latest
|
||||
networks:
|
||||
- internal
|
||||
labels:
|
||||
- traefik.frontend.rule=PathPrefix:/static, /robots.txt
|
||||
- traefik.port=8080
|
||||
- traefik.docker.network=internal
|
||||
traefik:
|
||||
image: traefik:1.7
|
||||
command: --api --docker
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
ports:
|
||||
- "0.0.0.0:80:80"
|
||||
- "0.0.0.0:443:443"
|
||||
- "0.0.0.0:8080:8080"
|
||||
networks:
|
||||
- internal
|
||||
|
||||
volumes:
|
||||
database:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
internal: {}
|
10
docker/uwsgi.ini
Normal file
10
docker/uwsgi.ini
Normal file
@ -0,0 +1,10 @@
|
||||
[uwsgi]
|
||||
http = 0.0.0.0:8000
|
||||
wsgi-file = passbook/root/wsgi.py
|
||||
processes = 2
|
||||
master = true
|
||||
threads = 2
|
||||
enable-threads = true
|
||||
uid = passbook
|
||||
gid = passbook
|
||||
disable-logging = True
|
14
docs/Dockerfile
Normal file
14
docs/Dockerfile
Normal file
@ -0,0 +1,14 @@
|
||||
FROM python:3.8-slim-buster as builder
|
||||
|
||||
WORKDIR /mkdocs
|
||||
|
||||
RUN pip install mkdocs mkdocs-material
|
||||
|
||||
COPY docs/ docs
|
||||
COPY mkdocs.yml .
|
||||
|
||||
RUN mkdocs build
|
||||
|
||||
FROM nginx
|
||||
|
||||
COPY --from=builder /mkdocs/site /usr/share/nginx/html
|
23
docs/factors.md
Normal file
23
docs/factors.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Factors
|
||||
|
||||
A factor represents a single authenticating factor for a user. Common examples of this would be a password or an OTP. These factors can be combined in any order, and can be dynamically enabled using policies.
|
||||
|
||||
## Password Factor
|
||||
|
||||
This is the standard Password Factor. It allows you to select which Backend the password is checked with. here you can also specify which Policies are used to check the password. You can also specify which Factors a User has to pass to recover their account.
|
||||
|
||||
## Dummy Factor
|
||||
|
||||
This factor waits a random amount of time. Mostly used for debugging.
|
||||
|
||||
## E-Mail Factor
|
||||
|
||||
This factor is mostly for recovery, and used in conjunction with the Password Factor.
|
||||
|
||||
## OTP Factor
|
||||
|
||||
This is your typical One-Time Password implementation, compatible with Authy and Google Authenticator. You can enfore this Factor so that every user has to configure it, or leave it optional.
|
||||
|
||||
## Captcha Factor
|
||||
|
||||
While this factor doesn't really authenticate a user, it is part of the Authentication Flow. passbook uses Google's reCaptcha implementation.
|
55
docs/images/logo.svg
Normal file
55
docs/images/logo.svg
Normal file
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<path style="fill:#57565C;" d="M407,512H105C47.103,512,0,464.897,0,407V105C0,47.103,47.103,0,105,0h302
|
||||
c57.897,0,105,47.103,105,105v302C512,464.897,464.897,512,407,512z"/>
|
||||
<path style="fill:#3E3D42;" d="M407,0H256v512h151c57.897,0,105-47.103,105-105V105C512,47.103,464.897,0,407,0z"/>
|
||||
<rect x="91" y="141" style="fill:#00C3FF;" width="330" height="44"/>
|
||||
<rect x="256" y="141" style="fill:#00AAF0;" width="165" height="44"/>
|
||||
<rect x="91" y="176" style="fill:#FFDC40;" width="330" height="44"/>
|
||||
<rect x="256" y="176" style="fill:#FFAB15;" width="165" height="44"/>
|
||||
<rect x="91" y="206" style="fill:#87E694;" width="330" height="44"/>
|
||||
<rect x="256" y="206" style="fill:#66CC70;" width="165" height="44"/>
|
||||
<path style="fill:#F2F2F2;" d="M421,381c0,8.284-6.716,15-15,15H106c-8.284,0-15-6.716-15-15v-85h89.997
|
||||
c9.31,0,17.688,4.938,21.868,12.888C213.277,328.695,233.638,341,256,341s42.723-12.305,53.135-32.111
|
||||
c4.18-7.95,12.559-12.889,21.868-12.889H421V381z"/>
|
||||
<path style="fill:#FF6849;" d="M421,266h-89.997c-20.487,0-39.041,11.085-48.423,28.929C277.369,304.842,267.185,311,256,311
|
||||
s-21.369-6.158-26.58-16.071C220.038,277.085,201.484,266,180.997,266H91v-30h330V266z"/>
|
||||
<path style="fill:#F2F2F2;" d="M421,146H91v-15c0-8.284,6.716-15,15-15h300c8.284,0,15,6.716,15,15V146z"/>
|
||||
<path style="fill:#E5E5E5;" d="M331.003,296c-9.31,0-17.688,4.938-21.868,12.889C298.723,328.695,278.362,341,256,341v55h150
|
||||
c8.284,0,15-6.716,15-15v-85H331.003z"/>
|
||||
<path style="fill:#FD4B2D;" d="M256,236v75c11.185,0,21.369-6.158,26.58-16.071C291.962,277.085,310.516,266,331.003,266H421v-30
|
||||
H256z"/>
|
||||
<path style="fill:#E5E5E5;" d="M406,116H256v30h165v-15C421,122.716,414.284,116,406,116z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
31
docs/index.md
Executable file
31
docs/index.md
Executable file
@ -0,0 +1,31 @@
|
||||
# Welcome
|
||||
|
||||
Welcome to the passbook Documentation. passbook is an open-source Identity Provider and Usermanagement software. It can be used as a central directory for users or customers and it can integrate with your existing Directory.
|
||||
|
||||
passbook can also be used as part of an Application to facilitate User Enrollment, Password recovery and Social Login.
|
||||
|
||||
passbook uses the following Terminology:
|
||||
|
||||
### Policy
|
||||
|
||||
A Policy is at a base level a yes/no gate. It will either evaluate to True or False depending on the Policy Kind and settings. For example, a "Group Membership Policy" evaluates to True if the User is member of the specified Group and False if not. This can be used to conditionally apply Factors and grant/deny access.
|
||||
|
||||
### Provider
|
||||
|
||||
A Provider is a way for other Applications to authenticate against passbook. Common Providers are OpenID Connect (OIDC) and SAML.
|
||||
|
||||
### Source
|
||||
|
||||
Sources are ways to get users into passbook. This might be an LDAP Connection to import Users from Active Directory, or an OAuth2 Connection to allow Social Logins.
|
||||
|
||||
### Application
|
||||
|
||||
An application links together Policies with a Provider, allowing you to control access. It also holds Information like UI Name, Icon and more.
|
||||
|
||||
### Factors
|
||||
|
||||
Factors represent Authentication Factors, like a Password or OTP. These Factors can be dynamically enabled using policies. This allows you to, for example, force users from a certain IP ranges to complete a Captcha to authenticate.
|
||||
|
||||
### Property Mappings
|
||||
|
||||
Property Mappings allow you to make Information available for external Applications. For example, if you want to login to AWS with passbook, you'd use Property Mappings to set the User's Roles based on their Groups.
|
26
docs/installation/docker-compose.md
Normal file
26
docs/installation/docker-compose.md
Normal file
@ -0,0 +1,26 @@
|
||||
# docker-compose
|
||||
|
||||
This installation Method is for test-setups and small-scale productive setups.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- docker
|
||||
- docker-compose
|
||||
|
||||
## Install
|
||||
|
||||
Download the latest `docker-compose.yml` from [here](https://raw.githubusercontent.com/BeryJu/passbook/master/docker-compose.yml). Place it in a directory of your choice.
|
||||
|
||||
passbook needs to know it's primary URL to create links in E-Mails and set cookies, so you have to run the following command:
|
||||
|
||||
```
|
||||
export PASSBOOK_DOMAIN=domain.tld # this can be any domain or IP, it just needs to point to passbook.
|
||||
```
|
||||
|
||||
The compose file references the current latest version, which can be overridden with the `SERVER_TAG` Environment variable.
|
||||
|
||||
If you plan to use this setup for production, it is also advised to change the PostgreSQL Password by setting `PG_PASS` to a password of your choice.
|
||||
|
||||
Now you can pull the Docker images needed by running `docker-compose pull`. After this has finished, run `docker-compose up -d` to start passbook.
|
||||
|
||||
passbook will then be reachable on Port 80. You can optionally configure the packaged traefik to use Let's Encrypt for TLS Encryption.
|
6
docs/installation/install.md
Executable file
6
docs/installation/install.md
Executable file
@ -0,0 +1,6 @@
|
||||
# Installation
|
||||
|
||||
There are two supported ways to install passbook:
|
||||
|
||||
- [docker-compose](docker-compose.md) for test- or small productive setups
|
||||
- [Kubernetes](./kubernetes.md) for larger Productive setups
|
3
docs/installation/kubernetes.md
Normal file
3
docs/installation/kubernetes.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Kubernetes
|
||||
|
||||
For a mid to high-load Installation, Kubernetes is recommended. passbook is installed using a helm-chart.
|
59
docs/integrations/services/gitlab/index.md
Normal file
59
docs/integrations/services/gitlab/index.md
Normal file
@ -0,0 +1,59 @@
|
||||
# GitLab Integration
|
||||
|
||||
## What is GitLab
|
||||
|
||||
From https://about.gitlab.com/what-is-gitlab/
|
||||
|
||||
```
|
||||
GitLab is a complete DevOps platform, delivered as a single application. This makes GitLab unique and makes Concurrent DevOps possible, unlocking your organization from the constraints of a pieced together toolchain. Join us for a live Q&A to learn how GitLab can give you unmatched visibility and higher levels of efficiency in a single application across the DevOps lifecycle.
|
||||
```
|
||||
|
||||
## Preparation
|
||||
|
||||
The following placeholders will be used:
|
||||
|
||||
- `gitlab.company` is the FQDN of the GitLab Install
|
||||
- `passbook.company` is the FQDN of the passbook Install
|
||||
|
||||
Create an application in passbook and note the slug, as this will be used later. Create a SAML Provider with the following Parameters:
|
||||
|
||||
- ACS URL: `https://gitlab.company/users/auth/saml/callback`
|
||||
- Audience: `https://gitlab.company`
|
||||
- Issuer: `https://gitlab.company`
|
||||
|
||||
You can of course use a custom Signing Certificate, and adjust the Assertion Length. To get the value for `idp_cert_fingerprint`, you can use a tool like [this](https://www.samltool.com/fingerprint.php).
|
||||
|
||||
## GitLab Configuration
|
||||
|
||||
Paste the following block in your `gitlab.rb` file, after replacing the placeholder values from above. The file is located in `/etc/gitlab`.
|
||||
|
||||
```ruby
|
||||
gitlab_rails['omniauth_enabled'] = true
|
||||
gitlab_rails['omniauth_allow_single_sign_on'] = ['saml']
|
||||
gitlab_rails['omniauth_sync_email_from_provider'] = 'saml'
|
||||
gitlab_rails['omniauth_sync_profile_from_provider'] = ['saml']
|
||||
gitlab_rails['omniauth_sync_profile_attributes'] = ['email']
|
||||
gitlab_rails['omniauth_auto_sign_in_with_provider'] = 'saml'
|
||||
gitlab_rails['omniauth_block_auto_created_users'] = false
|
||||
gitlab_rails['omniauth_auto_link_saml_user'] = true
|
||||
gitlab_rails['omniauth_providers'] = [
|
||||
{
|
||||
name: 'saml',
|
||||
args: {
|
||||
assertion_consumer_service_url: 'https://gitlab.company/users/auth/saml/callback',
|
||||
idp_cert_fingerprint: '4E:1E:CD:67:4A:67:5A:E9:6A:D0:3C:E6:DD:7A:F2:44:2E:76:00:6A',
|
||||
idp_sso_target_url: 'https://passbook.company/application/saml/<passbook application slug>/login/',
|
||||
issuer: 'https://gitlab.company',
|
||||
name_identifier_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
|
||||
attribute_statements: {
|
||||
email: ['urn:oid:1.3.6.1.4.1.5923.1.1.1.6'],
|
||||
first_name: ['urn:oid:2.5.4.3'],
|
||||
nickname: ['urn:oid:2.16.840.1.113730.3.1.241']
|
||||
}
|
||||
},
|
||||
label: 'passbook'
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Afterwards, either run `gitlab-ctl reconfigure` if you're running GitLab Omnibus, or restart the container if you're using the container.
|
BIN
docs/integrations/services/harbor/harbor.png
Normal file
BIN
docs/integrations/services/harbor/harbor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 348 KiB |
28
docs/integrations/services/harbor/index.md
Normal file
28
docs/integrations/services/harbor/index.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Harbor Integration
|
||||
|
||||
## What is Harbor
|
||||
|
||||
From https://goharbor.io
|
||||
|
||||
```
|
||||
Harbor is an open source container image registry that secures images with role-based access control, scans images for vulnerabilities, and signs images as trusted. A CNCF Incubating project, Harbor delivers compliance, performance, and interoperability to help you consistently and securely manage images across cloud native compute platforms like Kubernetes and Docker.
|
||||
```
|
||||
|
||||
## Preparation
|
||||
|
||||
The following placeholders will be used:
|
||||
|
||||
- `harbor.company` is the FQDN of the Harbor Install
|
||||
- `passbook.company` is the FQDN of the passbook Install
|
||||
|
||||
Create an application in passbook. Create an OpenID Provider with the following Parameters:
|
||||
|
||||
- Client Type: `Confidential`
|
||||
- Response types: `code (Authorization Code Flow)`
|
||||
- JWT Algorithm: `RS256`
|
||||
- Redirect URIs: `https://harbor.company/c/oidc/callback`
|
||||
- Scopes: `openid`
|
||||
|
||||
## Harbor
|
||||
|
||||

|
29
docs/integrations/services/rancher/index.md
Normal file
29
docs/integrations/services/rancher/index.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Rancher Integration
|
||||
|
||||
## What is Rancher
|
||||
|
||||
From https://rancher.com/products/rancher
|
||||
|
||||
```
|
||||
An Enterprise Platform for Managing Kubernetes Everywhere
|
||||
Rancher is a platform built to address the needs of the DevOps teams deploying applications with Kubernetes, and the IT staff responsible for delivering an enterprise-critical service.
|
||||
```
|
||||
|
||||
## Preparation
|
||||
|
||||
The following placeholders will be used:
|
||||
|
||||
- `rancher.company` is the FQDN of the Rancher Install
|
||||
- `passbook.company` is the FQDN of the passbook Install
|
||||
|
||||
Create an application in passbook and note the slug, as this will be used later. Create a SAML Provider with the following Parameters:
|
||||
|
||||
- ACS URL: `https://rancher.company/v1-saml/adfs/saml/acs`
|
||||
- Audience: `https://rancher.company/v1-saml/adfs/saml/metadata`
|
||||
- Issuer: `passbook`
|
||||
|
||||
You can of course use a custom Signing Certificate, and adjust the Assertion Length.
|
||||
|
||||
## Rancher
|
||||
|
||||

|
BIN
docs/integrations/services/rancher/rancher.png
Normal file
BIN
docs/integrations/services/rancher/rancher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 525 KiB |
42
docs/integrations/services/sentry/index.md
Normal file
42
docs/integrations/services/sentry/index.md
Normal file
@ -0,0 +1,42 @@
|
||||
# Sentry Integration
|
||||
|
||||
## What is Sentry
|
||||
|
||||
From https://sentry.io
|
||||
|
||||
```
|
||||
Sentry provides self-hosted and cloud-based error monitoring that helps all software
|
||||
teams discover, triage, and prioritize errors in real-time.
|
||||
|
||||
One million developers at over fifty thousand companies already ship
|
||||
better software faster with Sentry. Won’t you join them?
|
||||
```
|
||||
|
||||
## Preparation
|
||||
|
||||
The following placeholders will be used:
|
||||
|
||||
- `sentry.company` is the FQDN of the Sentry Install
|
||||
- `passbook.company` is the FQDN of the passbook Install
|
||||
|
||||
Create an application in passbook. Create an OpenID Provider with the following Parameters:
|
||||
|
||||
- Client Type: `Confidential`
|
||||
- Response types: `code (Authorization Code Flow)`
|
||||
- JWT Algorithm: `RS256`
|
||||
- Redirect URIs: `https://sentry.company/auth/sso/`
|
||||
- Scopes: `openid email`
|
||||
|
||||
## Sentry
|
||||
|
||||
**This guide assumes you've installed Sentry using [getsentry/onpremise](https://github.com/getsentry/onpremise)**
|
||||
|
||||
- Add `sentry-auth-oidc` to `onpremise/sentry/requirements.txt` (Create the file if it doesn't exist yet)
|
||||
- Add the following block to your `onpremise/sentry/sentry.conf.py`:
|
||||
```
|
||||
OIDC_ISSUER = "passbook"
|
||||
OIDC_CLIENT_ID = "<Client ID from passbook>"
|
||||
OIDC_CLIENT_SECRET = "<Client Secret from passbook>"
|
||||
OIDC_SCOPE = "openid email"
|
||||
OIDC_DOMAIN = "https://passbook.company/application/oidc/"
|
||||
```
|
33
docs/k8s/deployment.yml
Normal file
33
docs/k8s/deployment.yml
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
apiVersion: apps/v1beta2
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: passbook-docs
|
||||
namespace: prod-passbook-docs
|
||||
labels:
|
||||
app.kubernetes.io/name: passbook-docs
|
||||
app.kubernetes.io/managed-by: passbook-docs
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: passbook-docs
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: passbook-docs
|
||||
spec:
|
||||
containers:
|
||||
- name: passbook-docs
|
||||
image: "beryju/passbook-docs:latest"
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
resources:
|
||||
limits:
|
||||
cpu: 10m
|
||||
memory: 20Mi
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 20Mi
|
21
docs/k8s/ingress.yml
Normal file
21
docs/k8s/ingress.yml
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: passbook-docs
|
||||
name: passbook-docs
|
||||
namespace: prod-passbook-docs
|
||||
spec:
|
||||
rules:
|
||||
- host: docs.passbook.beryju.org
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: passbook-docs-http
|
||||
servicePort: http
|
||||
path: /
|
||||
tls:
|
||||
- hosts:
|
||||
- docs.passbook.beryju.org
|
||||
secretName: passbook-docs-acme
|
17
docs/k8s/service.yml
Normal file
17
docs/k8s/service.yml
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: passbook-docs-http
|
||||
namespace: prod-passbook-docs
|
||||
labels:
|
||||
app.kubernetes.io/name: passbook-docs
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app.kubernetes.io/name: passbook-docs
|
68
docs/policies.md
Normal file
68
docs/policies.md
Normal file
@ -0,0 +1,68 @@
|
||||
# Policies
|
||||
|
||||
## Kinds
|
||||
|
||||
There are two different Kind of policies, a Standard Policy and a Password Policy. Normal Policies just evaluate to True or False, and can be used everywhere. Password Policies apply when a Password is set (during User enrollment, Recovery or anywhere else). These policies can be used to apply Password Rules like length, etc. The can also be used to expire passwords after a certain amount of time.
|
||||
|
||||
## Standard Policies
|
||||
|
||||
---
|
||||
|
||||
### Group-Membership Policy
|
||||
|
||||
This policy evaluates to True if the current user is a Member of the selected group.
|
||||
|
||||
### Reputation Policy
|
||||
|
||||
passbook keeps track of failed login attempts by Source IP and Attempted Username. These values are saved as scores. Each failed login decreases the Score for the Client IP as well as the targeted Username by one.
|
||||
|
||||
This policy can be used to for example prompt Clients with a low score to pass a Captcha before they can continue.
|
||||
|
||||
### Field matcher Policy
|
||||
|
||||
This policy allows you to evaluate arbitrary comparisons against the User instance. Currently supported fields are:
|
||||
|
||||
- Username
|
||||
- E-Mail
|
||||
- Name
|
||||
- Is_active
|
||||
- Date joined
|
||||
|
||||
Any of the following operations are supported:
|
||||
|
||||
- Starts with
|
||||
- Ends with
|
||||
- Contains
|
||||
- Regexp (standard Python engine)
|
||||
- Exact
|
||||
|
||||
### SSO Policy
|
||||
|
||||
This policy evaluates to True if the current Authentication Flow has been initiated through an external Source, like OAuth and SAML.
|
||||
|
||||
### Webhook Policy
|
||||
|
||||
This policy allows you to send an arbitrary HTTP Request to any URL. You can then use JSONPath to extract the result you need.
|
||||
|
||||
## Password Policies
|
||||
|
||||
---
|
||||
|
||||
### Password Policy
|
||||
|
||||
This Policy allows you to specify Password rules, like Length and required Characters.
|
||||
The following rules can be set:
|
||||
|
||||
- Minimum amount of Uppercase Characters
|
||||
- Minimum amount of Lowercase Characters
|
||||
- Minimum amount of Symbols Characters
|
||||
- Minimum Length
|
||||
- Symbol charset (define which characters are counted as symbols)
|
||||
|
||||
### Have I Been Pwned Policy
|
||||
|
||||
This Policy checks the hashed Password against the [Have I Been Pwned](https://haveibeenpwned.com/) API. This only sends the first 5 characters of the hashed password. The remaining comparison is done within passbook.
|
||||
|
||||
### Password-Expiry Policy
|
||||
|
||||
This policy can enforce regular password rotation by expiring set Passwords after a finite amount of time. This forces users to set a new password.
|
21
docs/property-mappings.md
Normal file
21
docs/property-mappings.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Property Mappings
|
||||
|
||||
Property Mappings allow you to pass information to external Applications. For example, pass the current user's Groups as a SAML Parameter. Property Mappings are also used to map Source fields to passbook fields, for example when using LDAP.
|
||||
|
||||
## SAML Property Mapping
|
||||
|
||||
SAML Property Mappings allow you embed Information into the SAML AuthN Request. THis Information can then be used by the Application to assign permissions for example.
|
||||
|
||||
You can find examples [here](integrations/)
|
||||
|
||||
## LDAP Property Mapping
|
||||
|
||||
LDAP Property Mappings are used when you define a LDAP Source. These Mappings define which LDAP Property maps to which passbook Property. By default, these mappings are created:
|
||||
|
||||
- Autogenerated LDAP Mapping: givenName -> first_name
|
||||
- Autogenerated LDAP Mapping: mail -> email
|
||||
- Autogenerated LDAP Mapping: name -> name
|
||||
- Autogenerated LDAP Mapping: sAMAccountName -> username
|
||||
- Autogenerated LDAP Mapping: sn -> last_name
|
||||
|
||||
These are configured for the most common LDAP Setups.
|
23
docs/providers.md
Normal file
23
docs/providers.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Providers
|
||||
|
||||
Providers allow external Applications to authenticate against passbook and use its User Information.
|
||||
|
||||
## OpenID Provider
|
||||
|
||||
This provider uses the commonly used OpenID Connect variation of OAuth2.
|
||||
|
||||
## OAuth2 Provider
|
||||
|
||||
This provider is slightly different than the OpenID Provider. While it uses the same basic OAuth2 Protocol, it provides a GitHub-compatible Endpoint. This allows you to integrate Applications, which don't support Custom OpenID Providers.
|
||||
The API exposes Username, E-Mail, Name and Groups in a GitHub-compatible format.
|
||||
|
||||
## SAML Provider
|
||||
|
||||
This provider allows you to integrate Enterprise Software using the SAML2 Protocol. It supports signed Requests. This Provider also has [Property Mappings](property-mappings.md#saml-property-mapping), which allows you to expose Vendor-specific Fields.
|
||||
Default fields are:
|
||||
|
||||
- `eduPersonPrincipalName`: User's E-Mail
|
||||
- `cn`: User's Full Name
|
||||
- `mail`: User's E-Mail
|
||||
- `displayName`: User's Username
|
||||
- `uid`: User Unique Identifier
|
20
docs/reference/property-mappings/user-object.md
Normal file
20
docs/reference/property-mappings/user-object.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Passbook User Object
|
||||
|
||||
The User object has the following attributes:
|
||||
|
||||
- `username`: User's Username
|
||||
- `email` User's E-Mail
|
||||
- `name` User's Display Name
|
||||
- `is_staff` Boolean field if user is staff
|
||||
- `is_active` Boolean field if user is active
|
||||
- `date_joined` Date User joined/was created
|
||||
- `password_change_date` Date Password was last changed
|
||||
- `attributes` Dynamic Attributes
|
||||
|
||||
## Examples
|
||||
|
||||
List all the User's Group Names
|
||||
|
||||
```jinja2
|
||||
[{% for group in user.groups.all() %}'{{ group.name }}',{% endfor %}]
|
||||
```
|
39
docs/sources.md
Normal file
39
docs/sources.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Sources
|
||||
|
||||
Sources allow you to connect passbook to an existing User directory. They can also be used for Social-Login, using external Providers like Facebook, Twitter, etc.
|
||||
|
||||
## Generic OAuth Source
|
||||
|
||||
**All Integration-specific Sources are documented in the Integrations Section**
|
||||
|
||||
This source allows users to enroll themselves with an External OAuth-based Identity Provider. The Generic Provider expects the Endpoint to return OpenID-Connect compatible Information. Vendor specific Implementations have their own OAuth Source.
|
||||
|
||||
- Policies: Allow/Forbid Users from linking their Accounts with this Provider
|
||||
- Request Token URL: This field is used for OAuth v1 Implementations and will be provided by the Provider.
|
||||
- Authorization URL: This value will be provided by the Provider.
|
||||
- Access Token URL: This value will be provided by the Provider.
|
||||
- Profile URL: This URL is called by passbook to retrieve User information upon successful authentication.
|
||||
- Consumer key/Consumer secret: These values will be provided by the Provider.
|
||||
|
||||
## SAML Source
|
||||
|
||||
This source allows passbook to act as a SAML Service Provider. Just like the SAML Provider, it supports signed Requests. Vendor specific documentation can be found in the Integrations Section
|
||||
|
||||
## LDAP Source
|
||||
|
||||
This source allows you to import Users and Groups from an LDAP Server
|
||||
|
||||
- Server URI: URI to your LDAP Server/Domain Controller
|
||||
- Bind CN: CN to bind as, this can also be a UPN in the format of `user@domain.tld`
|
||||
- Bind password: Password used during the bind process
|
||||
- Enable Start TLS: Enables StartTLS functionality. To use SSL instead, use port `636`
|
||||
- Base DN: Base DN used for all LDAP queries
|
||||
- Addition User DN: Prepended to Base DN for User-queries.
|
||||
- Addition Group DN: Prepended to Base DN for Group-queries.
|
||||
- User object filter: Consider Objects matching this filter to be Users.
|
||||
- Group object filter: Consider Objects matching this filter to be Groups.
|
||||
- User group membership field: Field which contains Groups of user.
|
||||
- Object uniqueness field: Field which contains a unique Identifier.
|
||||
- Sync groups: Enable/disable Group synchronization. Groups are synced in the background every 5 minutes.
|
||||
- Sync parent group: Optionally set this Group as parent Group for all synced Groups (allows you to, for example, import AD Groups under a root `imported-from-ad` group.)
|
||||
- Property mappings: Define which LDAP Properties map to which passbook Properties. The default set of Property Mappings is generated for Active Directory. See also [LDAP Property Mappings](property-mappings.md#ldap-property-mapping)
|
8
gatekeeper/Dockerfile
Normal file
8
gatekeeper/Dockerfile
Normal file
@ -0,0 +1,8 @@
|
||||
FROM quay.io/pusher/oauth2_proxy
|
||||
|
||||
COPY templates /templates
|
||||
|
||||
ENV OAUTH2_PROXY_EMAIL_DOMAINS=*
|
||||
ENV OAUTH2_PROXY_PROVIDER=oidc
|
||||
ENV OAUTH2_PROXY_CUSTOM_TEMPLATES_DIR=/templates
|
||||
ENV OAUTH2_PROXY_HTTP_ADDRESS=:4180
|
18
gatekeeper/templates/error.html
Normal file
18
gatekeeper/templates/error.html
Normal file
@ -0,0 +1,18 @@
|
||||
{{define "error.html"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" charset="utf-8">
|
||||
|
||||
<head>
|
||||
<title>{{.Title}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>{{.Title}}</h2>
|
||||
<p>{{.Message}}</p>
|
||||
<hr>
|
||||
<p><a href="{{.ProxyPrefix}}/sign_in">Sign In</a></p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
{{end}}
|
119
gatekeeper/templates/sign_in.html
Normal file
119
gatekeeper/templates/sign_in.html
Normal file
@ -0,0 +1,119 @@
|
||||
{{define "sign_in.html"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" charset="utf-8">
|
||||
<head>
|
||||
<title>Sign In with passbook</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<style>
|
||||
body {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.42857143;
|
||||
color: #333;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.signin {
|
||||
display: block;
|
||||
margin: 20px auto;
|
||||
max-width: 400px;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
color: #fff;
|
||||
background-color: #428bca;
|
||||
border: 1px solid #357ebd;
|
||||
-webkit-border-radius: 4;
|
||||
-moz-border-radius: 4;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
padding: 6px 12px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #3071a9;
|
||||
border-color: #285e8e;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.42857143;
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
|
||||
-webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
|
||||
-o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: block;
|
||||
font-size: 10px;
|
||||
color: #aaa;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
footer a {
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
color: #aaa;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
color: #aaa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="signin center">
|
||||
<form method="GET" action="{{.ProxyPrefix}}/start">
|
||||
<input type="hidden" name="rd" value="{{.Redirect}}">
|
||||
<button type="submit" class="btn">Sign in with passbook</button><br />
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
if (window.location.hash) {
|
||||
(function () {
|
||||
var inputs = document.getElementsByName('rd');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
inputs[i].value += window.location.hash;
|
||||
}
|
||||
})();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
{{end}}
|
@ -1,9 +1,9 @@
|
||||
dependencies:
|
||||
- name: redis
|
||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
||||
version: 5.1.0
|
||||
- name: postgresql
|
||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
||||
version: 3.10.1
|
||||
digest: sha256:04bd136761f070e94a2ff32ff48ff87f5e07fbd451e5fd7f65551e3bd4680e5e
|
||||
generated: 2019-02-08T12:08:49.090666+01:00
|
||||
version: 6.5.8
|
||||
- name: redis
|
||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
||||
version: 9.5.1
|
||||
digest: sha256:f18b5dc8d0be13d584407405c60d10b6b84d25f7fa8aaa3dd0e5385c38f5c516
|
||||
generated: "2019-12-14T13:33:48.4341939Z"
|
6
helm/Chart.yaml
Normal file
6
helm/Chart.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
appVersion: "0.7.15-beta"
|
||||
description: A Helm chart for passbook.
|
||||
name: passbook
|
||||
version: "0.7.15-beta"
|
||||
icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png
|
@ -1,6 +0,0 @@
|
||||
apiVersion: v1
|
||||
appVersion: "0.0.7-alpha"
|
||||
description: A Helm chart for passbook.
|
||||
name: passbook
|
||||
version: 1.0.0
|
||||
icon: https://passbook.beryju.org/images/logo.png
|
Binary file not shown.
Binary file not shown.
@ -1,138 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "passbook.fullname" . }}-config
|
||||
data:
|
||||
config.yml: |
|
||||
# Env for Docker images
|
||||
databases:
|
||||
default:
|
||||
engine: django.db.backends.postgresql
|
||||
name: {{ .Values.postgresql.postgresqlDatabase }}
|
||||
user: postgres
|
||||
password: {{ .Values.postgresql.postgresqlPassword }}
|
||||
host: {{ .Release.Name }}-postgresql
|
||||
port: ''
|
||||
log:
|
||||
level:
|
||||
console: DEBUG
|
||||
file: DEBUG
|
||||
file: /dev/null
|
||||
syslog:
|
||||
host: 127.0.0.1
|
||||
port: 514
|
||||
email:
|
||||
host: localhost
|
||||
port: 25
|
||||
user: ''
|
||||
password: ''
|
||||
use_tls: false
|
||||
use_ssl: false
|
||||
from: passbook <passbook@domain.tld>
|
||||
web:
|
||||
listen: 0.0.0.0
|
||||
port: 8000
|
||||
threads: 30
|
||||
debug: false
|
||||
secure_proxy_header:
|
||||
HTTP_X_FORWARDED_PROTO: https
|
||||
redis: {{ .Release.Name }}-redis
|
||||
# Error reporting, sends stacktrace to sentry.services.beryju.org
|
||||
error_report_enabled: {{ .Values.config.error_reporting }}
|
||||
|
||||
{{- if .Values.config.secret_key }}
|
||||
secret_key: {{ .Values.config.secret_key }}
|
||||
{{- else }}
|
||||
secret_key: {{ randAlphaNum 50 }}
|
||||
{{- end }}
|
||||
|
||||
domains:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
|
||||
passbook:
|
||||
sign_up:
|
||||
# Enables signup, created users are stored in internal Database and created in LDAP if ldap.create_users is true
|
||||
enabled: true
|
||||
password_reset:
|
||||
# Enable password reset, passwords are reset in internal Database and in LDAP if ldap.reset_password is true
|
||||
enabled: true
|
||||
# Verification the user has to provide in order to be able to reset passwords. Can be any combination of `email`, `2fa`, `security_questions`
|
||||
verification:
|
||||
- email
|
||||
# Text used in title, on login page and multiple other places
|
||||
branding: passbook
|
||||
login:
|
||||
# Override URL used for logo
|
||||
logo_url: null
|
||||
# Override URL used for Background on Login page
|
||||
bg_url: null
|
||||
# Optionally add a subtext, placed below logo on the login page
|
||||
subtext: null
|
||||
footer:
|
||||
links:
|
||||
# Optionally add links to the footer on the login page
|
||||
# - name: test
|
||||
# href: https://test
|
||||
# Specify which fields can be used to authenticate. Can be any combination of `username` and `email`
|
||||
uid_fields:
|
||||
- username
|
||||
- email
|
||||
session:
|
||||
remember_age: 2592000 # 60 * 60 * 24 * 30, one month
|
||||
# Provider-specific settings
|
||||
ldap:
|
||||
# # Completely enable or disable LDAP provider
|
||||
# enabled: false
|
||||
# # AD Domain, used to generate `userPrincipalName`
|
||||
# domain: corp.contoso.com
|
||||
# # Base DN in which passbook should look for users
|
||||
# base_dn: dn=corp,dn=contoso,dn=com
|
||||
# # LDAP field which is used to set the django username
|
||||
# username_field: sAMAccountName
|
||||
# # LDAP server to connect to, can be set to `<domain_name>`
|
||||
# server:
|
||||
# name: corp.contoso.com
|
||||
# use_tls: false
|
||||
# # Bind credentials, used for account creation
|
||||
# bind:
|
||||
# username: Administraotr@corp.contoso.com
|
||||
# password: VerySecurePassword!
|
||||
# Which field from `uid_fields` maps to which LDAP Attribute
|
||||
login_field_map:
|
||||
username: sAMAccountName
|
||||
email: mail # or userPrincipalName
|
||||
user_attribute_map:
|
||||
active_directory:
|
||||
sAMAccountName: username
|
||||
mail: email
|
||||
given_name: first_name
|
||||
name: last_name
|
||||
# # Create new users in LDAP upon sign-up
|
||||
# create_users: true
|
||||
# # Reset LDAP password when user reset their password
|
||||
# reset_password: true
|
||||
oauth_client:
|
||||
# List of python packages with sources types to load.
|
||||
types:
|
||||
- passbook.oauth_client.source_types.discord
|
||||
- passbook.oauth_client.source_types.facebook
|
||||
- passbook.oauth_client.source_types.github
|
||||
- passbook.oauth_client.source_types.google
|
||||
- passbook.oauth_client.source_types.reddit
|
||||
- passbook.oauth_client.source_types.supervisr
|
||||
- passbook.oauth_client.source_types.twitter
|
||||
saml_idp:
|
||||
signing: true
|
||||
autosubmit: false
|
||||
issuer: passbook
|
||||
assertion_valid_for: 86400
|
||||
# List of python packages with provider types to load.
|
||||
types:
|
||||
- passbook.saml_idp.processors.generic
|
||||
- passbook.saml_idp.processors.gitlab
|
||||
- passbook.saml_idp.processors.nextcloud
|
||||
- passbook.saml_idp.processors.salesforce
|
||||
- passbook.saml_idp.processors.shibboleth
|
||||
- passbook.saml_idp.processors.wordpress_orange
|
@ -1,66 +0,0 @@
|
||||
apiVersion: apps/v1beta2
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "passbook.fullname" . }}-web
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
helm.sh/chart: {{ include "passbook.chart" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
spec:
|
||||
volumes:
|
||||
- name: config-volume
|
||||
configMap:
|
||||
name: {{ include "passbook.fullname" . }}-config
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "docker.pkg.beryju.org/passbook:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["/bin/sh","-c"]
|
||||
args: ["./manage.py migrate && ./manage.py web"]
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8000
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- mountPath: /etc/passbook
|
||||
name: config-volume
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
httpHeaders:
|
||||
- name: Host
|
||||
value: kubernetes-healthcheck-host
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
httpHeaders:
|
||||
- name: Host
|
||||
value: kubernetes-healthcheck-host
|
||||
resources:
|
||||
{{ toYaml .Values.resources | indent 12 }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
@ -1,51 +0,0 @@
|
||||
apiVersion: apps/v1beta2
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "passbook.fullname" . }}-worker
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
helm.sh/chart: {{ include "passbook.chart" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
spec:
|
||||
volumes:
|
||||
- name: config-volume
|
||||
configMap:
|
||||
name: {{ include "passbook.fullname" . }}-config
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "docker.pkg.beryju.org/passbook:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["./manage.py", "worker"]
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8000
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- mountPath: /etc/passbook
|
||||
name: config-volume
|
||||
resources:
|
||||
{{ toYaml .Values.resources | indent 12 }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
@ -1,57 +0,0 @@
|
||||
# Default values for passbook.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
tag: latest
|
||||
|
||||
nameOverride: ""
|
||||
|
||||
config:
|
||||
# Optionally specify fixed secret_key, otherwise generated automatically
|
||||
# secret_key: _k*@6h2u2@q-dku57hhgzb7tnx*ba9wodcb^s9g0j59@=y(@_o
|
||||
# Enable error reporting
|
||||
error_reporting: true
|
||||
|
||||
postgresql:
|
||||
postgresqlDatabase: passbook
|
||||
postgresqlPassword: foo
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
annotations: {}
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# kubernetes.io/tls-acme: "true"
|
||||
path: /
|
||||
hosts:
|
||||
- passbook.k8s.local
|
||||
- kubernetes-healthcheck-host
|
||||
defaultHost: passbook.k8s.local
|
||||
tls: []
|
||||
# - secretName: chart-example-tls
|
||||
# hosts:
|
||||
# - passbook.k8s.local
|
||||
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
# choice for the user. This also increases chances charts run on environments with little
|
||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
9
helm/requirements.lock
Normal file
9
helm/requirements.lock
Normal file
@ -0,0 +1,9 @@
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
||||
version: 6.5.8
|
||||
- name: redis
|
||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
||||
version: 9.5.1
|
||||
digest: sha256:476834fb82f66bc7242c4a5e7343d0a859d8307cb301256beb0eb749983014e4
|
||||
generated: "2019-11-07T10:21:30.902415+01:00"
|
@ -1,7 +1,7 @@
|
||||
dependencies:
|
||||
- name: redis
|
||||
version: 5.1.0
|
||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
||||
- name: postgresql
|
||||
version: 3.10.1
|
||||
version: 6.5.8
|
||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
||||
- name: redis
|
||||
version: 9.5.1
|
||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
16
helm/templates/configmap.yaml
Normal file
16
helm/templates/configmap.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "passbook.fullname" . }}-config
|
||||
data:
|
||||
config.yml: |
|
||||
postgresql:
|
||||
host: "{{ .Release.Name }}-postgresql"
|
||||
name: "{{ .Values.postgresql.postgresqlDatabase }}"
|
||||
user: postgres
|
||||
redis:
|
||||
host: "{{ .Release.Name }}-redis-master"
|
||||
cache_db: 0
|
||||
message_queue_db: 1
|
||||
error_reporting: {{ .Values.config.error_reporting }}
|
||||
domain: ".{{ index .Values.ingress.hosts 0 }}"
|
@ -1,6 +1,5 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
{{- $fullName := include "passbook.fullname" . -}}
|
||||
{{- $ingressPath := .Values.ingress.path -}}
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
@ -30,9 +29,17 @@ spec:
|
||||
- host: {{ . | quote }}
|
||||
http:
|
||||
paths:
|
||||
- path: {{ $ingressPath }}
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: {{ $fullName }}
|
||||
serviceName: {{ $fullName }}-web
|
||||
servicePort: http
|
||||
- path: /static/
|
||||
backend:
|
||||
serviceName: {{ $fullName }}-static
|
||||
servicePort: http
|
||||
- path: /robots.txt
|
||||
backend:
|
||||
serviceName: {{ $fullName }}-static
|
||||
servicePort: http
|
||||
{{- end }}
|
||||
{{- end }}
|
121
helm/templates/prom-rules.yaml
Normal file
121
helm/templates/prom-rules.yaml
Normal file
@ -0,0 +1,121 @@
|
||||
{{- if .Values.monitoring.enabled -}}
|
||||
---
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: PrometheusRule
|
||||
metadata:
|
||||
name: {{ include "passbook.fullname" . }}-static-rules
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
helm.sh/chart: {{ include "passbook.chart" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
spec:
|
||||
groups:
|
||||
- name: Aggregate request counters
|
||||
rules:
|
||||
- record: job:django_http_requests_before_middlewares_total:sum_rate30s
|
||||
expr: sum(rate(django_http_requests_before_middlewares_total[30s])) by (job)
|
||||
- record: job:django_http_requests_unknown_latency_total:sum_rate30s
|
||||
expr: sum(rate(django_http_requests_unknown_latency_total[30s])) by (job)
|
||||
- record: job:django_http_ajax_requests_total:sum_rate30s
|
||||
expr: sum(rate(django_http_ajax_requests_total[30s])) by (job)
|
||||
- record: job:django_http_responses_before_middlewares_total:sum_rate30s
|
||||
expr: sum(rate(django_http_responses_before_middlewares_total[30s])) by (job)
|
||||
- record: job:django_http_requests_unknown_latency_including_middlewares_total:sum_rate30s
|
||||
expr: sum(rate(django_http_requests_unknown_latency_including_middlewares_total[30s])) by (job)
|
||||
- record: job:django_http_requests_body_total_bytes:sum_rate30s
|
||||
expr: sum(rate(django_http_requests_body_total_bytes[30s])) by (job)
|
||||
- record: job:django_http_responses_streaming_total:sum_rate30s
|
||||
expr: sum(rate(django_http_responses_streaming_total[30s])) by (job)
|
||||
- record: job:django_http_responses_body_total_bytes:sum_rate30s
|
||||
expr: sum(rate(django_http_responses_body_total_bytes[30s])) by (job)
|
||||
- record: job:django_http_requests_total:sum_rate30s
|
||||
expr: sum(rate(django_http_requests_total_by_method[30s])) by (job)
|
||||
- record: job:django_http_requests_total_by_method:sum_rate30s
|
||||
expr: sum(rate(django_http_requests_total_by_method[30s])) by (job,method)
|
||||
- record: job:django_http_requests_total_by_transport:sum_rate30s
|
||||
expr: sum(rate(django_http_requests_total_by_transport[30s])) by (job,transport)
|
||||
- record: job:django_http_requests_total_by_view:sum_rate30s
|
||||
expr: sum(rate(django_http_requests_total_by_view_transport_method[30s])) by (job,view)
|
||||
- record: job:django_http_requests_total_by_view_transport_method:sum_rate30s
|
||||
expr: sum(rate(django_http_requests_total_by_view_transport_method[30s])) by (job,view,transport,method)
|
||||
- record: job:django_http_responses_total_by_templatename:sum_rate30s
|
||||
expr: sum(rate(django_http_responses_total_by_templatename[30s])) by (job,templatename)
|
||||
- record: job:django_http_responses_total_by_status:sum_rate30s
|
||||
expr: sum(rate(django_http_responses_total_by_status[30s])) by (job,status)
|
||||
- record: job:django_http_responses_total_by_status_name_method:sum_rate30s
|
||||
expr: sum(rate(django_http_responses_total_by_status_name_method[30s])) by (job,status,name,method)
|
||||
- record: job:django_http_responses_total_by_charset:sum_rate30s
|
||||
expr: sum(rate(django_http_responses_total_by_charset[30s])) by (job,charset)
|
||||
- record: job:django_http_exceptions_total_by_type:sum_rate30s
|
||||
expr: sum(rate(django_http_exceptions_total_by_type[30s])) by (job,type)
|
||||
- record: job:django_http_exceptions_total_by_view:sum_rate30s
|
||||
expr: sum(rate(django_http_exceptions_total_by_view[30s])) by (job,view)
|
||||
- name: Aggregate latency histograms
|
||||
rules:
|
||||
- record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s
|
||||
expr: histogram_quantile(0.50, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le))
|
||||
labels:
|
||||
quantile: "50"
|
||||
- record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s
|
||||
expr: histogram_quantile(0.95, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le))
|
||||
labels:
|
||||
quantile: "95"
|
||||
- record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s
|
||||
expr: histogram_quantile(0.99, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le))
|
||||
labels:
|
||||
quantile: "99"
|
||||
- record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s
|
||||
expr: histogram_quantile(0.999, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le))
|
||||
labels:
|
||||
quantile: "99.9"
|
||||
- record: job:django_http_requests_latency_seconds:quantile_rate30s
|
||||
expr: histogram_quantile(0.50, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le))
|
||||
labels:
|
||||
quantile: "50"
|
||||
- record: job:django_http_requests_latency_seconds:quantile_rate30s
|
||||
expr: histogram_quantile(0.95, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le))
|
||||
labels:
|
||||
quantile: "95"
|
||||
- record: job:django_http_requests_latency_seconds:quantile_rate30s
|
||||
expr: histogram_quantile(0.99, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le))
|
||||
labels:
|
||||
quantile: "99"
|
||||
- record: job:django_http_requests_latency_seconds:quantile_rate30s
|
||||
expr: histogram_quantile(0.999, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le))
|
||||
labels:
|
||||
quantile: "99.9"
|
||||
- name: Aggregate model operations
|
||||
rules:
|
||||
- record: job:django_model_inserts_total:sum_rate1m
|
||||
expr: sum(rate(django_model_inserts_total[1m])) by (job, model)
|
||||
- record: job:django_model_updates_total:sum_rate1m
|
||||
expr: sum(rate(django_model_updates_total[1m])) by (job, model)
|
||||
- record: job:django_model_deletes_total:sum_rate1m
|
||||
expr: sum(rate(django_model_deletes_total[1m])) by (job, model)
|
||||
- name: Aggregate database operations
|
||||
rules:
|
||||
- record: job:django_db_new_connections_total:sum_rate30s
|
||||
expr: sum(rate(django_db_new_connections_total[30s])) by (alias, vendor)
|
||||
- record: job:django_db_new_connection_errors_total:sum_rate30s
|
||||
expr: sum(rate(django_db_new_connection_errors_total[30s])) by (alias, vendor)
|
||||
- record: job:django_db_execute_total:sum_rate30s
|
||||
expr: sum(rate(django_db_execute_total[30s])) by (alias, vendor)
|
||||
- record: job:django_db_execute_many_total:sum_rate30s
|
||||
expr: sum(rate(django_db_execute_many_total[30s])) by (alias, vendor)
|
||||
- record: job:django_db_errors_total:sum_rate30s
|
||||
expr: sum(rate(django_db_errors_total[30s])) by (alias, vendor, type)
|
||||
- name: Aggregate migrations
|
||||
rules:
|
||||
- record: job:django_migrations_applied_total:max
|
||||
expr: max(django_migrations_applied_total) by (job, connection)
|
||||
- record: job:django_migrations_unapplied_total:max
|
||||
expr: max(django_migrations_unapplied_total) by (job, connection)
|
||||
- name: Alerts
|
||||
rules:
|
||||
- alert: UnappliedMigrations
|
||||
expr: job:django_migrations_unapplied_total:max > 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: testing
|
||||
{{- end }}
|
12
helm/templates/secret.yaml
Normal file
12
helm/templates/secret.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: Opaque
|
||||
metadata:
|
||||
name: {{ include "passbook.fullname" . }}-secret-key
|
||||
data:
|
||||
monitoring_username: bW9uaXRvcg== # monitor in base64
|
||||
{{- if .Values.config.secret_key }}
|
||||
secret_key: {{ .Values.config.secret_key | b64enc | quote }}
|
||||
{{- else }}
|
||||
secret_key: {{ randAlphaNum 50 | b64enc | quote}}
|
||||
{{- end }}
|
48
helm/templates/static-deployment.yaml
Normal file
48
helm/templates/static-deployment.yaml
Normal file
@ -0,0 +1,48 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "passbook.fullname" . }}-static
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
helm.sh/chart: {{ include "passbook.chart" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
k8s.passbook.io/component: static
|
||||
spec:
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}-static
|
||||
image: "beryju/passbook-static:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 10
|
||||
timeoutSeconds: 5
|
||||
httpGet:
|
||||
path: /-/ping
|
||||
port: http
|
||||
readinessProbe:
|
||||
initialDelaySeconds: 10
|
||||
timeoutSeconds: 5
|
||||
httpGet:
|
||||
path: /-/ping
|
||||
port: http
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 10M
|
||||
limits:
|
||||
cpu: 20m
|
||||
memory: 20M
|
21
helm/templates/static-service.yaml
Normal file
21
helm/templates/static-service.yaml
Normal file
@ -0,0 +1,21 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "passbook.fullname" . }}-static
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
helm.sh/chart: {{ include "passbook.chart" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
k8s.passbook.io/component: static
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 8080
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
k8s.passbook.io/component: static
|
17
helm/templates/static-sm.yaml
Normal file
17
helm/templates/static-sm.yaml
Normal file
@ -0,0 +1,17 @@
|
||||
{{- if .Values.monitoring.enabled -}}
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
helm.sh/chart: {{ include "passbook.chart" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
name: {{ include "passbook.fullname" . }}-static-monitoring
|
||||
spec:
|
||||
endpoints:
|
||||
- port: http
|
||||
selector:
|
||||
matchLabels:
|
||||
k8s.passbook.io/component: static
|
||||
{{- end }}
|
112
helm/templates/web-deployment.yaml
Normal file
112
helm/templates/web-deployment.yaml
Normal file
@ -0,0 +1,112 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "passbook.fullname" . }}-web
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
helm.sh/chart: {{ include "passbook.chart" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
passbook.io/component: web
|
||||
spec:
|
||||
volumes:
|
||||
- name: config-volume
|
||||
configMap:
|
||||
name: {{ include "passbook.fullname" . }}-config
|
||||
initContainers:
|
||||
- name: passbook-database-migrations
|
||||
image: "beryju/passbook:{{ .Values.image.tag }}"
|
||||
command:
|
||||
- ./manage.py
|
||||
args:
|
||||
- migrate
|
||||
volumeMounts:
|
||||
- mountPath: /etc/passbook
|
||||
name: config-volume
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "passbook.fullname" . }}-config
|
||||
prefix: PASSBOOK_
|
||||
env:
|
||||
- name: PASSBOOK_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "passbook.fullname" . }}-secret-key
|
||||
key: secret_key
|
||||
- name: PASSBOOK_REDIS__PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Release.Name }}-redis"
|
||||
key: redis-password
|
||||
- name: PASSBOOK_POSTGRESQL__PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Release.Name }}-postgresql"
|
||||
key: postgresql-password
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "beryju/passbook:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- uwsgi
|
||||
args:
|
||||
- uwsgi.ini
|
||||
volumeMounts:
|
||||
- mountPath: /etc/passbook
|
||||
name: config-volume
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "passbook.fullname" . }}-config
|
||||
prefix: PASSBOOK_
|
||||
env:
|
||||
- name: PASSBOOK_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "passbook.fullname" . }}-secret-key
|
||||
key: secret_key
|
||||
- name: PASSBOOK_REDIS__PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Release.Name }}-redis"
|
||||
key: redis-password
|
||||
- name: PASSBOOK_POSTGRESQL__PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Release.Name }}-postgresql"
|
||||
key: postgresql-password
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8000
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
httpHeaders:
|
||||
- name: Host
|
||||
value: kubernetes-healthcheck-host
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
httpHeaders:
|
||||
- name: Host
|
||||
value: kubernetes-healthcheck-host
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 200M
|
||||
limits:
|
||||
cpu: 300m
|
||||
memory: 350M
|
@ -1,19 +1,21 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "passbook.fullname" . }}
|
||||
name: {{ include "passbook.fullname" . }}-web
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
helm.sh/chart: {{ include "passbook.chart" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
helm.sh/chart: {{ include "passbook.chart" . }}
|
||||
k8s.passbook.io/component: web
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
- port: 80
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
passbook.io/component: web
|
26
helm/templates/web-sm.yaml
Normal file
26
helm/templates/web-sm.yaml
Normal file
@ -0,0 +1,26 @@
|
||||
{{- if .Values.monitoring.enabled -}}
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
helm.sh/chart: {{ include "passbook.chart" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
name: {{ include "passbook.fullname" . }}-web-monitoring
|
||||
spec:
|
||||
endpoints:
|
||||
- basicAuth:
|
||||
password:
|
||||
name: {{ include "passbook.fullname" . }}-secret-key
|
||||
key: secret_key
|
||||
username:
|
||||
name: {{ include "passbook.fullname" . }}-secret-key
|
||||
key: monitoring_username
|
||||
port: http
|
||||
path: /metrics/
|
||||
interval: 10s
|
||||
selector:
|
||||
matchLabels:
|
||||
k8s.passbook.io/component: web
|
||||
{{- end }}
|
69
helm/templates/worker-deployment.yaml
Normal file
69
helm/templates/worker-deployment.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "passbook.fullname" . }}-worker
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
helm.sh/chart: {{ include "passbook.chart" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
passbook.io/component: worker
|
||||
spec:
|
||||
volumes:
|
||||
- name: config-volume
|
||||
configMap:
|
||||
name: {{ include "passbook.fullname" . }}-config
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "beryju/passbook:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- celery
|
||||
args:
|
||||
- worker
|
||||
- --autoscale=10,3
|
||||
- -E
|
||||
- -B
|
||||
- -A=passbook.root.celery
|
||||
- -s=/tmp/celerybeat-schedule
|
||||
volumeMounts:
|
||||
- mountPath: /etc/passbook
|
||||
name: config-volume
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "passbook.fullname" . }}-config
|
||||
prefix: PASSBOOK_
|
||||
env:
|
||||
- name: PASSBOOK_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "passbook.fullname" . }}-secret-key
|
||||
key: secret_key
|
||||
- name: PASSBOOK_REDIS__PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Release.Name }}-redis"
|
||||
key: redis-password
|
||||
- name: PASSBOOK_POSTGRESQL__PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Release.Name }}-postgresql"
|
||||
key: postgresql-password
|
||||
resources:
|
||||
requests:
|
||||
cpu: 150m
|
||||
memory: 300M
|
||||
limits:
|
||||
cpu: 300m
|
||||
memory: 500M
|
44
helm/values.yaml
Normal file
44
helm/values.yaml
Normal file
@ -0,0 +1,44 @@
|
||||
# Default values for passbook.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
image:
|
||||
tag: 0.7.15-beta
|
||||
|
||||
nameOverride: ""
|
||||
|
||||
config:
|
||||
# Optionally specify fixed secret_key, otherwise generated automatically
|
||||
# secret_key: _k*@6h2u2@q-dku57hhgzb7tnx*ba9wodcb^s9g0j59@=y(@_o
|
||||
# Enable error reporting
|
||||
error_reporting: false
|
||||
email:
|
||||
host: localhost
|
||||
|
||||
# This Helm chart ships with built-in Prometheus ServiceMonitors and Rules.
|
||||
# This requires the CoreOS Prometheus Operator.
|
||||
monitoring:
|
||||
enabled: false
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
annotations: {}
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# kubernetes.io/tls-acme: "true"
|
||||
path: /
|
||||
hosts:
|
||||
- passbook.k8s.local
|
||||
tls: []
|
||||
# - secretName: chart-example-tls
|
||||
# hosts:
|
||||
# - passbook.k8s.local
|
||||
|
||||
# These settings configure the packaged PostgreSQL and Redis chart.
|
||||
postgresql:
|
||||
postgresqlDatabase: passbook
|
||||
|
||||
redis:
|
||||
cluster:
|
||||
enabled: false
|
||||
master:
|
||||
persistence:
|
||||
enabled: false
|
@ -4,7 +4,7 @@ import os
|
||||
import sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'passbook.core.settings')
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'passbook.root.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
|
34
mkdocs.yml
Normal file
34
mkdocs.yml
Normal file
@ -0,0 +1,34 @@
|
||||
site_name: passbook Docs
|
||||
site_url: https://beryju.github.io/passbook
|
||||
copyright: "Copyright © 2019 - 2020 BeryJu.org"
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Installation:
|
||||
- Installation: installation/install.md
|
||||
- docker-compose: installation/docker-compose.md
|
||||
- Kubernetes: installation/kubernetes.md
|
||||
- Sources: sources.md
|
||||
- Providers: providers.md
|
||||
- Property Mappings: property-mappings.md
|
||||
- Factors: factors.md
|
||||
- Policies: policies.md
|
||||
- Integrations:
|
||||
- as Provider:
|
||||
- GitLab: integrations/services/gitlab/index.md
|
||||
- Rancher: integrations/services/rancher/index.md
|
||||
- Harbor: integrations/services/harbor/index.md
|
||||
- Sentry: integrations/services/sentry/index.md
|
||||
- Reference:
|
||||
- Property Mappings:
|
||||
- User Object: reference/property-mappings/user-object.md
|
||||
|
||||
repo_name: "BeryJu.org/passbook"
|
||||
repo_url: https://github.com/BeryJu/passbook
|
||||
theme:
|
||||
name: "material"
|
||||
logo: "images/logo.svg"
|
||||
|
||||
markdown_extensions:
|
||||
- toc:
|
||||
permalink: "¶"
|
@ -1,2 +1,2 @@
|
||||
"""passbook"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
__version__ = "0.7.15-beta"
|
||||
|
@ -1,2 +0,0 @@
|
||||
"""passbook admin"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
|
@ -1,6 +0,0 @@
|
||||
"""Versioned Admin API Urls"""
|
||||
from django.conf.urls import include, url
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^v1/', include('passbook.admin.api.v1.urls', namespace='v1')),
|
||||
]
|
@ -1,36 +0,0 @@
|
||||
"""passbook admin gorup API"""
|
||||
from rest_framework.permissions import IsAdminUser
|
||||
from rest_framework.serializers import ModelSerializer, Serializer
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from passbook.core.models import Group
|
||||
|
||||
|
||||
class RecursiveField(Serializer):
|
||||
"""Recursive field for manytomanyfield"""
|
||||
|
||||
def to_representation(self, value):
|
||||
serializer = self.parent.parent.__class__(value, context=self.context)
|
||||
return serializer.data
|
||||
|
||||
def create(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def update(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
class GroupSerializer(ModelSerializer):
|
||||
"""Group Serializer"""
|
||||
|
||||
children = RecursiveField(many=True)
|
||||
|
||||
class Meta:
|
||||
model = Group
|
||||
fields = '__all__'
|
||||
|
||||
class GroupViewSet(ModelViewSet):
|
||||
"""Group Viewset"""
|
||||
|
||||
permission_classes = [IsAdminUser]
|
||||
serializer_class = GroupSerializer
|
||||
queryset = Group.objects.filter(parent__isnull=True)
|
@ -1,33 +0,0 @@
|
||||
"""passbook admin API URLs"""
|
||||
from django.urls import path
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.views import get_schema_view
|
||||
from rest_framework import permissions
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
from passbook.admin.api.v1.applications import ApplicationViewSet
|
||||
from passbook.admin.api.v1.groups import GroupViewSet
|
||||
from passbook.admin.api.v1.users import UserViewSet
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('applications', ApplicationViewSet)
|
||||
router.register('groups', GroupViewSet)
|
||||
router.register('users', UserViewSet)
|
||||
|
||||
SchemaView = get_schema_view(
|
||||
openapi.Info(
|
||||
title="passbook Administration API",
|
||||
default_version='v1',
|
||||
description="Internal passbook API for Administration Interface",
|
||||
contact=openapi.Contact(email="contact@snippets.local"),
|
||||
license=openapi.License(name="MIT License"),
|
||||
),
|
||||
public=True,
|
||||
permission_classes=(permissions.IsAdminUser,),
|
||||
)
|
||||
|
||||
urlpatterns = router.urls + [
|
||||
path('swagger.yml', SchemaView.without_ui(cache_timeout=0), name='schema-json'),
|
||||
path('swagger/', SchemaView.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
|
||||
]
|
||||
app_name = 'passbook.admin'
|
@ -5,7 +5,7 @@ from django.apps import AppConfig
|
||||
class PassbookAdminConfig(AppConfig):
|
||||
"""passbook admin app config"""
|
||||
|
||||
name = 'passbook.admin'
|
||||
label = 'passbook_admin'
|
||||
mountpoint = 'administration/'
|
||||
verbose_name = 'passbook Admin'
|
||||
name = "passbook.admin"
|
||||
label = "passbook_admin"
|
||||
mountpoint = "administration/"
|
||||
verbose_name = "passbook Admin"
|
||||
|
59
passbook/admin/fields.py
Normal file
59
passbook/admin/fields.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""YAML fields"""
|
||||
import yaml
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class InvalidYAMLInput(str):
|
||||
"""Invalid YAML String type"""
|
||||
|
||||
|
||||
class YAMLString(str):
|
||||
"""YAML String type"""
|
||||
|
||||
|
||||
class YAMLField(forms.CharField):
|
||||
"""Django's JSON Field converted to YAML"""
|
||||
|
||||
default_error_messages = {
|
||||
"invalid": _("'%(value)s' value must be valid YAML."),
|
||||
}
|
||||
widget = forms.Textarea
|
||||
|
||||
def to_python(self, value):
|
||||
if self.disabled:
|
||||
return value
|
||||
if value in self.empty_values:
|
||||
return None
|
||||
if isinstance(value, (list, dict, int, float, YAMLString)):
|
||||
return value
|
||||
try:
|
||||
converted = yaml.safe_load(value)
|
||||
except yaml.YAMLError:
|
||||
raise forms.ValidationError(
|
||||
self.error_messages["invalid"], code="invalid", params={"value": value},
|
||||
)
|
||||
if isinstance(converted, str):
|
||||
return YAMLString(converted)
|
||||
return converted
|
||||
|
||||
def bound_data(self, data, initial):
|
||||
if self.disabled:
|
||||
return initial
|
||||
try:
|
||||
return yaml.safe_load(data)
|
||||
except yaml.YAMLError:
|
||||
return InvalidYAMLInput(data)
|
||||
|
||||
def prepare_value(self, value):
|
||||
if isinstance(value, InvalidYAMLInput):
|
||||
return value
|
||||
return yaml.dump(value, explicit_start=True)
|
||||
|
||||
def has_changed(self, initial, data):
|
||||
if super().has_changed(initial, data):
|
||||
return True
|
||||
# For purposes of seeing whether something has changed, True isn't the
|
||||
# same as 1 and the order of keys doesn't matter.
|
||||
data = self.to_python(data)
|
||||
return yaml.dump(initial, sort_keys=True) != yaml.dump(data, sort_keys=True)
|
40
passbook/admin/forms/base.py
Normal file
40
passbook/admin/forms/base.py
Normal file
@ -0,0 +1,40 @@
|
||||
"""passbook form helpers"""
|
||||
from django import forms
|
||||
|
||||
from passbook.admin.fields import YAMLField
|
||||
|
||||
|
||||
class TagModelForm(forms.ModelForm):
|
||||
"""Base form for models that have attributes"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Check if we have an instance, load tags otherwise use an empty dict
|
||||
instance = kwargs.get("instance", None)
|
||||
tags = instance.tags if instance else {}
|
||||
# Make sure all predefined tags exist in tags, and set default if they don't
|
||||
predefined_tags = (
|
||||
self._meta.model().get_predefined_tags() # pylint: disable=no-member
|
||||
)
|
||||
for key, value in predefined_tags.items():
|
||||
if key not in tags:
|
||||
tags[key] = value
|
||||
# Format JSON
|
||||
kwargs["initial"]["tags"] = tags
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def clean_tags(self):
|
||||
"""Make sure all required tags are set"""
|
||||
if hasattr(self.instance, "get_required_keys") and hasattr(
|
||||
self.instance, "tags"
|
||||
):
|
||||
for key in self.instance.get_required_keys():
|
||||
if key not in self.cleaned_data.get("tags"):
|
||||
raise forms.ValidationError("Tag %s missing." % key)
|
||||
return self.cleaned_data.get("tags")
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class TagModelFormMeta:
|
||||
"""Base Meta class that uses the YAMLField"""
|
||||
|
||||
field_classes = {"tags": YAMLField}
|
@ -1,6 +1,7 @@
|
||||
"""passbook core source form fields"""
|
||||
# from django import forms
|
||||
|
||||
SOURCE_FORM_FIELDS = ['name', 'slug', 'enabled', 'policies']
|
||||
SOURCE_FORM_FIELDS = ["name", "slug", "enabled", "policies"]
|
||||
SOURCE_SERIALIZER_FIELDS = ["pk", "name", "slug", "enabled", "policies"]
|
||||
|
||||
# class SourceForm(forms.Form)
|
||||
|
21
passbook/admin/forms/users.py
Normal file
21
passbook/admin/forms/users.py
Normal file
@ -0,0 +1,21 @@
|
||||
"""passbook administrative user forms"""
|
||||
|
||||
from django import forms
|
||||
|
||||
from passbook.admin.fields import YAMLField
|
||||
from passbook.core.models import User
|
||||
|
||||
|
||||
class UserForm(forms.ModelForm):
|
||||
"""Update User Details"""
|
||||
|
||||
class Meta:
|
||||
|
||||
model = User
|
||||
fields = ["username", "name", "email", "is_staff", "is_active", "attributes"]
|
||||
widgets = {
|
||||
"name": forms.TextInput,
|
||||
}
|
||||
field_classes = {
|
||||
"attributes": YAMLField,
|
||||
}
|
26
passbook/admin/middleware.py
Normal file
26
passbook/admin/middleware.py
Normal file
@ -0,0 +1,26 @@
|
||||
"""passbook admin Middleware to impersonate users"""
|
||||
|
||||
from passbook.core.models import User
|
||||
|
||||
|
||||
def impersonate(get_response):
|
||||
"""Middleware to impersonate users"""
|
||||
|
||||
def middleware(request):
|
||||
"""Middleware to impersonate users"""
|
||||
|
||||
# User is superuser and has __impersonate ID set
|
||||
if request.user.is_superuser and "__impersonate" in request.GET:
|
||||
request.session["impersonate_id"] = request.GET["__impersonate"]
|
||||
# user wants to stop impersonation
|
||||
elif "__unimpersonate" in request.GET and "impersonate_id" in request.session:
|
||||
del request.session["impersonate_id"]
|
||||
|
||||
# Actually impersonate user
|
||||
if request.user.is_superuser and "impersonate_id" in request.session:
|
||||
request.user = User.objects.get(pk=request.session["impersonate_id"])
|
||||
|
||||
response = get_response(request)
|
||||
return response
|
||||
|
||||
return middleware
|
@ -1,2 +0,0 @@
|
||||
django-rest-framework
|
||||
drf_yasg
|
5
passbook/admin/settings.py
Normal file
5
passbook/admin/settings.py
Normal file
@ -0,0 +1,5 @@
|
||||
"""passbook admin settings"""
|
||||
|
||||
MIDDLEWARE = [
|
||||
"passbook.admin.middleware.impersonate",
|
||||
]
|
209
passbook/admin/static/codemirror/addon/comment/comment.js
vendored
Normal file
209
passbook/admin/static/codemirror/addon/comment/comment.js
vendored
Normal file
@ -0,0 +1,209 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
var noOptions = {};
|
||||
var nonWS = /[^\s\u00a0]/;
|
||||
var Pos = CodeMirror.Pos;
|
||||
|
||||
function firstNonWS(str) {
|
||||
var found = str.search(nonWS);
|
||||
return found == -1 ? 0 : found;
|
||||
}
|
||||
|
||||
CodeMirror.commands.toggleComment = function(cm) {
|
||||
cm.toggleComment();
|
||||
};
|
||||
|
||||
CodeMirror.defineExtension("toggleComment", function(options) {
|
||||
if (!options) options = noOptions;
|
||||
var cm = this;
|
||||
var minLine = Infinity, ranges = this.listSelections(), mode = null;
|
||||
for (var i = ranges.length - 1; i >= 0; i--) {
|
||||
var from = ranges[i].from(), to = ranges[i].to();
|
||||
if (from.line >= minLine) continue;
|
||||
if (to.line >= minLine) to = Pos(minLine, 0);
|
||||
minLine = from.line;
|
||||
if (mode == null) {
|
||||
if (cm.uncomment(from, to, options)) mode = "un";
|
||||
else { cm.lineComment(from, to, options); mode = "line"; }
|
||||
} else if (mode == "un") {
|
||||
cm.uncomment(from, to, options);
|
||||
} else {
|
||||
cm.lineComment(from, to, options);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Rough heuristic to try and detect lines that are part of multi-line string
|
||||
function probablyInsideString(cm, pos, line) {
|
||||
return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line)
|
||||
}
|
||||
|
||||
function getMode(cm, pos) {
|
||||
var mode = cm.getMode()
|
||||
return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos)
|
||||
}
|
||||
|
||||
CodeMirror.defineExtension("lineComment", function(from, to, options) {
|
||||
if (!options) options = noOptions;
|
||||
var self = this, mode = getMode(self, from);
|
||||
var firstLine = self.getLine(from.line);
|
||||
if (firstLine == null || probablyInsideString(self, from, firstLine)) return;
|
||||
|
||||
var commentString = options.lineComment || mode.lineComment;
|
||||
if (!commentString) {
|
||||
if (options.blockCommentStart || mode.blockCommentStart) {
|
||||
options.fullLines = true;
|
||||
self.blockComment(from, to, options);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);
|
||||
var pad = options.padding == null ? " " : options.padding;
|
||||
var blankLines = options.commentBlankLines || from.line == to.line;
|
||||
|
||||
self.operation(function() {
|
||||
if (options.indent) {
|
||||
var baseString = null;
|
||||
for (var i = from.line; i < end; ++i) {
|
||||
var line = self.getLine(i);
|
||||
var whitespace = line.slice(0, firstNonWS(line));
|
||||
if (baseString == null || baseString.length > whitespace.length) {
|
||||
baseString = whitespace;
|
||||
}
|
||||
}
|
||||
for (var i = from.line; i < end; ++i) {
|
||||
var line = self.getLine(i), cut = baseString.length;
|
||||
if (!blankLines && !nonWS.test(line)) continue;
|
||||
if (line.slice(0, cut) != baseString) cut = firstNonWS(line);
|
||||
self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));
|
||||
}
|
||||
} else {
|
||||
for (var i = from.line; i < end; ++i) {
|
||||
if (blankLines || nonWS.test(self.getLine(i)))
|
||||
self.replaceRange(commentString + pad, Pos(i, 0));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
CodeMirror.defineExtension("blockComment", function(from, to, options) {
|
||||
if (!options) options = noOptions;
|
||||
var self = this, mode = getMode(self, from);
|
||||
var startString = options.blockCommentStart || mode.blockCommentStart;
|
||||
var endString = options.blockCommentEnd || mode.blockCommentEnd;
|
||||
if (!startString || !endString) {
|
||||
if ((options.lineComment || mode.lineComment) && options.fullLines != false)
|
||||
self.lineComment(from, to, options);
|
||||
return;
|
||||
}
|
||||
if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return
|
||||
|
||||
var end = Math.min(to.line, self.lastLine());
|
||||
if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;
|
||||
|
||||
var pad = options.padding == null ? " " : options.padding;
|
||||
if (from.line > end) return;
|
||||
|
||||
self.operation(function() {
|
||||
if (options.fullLines != false) {
|
||||
var lastLineHasText = nonWS.test(self.getLine(end));
|
||||
self.replaceRange(pad + endString, Pos(end));
|
||||
self.replaceRange(startString + pad, Pos(from.line, 0));
|
||||
var lead = options.blockCommentLead || mode.blockCommentLead;
|
||||
if (lead != null) for (var i = from.line + 1; i <= end; ++i)
|
||||
if (i != end || lastLineHasText)
|
||||
self.replaceRange(lead + pad, Pos(i, 0));
|
||||
} else {
|
||||
self.replaceRange(endString, to);
|
||||
self.replaceRange(startString, from);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
CodeMirror.defineExtension("uncomment", function(from, to, options) {
|
||||
if (!options) options = noOptions;
|
||||
var self = this, mode = getMode(self, from);
|
||||
var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end);
|
||||
|
||||
// Try finding line comments
|
||||
var lineString = options.lineComment || mode.lineComment, lines = [];
|
||||
var pad = options.padding == null ? " " : options.padding, didSomething;
|
||||
lineComment: {
|
||||
if (!lineString) break lineComment;
|
||||
for (var i = start; i <= end; ++i) {
|
||||
var line = self.getLine(i);
|
||||
var found = line.indexOf(lineString);
|
||||
if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;
|
||||
if (found == -1 && nonWS.test(line)) break lineComment;
|
||||
if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
|
||||
lines.push(line);
|
||||
}
|
||||
self.operation(function() {
|
||||
for (var i = start; i <= end; ++i) {
|
||||
var line = lines[i - start];
|
||||
var pos = line.indexOf(lineString), endPos = pos + lineString.length;
|
||||
if (pos < 0) continue;
|
||||
if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
|
||||
didSomething = true;
|
||||
self.replaceRange("", Pos(i, pos), Pos(i, endPos));
|
||||
}
|
||||
});
|
||||
if (didSomething) return true;
|
||||
}
|
||||
|
||||
// Try block comments
|
||||
var startString = options.blockCommentStart || mode.blockCommentStart;
|
||||
var endString = options.blockCommentEnd || mode.blockCommentEnd;
|
||||
if (!startString || !endString) return false;
|
||||
var lead = options.blockCommentLead || mode.blockCommentLead;
|
||||
var startLine = self.getLine(start), open = startLine.indexOf(startString)
|
||||
if (open == -1) return false
|
||||
var endLine = end == start ? startLine : self.getLine(end)
|
||||
var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);
|
||||
var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1)
|
||||
if (close == -1 ||
|
||||
!/comment/.test(self.getTokenTypeAt(insideStart)) ||
|
||||
!/comment/.test(self.getTokenTypeAt(insideEnd)) ||
|
||||
self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1)
|
||||
return false;
|
||||
|
||||
// Avoid killing block comments completely outside the selection.
|
||||
// Positions of the last startString before the start of the selection, and the first endString after it.
|
||||
var lastStart = startLine.lastIndexOf(startString, from.ch);
|
||||
var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);
|
||||
if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;
|
||||
// Positions of the first endString after the end of the selection, and the last startString before it.
|
||||
firstEnd = endLine.indexOf(endString, to.ch);
|
||||
var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
|
||||
lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;
|
||||
if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;
|
||||
|
||||
self.operation(function() {
|
||||
self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
|
||||
Pos(end, close + endString.length));
|
||||
var openEnd = open + startString.length;
|
||||
if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;
|
||||
self.replaceRange("", Pos(start, open), Pos(start, openEnd));
|
||||
if (lead) for (var i = start + 1; i <= end; ++i) {
|
||||
var line = self.getLine(i), found = line.indexOf(lead);
|
||||
if (found == -1 || nonWS.test(line.slice(0, found))) continue;
|
||||
var foundEnd = found + lead.length;
|
||||
if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;
|
||||
self.replaceRange("", Pos(i, found), Pos(i, foundEnd));
|
||||
}
|
||||
});
|
||||
return true;
|
||||
});
|
||||
});
|
78
passbook/admin/static/codemirror/addon/comment/continuecomment.js
vendored
Normal file
78
passbook/admin/static/codemirror/addon/comment/continuecomment.js
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
function continueComment(cm) {
|
||||
if (cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||
var ranges = cm.listSelections(), mode, inserts = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var pos = ranges[i].head
|
||||
if (!/\bcomment\b/.test(cm.getTokenTypeAt(pos))) return CodeMirror.Pass;
|
||||
var modeHere = cm.getModeAt(pos)
|
||||
if (!mode) mode = modeHere;
|
||||
else if (mode != modeHere) return CodeMirror.Pass;
|
||||
|
||||
var insert = null;
|
||||
if (mode.blockCommentStart && mode.blockCommentContinue) {
|
||||
var line = cm.getLine(pos.line).slice(0, pos.ch)
|
||||
var end = line.lastIndexOf(mode.blockCommentEnd), found
|
||||
if (end != -1 && end == pos.ch - mode.blockCommentEnd.length) {
|
||||
// Comment ended, don't continue it
|
||||
} else if ((found = line.lastIndexOf(mode.blockCommentStart)) > -1 && found > end) {
|
||||
insert = line.slice(0, found)
|
||||
if (/\S/.test(insert)) {
|
||||
insert = ""
|
||||
for (var j = 0; j < found; ++j) insert += " "
|
||||
}
|
||||
} else if ((found = line.indexOf(mode.blockCommentContinue)) > -1 && !/\S/.test(line.slice(0, found))) {
|
||||
insert = line.slice(0, found)
|
||||
}
|
||||
if (insert != null) insert += mode.blockCommentContinue
|
||||
}
|
||||
if (insert == null && mode.lineComment && continueLineCommentEnabled(cm)) {
|
||||
var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment);
|
||||
if (found > -1) {
|
||||
insert = line.slice(0, found);
|
||||
if (/\S/.test(insert)) insert = null;
|
||||
else insert += mode.lineComment + line.slice(found + mode.lineComment.length).match(/^\s*/)[0];
|
||||
}
|
||||
}
|
||||
if (insert == null) return CodeMirror.Pass;
|
||||
inserts[i] = "\n" + insert;
|
||||
}
|
||||
|
||||
cm.operation(function() {
|
||||
for (var i = ranges.length - 1; i >= 0; i--)
|
||||
cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), "+insert");
|
||||
});
|
||||
}
|
||||
|
||||
function continueLineCommentEnabled(cm) {
|
||||
var opt = cm.getOption("continueComments");
|
||||
if (opt && typeof opt == "object")
|
||||
return opt.continueLineComment !== false;
|
||||
return true;
|
||||
}
|
||||
|
||||
CodeMirror.defineOption("continueComments", null, function(cm, val, prev) {
|
||||
if (prev && prev != CodeMirror.Init)
|
||||
cm.removeKeyMap("continueComment");
|
||||
if (val) {
|
||||
var key = "Enter";
|
||||
if (typeof val == "string")
|
||||
key = val;
|
||||
else if (typeof val == "object" && val.key)
|
||||
key = val.key;
|
||||
var map = {name: "continueComment"};
|
||||
map[key] = continueComment;
|
||||
cm.addKeyMap(map);
|
||||
}
|
||||
});
|
||||
});
|
32
passbook/admin/static/codemirror/addon/dialog/dialog.css
vendored
Normal file
32
passbook/admin/static/codemirror/addon/dialog/dialog.css
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
.CodeMirror-dialog {
|
||||
position: absolute;
|
||||
left: 0; right: 0;
|
||||
background: inherit;
|
||||
z-index: 15;
|
||||
padding: .1em .8em;
|
||||
overflow: hidden;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.CodeMirror-dialog-top {
|
||||
border-bottom: 1px solid #eee;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-dialog-bottom {
|
||||
border-top: 1px solid #eee;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-dialog input {
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
width: 20em;
|
||||
color: inherit;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.CodeMirror-dialog button {
|
||||
font-size: 70%;
|
||||
}
|
161
passbook/admin/static/codemirror/addon/dialog/dialog.js
vendored
Normal file
161
passbook/admin/static/codemirror/addon/dialog/dialog.js
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
// Open simple dialogs on top of an editor. Relies on dialog.css.
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
function dialogDiv(cm, template, bottom) {
|
||||
var wrap = cm.getWrapperElement();
|
||||
var dialog;
|
||||
dialog = wrap.appendChild(document.createElement("div"));
|
||||
if (bottom)
|
||||
dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
|
||||
else
|
||||
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
|
||||
|
||||
if (typeof template == "string") {
|
||||
dialog.innerHTML = template;
|
||||
} else { // Assuming it's a detached DOM element.
|
||||
dialog.appendChild(template);
|
||||
}
|
||||
CodeMirror.addClass(wrap, 'dialog-opened');
|
||||
return dialog;
|
||||
}
|
||||
|
||||
function closeNotification(cm, newVal) {
|
||||
if (cm.state.currentNotificationClose)
|
||||
cm.state.currentNotificationClose();
|
||||
cm.state.currentNotificationClose = newVal;
|
||||
}
|
||||
|
||||
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
|
||||
if (!options) options = {};
|
||||
|
||||
closeNotification(this, null);
|
||||
|
||||
var dialog = dialogDiv(this, template, options.bottom);
|
||||
var closed = false, me = this;
|
||||
function close(newVal) {
|
||||
if (typeof newVal == 'string') {
|
||||
inp.value = newVal;
|
||||
} else {
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
|
||||
dialog.parentNode.removeChild(dialog);
|
||||
me.focus();
|
||||
|
||||
if (options.onClose) options.onClose(dialog);
|
||||
}
|
||||
}
|
||||
|
||||
var inp = dialog.getElementsByTagName("input")[0], button;
|
||||
if (inp) {
|
||||
inp.focus();
|
||||
|
||||
if (options.value) {
|
||||
inp.value = options.value;
|
||||
if (options.selectValueOnOpen !== false) {
|
||||
inp.select();
|
||||
}
|
||||
}
|
||||
|
||||
if (options.onInput)
|
||||
CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
|
||||
if (options.onKeyUp)
|
||||
CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
|
||||
|
||||
CodeMirror.on(inp, "keydown", function(e) {
|
||||
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
|
||||
if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
|
||||
inp.blur();
|
||||
CodeMirror.e_stop(e);
|
||||
close();
|
||||
}
|
||||
if (e.keyCode == 13) callback(inp.value, e);
|
||||
});
|
||||
|
||||
if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close);
|
||||
} else if (button = dialog.getElementsByTagName("button")[0]) {
|
||||
CodeMirror.on(button, "click", function() {
|
||||
close();
|
||||
me.focus();
|
||||
});
|
||||
|
||||
if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
|
||||
|
||||
button.focus();
|
||||
}
|
||||
return close;
|
||||
});
|
||||
|
||||
CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
|
||||
closeNotification(this, null);
|
||||
var dialog = dialogDiv(this, template, options && options.bottom);
|
||||
var buttons = dialog.getElementsByTagName("button");
|
||||
var closed = false, me = this, blurring = 1;
|
||||
function close() {
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
|
||||
dialog.parentNode.removeChild(dialog);
|
||||
me.focus();
|
||||
}
|
||||
buttons[0].focus();
|
||||
for (var i = 0; i < buttons.length; ++i) {
|
||||
var b = buttons[i];
|
||||
(function(callback) {
|
||||
CodeMirror.on(b, "click", function(e) {
|
||||
CodeMirror.e_preventDefault(e);
|
||||
close();
|
||||
if (callback) callback(me);
|
||||
});
|
||||
})(callbacks[i]);
|
||||
CodeMirror.on(b, "blur", function() {
|
||||
--blurring;
|
||||
setTimeout(function() { if (blurring <= 0) close(); }, 200);
|
||||
});
|
||||
CodeMirror.on(b, "focus", function() { ++blurring; });
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* openNotification
|
||||
* Opens a notification, that can be closed with an optional timer
|
||||
* (default 5000ms timer) and always closes on click.
|
||||
*
|
||||
* If a notification is opened while another is opened, it will close the
|
||||
* currently opened one and open the new one immediately.
|
||||
*/
|
||||
CodeMirror.defineExtension("openNotification", function(template, options) {
|
||||
closeNotification(this, close);
|
||||
var dialog = dialogDiv(this, template, options && options.bottom);
|
||||
var closed = false, doneTimer;
|
||||
var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
|
||||
|
||||
function close() {
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
clearTimeout(doneTimer);
|
||||
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
|
||||
dialog.parentNode.removeChild(dialog);
|
||||
}
|
||||
|
||||
CodeMirror.on(dialog, 'click', function(e) {
|
||||
CodeMirror.e_preventDefault(e);
|
||||
close();
|
||||
});
|
||||
|
||||
if (duration)
|
||||
doneTimer = setTimeout(close, duration);
|
||||
|
||||
return close;
|
||||
});
|
||||
});
|
47
passbook/admin/static/codemirror/addon/display/autorefresh.js
vendored
Normal file
47
passbook/admin/static/codemirror/addon/display/autorefresh.js
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"))
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod)
|
||||
else // Plain browser env
|
||||
mod(CodeMirror)
|
||||
})(function(CodeMirror) {
|
||||
"use strict"
|
||||
|
||||
CodeMirror.defineOption("autoRefresh", false, function(cm, val) {
|
||||
if (cm.state.autoRefresh) {
|
||||
stopListening(cm, cm.state.autoRefresh)
|
||||
cm.state.autoRefresh = null
|
||||
}
|
||||
if (val && cm.display.wrapper.offsetHeight == 0)
|
||||
startListening(cm, cm.state.autoRefresh = {delay: val.delay || 250})
|
||||
})
|
||||
|
||||
function startListening(cm, state) {
|
||||
function check() {
|
||||
if (cm.display.wrapper.offsetHeight) {
|
||||
stopListening(cm, state)
|
||||
if (cm.display.lastWrapHeight != cm.display.wrapper.clientHeight)
|
||||
cm.refresh()
|
||||
} else {
|
||||
state.timeout = setTimeout(check, state.delay)
|
||||
}
|
||||
}
|
||||
state.timeout = setTimeout(check, state.delay)
|
||||
state.hurry = function() {
|
||||
clearTimeout(state.timeout)
|
||||
state.timeout = setTimeout(check, 50)
|
||||
}
|
||||
CodeMirror.on(window, "mouseup", state.hurry)
|
||||
CodeMirror.on(window, "keyup", state.hurry)
|
||||
}
|
||||
|
||||
function stopListening(_cm, state) {
|
||||
clearTimeout(state.timeout)
|
||||
CodeMirror.off(window, "mouseup", state.hurry)
|
||||
CodeMirror.off(window, "keyup", state.hurry)
|
||||
}
|
||||
});
|
6
passbook/admin/static/codemirror/addon/display/fullscreen.css
vendored
Normal file
6
passbook/admin/static/codemirror/addon/display/fullscreen.css
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
.CodeMirror-fullscreen {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
height: auto;
|
||||
z-index: 9;
|
||||
}
|
41
passbook/admin/static/codemirror/addon/display/fullscreen.js
vendored
Normal file
41
passbook/admin/static/codemirror/addon/display/fullscreen.js
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
CodeMirror.defineOption("fullScreen", false, function(cm, val, old) {
|
||||
if (old == CodeMirror.Init) old = false;
|
||||
if (!old == !val) return;
|
||||
if (val) setFullscreen(cm);
|
||||
else setNormal(cm);
|
||||
});
|
||||
|
||||
function setFullscreen(cm) {
|
||||
var wrap = cm.getWrapperElement();
|
||||
cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset,
|
||||
width: wrap.style.width, height: wrap.style.height};
|
||||
wrap.style.width = "";
|
||||
wrap.style.height = "auto";
|
||||
wrap.className += " CodeMirror-fullscreen";
|
||||
document.documentElement.style.overflow = "hidden";
|
||||
cm.refresh();
|
||||
}
|
||||
|
||||
function setNormal(cm) {
|
||||
var wrap = cm.getWrapperElement();
|
||||
wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, "");
|
||||
document.documentElement.style.overflow = "";
|
||||
var info = cm.state.fullScreenRestore;
|
||||
wrap.style.width = info.width; wrap.style.height = info.height;
|
||||
window.scrollTo(info.scrollLeft, info.scrollTop);
|
||||
cm.refresh();
|
||||
}
|
||||
});
|
127
passbook/admin/static/codemirror/addon/display/panel.js
vendored
Normal file
127
passbook/admin/static/codemirror/addon/display/panel.js
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
CodeMirror.defineExtension("addPanel", function(node, options) {
|
||||
options = options || {};
|
||||
|
||||
if (!this.state.panels) initPanels(this);
|
||||
|
||||
var info = this.state.panels;
|
||||
var wrapper = info.wrapper;
|
||||
var cmWrapper = this.getWrapperElement();
|
||||
var replace = options.replace instanceof Panel && !options.replace.cleared;
|
||||
|
||||
if (options.after instanceof Panel && !options.after.cleared) {
|
||||
wrapper.insertBefore(node, options.before.node.nextSibling);
|
||||
} else if (options.before instanceof Panel && !options.before.cleared) {
|
||||
wrapper.insertBefore(node, options.before.node);
|
||||
} else if (replace) {
|
||||
wrapper.insertBefore(node, options.replace.node);
|
||||
info.panels++;
|
||||
options.replace.clear();
|
||||
} else if (options.position == "bottom") {
|
||||
wrapper.appendChild(node);
|
||||
} else if (options.position == "before-bottom") {
|
||||
wrapper.insertBefore(node, cmWrapper.nextSibling);
|
||||
} else if (options.position == "after-top") {
|
||||
wrapper.insertBefore(node, cmWrapper);
|
||||
} else {
|
||||
wrapper.insertBefore(node, wrapper.firstChild);
|
||||
}
|
||||
|
||||
var height = (options && options.height) || node.offsetHeight;
|
||||
this._setSize(null, info.heightLeft -= height);
|
||||
if (!replace) {
|
||||
info.panels++;
|
||||
}
|
||||
if (options.stable && isAtTop(this, node))
|
||||
this.scrollTo(null, this.getScrollInfo().top + height)
|
||||
|
||||
return new Panel(this, node, options, height);
|
||||
});
|
||||
|
||||
function Panel(cm, node, options, height) {
|
||||
this.cm = cm;
|
||||
this.node = node;
|
||||
this.options = options;
|
||||
this.height = height;
|
||||
this.cleared = false;
|
||||
}
|
||||
|
||||
Panel.prototype.clear = function() {
|
||||
if (this.cleared) return;
|
||||
this.cleared = true;
|
||||
var info = this.cm.state.panels;
|
||||
this.cm._setSize(null, info.heightLeft += this.height);
|
||||
if (this.options.stable && isAtTop(this.cm, this.node))
|
||||
this.cm.scrollTo(null, this.cm.getScrollInfo().top - this.height)
|
||||
info.wrapper.removeChild(this.node);
|
||||
if (--info.panels == 0) removePanels(this.cm);
|
||||
};
|
||||
|
||||
Panel.prototype.changed = function(height) {
|
||||
var newHeight = height == null ? this.node.offsetHeight : height;
|
||||
var info = this.cm.state.panels;
|
||||
this.cm._setSize(null, info.heightLeft -= (newHeight - this.height));
|
||||
this.height = newHeight;
|
||||
};
|
||||
|
||||
function initPanels(cm) {
|
||||
var wrap = cm.getWrapperElement();
|
||||
var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle;
|
||||
var height = parseInt(style.height);
|
||||
var info = cm.state.panels = {
|
||||
setHeight: wrap.style.height,
|
||||
heightLeft: height,
|
||||
panels: 0,
|
||||
wrapper: document.createElement("div")
|
||||
};
|
||||
wrap.parentNode.insertBefore(info.wrapper, wrap);
|
||||
var hasFocus = cm.hasFocus();
|
||||
info.wrapper.appendChild(wrap);
|
||||
if (hasFocus) cm.focus();
|
||||
|
||||
cm._setSize = cm.setSize;
|
||||
if (height != null) cm.setSize = function(width, newHeight) {
|
||||
if (newHeight == null) return this._setSize(width, newHeight);
|
||||
info.setHeight = newHeight;
|
||||
if (typeof newHeight != "number") {
|
||||
var px = /^(\d+\.?\d*)px$/.exec(newHeight);
|
||||
if (px) {
|
||||
newHeight = Number(px[1]);
|
||||
} else {
|
||||
info.wrapper.style.height = newHeight;
|
||||
newHeight = info.wrapper.offsetHeight;
|
||||
info.wrapper.style.height = "";
|
||||
}
|
||||
}
|
||||
cm._setSize(width, info.heightLeft += (newHeight - height));
|
||||
height = newHeight;
|
||||
};
|
||||
}
|
||||
|
||||
function removePanels(cm) {
|
||||
var info = cm.state.panels;
|
||||
cm.state.panels = null;
|
||||
|
||||
var wrap = cm.getWrapperElement();
|
||||
info.wrapper.parentNode.replaceChild(wrap, info.wrapper);
|
||||
wrap.style.height = info.setHeight;
|
||||
cm.setSize = cm._setSize;
|
||||
cm.setSize();
|
||||
}
|
||||
|
||||
function isAtTop(cm, dom) {
|
||||
for (var sibling = dom.nextSibling; sibling; sibling = sibling.nextSibling)
|
||||
if (sibling == cm.getWrapperElement()) return true
|
||||
return false
|
||||
}
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user