##// END OF EJS Templates
release: merge back stable branch into default
milka -
r4612:cbbdebfd merge default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,43 b''
1 |RCE| 4.23.1 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2020-11-25
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13
14
15 General
16 ^^^^^^^
17
18
19
20 Security
21 ^^^^^^^^
22
23
24
25 Performance
26 ^^^^^^^^^^^
27
28 -
29
30
31 Fixes
32 ^^^^^
33
34 - Comments: fixed inline comments TODO resolution
35 - Comments: fixed some styling for TODO resolution.
36 - Comments: fixed general comments live push.
37 - Comments: fixed reply-to links from emails.
38 - Reviewers: fixed some UI issues on larger screen when editing reviewers.
39
40 Upgrade notes
41 ^^^^^^^^^^^^^
42
43 - Un-scheduled release addressing problems in 4.23.X releases.
@@ -0,0 +1,48 b''
1 |RCE| 4.23.2 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2020-12-06
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13
14
15 General
16 ^^^^^^^
17
18 - Repo extra keys: fixed some texts to improve UI.
19
20
21 Security
22 ^^^^^^^^
23
24
25
26 Performance
27 ^^^^^^^^^^^
28
29 - Core: speed up cache loading on application startup.
30
31
32 Fixes
33 ^^^^^
34
35 - Diffs: added scroll down/scroll up helper. Fixes #5643
36 - Diffs: fixed diff rendering when a common ancestor was a different commit than the source of changes.
37 - Commits / changelog: small fixes from found problems.
38 - Comments: side-bar comments hover also shows an ID of comment now.
39 - Comments: make dismiss less prominent, and text only to not mix icons/text together.
40 - Comments: UX improvements for comment buttons.
41 - Reviewers: small ui fixes for display of review rules, and added new reviewer entries.
42 - Pull-requests: fixed source/target in PR creation, affecting how we load default reviewers based on branches.
43
44
45 Upgrade notes
46 ^^^^^^^^^^^^^
47
48 - Un-scheduled release addressing problems in 4.23.X releases.
@@ -1,72 +1,75 b''
1 1bd3e92b7e2e2d2024152b34bb88dff1db544a71 v4.0.0
1 1bd3e92b7e2e2d2024152b34bb88dff1db544a71 v4.0.0
2 170c5398320ea6cddd50955e88d408794c21d43a v4.0.1
2 170c5398320ea6cddd50955e88d408794c21d43a v4.0.1
3 c3fe200198f5aa34cf2e4066df2881a9cefe3704 v4.1.0
3 c3fe200198f5aa34cf2e4066df2881a9cefe3704 v4.1.0
4 7fd5c850745e2ea821fb4406af5f4bff9b0a7526 v4.1.1
4 7fd5c850745e2ea821fb4406af5f4bff9b0a7526 v4.1.1
5 41c87da28a179953df86061d817bc35533c66dd2 v4.1.2
5 41c87da28a179953df86061d817bc35533c66dd2 v4.1.2
6 baaf9f5bcea3bae0ef12ae20c8b270482e62abb6 v4.2.0
6 baaf9f5bcea3bae0ef12ae20c8b270482e62abb6 v4.2.0
7 32a70c7e56844a825f61df496ee5eaf8c3c4e189 v4.2.1
7 32a70c7e56844a825f61df496ee5eaf8c3c4e189 v4.2.1
8 fa695cdb411d294679ac081d595ac654e5613b03 v4.3.0
8 fa695cdb411d294679ac081d595ac654e5613b03 v4.3.0
9 0e4dc11b58cad833c513fe17bac39e6850edf959 v4.3.1
9 0e4dc11b58cad833c513fe17bac39e6850edf959 v4.3.1
10 8a876f48f5cb1d018b837db28ff928500cb32cfb v4.4.0
10 8a876f48f5cb1d018b837db28ff928500cb32cfb v4.4.0
11 8dd86b410b1aac086ffdfc524ef300f896af5047 v4.4.1
11 8dd86b410b1aac086ffdfc524ef300f896af5047 v4.4.1
12 d2514226abc8d3b4f6fb57765f47d1b6fb360a05 v4.4.2
12 d2514226abc8d3b4f6fb57765f47d1b6fb360a05 v4.4.2
13 27d783325930af6dad2741476c0d0b1b7c8415c2 v4.5.0
13 27d783325930af6dad2741476c0d0b1b7c8415c2 v4.5.0
14 7f2016f352abcbdba4a19d4039c386e9629449da v4.5.1
14 7f2016f352abcbdba4a19d4039c386e9629449da v4.5.1
15 416fec799314c70a5c780fb28b3357b08869333a v4.5.2
15 416fec799314c70a5c780fb28b3357b08869333a v4.5.2
16 27c3b85fafc83143e6678fbc3da69e1615bcac55 v4.6.0
16 27c3b85fafc83143e6678fbc3da69e1615bcac55 v4.6.0
17 5ad13deb9118c2a5243d4032d4d9cc174e5872db v4.6.1
17 5ad13deb9118c2a5243d4032d4d9cc174e5872db v4.6.1
18 2be921e01fa24bb102696ada596f87464c3666f6 v4.7.0
18 2be921e01fa24bb102696ada596f87464c3666f6 v4.7.0
19 7198bdec29c2872c974431d55200d0398354cdb1 v4.7.1
19 7198bdec29c2872c974431d55200d0398354cdb1 v4.7.1
20 bd1c8d230fe741c2dfd7100a0ef39fd0774fd581 v4.7.2
20 bd1c8d230fe741c2dfd7100a0ef39fd0774fd581 v4.7.2
21 9731914f89765d9628dc4dddc84bc9402aa124c8 v4.8.0
21 9731914f89765d9628dc4dddc84bc9402aa124c8 v4.8.0
22 c5a2b7d0e4bbdebc4a62d7b624befe375207b659 v4.9.0
22 c5a2b7d0e4bbdebc4a62d7b624befe375207b659 v4.9.0
23 d9aa3b27ac9f7e78359775c75fedf7bfece232f1 v4.9.1
23 d9aa3b27ac9f7e78359775c75fedf7bfece232f1 v4.9.1
24 4ba4d74981cec5d6b28b158f875a2540952c2f74 v4.10.0
24 4ba4d74981cec5d6b28b158f875a2540952c2f74 v4.10.0
25 0a6821cbd6b0b3c21503002f88800679fa35ab63 v4.10.1
25 0a6821cbd6b0b3c21503002f88800679fa35ab63 v4.10.1
26 434ad90ec8d621f4416074b84f6e9ce03964defb v4.10.2
26 434ad90ec8d621f4416074b84f6e9ce03964defb v4.10.2
27 68baee10e698da2724c6e0f698c03a6abb993bf2 v4.10.3
27 68baee10e698da2724c6e0f698c03a6abb993bf2 v4.10.3
28 00821d3afd1dce3f4767cc353f84a17f7d5218a1 v4.10.4
28 00821d3afd1dce3f4767cc353f84a17f7d5218a1 v4.10.4
29 22f6744ad8cc274311825f63f953e4dee2ea5cb9 v4.10.5
29 22f6744ad8cc274311825f63f953e4dee2ea5cb9 v4.10.5
30 96eb24bea2f5f9258775245e3f09f6fa0a4dda01 v4.10.6
30 96eb24bea2f5f9258775245e3f09f6fa0a4dda01 v4.10.6
31 3121217a812c956d7dd5a5875821bd73e8002a32 v4.11.0
31 3121217a812c956d7dd5a5875821bd73e8002a32 v4.11.0
32 fa98b454715ac5b912f39e84af54345909a2a805 v4.11.1
32 fa98b454715ac5b912f39e84af54345909a2a805 v4.11.1
33 3982abcfdcc229a723cebe52d3a9bcff10bba08e v4.11.2
33 3982abcfdcc229a723cebe52d3a9bcff10bba08e v4.11.2
34 33195f145db9172f0a8f1487e09207178a6ab065 v4.11.3
34 33195f145db9172f0a8f1487e09207178a6ab065 v4.11.3
35 194c74f33e32bbae6fc4d71ec5a999cff3c13605 v4.11.4
35 194c74f33e32bbae6fc4d71ec5a999cff3c13605 v4.11.4
36 8fbd8b0c3ddc2fa4ac9e4ca16942a03eb593df2d v4.11.5
36 8fbd8b0c3ddc2fa4ac9e4ca16942a03eb593df2d v4.11.5
37 f0609aa5d5d05a1ca2f97c3995542236131c9d8a v4.11.6
37 f0609aa5d5d05a1ca2f97c3995542236131c9d8a v4.11.6
38 b5b30547d90d2e088472a70c84878f429ffbf40d v4.12.0
38 b5b30547d90d2e088472a70c84878f429ffbf40d v4.12.0
39 9072253aa8894d20c00b4a43dc61c2168c1eff94 v4.12.1
39 9072253aa8894d20c00b4a43dc61c2168c1eff94 v4.12.1
40 6a517543ea9ef9987d74371bd2a315eb0b232dc9 v4.12.2
40 6a517543ea9ef9987d74371bd2a315eb0b232dc9 v4.12.2
41 7fc0731b024c3114be87865eda7ab621cc957e32 v4.12.3
41 7fc0731b024c3114be87865eda7ab621cc957e32 v4.12.3
42 6d531c0b068c6eda62dddceedc9f845ecb6feb6f v4.12.4
42 6d531c0b068c6eda62dddceedc9f845ecb6feb6f v4.12.4
43 3d6bf2d81b1564830eb5e83396110d2a9a93eb1e v4.13.0
43 3d6bf2d81b1564830eb5e83396110d2a9a93eb1e v4.13.0
44 5468fc89e708bd90e413cd0d54350017abbdbc0e v4.13.1
44 5468fc89e708bd90e413cd0d54350017abbdbc0e v4.13.1
45 610d621550521c314ee97b3d43473ac0bcf06fb8 v4.13.2
45 610d621550521c314ee97b3d43473ac0bcf06fb8 v4.13.2
46 7dc62c090881fb5d03268141e71e0940d7c3295d v4.13.3
46 7dc62c090881fb5d03268141e71e0940d7c3295d v4.13.3
47 9151328c1c46b72ba6f00d7640d9141e75aa1ca2 v4.14.0
47 9151328c1c46b72ba6f00d7640d9141e75aa1ca2 v4.14.0
48 a47eeac5dfa41fa6779d90452affba4091c3ade8 v4.14.1
48 a47eeac5dfa41fa6779d90452affba4091c3ade8 v4.14.1
49 4b34ce0d2c3c10510626b3b65044939bb7a2cddf v4.15.0
49 4b34ce0d2c3c10510626b3b65044939bb7a2cddf v4.15.0
50 14502561d22e6b70613674cd675ae9a604b7989f v4.15.1
50 14502561d22e6b70613674cd675ae9a604b7989f v4.15.1
51 4aaa40b605b01af78a9f6882eca561c54b525ef0 v4.15.2
51 4aaa40b605b01af78a9f6882eca561c54b525ef0 v4.15.2
52 797744642eca86640ed20bef2cd77445780abaec v4.16.0
52 797744642eca86640ed20bef2cd77445780abaec v4.16.0
53 6c3452c7c25ed35ff269690929e11960ed6ad7d3 v4.16.1
53 6c3452c7c25ed35ff269690929e11960ed6ad7d3 v4.16.1
54 5d8057df561c4b6b81b6401aed7d2f911e6e77f7 v4.16.2
54 5d8057df561c4b6b81b6401aed7d2f911e6e77f7 v4.16.2
55 13acfc008896ef4c62546bab5074e8f6f89b4fa7 v4.17.0
55 13acfc008896ef4c62546bab5074e8f6f89b4fa7 v4.17.0
56 45b9b610976f483877142fe75321808ce9ebac59 v4.17.1
56 45b9b610976f483877142fe75321808ce9ebac59 v4.17.1
57 ad5bd0c4bd322fdbd04bb825a3d027e08f7a3901 v4.17.2
57 ad5bd0c4bd322fdbd04bb825a3d027e08f7a3901 v4.17.2
58 037f5794b55a6236d68f6485a485372dde6566e0 v4.17.3
58 037f5794b55a6236d68f6485a485372dde6566e0 v4.17.3
59 83bc3100cfd6094c1d04f475ddb299b7dc3d0b33 v4.17.4
59 83bc3100cfd6094c1d04f475ddb299b7dc3d0b33 v4.17.4
60 e3de8c95baf8cc9109ca56aee8193a2cb6a54c8a v4.17.4
60 e3de8c95baf8cc9109ca56aee8193a2cb6a54c8a v4.17.4
61 f37a3126570477543507f0bc9d245ce75546181a v4.18.0
61 f37a3126570477543507f0bc9d245ce75546181a v4.18.0
62 71d8791463e87b64c1a18475de330ee600d37561 v4.18.1
62 71d8791463e87b64c1a18475de330ee600d37561 v4.18.1
63 4bd6b75dac1d25c64885d4d49385e5533f21c525 v4.18.2
63 4bd6b75dac1d25c64885d4d49385e5533f21c525 v4.18.2
64 12ed92fe57f2e9fc7b71dc0b65e26c2da5c7085f v4.18.3
64 12ed92fe57f2e9fc7b71dc0b65e26c2da5c7085f v4.18.3
65 ddef396a6567117de531d67d44c739cbbfc3eebb v4.19.0
65 ddef396a6567117de531d67d44c739cbbfc3eebb v4.19.0
66 c0c65acd73914bf4368222d510afe1161ab8c07c v4.19.1
66 c0c65acd73914bf4368222d510afe1161ab8c07c v4.19.1
67 7ac623a4a2405917e2af660d645ded662011e40d v4.19.2
67 7ac623a4a2405917e2af660d645ded662011e40d v4.19.2
68 ef7ffda65eeb90c3ba88590a6cb816ef9b0bc232 v4.19.3
68 ef7ffda65eeb90c3ba88590a6cb816ef9b0bc232 v4.19.3
69 3e635489bb7961df93b01e42454ad1a8730ae968 v4.20.0
69 3e635489bb7961df93b01e42454ad1a8730ae968 v4.20.0
70 7e2eb896a02ca7cd2cd9f0f853ef3dac3f0039e3 v4.20.1
70 7e2eb896a02ca7cd2cd9f0f853ef3dac3f0039e3 v4.20.1
71 8bb5fece08ab65986225b184e46f53d2a71729cb v4.21.0
71 8bb5fece08ab65986225b184e46f53d2a71729cb v4.21.0
72 90734aac31ee4563bbe665a43ff73190cc762275 v4.22.0
72 90734aac31ee4563bbe665a43ff73190cc762275 v4.22.0
73 a9655707f7cf4146affc51c12fe5ed8e02898a57 v4.23.0
74 56310d93b33b97535908ef9c7b0985b89bb7fad2 v4.23.1
75 7637c38528fa38c1eabc1fde6a869c20995a0da7 v4.23.2
@@ -1,47 +1,49 b''
1 .. _nginx-proxy-conf:
1 .. _nginx-proxy-conf:
2
2
3 Nginx Proxy Config
3 Nginx Proxy Config
4 ------------------
4 ------------------
5
5
6
6
7 Set the following properties in your ``/etc/nginx/proxy.conf`` so it does not
7 Set the following properties in your ``/etc/nginx/proxy.conf`` so it does not
8 timeout during large pushes.
8 timeout during large pushes.
9
9
10 .. code-block:: nginx
10 .. code-block:: nginx
11
11
12 proxy_redirect off;
12 proxy_redirect off;
13 proxy_set_header Host $http_host;
13 proxy_set_header Host $http_host;
14
14
15 ## If you use HTTPS make sure you disable gzip compression
15 ## If you use HTTPS make sure you disable gzip compression
16 ## to be safe against BREACH attack.
16 ## to be safe against BREACH attack.
17 gzip off;
17 gzip off;
18
18
19 # Don't buffer requests in NGINX stream them using chunked-encoding
19 # Don't buffer requests in NGINX stream them using chunked-encoding
20 proxy_buffering off;
20 proxy_buffering off;
21
21
22 ## This is also required for later GIT to use streaming.
22 ## This is also required for later GIT to use streaming.
23 ## Works only for Nginx 1.7.11 and newer
23 ## Works only for Nginx 1.7.11 and newer
24 proxy_request_buffering off;
24 proxy_request_buffering off;
25 proxy_http_version 1.1;
25 proxy_http_version 1.1;
26
26
27 ## Set this to a larger number if you experience timeouts
27 ## Set this to a larger number if you experience timeouts
28 ## or 413 Request Entity Too Large, 10GB is enough for most cases
28 ## or 413 Request Entity Too Large, 10GB is enough for most cases
29 client_max_body_size 10240m;
29 client_max_body_size 10240m;
30
30
31 ## needed for container auth
31 ## needed for container auth
32 # proxy_set_header REMOTE_USER $remote_user;
32 # proxy_set_header REMOTE_USER $remote_user;
33 # proxy_set_header X-Forwarded-User $remote_user;
33 # proxy_set_header X-Forwarded-User $remote_user;
34
34
35 proxy_set_header X-Url-Scheme $scheme;
35 proxy_set_header X-Url-Scheme $scheme;
36 proxy_set_header X-Host $http_host;
36 proxy_set_header X-Host $http_host;
37 proxy_set_header X-Real-IP $remote_addr;
37 proxy_set_header X-Real-IP $remote_addr;
38 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
38 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
39 proxy_set_header X-Forwarded-Proto $proto;
40 proxy_set_header X-Url-Scheme $scheme;
39 proxy_set_header Proxy-host $proxy_host;
41 proxy_set_header Proxy-host $proxy_host;
40
42
41 proxy_connect_timeout 7200;
43 proxy_connect_timeout 7200;
42 proxy_send_timeout 7200;
44 proxy_send_timeout 7200;
43 proxy_read_timeout 7200;
45 proxy_read_timeout 7200;
44 proxy_buffers 8 32k;
46 proxy_buffers 8 32k;
45
47
46 add_header X-Frame-Options SAMEORIGIN;
48 add_header X-Frame-Options SAMEORIGIN;
47 add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
49 add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
@@ -1,149 +1,151 b''
1 .. _rhodecode-release-notes-ref:
1 .. _rhodecode-release-notes-ref:
2
2
3 Release Notes
3 Release Notes
4 =============
4 =============
5
5
6 |RCE| 4.x Versions
6 |RCE| 4.x Versions
7 ------------------
7 ------------------
8
8
9 .. toctree::
9 .. toctree::
10 :maxdepth: 1
10 :maxdepth: 1
11
11
12 release-notes-4.23.2.rst
13 release-notes-4.23.1.rst
12 release-notes-4.23.0.rst
14 release-notes-4.23.0.rst
13 release-notes-4.22.0.rst
15 release-notes-4.22.0.rst
14 release-notes-4.21.0.rst
16 release-notes-4.21.0.rst
15 release-notes-4.20.1.rst
17 release-notes-4.20.1.rst
16 release-notes-4.20.0.rst
18 release-notes-4.20.0.rst
17 release-notes-4.19.3.rst
19 release-notes-4.19.3.rst
18 release-notes-4.19.2.rst
20 release-notes-4.19.2.rst
19 release-notes-4.19.1.rst
21 release-notes-4.19.1.rst
20 release-notes-4.19.0.rst
22 release-notes-4.19.0.rst
21 release-notes-4.18.3.rst
23 release-notes-4.18.3.rst
22 release-notes-4.18.2.rst
24 release-notes-4.18.2.rst
23 release-notes-4.18.1.rst
25 release-notes-4.18.1.rst
24 release-notes-4.18.0.rst
26 release-notes-4.18.0.rst
25 release-notes-4.17.4.rst
27 release-notes-4.17.4.rst
26 release-notes-4.17.3.rst
28 release-notes-4.17.3.rst
27 release-notes-4.17.2.rst
29 release-notes-4.17.2.rst
28 release-notes-4.17.1.rst
30 release-notes-4.17.1.rst
29 release-notes-4.17.0.rst
31 release-notes-4.17.0.rst
30 release-notes-4.16.2.rst
32 release-notes-4.16.2.rst
31 release-notes-4.16.1.rst
33 release-notes-4.16.1.rst
32 release-notes-4.16.0.rst
34 release-notes-4.16.0.rst
33 release-notes-4.15.2.rst
35 release-notes-4.15.2.rst
34 release-notes-4.15.1.rst
36 release-notes-4.15.1.rst
35 release-notes-4.15.0.rst
37 release-notes-4.15.0.rst
36 release-notes-4.14.1.rst
38 release-notes-4.14.1.rst
37 release-notes-4.14.0.rst
39 release-notes-4.14.0.rst
38 release-notes-4.13.3.rst
40 release-notes-4.13.3.rst
39 release-notes-4.13.2.rst
41 release-notes-4.13.2.rst
40 release-notes-4.13.1.rst
42 release-notes-4.13.1.rst
41 release-notes-4.13.0.rst
43 release-notes-4.13.0.rst
42 release-notes-4.12.4.rst
44 release-notes-4.12.4.rst
43 release-notes-4.12.3.rst
45 release-notes-4.12.3.rst
44 release-notes-4.12.2.rst
46 release-notes-4.12.2.rst
45 release-notes-4.12.1.rst
47 release-notes-4.12.1.rst
46 release-notes-4.12.0.rst
48 release-notes-4.12.0.rst
47 release-notes-4.11.6.rst
49 release-notes-4.11.6.rst
48 release-notes-4.11.5.rst
50 release-notes-4.11.5.rst
49 release-notes-4.11.4.rst
51 release-notes-4.11.4.rst
50 release-notes-4.11.3.rst
52 release-notes-4.11.3.rst
51 release-notes-4.11.2.rst
53 release-notes-4.11.2.rst
52 release-notes-4.11.1.rst
54 release-notes-4.11.1.rst
53 release-notes-4.11.0.rst
55 release-notes-4.11.0.rst
54 release-notes-4.10.6.rst
56 release-notes-4.10.6.rst
55 release-notes-4.10.5.rst
57 release-notes-4.10.5.rst
56 release-notes-4.10.4.rst
58 release-notes-4.10.4.rst
57 release-notes-4.10.3.rst
59 release-notes-4.10.3.rst
58 release-notes-4.10.2.rst
60 release-notes-4.10.2.rst
59 release-notes-4.10.1.rst
61 release-notes-4.10.1.rst
60 release-notes-4.10.0.rst
62 release-notes-4.10.0.rst
61 release-notes-4.9.1.rst
63 release-notes-4.9.1.rst
62 release-notes-4.9.0.rst
64 release-notes-4.9.0.rst
63 release-notes-4.8.0.rst
65 release-notes-4.8.0.rst
64 release-notes-4.7.2.rst
66 release-notes-4.7.2.rst
65 release-notes-4.7.1.rst
67 release-notes-4.7.1.rst
66 release-notes-4.7.0.rst
68 release-notes-4.7.0.rst
67 release-notes-4.6.1.rst
69 release-notes-4.6.1.rst
68 release-notes-4.6.0.rst
70 release-notes-4.6.0.rst
69 release-notes-4.5.2.rst
71 release-notes-4.5.2.rst
70 release-notes-4.5.1.rst
72 release-notes-4.5.1.rst
71 release-notes-4.5.0.rst
73 release-notes-4.5.0.rst
72 release-notes-4.4.2.rst
74 release-notes-4.4.2.rst
73 release-notes-4.4.1.rst
75 release-notes-4.4.1.rst
74 release-notes-4.4.0.rst
76 release-notes-4.4.0.rst
75 release-notes-4.3.1.rst
77 release-notes-4.3.1.rst
76 release-notes-4.3.0.rst
78 release-notes-4.3.0.rst
77 release-notes-4.2.1.rst
79 release-notes-4.2.1.rst
78 release-notes-4.2.0.rst
80 release-notes-4.2.0.rst
79 release-notes-4.1.2.rst
81 release-notes-4.1.2.rst
80 release-notes-4.1.1.rst
82 release-notes-4.1.1.rst
81 release-notes-4.1.0.rst
83 release-notes-4.1.0.rst
82 release-notes-4.0.1.rst
84 release-notes-4.0.1.rst
83 release-notes-4.0.0.rst
85 release-notes-4.0.0.rst
84
86
85 |RCE| 3.x Versions
87 |RCE| 3.x Versions
86 ------------------
88 ------------------
87
89
88 .. toctree::
90 .. toctree::
89 :maxdepth: 1
91 :maxdepth: 1
90
92
91 release-notes-3.8.4.rst
93 release-notes-3.8.4.rst
92 release-notes-3.8.3.rst
94 release-notes-3.8.3.rst
93 release-notes-3.8.2.rst
95 release-notes-3.8.2.rst
94 release-notes-3.8.1.rst
96 release-notes-3.8.1.rst
95 release-notes-3.8.0.rst
97 release-notes-3.8.0.rst
96 release-notes-3.7.1.rst
98 release-notes-3.7.1.rst
97 release-notes-3.7.0.rst
99 release-notes-3.7.0.rst
98 release-notes-3.6.1.rst
100 release-notes-3.6.1.rst
99 release-notes-3.6.0.rst
101 release-notes-3.6.0.rst
100 release-notes-3.5.2.rst
102 release-notes-3.5.2.rst
101 release-notes-3.5.1.rst
103 release-notes-3.5.1.rst
102 release-notes-3.5.0.rst
104 release-notes-3.5.0.rst
103 release-notes-3.4.1.rst
105 release-notes-3.4.1.rst
104 release-notes-3.4.0.rst
106 release-notes-3.4.0.rst
105 release-notes-3.3.4.rst
107 release-notes-3.3.4.rst
106 release-notes-3.3.3.rst
108 release-notes-3.3.3.rst
107 release-notes-3.3.2.rst
109 release-notes-3.3.2.rst
108 release-notes-3.3.1.rst
110 release-notes-3.3.1.rst
109 release-notes-3.3.0.rst
111 release-notes-3.3.0.rst
110 release-notes-3.2.3.rst
112 release-notes-3.2.3.rst
111 release-notes-3.2.2.rst
113 release-notes-3.2.2.rst
112 release-notes-3.2.1.rst
114 release-notes-3.2.1.rst
113 release-notes-3.2.0.rst
115 release-notes-3.2.0.rst
114 release-notes-3.1.1.rst
116 release-notes-3.1.1.rst
115 release-notes-3.1.0.rst
117 release-notes-3.1.0.rst
116 release-notes-3.0.2.rst
118 release-notes-3.0.2.rst
117 release-notes-3.0.1.rst
119 release-notes-3.0.1.rst
118 release-notes-3.0.0.rst
120 release-notes-3.0.0.rst
119
121
120 |RCE| 2.x Versions
122 |RCE| 2.x Versions
121 ------------------
123 ------------------
122
124
123 .. toctree::
125 .. toctree::
124 :maxdepth: 1
126 :maxdepth: 1
125
127
126 release-notes-2.2.8.rst
128 release-notes-2.2.8.rst
127 release-notes-2.2.7.rst
129 release-notes-2.2.7.rst
128 release-notes-2.2.6.rst
130 release-notes-2.2.6.rst
129 release-notes-2.2.5.rst
131 release-notes-2.2.5.rst
130 release-notes-2.2.4.rst
132 release-notes-2.2.4.rst
131 release-notes-2.2.3.rst
133 release-notes-2.2.3.rst
132 release-notes-2.2.2.rst
134 release-notes-2.2.2.rst
133 release-notes-2.2.1.rst
135 release-notes-2.2.1.rst
134 release-notes-2.2.0.rst
136 release-notes-2.2.0.rst
135 release-notes-2.1.0.rst
137 release-notes-2.1.0.rst
136 release-notes-2.0.2.rst
138 release-notes-2.0.2.rst
137 release-notes-2.0.1.rst
139 release-notes-2.0.1.rst
138 release-notes-2.0.0.rst
140 release-notes-2.0.0.rst
139
141
140 |RCE| 1.x Versions
142 |RCE| 1.x Versions
141 ------------------
143 ------------------
142
144
143 .. toctree::
145 .. toctree::
144 :maxdepth: 1
146 :maxdepth: 1
145
147
146 release-notes-1.7.2.rst
148 release-notes-1.7.2.rst
147 release-notes-1.7.1.rst
149 release-notes-1.7.1.rst
148 release-notes-1.7.0.rst
150 release-notes-1.7.0.rst
149 release-notes-1.6.0.rst
151 release-notes-1.6.0.rst
@@ -1,2509 +1,2509 b''
1 # Generated by pip2nix 0.8.0.dev1
1 # Generated by pip2nix 0.8.0.dev1
2 # See https://github.com/johbo/pip2nix
2 # See https://github.com/johbo/pip2nix
3
3
4 { pkgs, fetchurl, fetchgit, fetchhg }:
4 { pkgs, fetchurl, fetchgit, fetchhg }:
5
5
6 self: super: {
6 self: super: {
7 "alembic" = super.buildPythonPackage {
7 "alembic" = super.buildPythonPackage {
8 name = "alembic-1.4.2";
8 name = "alembic-1.4.2";
9 doCheck = false;
9 doCheck = false;
10 propagatedBuildInputs = [
10 propagatedBuildInputs = [
11 self."sqlalchemy"
11 self."sqlalchemy"
12 self."mako"
12 self."mako"
13 self."python-editor"
13 self."python-editor"
14 self."python-dateutil"
14 self."python-dateutil"
15 ];
15 ];
16 src = fetchurl {
16 src = fetchurl {
17 url = "https://files.pythonhosted.org/packages/60/1e/cabc75a189de0fbb2841d0975243e59bde8b7822bacbb95008ac6fe9ad47/alembic-1.4.2.tar.gz";
17 url = "https://files.pythonhosted.org/packages/60/1e/cabc75a189de0fbb2841d0975243e59bde8b7822bacbb95008ac6fe9ad47/alembic-1.4.2.tar.gz";
18 sha256 = "1gsdrzx9h7wfva200qvvsc9sn4w79mk2vs0bbnzjhxi1jw2b0nh3";
18 sha256 = "1gsdrzx9h7wfva200qvvsc9sn4w79mk2vs0bbnzjhxi1jw2b0nh3";
19 };
19 };
20 meta = {
20 meta = {
21 license = [ pkgs.lib.licenses.mit ];
21 license = [ pkgs.lib.licenses.mit ];
22 };
22 };
23 };
23 };
24 "amqp" = super.buildPythonPackage {
24 "amqp" = super.buildPythonPackage {
25 name = "amqp-2.5.2";
25 name = "amqp-2.5.2";
26 doCheck = false;
26 doCheck = false;
27 propagatedBuildInputs = [
27 propagatedBuildInputs = [
28 self."vine"
28 self."vine"
29 ];
29 ];
30 src = fetchurl {
30 src = fetchurl {
31 url = "https://files.pythonhosted.org/packages/92/1d/433541994a5a69f4ad2fff39746ddbb0bdedb0ea0d85673eb0db68a7edd9/amqp-2.5.2.tar.gz";
31 url = "https://files.pythonhosted.org/packages/92/1d/433541994a5a69f4ad2fff39746ddbb0bdedb0ea0d85673eb0db68a7edd9/amqp-2.5.2.tar.gz";
32 sha256 = "13dhhfxjrqcjybnq4zahg92mydhpg2l76nxcmq7d560687wsxwbp";
32 sha256 = "13dhhfxjrqcjybnq4zahg92mydhpg2l76nxcmq7d560687wsxwbp";
33 };
33 };
34 meta = {
34 meta = {
35 license = [ pkgs.lib.licenses.bsdOriginal ];
35 license = [ pkgs.lib.licenses.bsdOriginal ];
36 };
36 };
37 };
37 };
38 "apispec" = super.buildPythonPackage {
38 "apispec" = super.buildPythonPackage {
39 name = "apispec-1.0.0";
39 name = "apispec-1.0.0";
40 doCheck = false;
40 doCheck = false;
41 propagatedBuildInputs = [
41 propagatedBuildInputs = [
42 self."PyYAML"
42 self."PyYAML"
43 ];
43 ];
44 src = fetchurl {
44 src = fetchurl {
45 url = "https://files.pythonhosted.org/packages/67/15/346c04988dd67d36007e28145504c520491930c878b1f484a97b27a8f497/apispec-1.0.0.tar.gz";
45 url = "https://files.pythonhosted.org/packages/67/15/346c04988dd67d36007e28145504c520491930c878b1f484a97b27a8f497/apispec-1.0.0.tar.gz";
46 sha256 = "1712w1anvqrvadjjpvai84vbaygaxabd3zz5lxihdzwzs4gvi9sp";
46 sha256 = "1712w1anvqrvadjjpvai84vbaygaxabd3zz5lxihdzwzs4gvi9sp";
47 };
47 };
48 meta = {
48 meta = {
49 license = [ pkgs.lib.licenses.mit ];
49 license = [ pkgs.lib.licenses.mit ];
50 };
50 };
51 };
51 };
52 "appenlight-client" = super.buildPythonPackage {
52 "appenlight-client" = super.buildPythonPackage {
53 name = "appenlight-client-0.6.26";
53 name = "appenlight-client-0.6.26";
54 doCheck = false;
54 doCheck = false;
55 propagatedBuildInputs = [
55 propagatedBuildInputs = [
56 self."webob"
56 self."webob"
57 self."requests"
57 self."requests"
58 self."six"
58 self."six"
59 ];
59 ];
60 src = fetchurl {
60 src = fetchurl {
61 url = "https://files.pythonhosted.org/packages/2e/56/418fc10379b96e795ee39a15e69a730c222818af04c3821fa354eaa859ec/appenlight_client-0.6.26.tar.gz";
61 url = "https://files.pythonhosted.org/packages/2e/56/418fc10379b96e795ee39a15e69a730c222818af04c3821fa354eaa859ec/appenlight_client-0.6.26.tar.gz";
62 sha256 = "0s9xw3sb8s3pk73k78nnq4jil3q4mk6bczfa1fmgfx61kdxl2712";
62 sha256 = "0s9xw3sb8s3pk73k78nnq4jil3q4mk6bczfa1fmgfx61kdxl2712";
63 };
63 };
64 meta = {
64 meta = {
65 license = [ pkgs.lib.licenses.bsdOriginal ];
65 license = [ pkgs.lib.licenses.bsdOriginal ];
66 };
66 };
67 };
67 };
68 "asn1crypto" = super.buildPythonPackage {
68 "asn1crypto" = super.buildPythonPackage {
69 name = "asn1crypto-0.24.0";
69 name = "asn1crypto-0.24.0";
70 doCheck = false;
70 doCheck = false;
71 src = fetchurl {
71 src = fetchurl {
72 url = "https://files.pythonhosted.org/packages/fc/f1/8db7daa71f414ddabfa056c4ef792e1461ff655c2ae2928a2b675bfed6b4/asn1crypto-0.24.0.tar.gz";
72 url = "https://files.pythonhosted.org/packages/fc/f1/8db7daa71f414ddabfa056c4ef792e1461ff655c2ae2928a2b675bfed6b4/asn1crypto-0.24.0.tar.gz";
73 sha256 = "0jaf8rf9dx1lf23xfv2cdd5h52f1qr3w8k63985bc35g3d220p4x";
73 sha256 = "0jaf8rf9dx1lf23xfv2cdd5h52f1qr3w8k63985bc35g3d220p4x";
74 };
74 };
75 meta = {
75 meta = {
76 license = [ pkgs.lib.licenses.mit ];
76 license = [ pkgs.lib.licenses.mit ];
77 };
77 };
78 };
78 };
79 "atomicwrites" = super.buildPythonPackage {
79 "atomicwrites" = super.buildPythonPackage {
80 name = "atomicwrites-1.3.0";
80 name = "atomicwrites-1.3.0";
81 doCheck = false;
81 doCheck = false;
82 src = fetchurl {
82 src = fetchurl {
83 url = "https://files.pythonhosted.org/packages/ec/0f/cd484ac8820fed363b374af30049adc8fd13065720fd4f4c6be8a2309da7/atomicwrites-1.3.0.tar.gz";
83 url = "https://files.pythonhosted.org/packages/ec/0f/cd484ac8820fed363b374af30049adc8fd13065720fd4f4c6be8a2309da7/atomicwrites-1.3.0.tar.gz";
84 sha256 = "19ngcscdf3jsqmpcxn6zl5b6anmsajb6izp1smcd1n02midl9abm";
84 sha256 = "19ngcscdf3jsqmpcxn6zl5b6anmsajb6izp1smcd1n02midl9abm";
85 };
85 };
86 meta = {
86 meta = {
87 license = [ pkgs.lib.licenses.mit ];
87 license = [ pkgs.lib.licenses.mit ];
88 };
88 };
89 };
89 };
90 "attrs" = super.buildPythonPackage {
90 "attrs" = super.buildPythonPackage {
91 name = "attrs-19.3.0";
91 name = "attrs-19.3.0";
92 doCheck = false;
92 doCheck = false;
93 src = fetchurl {
93 src = fetchurl {
94 url = "https://files.pythonhosted.org/packages/98/c3/2c227e66b5e896e15ccdae2e00bbc69aa46e9a8ce8869cc5fa96310bf612/attrs-19.3.0.tar.gz";
94 url = "https://files.pythonhosted.org/packages/98/c3/2c227e66b5e896e15ccdae2e00bbc69aa46e9a8ce8869cc5fa96310bf612/attrs-19.3.0.tar.gz";
95 sha256 = "0wky4h28n7xnr6xv69p9z6kv8bzn50d10c3drmd9ds8gawbcxdzp";
95 sha256 = "0wky4h28n7xnr6xv69p9z6kv8bzn50d10c3drmd9ds8gawbcxdzp";
96 };
96 };
97 meta = {
97 meta = {
98 license = [ pkgs.lib.licenses.mit ];
98 license = [ pkgs.lib.licenses.mit ];
99 };
99 };
100 };
100 };
101 "babel" = super.buildPythonPackage {
101 "babel" = super.buildPythonPackage {
102 name = "babel-1.3";
102 name = "babel-1.3";
103 doCheck = false;
103 doCheck = false;
104 propagatedBuildInputs = [
104 propagatedBuildInputs = [
105 self."pytz"
105 self."pytz"
106 ];
106 ];
107 src = fetchurl {
107 src = fetchurl {
108 url = "https://files.pythonhosted.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
108 url = "https://files.pythonhosted.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
109 sha256 = "0bnin777lc53nxd1hp3apq410jj5wx92n08h7h4izpl4f4sx00lz";
109 sha256 = "0bnin777lc53nxd1hp3apq410jj5wx92n08h7h4izpl4f4sx00lz";
110 };
110 };
111 meta = {
111 meta = {
112 license = [ pkgs.lib.licenses.bsdOriginal ];
112 license = [ pkgs.lib.licenses.bsdOriginal ];
113 };
113 };
114 };
114 };
115 "backports.shutil-get-terminal-size" = super.buildPythonPackage {
115 "backports.shutil-get-terminal-size" = super.buildPythonPackage {
116 name = "backports.shutil-get-terminal-size-1.0.0";
116 name = "backports.shutil-get-terminal-size-1.0.0";
117 doCheck = false;
117 doCheck = false;
118 src = fetchurl {
118 src = fetchurl {
119 url = "https://files.pythonhosted.org/packages/ec/9c/368086faa9c016efce5da3e0e13ba392c9db79e3ab740b763fe28620b18b/backports.shutil_get_terminal_size-1.0.0.tar.gz";
119 url = "https://files.pythonhosted.org/packages/ec/9c/368086faa9c016efce5da3e0e13ba392c9db79e3ab740b763fe28620b18b/backports.shutil_get_terminal_size-1.0.0.tar.gz";
120 sha256 = "107cmn7g3jnbkp826zlj8rrj19fam301qvaqf0f3905f5217lgki";
120 sha256 = "107cmn7g3jnbkp826zlj8rrj19fam301qvaqf0f3905f5217lgki";
121 };
121 };
122 meta = {
122 meta = {
123 license = [ pkgs.lib.licenses.mit ];
123 license = [ pkgs.lib.licenses.mit ];
124 };
124 };
125 };
125 };
126 "beaker" = super.buildPythonPackage {
126 "beaker" = super.buildPythonPackage {
127 name = "beaker-1.9.1";
127 name = "beaker-1.9.1";
128 doCheck = false;
128 doCheck = false;
129 propagatedBuildInputs = [
129 propagatedBuildInputs = [
130 self."funcsigs"
130 self."funcsigs"
131 ];
131 ];
132 src = fetchurl {
132 src = fetchurl {
133 url = "https://files.pythonhosted.org/packages/ca/14/a626188d0d0c7b55dd7cf1902046c2743bd392a7078bb53073e13280eb1e/Beaker-1.9.1.tar.gz";
133 url = "https://files.pythonhosted.org/packages/ca/14/a626188d0d0c7b55dd7cf1902046c2743bd392a7078bb53073e13280eb1e/Beaker-1.9.1.tar.gz";
134 sha256 = "08arsn61r255lhz6hcpn2lsiqpg30clla805ysx06wmbhvb6w9rj";
134 sha256 = "08arsn61r255lhz6hcpn2lsiqpg30clla805ysx06wmbhvb6w9rj";
135 };
135 };
136 meta = {
136 meta = {
137 license = [ pkgs.lib.licenses.bsdOriginal ];
137 license = [ pkgs.lib.licenses.bsdOriginal ];
138 };
138 };
139 };
139 };
140 "beautifulsoup4" = super.buildPythonPackage {
140 "beautifulsoup4" = super.buildPythonPackage {
141 name = "beautifulsoup4-4.6.3";
141 name = "beautifulsoup4-4.6.3";
142 doCheck = false;
142 doCheck = false;
143 src = fetchurl {
143 src = fetchurl {
144 url = "https://files.pythonhosted.org/packages/88/df/86bffad6309f74f3ff85ea69344a078fc30003270c8df6894fca7a3c72ff/beautifulsoup4-4.6.3.tar.gz";
144 url = "https://files.pythonhosted.org/packages/88/df/86bffad6309f74f3ff85ea69344a078fc30003270c8df6894fca7a3c72ff/beautifulsoup4-4.6.3.tar.gz";
145 sha256 = "041dhalzjciw6qyzzq7a2k4h1yvyk76xigp35hv5ibnn448ydy4h";
145 sha256 = "041dhalzjciw6qyzzq7a2k4h1yvyk76xigp35hv5ibnn448ydy4h";
146 };
146 };
147 meta = {
147 meta = {
148 license = [ pkgs.lib.licenses.mit ];
148 license = [ pkgs.lib.licenses.mit ];
149 };
149 };
150 };
150 };
151 "billiard" = super.buildPythonPackage {
151 "billiard" = super.buildPythonPackage {
152 name = "billiard-3.6.1.0";
152 name = "billiard-3.6.1.0";
153 doCheck = false;
153 doCheck = false;
154 src = fetchurl {
154 src = fetchurl {
155 url = "https://files.pythonhosted.org/packages/68/1d/2aea8fbb0b1e1260a8a2e77352de2983d36d7ac01207cf14c2b9c6cc860e/billiard-3.6.1.0.tar.gz";
155 url = "https://files.pythonhosted.org/packages/68/1d/2aea8fbb0b1e1260a8a2e77352de2983d36d7ac01207cf14c2b9c6cc860e/billiard-3.6.1.0.tar.gz";
156 sha256 = "09hzy3aqi7visy4vmf4xiish61n0rq5nd3iwjydydps8yrs9r05q";
156 sha256 = "09hzy3aqi7visy4vmf4xiish61n0rq5nd3iwjydydps8yrs9r05q";
157 };
157 };
158 meta = {
158 meta = {
159 license = [ pkgs.lib.licenses.bsdOriginal ];
159 license = [ pkgs.lib.licenses.bsdOriginal ];
160 };
160 };
161 };
161 };
162 "bleach" = super.buildPythonPackage {
162 "bleach" = super.buildPythonPackage {
163 name = "bleach-3.1.3";
163 name = "bleach-3.1.3";
164 doCheck = false;
164 doCheck = false;
165 propagatedBuildInputs = [
165 propagatedBuildInputs = [
166 self."six"
166 self."six"
167 self."webencodings"
167 self."webencodings"
168 ];
168 ];
169 src = fetchurl {
169 src = fetchurl {
170 url = "https://files.pythonhosted.org/packages/de/09/5267f8577a92487ed43bc694476c4629c6eca2e3c93fcf690a26bfe39e1d/bleach-3.1.3.tar.gz";
170 url = "https://files.pythonhosted.org/packages/de/09/5267f8577a92487ed43bc694476c4629c6eca2e3c93fcf690a26bfe39e1d/bleach-3.1.3.tar.gz";
171 sha256 = "0al437aw4p2xp83az5hhlrp913nsf0cg6kg4qj3fjhv4wakxipzq";
171 sha256 = "0al437aw4p2xp83az5hhlrp913nsf0cg6kg4qj3fjhv4wakxipzq";
172 };
172 };
173 meta = {
173 meta = {
174 license = [ pkgs.lib.licenses.asl20 ];
174 license = [ pkgs.lib.licenses.asl20 ];
175 };
175 };
176 };
176 };
177 "bumpversion" = super.buildPythonPackage {
177 "bumpversion" = super.buildPythonPackage {
178 name = "bumpversion-0.5.3";
178 name = "bumpversion-0.5.3";
179 doCheck = false;
179 doCheck = false;
180 src = fetchurl {
180 src = fetchurl {
181 url = "https://files.pythonhosted.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
181 url = "https://files.pythonhosted.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
182 sha256 = "0zn7694yfipxg35ikkfh7kvgl2fissha3dnqad2c5bvsvmrwhi37";
182 sha256 = "0zn7694yfipxg35ikkfh7kvgl2fissha3dnqad2c5bvsvmrwhi37";
183 };
183 };
184 meta = {
184 meta = {
185 license = [ pkgs.lib.licenses.mit ];
185 license = [ pkgs.lib.licenses.mit ];
186 };
186 };
187 };
187 };
188 "cachetools" = super.buildPythonPackage {
188 "cachetools" = super.buildPythonPackage {
189 name = "cachetools-3.1.1";
189 name = "cachetools-3.1.1";
190 doCheck = false;
190 doCheck = false;
191 src = fetchurl {
191 src = fetchurl {
192 url = "https://files.pythonhosted.org/packages/ae/37/7fd45996b19200e0cb2027a0b6bef4636951c4ea111bfad36c71287247f6/cachetools-3.1.1.tar.gz";
192 url = "https://files.pythonhosted.org/packages/ae/37/7fd45996b19200e0cb2027a0b6bef4636951c4ea111bfad36c71287247f6/cachetools-3.1.1.tar.gz";
193 sha256 = "16m69l6n6y1r1y7cklm92rr7v69ldig2n3lbl3j323w5jz7d78lf";
193 sha256 = "16m69l6n6y1r1y7cklm92rr7v69ldig2n3lbl3j323w5jz7d78lf";
194 };
194 };
195 meta = {
195 meta = {
196 license = [ pkgs.lib.licenses.mit ];
196 license = [ pkgs.lib.licenses.mit ];
197 };
197 };
198 };
198 };
199 "celery" = super.buildPythonPackage {
199 "celery" = super.buildPythonPackage {
200 name = "celery-4.3.0";
200 name = "celery-4.3.0";
201 doCheck = false;
201 doCheck = false;
202 propagatedBuildInputs = [
202 propagatedBuildInputs = [
203 self."pytz"
203 self."pytz"
204 self."billiard"
204 self."billiard"
205 self."kombu"
205 self."kombu"
206 self."vine"
206 self."vine"
207 ];
207 ];
208 src = fetchurl {
208 src = fetchurl {
209 url = "https://files.pythonhosted.org/packages/a2/4b/d020836f751617e907e84753a41c92231cd4b673ff991b8ee9da52361323/celery-4.3.0.tar.gz";
209 url = "https://files.pythonhosted.org/packages/a2/4b/d020836f751617e907e84753a41c92231cd4b673ff991b8ee9da52361323/celery-4.3.0.tar.gz";
210 sha256 = "1y8y0gbgkwimpxqnxq2rm5qz2vy01fvjiybnpm00y5rzd2m34iac";
210 sha256 = "1y8y0gbgkwimpxqnxq2rm5qz2vy01fvjiybnpm00y5rzd2m34iac";
211 };
211 };
212 meta = {
212 meta = {
213 license = [ pkgs.lib.licenses.bsdOriginal ];
213 license = [ pkgs.lib.licenses.bsdOriginal ];
214 };
214 };
215 };
215 };
216 "certifi" = super.buildPythonPackage {
216 "certifi" = super.buildPythonPackage {
217 name = "certifi-2020.4.5.1";
217 name = "certifi-2020.4.5.1";
218 doCheck = false;
218 doCheck = false;
219 src = fetchurl {
219 src = fetchurl {
220 url = "https://files.pythonhosted.org/packages/b8/e2/a3a86a67c3fc8249ed305fc7b7d290ebe5e4d46ad45573884761ef4dea7b/certifi-2020.4.5.1.tar.gz";
220 url = "https://files.pythonhosted.org/packages/b8/e2/a3a86a67c3fc8249ed305fc7b7d290ebe5e4d46ad45573884761ef4dea7b/certifi-2020.4.5.1.tar.gz";
221 sha256 = "06b5gfs7wmmipln8f3z928d2mmx2j4b3x7pnqmj6cvmyfh8v7z2i";
221 sha256 = "06b5gfs7wmmipln8f3z928d2mmx2j4b3x7pnqmj6cvmyfh8v7z2i";
222 };
222 };
223 meta = {
223 meta = {
224 license = [ pkgs.lib.licenses.mpl20 { fullName = "Mozilla Public License 2.0 (MPL 2.0)"; } ];
224 license = [ pkgs.lib.licenses.mpl20 { fullName = "Mozilla Public License 2.0 (MPL 2.0)"; } ];
225 };
225 };
226 };
226 };
227 "cffi" = super.buildPythonPackage {
227 "cffi" = super.buildPythonPackage {
228 name = "cffi-1.12.3";
228 name = "cffi-1.12.3";
229 doCheck = false;
229 doCheck = false;
230 propagatedBuildInputs = [
230 propagatedBuildInputs = [
231 self."pycparser"
231 self."pycparser"
232 ];
232 ];
233 src = fetchurl {
233 src = fetchurl {
234 url = "https://files.pythonhosted.org/packages/93/1a/ab8c62b5838722f29f3daffcc8d4bd61844aa9b5f437341cc890ceee483b/cffi-1.12.3.tar.gz";
234 url = "https://files.pythonhosted.org/packages/93/1a/ab8c62b5838722f29f3daffcc8d4bd61844aa9b5f437341cc890ceee483b/cffi-1.12.3.tar.gz";
235 sha256 = "0x075521fxwv0mfp4cqzk7lvmw4n94bjw601qkcv314z5s182704";
235 sha256 = "0x075521fxwv0mfp4cqzk7lvmw4n94bjw601qkcv314z5s182704";
236 };
236 };
237 meta = {
237 meta = {
238 license = [ pkgs.lib.licenses.mit ];
238 license = [ pkgs.lib.licenses.mit ];
239 };
239 };
240 };
240 };
241 "chameleon" = super.buildPythonPackage {
241 "chameleon" = super.buildPythonPackage {
242 name = "chameleon-2.24";
242 name = "chameleon-2.24";
243 doCheck = false;
243 doCheck = false;
244 src = fetchurl {
244 src = fetchurl {
245 url = "https://files.pythonhosted.org/packages/5a/9e/637379ffa13c5172b5c0e704833ffea6bf51cec7567f93fd6e903d53ed74/Chameleon-2.24.tar.gz";
245 url = "https://files.pythonhosted.org/packages/5a/9e/637379ffa13c5172b5c0e704833ffea6bf51cec7567f93fd6e903d53ed74/Chameleon-2.24.tar.gz";
246 sha256 = "0ykqr7syxfa6h9adjfnsv1gdsca2xzm22vmic8859n0f0j09abj5";
246 sha256 = "0ykqr7syxfa6h9adjfnsv1gdsca2xzm22vmic8859n0f0j09abj5";
247 };
247 };
248 meta = {
248 meta = {
249 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
249 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
250 };
250 };
251 };
251 };
252 "channelstream" = super.buildPythonPackage {
252 "channelstream" = super.buildPythonPackage {
253 name = "channelstream-0.6.14";
253 name = "channelstream-0.6.14";
254 doCheck = false;
254 doCheck = false;
255 propagatedBuildInputs = [
255 propagatedBuildInputs = [
256 self."gevent"
256 self."gevent"
257 self."ws4py"
257 self."ws4py"
258 self."marshmallow"
258 self."marshmallow"
259 self."python-dateutil"
259 self."python-dateutil"
260 self."pyramid"
260 self."pyramid"
261 self."pyramid-jinja2"
261 self."pyramid-jinja2"
262 self."pyramid-apispec"
262 self."pyramid-apispec"
263 self."itsdangerous"
263 self."itsdangerous"
264 self."requests"
264 self."requests"
265 self."six"
265 self."six"
266 ];
266 ];
267 src = fetchurl {
267 src = fetchurl {
268 url = "https://files.pythonhosted.org/packages/d4/2d/86d6757ccd06ce673ee224123471da3d45251d061da7c580bfc259bad853/channelstream-0.6.14.tar.gz";
268 url = "https://files.pythonhosted.org/packages/d4/2d/86d6757ccd06ce673ee224123471da3d45251d061da7c580bfc259bad853/channelstream-0.6.14.tar.gz";
269 sha256 = "0qgy5j3rj6c8cslzidh32glhkrhbbdxjc008y69v8a0y3zyaz2d3";
269 sha256 = "0qgy5j3rj6c8cslzidh32glhkrhbbdxjc008y69v8a0y3zyaz2d3";
270 };
270 };
271 meta = {
271 meta = {
272 license = [ pkgs.lib.licenses.bsdOriginal ];
272 license = [ pkgs.lib.licenses.bsdOriginal ];
273 };
273 };
274 };
274 };
275 "chardet" = super.buildPythonPackage {
275 "chardet" = super.buildPythonPackage {
276 name = "chardet-3.0.4";
276 name = "chardet-3.0.4";
277 doCheck = false;
277 doCheck = false;
278 src = fetchurl {
278 src = fetchurl {
279 url = "https://files.pythonhosted.org/packages/fc/bb/a5768c230f9ddb03acc9ef3f0d4a3cf93462473795d18e9535498c8f929d/chardet-3.0.4.tar.gz";
279 url = "https://files.pythonhosted.org/packages/fc/bb/a5768c230f9ddb03acc9ef3f0d4a3cf93462473795d18e9535498c8f929d/chardet-3.0.4.tar.gz";
280 sha256 = "1bpalpia6r5x1kknbk11p1fzph56fmmnp405ds8icksd3knr5aw4";
280 sha256 = "1bpalpia6r5x1kknbk11p1fzph56fmmnp405ds8icksd3knr5aw4";
281 };
281 };
282 meta = {
282 meta = {
283 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
283 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
284 };
284 };
285 };
285 };
286 "click" = super.buildPythonPackage {
286 "click" = super.buildPythonPackage {
287 name = "click-7.0";
287 name = "click-7.0";
288 doCheck = false;
288 doCheck = false;
289 src = fetchurl {
289 src = fetchurl {
290 url = "https://files.pythonhosted.org/packages/f8/5c/f60e9d8a1e77005f664b76ff8aeaee5bc05d0a91798afd7f53fc998dbc47/Click-7.0.tar.gz";
290 url = "https://files.pythonhosted.org/packages/f8/5c/f60e9d8a1e77005f664b76ff8aeaee5bc05d0a91798afd7f53fc998dbc47/Click-7.0.tar.gz";
291 sha256 = "1mzjixd4vjbjvzb6vylki9w1556a9qmdh35kzmq6cign46av952v";
291 sha256 = "1mzjixd4vjbjvzb6vylki9w1556a9qmdh35kzmq6cign46av952v";
292 };
292 };
293 meta = {
293 meta = {
294 license = [ pkgs.lib.licenses.bsdOriginal ];
294 license = [ pkgs.lib.licenses.bsdOriginal ];
295 };
295 };
296 };
296 };
297 "colander" = super.buildPythonPackage {
297 "colander" = super.buildPythonPackage {
298 name = "colander-1.7.0";
298 name = "colander-1.7.0";
299 doCheck = false;
299 doCheck = false;
300 propagatedBuildInputs = [
300 propagatedBuildInputs = [
301 self."translationstring"
301 self."translationstring"
302 self."iso8601"
302 self."iso8601"
303 self."enum34"
303 self."enum34"
304 ];
304 ];
305 src = fetchurl {
305 src = fetchurl {
306 url = "https://files.pythonhosted.org/packages/db/e4/74ab06f54211917b41865cafc987ce511e35503de48da9bfe9358a1bdc3e/colander-1.7.0.tar.gz";
306 url = "https://files.pythonhosted.org/packages/db/e4/74ab06f54211917b41865cafc987ce511e35503de48da9bfe9358a1bdc3e/colander-1.7.0.tar.gz";
307 sha256 = "1wl1bqab307lbbcjx81i28s3yl6dlm4rf15fxawkjb6j48x1cn6p";
307 sha256 = "1wl1bqab307lbbcjx81i28s3yl6dlm4rf15fxawkjb6j48x1cn6p";
308 };
308 };
309 meta = {
309 meta = {
310 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
310 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
311 };
311 };
312 };
312 };
313 "configobj" = super.buildPythonPackage {
313 "configobj" = super.buildPythonPackage {
314 name = "configobj-5.0.6";
314 name = "configobj-5.0.6";
315 doCheck = false;
315 doCheck = false;
316 propagatedBuildInputs = [
316 propagatedBuildInputs = [
317 self."six"
317 self."six"
318 ];
318 ];
319 src = fetchurl {
319 src = fetchurl {
320 url = "https://code.rhodecode.com/upstream/configobj/artifacts/download/0-012de99a-b1e1-4f64-a5c0-07a98a41b324.tar.gz?md5=6a513f51fe04b2c18cf84c1395a7c626";
320 url = "https://code.rhodecode.com/upstream/configobj/artifacts/download/0-012de99a-b1e1-4f64-a5c0-07a98a41b324.tar.gz?md5=6a513f51fe04b2c18cf84c1395a7c626";
321 sha256 = "0kqfrdfr14mw8yd8qwq14dv2xghpkjmd3yjsy8dfcbvpcc17xnxp";
321 sha256 = "0kqfrdfr14mw8yd8qwq14dv2xghpkjmd3yjsy8dfcbvpcc17xnxp";
322 };
322 };
323 meta = {
323 meta = {
324 license = [ pkgs.lib.licenses.bsdOriginal ];
324 license = [ pkgs.lib.licenses.bsdOriginal ];
325 };
325 };
326 };
326 };
327 "configparser" = super.buildPythonPackage {
327 "configparser" = super.buildPythonPackage {
328 name = "configparser-4.0.2";
328 name = "configparser-4.0.2";
329 doCheck = false;
329 doCheck = false;
330 src = fetchurl {
330 src = fetchurl {
331 url = "https://files.pythonhosted.org/packages/16/4f/48975536bd488d3a272549eb795ac4a13a5f7fcdc8995def77fbef3532ee/configparser-4.0.2.tar.gz";
331 url = "https://files.pythonhosted.org/packages/16/4f/48975536bd488d3a272549eb795ac4a13a5f7fcdc8995def77fbef3532ee/configparser-4.0.2.tar.gz";
332 sha256 = "1priacxym85yjcf68hh38w55nqswaxp71ryjyfdk222kg9l85ln7";
332 sha256 = "1priacxym85yjcf68hh38w55nqswaxp71ryjyfdk222kg9l85ln7";
333 };
333 };
334 meta = {
334 meta = {
335 license = [ pkgs.lib.licenses.mit ];
335 license = [ pkgs.lib.licenses.mit ];
336 };
336 };
337 };
337 };
338 "contextlib2" = super.buildPythonPackage {
338 "contextlib2" = super.buildPythonPackage {
339 name = "contextlib2-0.6.0.post1";
339 name = "contextlib2-0.6.0.post1";
340 doCheck = false;
340 doCheck = false;
341 src = fetchurl {
341 src = fetchurl {
342 url = "https://files.pythonhosted.org/packages/02/54/669207eb72e3d8ae8b38aa1f0703ee87a0e9f88f30d3c0a47bebdb6de242/contextlib2-0.6.0.post1.tar.gz";
342 url = "https://files.pythonhosted.org/packages/02/54/669207eb72e3d8ae8b38aa1f0703ee87a0e9f88f30d3c0a47bebdb6de242/contextlib2-0.6.0.post1.tar.gz";
343 sha256 = "0bhnr2ac7wy5l85ji909gyljyk85n92w8pdvslmrvc8qih4r1x01";
343 sha256 = "0bhnr2ac7wy5l85ji909gyljyk85n92w8pdvslmrvc8qih4r1x01";
344 };
344 };
345 meta = {
345 meta = {
346 license = [ pkgs.lib.licenses.psfl ];
346 license = [ pkgs.lib.licenses.psfl ];
347 };
347 };
348 };
348 };
349 "cov-core" = super.buildPythonPackage {
349 "cov-core" = super.buildPythonPackage {
350 name = "cov-core-1.15.0";
350 name = "cov-core-1.15.0";
351 doCheck = false;
351 doCheck = false;
352 propagatedBuildInputs = [
352 propagatedBuildInputs = [
353 self."coverage"
353 self."coverage"
354 ];
354 ];
355 src = fetchurl {
355 src = fetchurl {
356 url = "https://files.pythonhosted.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
356 url = "https://files.pythonhosted.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
357 sha256 = "0k3np9ymh06yv1ib96sb6wfsxjkqhmik8qfsn119vnhga9ywc52a";
357 sha256 = "0k3np9ymh06yv1ib96sb6wfsxjkqhmik8qfsn119vnhga9ywc52a";
358 };
358 };
359 meta = {
359 meta = {
360 license = [ pkgs.lib.licenses.mit ];
360 license = [ pkgs.lib.licenses.mit ];
361 };
361 };
362 };
362 };
363 "coverage" = super.buildPythonPackage {
363 "coverage" = super.buildPythonPackage {
364 name = "coverage-4.5.4";
364 name = "coverage-4.5.4";
365 doCheck = false;
365 doCheck = false;
366 src = fetchurl {
366 src = fetchurl {
367 url = "https://files.pythonhosted.org/packages/85/d5/818d0e603685c4a613d56f065a721013e942088047ff1027a632948bdae6/coverage-4.5.4.tar.gz";
367 url = "https://files.pythonhosted.org/packages/85/d5/818d0e603685c4a613d56f065a721013e942088047ff1027a632948bdae6/coverage-4.5.4.tar.gz";
368 sha256 = "0p0j4di6h8k6ica7jwwj09azdcg4ycxq60i9qsskmsg94cd9yzg0";
368 sha256 = "0p0j4di6h8k6ica7jwwj09azdcg4ycxq60i9qsskmsg94cd9yzg0";
369 };
369 };
370 meta = {
370 meta = {
371 license = [ pkgs.lib.licenses.asl20 ];
371 license = [ pkgs.lib.licenses.asl20 ];
372 };
372 };
373 };
373 };
374 "cryptography" = super.buildPythonPackage {
374 "cryptography" = super.buildPythonPackage {
375 name = "cryptography-2.6.1";
375 name = "cryptography-2.6.1";
376 doCheck = false;
376 doCheck = false;
377 propagatedBuildInputs = [
377 propagatedBuildInputs = [
378 self."asn1crypto"
378 self."asn1crypto"
379 self."six"
379 self."six"
380 self."cffi"
380 self."cffi"
381 self."enum34"
381 self."enum34"
382 self."ipaddress"
382 self."ipaddress"
383 ];
383 ];
384 src = fetchurl {
384 src = fetchurl {
385 url = "https://files.pythonhosted.org/packages/07/ca/bc827c5e55918ad223d59d299fff92f3563476c3b00d0a9157d9c0217449/cryptography-2.6.1.tar.gz";
385 url = "https://files.pythonhosted.org/packages/07/ca/bc827c5e55918ad223d59d299fff92f3563476c3b00d0a9157d9c0217449/cryptography-2.6.1.tar.gz";
386 sha256 = "19iwz5avym5zl6jrrrkym1rdaa9h61j20ph4cswsqgv8xg5j3j16";
386 sha256 = "19iwz5avym5zl6jrrrkym1rdaa9h61j20ph4cswsqgv8xg5j3j16";
387 };
387 };
388 meta = {
388 meta = {
389 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "BSD or Apache License, Version 2.0"; } pkgs.lib.licenses.asl20 ];
389 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "BSD or Apache License, Version 2.0"; } pkgs.lib.licenses.asl20 ];
390 };
390 };
391 };
391 };
392 "cssselect" = super.buildPythonPackage {
392 "cssselect" = super.buildPythonPackage {
393 name = "cssselect-1.0.3";
393 name = "cssselect-1.0.3";
394 doCheck = false;
394 doCheck = false;
395 src = fetchurl {
395 src = fetchurl {
396 url = "https://files.pythonhosted.org/packages/52/ea/f31e1d2e9eb130fda2a631e22eac369dc644e8807345fbed5113f2d6f92b/cssselect-1.0.3.tar.gz";
396 url = "https://files.pythonhosted.org/packages/52/ea/f31e1d2e9eb130fda2a631e22eac369dc644e8807345fbed5113f2d6f92b/cssselect-1.0.3.tar.gz";
397 sha256 = "011jqa2jhmydhi0iz4v1w3cr540z5zas8g2bw8brdw4s4b2qnv86";
397 sha256 = "011jqa2jhmydhi0iz4v1w3cr540z5zas8g2bw8brdw4s4b2qnv86";
398 };
398 };
399 meta = {
399 meta = {
400 license = [ pkgs.lib.licenses.bsdOriginal ];
400 license = [ pkgs.lib.licenses.bsdOriginal ];
401 };
401 };
402 };
402 };
403 "cssutils" = super.buildPythonPackage {
403 "cssutils" = super.buildPythonPackage {
404 name = "cssutils-1.0.2";
404 name = "cssutils-1.0.2";
405 doCheck = false;
405 doCheck = false;
406 src = fetchurl {
406 src = fetchurl {
407 url = "https://files.pythonhosted.org/packages/5c/0b/c5f29d29c037e97043770b5e7c740b6252993e4b57f029b3cd03c78ddfec/cssutils-1.0.2.tar.gz";
407 url = "https://files.pythonhosted.org/packages/5c/0b/c5f29d29c037e97043770b5e7c740b6252993e4b57f029b3cd03c78ddfec/cssutils-1.0.2.tar.gz";
408 sha256 = "1bxchrbqzapwijap0yhlxdil1w9bmwvgx77aizlkhc2mcxjg1z52";
408 sha256 = "1bxchrbqzapwijap0yhlxdil1w9bmwvgx77aizlkhc2mcxjg1z52";
409 };
409 };
410 meta = {
410 meta = {
411 license = [ { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL 2.1 or later, see also http://cthedot.de/cssutils/"; } ];
411 license = [ { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL 2.1 or later, see also http://cthedot.de/cssutils/"; } ];
412 };
412 };
413 };
413 };
414 "decorator" = super.buildPythonPackage {
414 "decorator" = super.buildPythonPackage {
415 name = "decorator-4.1.2";
415 name = "decorator-4.1.2";
416 doCheck = false;
416 doCheck = false;
417 src = fetchurl {
417 src = fetchurl {
418 url = "https://files.pythonhosted.org/packages/bb/e0/f6e41e9091e130bf16d4437dabbac3993908e4d6485ecbc985ef1352db94/decorator-4.1.2.tar.gz";
418 url = "https://files.pythonhosted.org/packages/bb/e0/f6e41e9091e130bf16d4437dabbac3993908e4d6485ecbc985ef1352db94/decorator-4.1.2.tar.gz";
419 sha256 = "1d8npb11kxyi36mrvjdpcjij76l5zfyrz2f820brf0l0rcw4vdkw";
419 sha256 = "1d8npb11kxyi36mrvjdpcjij76l5zfyrz2f820brf0l0rcw4vdkw";
420 };
420 };
421 meta = {
421 meta = {
422 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "new BSD License"; } ];
422 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "new BSD License"; } ];
423 };
423 };
424 };
424 };
425 "deform" = super.buildPythonPackage {
425 "deform" = super.buildPythonPackage {
426 name = "deform-2.0.8";
426 name = "deform-2.0.8";
427 doCheck = false;
427 doCheck = false;
428 propagatedBuildInputs = [
428 propagatedBuildInputs = [
429 self."chameleon"
429 self."chameleon"
430 self."colander"
430 self."colander"
431 self."iso8601"
431 self."iso8601"
432 self."peppercorn"
432 self."peppercorn"
433 self."translationstring"
433 self."translationstring"
434 self."zope.deprecation"
434 self."zope.deprecation"
435 ];
435 ];
436 src = fetchurl {
436 src = fetchurl {
437 url = "https://files.pythonhosted.org/packages/21/d0/45fdf891a82722c02fc2da319cf2d1ae6b5abf9e470ad3762135a895a868/deform-2.0.8.tar.gz";
437 url = "https://files.pythonhosted.org/packages/21/d0/45fdf891a82722c02fc2da319cf2d1ae6b5abf9e470ad3762135a895a868/deform-2.0.8.tar.gz";
438 sha256 = "0wbjv98sib96649aqaygzxnrkclyy50qij2rha6fn1i4c86bfdl9";
438 sha256 = "0wbjv98sib96649aqaygzxnrkclyy50qij2rha6fn1i4c86bfdl9";
439 };
439 };
440 meta = {
440 meta = {
441 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
441 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
442 };
442 };
443 };
443 };
444 "defusedxml" = super.buildPythonPackage {
444 "defusedxml" = super.buildPythonPackage {
445 name = "defusedxml-0.6.0";
445 name = "defusedxml-0.6.0";
446 doCheck = false;
446 doCheck = false;
447 src = fetchurl {
447 src = fetchurl {
448 url = "https://files.pythonhosted.org/packages/a4/5f/f8aa58ca0cf01cbcee728abc9d88bfeb74e95e6cb4334cfd5bed5673ea77/defusedxml-0.6.0.tar.gz";
448 url = "https://files.pythonhosted.org/packages/a4/5f/f8aa58ca0cf01cbcee728abc9d88bfeb74e95e6cb4334cfd5bed5673ea77/defusedxml-0.6.0.tar.gz";
449 sha256 = "1xbp8fivl3wlbyg2jrvs4lalaqv1xp9a9f29p75wdx2s2d6h717n";
449 sha256 = "1xbp8fivl3wlbyg2jrvs4lalaqv1xp9a9f29p75wdx2s2d6h717n";
450 };
450 };
451 meta = {
451 meta = {
452 license = [ pkgs.lib.licenses.psfl ];
452 license = [ pkgs.lib.licenses.psfl ];
453 };
453 };
454 };
454 };
455 "dm.xmlsec.binding" = super.buildPythonPackage {
455 "dm.xmlsec.binding" = super.buildPythonPackage {
456 name = "dm.xmlsec.binding-1.3.7";
456 name = "dm.xmlsec.binding-1.3.7";
457 doCheck = false;
457 doCheck = false;
458 propagatedBuildInputs = [
458 propagatedBuildInputs = [
459 self."setuptools"
459 self."setuptools"
460 self."lxml"
460 self."lxml"
461 ];
461 ];
462 src = fetchurl {
462 src = fetchurl {
463 url = "https://files.pythonhosted.org/packages/2c/9e/7651982d50252692991acdae614af821fd6c79bc8dcd598ad71d55be8fc7/dm.xmlsec.binding-1.3.7.tar.gz";
463 url = "https://files.pythonhosted.org/packages/2c/9e/7651982d50252692991acdae614af821fd6c79bc8dcd598ad71d55be8fc7/dm.xmlsec.binding-1.3.7.tar.gz";
464 sha256 = "03jjjscx1pz2nc0dwiw9nia02qbz1c6f0f9zkyr8fmvys2n5jkb3";
464 sha256 = "03jjjscx1pz2nc0dwiw9nia02qbz1c6f0f9zkyr8fmvys2n5jkb3";
465 };
465 };
466 meta = {
466 meta = {
467 license = [ pkgs.lib.licenses.bsdOriginal ];
467 license = [ pkgs.lib.licenses.bsdOriginal ];
468 };
468 };
469 };
469 };
470 "docutils" = super.buildPythonPackage {
470 "docutils" = super.buildPythonPackage {
471 name = "docutils-0.16";
471 name = "docutils-0.16";
472 doCheck = false;
472 doCheck = false;
473 src = fetchurl {
473 src = fetchurl {
474 url = "https://files.pythonhosted.org/packages/2f/e0/3d435b34abd2d62e8206171892f174b180cd37b09d57b924ca5c2ef2219d/docutils-0.16.tar.gz";
474 url = "https://files.pythonhosted.org/packages/2f/e0/3d435b34abd2d62e8206171892f174b180cd37b09d57b924ca5c2ef2219d/docutils-0.16.tar.gz";
475 sha256 = "1z3qliszqca9m719q3qhdkh0ghh90g500avzdgi7pl77x5h3mpn2";
475 sha256 = "1z3qliszqca9m719q3qhdkh0ghh90g500avzdgi7pl77x5h3mpn2";
476 };
476 };
477 meta = {
477 meta = {
478 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.publicDomain pkgs.lib.licenses.gpl1 { fullName = "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)"; } pkgs.lib.licenses.psfl ];
478 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.publicDomain pkgs.lib.licenses.gpl1 { fullName = "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)"; } pkgs.lib.licenses.psfl ];
479 };
479 };
480 };
480 };
481 "dogpile.cache" = super.buildPythonPackage {
481 "dogpile.cache" = super.buildPythonPackage {
482 name = "dogpile.cache-0.9.0";
482 name = "dogpile.cache-0.9.0";
483 doCheck = false;
483 doCheck = false;
484 propagatedBuildInputs = [
484 propagatedBuildInputs = [
485 self."decorator"
485 self."decorator"
486 ];
486 ];
487 src = fetchurl {
487 src = fetchurl {
488 url = "https://files.pythonhosted.org/packages/ac/6a/9ac405686a94b7f009a20a50070a5786b0e1aedc707b88d40d0c4b51a82e/dogpile.cache-0.9.0.tar.gz";
488 url = "https://files.pythonhosted.org/packages/ac/6a/9ac405686a94b7f009a20a50070a5786b0e1aedc707b88d40d0c4b51a82e/dogpile.cache-0.9.0.tar.gz";
489 sha256 = "0sr1fn6b4k5bh0cscd9yi8csqxvj4ngzildav58x5p694mc86j5k";
489 sha256 = "0sr1fn6b4k5bh0cscd9yi8csqxvj4ngzildav58x5p694mc86j5k";
490 };
490 };
491 meta = {
491 meta = {
492 license = [ pkgs.lib.licenses.bsdOriginal ];
492 license = [ pkgs.lib.licenses.bsdOriginal ];
493 };
493 };
494 };
494 };
495 "dogpile.core" = super.buildPythonPackage {
495 "dogpile.core" = super.buildPythonPackage {
496 name = "dogpile.core-0.4.1";
496 name = "dogpile.core-0.4.1";
497 doCheck = false;
497 doCheck = false;
498 src = fetchurl {
498 src = fetchurl {
499 url = "https://files.pythonhosted.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
499 url = "https://files.pythonhosted.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
500 sha256 = "0xpdvg4kr1isfkrh1rfsh7za4q5a5s6l2kf9wpvndbwf3aqjyrdy";
500 sha256 = "0xpdvg4kr1isfkrh1rfsh7za4q5a5s6l2kf9wpvndbwf3aqjyrdy";
501 };
501 };
502 meta = {
502 meta = {
503 license = [ pkgs.lib.licenses.bsdOriginal ];
503 license = [ pkgs.lib.licenses.bsdOriginal ];
504 };
504 };
505 };
505 };
506 "ecdsa" = super.buildPythonPackage {
506 "ecdsa" = super.buildPythonPackage {
507 name = "ecdsa-0.13.2";
507 name = "ecdsa-0.13.2";
508 doCheck = false;
508 doCheck = false;
509 src = fetchurl {
509 src = fetchurl {
510 url = "https://files.pythonhosted.org/packages/51/76/139bf6e9b7b6684d5891212cdbd9e0739f2bfc03f380a1a6ffa700f392ac/ecdsa-0.13.2.tar.gz";
510 url = "https://files.pythonhosted.org/packages/51/76/139bf6e9b7b6684d5891212cdbd9e0739f2bfc03f380a1a6ffa700f392ac/ecdsa-0.13.2.tar.gz";
511 sha256 = "116qaq7bh4lcynzi613960jhsnn19v0kmsqwahiwjfj14gx4y0sw";
511 sha256 = "116qaq7bh4lcynzi613960jhsnn19v0kmsqwahiwjfj14gx4y0sw";
512 };
512 };
513 meta = {
513 meta = {
514 license = [ pkgs.lib.licenses.mit ];
514 license = [ pkgs.lib.licenses.mit ];
515 };
515 };
516 };
516 };
517 "elasticsearch" = super.buildPythonPackage {
517 "elasticsearch" = super.buildPythonPackage {
518 name = "elasticsearch-6.3.1";
518 name = "elasticsearch-6.3.1";
519 doCheck = false;
519 doCheck = false;
520 propagatedBuildInputs = [
520 propagatedBuildInputs = [
521 self."urllib3"
521 self."urllib3"
522 ];
522 ];
523 src = fetchurl {
523 src = fetchurl {
524 url = "https://files.pythonhosted.org/packages/9d/ce/c4664e8380e379a9402ecfbaf158e56396da90d520daba21cfa840e0eb71/elasticsearch-6.3.1.tar.gz";
524 url = "https://files.pythonhosted.org/packages/9d/ce/c4664e8380e379a9402ecfbaf158e56396da90d520daba21cfa840e0eb71/elasticsearch-6.3.1.tar.gz";
525 sha256 = "12y93v0yn7a4xmf969239g8gb3l4cdkclfpbk1qc8hx5qkymrnma";
525 sha256 = "12y93v0yn7a4xmf969239g8gb3l4cdkclfpbk1qc8hx5qkymrnma";
526 };
526 };
527 meta = {
527 meta = {
528 license = [ pkgs.lib.licenses.asl20 ];
528 license = [ pkgs.lib.licenses.asl20 ];
529 };
529 };
530 };
530 };
531 "elasticsearch-dsl" = super.buildPythonPackage {
531 "elasticsearch-dsl" = super.buildPythonPackage {
532 name = "elasticsearch-dsl-6.3.1";
532 name = "elasticsearch-dsl-6.3.1";
533 doCheck = false;
533 doCheck = false;
534 propagatedBuildInputs = [
534 propagatedBuildInputs = [
535 self."six"
535 self."six"
536 self."python-dateutil"
536 self."python-dateutil"
537 self."elasticsearch"
537 self."elasticsearch"
538 self."ipaddress"
538 self."ipaddress"
539 ];
539 ];
540 src = fetchurl {
540 src = fetchurl {
541 url = "https://files.pythonhosted.org/packages/4c/0d/1549f50c591db6bb4e66cbcc8d34a6e537c3d89aa426b167c244fd46420a/elasticsearch-dsl-6.3.1.tar.gz";
541 url = "https://files.pythonhosted.org/packages/4c/0d/1549f50c591db6bb4e66cbcc8d34a6e537c3d89aa426b167c244fd46420a/elasticsearch-dsl-6.3.1.tar.gz";
542 sha256 = "1gh8a0shqi105k325hgwb9avrpdjh0mc6mxwfg9ba7g6lssb702z";
542 sha256 = "1gh8a0shqi105k325hgwb9avrpdjh0mc6mxwfg9ba7g6lssb702z";
543 };
543 };
544 meta = {
544 meta = {
545 license = [ pkgs.lib.licenses.asl20 ];
545 license = [ pkgs.lib.licenses.asl20 ];
546 };
546 };
547 };
547 };
548 "elasticsearch1" = super.buildPythonPackage {
548 "elasticsearch1" = super.buildPythonPackage {
549 name = "elasticsearch1-1.10.0";
549 name = "elasticsearch1-1.10.0";
550 doCheck = false;
550 doCheck = false;
551 propagatedBuildInputs = [
551 propagatedBuildInputs = [
552 self."urllib3"
552 self."urllib3"
553 ];
553 ];
554 src = fetchurl {
554 src = fetchurl {
555 url = "https://files.pythonhosted.org/packages/a6/eb/73e75f9681fa71e3157b8ee878534235d57f24ee64f0e77f8d995fb57076/elasticsearch1-1.10.0.tar.gz";
555 url = "https://files.pythonhosted.org/packages/a6/eb/73e75f9681fa71e3157b8ee878534235d57f24ee64f0e77f8d995fb57076/elasticsearch1-1.10.0.tar.gz";
556 sha256 = "0g89444kd5zwql4vbvyrmi2m6l6dcj6ga98j4hqxyyyz6z20aki2";
556 sha256 = "0g89444kd5zwql4vbvyrmi2m6l6dcj6ga98j4hqxyyyz6z20aki2";
557 };
557 };
558 meta = {
558 meta = {
559 license = [ pkgs.lib.licenses.asl20 ];
559 license = [ pkgs.lib.licenses.asl20 ];
560 };
560 };
561 };
561 };
562 "elasticsearch1-dsl" = super.buildPythonPackage {
562 "elasticsearch1-dsl" = super.buildPythonPackage {
563 name = "elasticsearch1-dsl-0.0.12";
563 name = "elasticsearch1-dsl-0.0.12";
564 doCheck = false;
564 doCheck = false;
565 propagatedBuildInputs = [
565 propagatedBuildInputs = [
566 self."six"
566 self."six"
567 self."python-dateutil"
567 self."python-dateutil"
568 self."elasticsearch1"
568 self."elasticsearch1"
569 ];
569 ];
570 src = fetchurl {
570 src = fetchurl {
571 url = "https://files.pythonhosted.org/packages/eb/9d/785342775cb10eddc9b8d7457d618a423b4f0b89d8b2b2d1bc27190d71db/elasticsearch1-dsl-0.0.12.tar.gz";
571 url = "https://files.pythonhosted.org/packages/eb/9d/785342775cb10eddc9b8d7457d618a423b4f0b89d8b2b2d1bc27190d71db/elasticsearch1-dsl-0.0.12.tar.gz";
572 sha256 = "0ig1ly39v93hba0z975wnhbmzwj28w6w1sqlr2g7cn5spp732bhk";
572 sha256 = "0ig1ly39v93hba0z975wnhbmzwj28w6w1sqlr2g7cn5spp732bhk";
573 };
573 };
574 meta = {
574 meta = {
575 license = [ pkgs.lib.licenses.asl20 ];
575 license = [ pkgs.lib.licenses.asl20 ];
576 };
576 };
577 };
577 };
578 "elasticsearch2" = super.buildPythonPackage {
578 "elasticsearch2" = super.buildPythonPackage {
579 name = "elasticsearch2-2.5.1";
579 name = "elasticsearch2-2.5.1";
580 doCheck = false;
580 doCheck = false;
581 propagatedBuildInputs = [
581 propagatedBuildInputs = [
582 self."urllib3"
582 self."urllib3"
583 ];
583 ];
584 src = fetchurl {
584 src = fetchurl {
585 url = "https://files.pythonhosted.org/packages/f6/09/f9b24aa6b1120bea371cd57ef6f57c7694cf16660469456a8be6c2bdbe22/elasticsearch2-2.5.1.tar.gz";
585 url = "https://files.pythonhosted.org/packages/f6/09/f9b24aa6b1120bea371cd57ef6f57c7694cf16660469456a8be6c2bdbe22/elasticsearch2-2.5.1.tar.gz";
586 sha256 = "19k2znpjfyp0hrq73cz7pjyj289040xpsxsm0xhh4jfh6y551g7k";
586 sha256 = "19k2znpjfyp0hrq73cz7pjyj289040xpsxsm0xhh4jfh6y551g7k";
587 };
587 };
588 meta = {
588 meta = {
589 license = [ pkgs.lib.licenses.asl20 ];
589 license = [ pkgs.lib.licenses.asl20 ];
590 };
590 };
591 };
591 };
592 "entrypoints" = super.buildPythonPackage {
592 "entrypoints" = super.buildPythonPackage {
593 name = "entrypoints-0.2.2";
593 name = "entrypoints-0.2.2";
594 doCheck = false;
594 doCheck = false;
595 propagatedBuildInputs = [
595 propagatedBuildInputs = [
596 self."configparser"
596 self."configparser"
597 ];
597 ];
598 src = fetchurl {
598 src = fetchurl {
599 url = "https://code.rhodecode.com/upstream/entrypoints/artifacts/download/0-8e9ee9e4-c4db-409c-b07e-81568fd1832d.tar.gz?md5=3a027b8ff1d257b91fe257de6c43357d";
599 url = "https://code.rhodecode.com/upstream/entrypoints/artifacts/download/0-8e9ee9e4-c4db-409c-b07e-81568fd1832d.tar.gz?md5=3a027b8ff1d257b91fe257de6c43357d";
600 sha256 = "0qih72n2myclanplqipqxpgpj9d2yhff1pz5d02zq1cfqyd173w5";
600 sha256 = "0qih72n2myclanplqipqxpgpj9d2yhff1pz5d02zq1cfqyd173w5";
601 };
601 };
602 meta = {
602 meta = {
603 license = [ pkgs.lib.licenses.mit ];
603 license = [ pkgs.lib.licenses.mit ];
604 };
604 };
605 };
605 };
606 "enum34" = super.buildPythonPackage {
606 "enum34" = super.buildPythonPackage {
607 name = "enum34-1.1.10";
607 name = "enum34-1.1.10";
608 doCheck = false;
608 doCheck = false;
609 src = fetchurl {
609 src = fetchurl {
610 url = "https://files.pythonhosted.org/packages/11/c4/2da1f4952ba476677a42f25cd32ab8aaf0e1c0d0e00b89822b835c7e654c/enum34-1.1.10.tar.gz";
610 url = "https://files.pythonhosted.org/packages/11/c4/2da1f4952ba476677a42f25cd32ab8aaf0e1c0d0e00b89822b835c7e654c/enum34-1.1.10.tar.gz";
611 sha256 = "0j7ji699fwswm4vg6w1v07fkbf8dkzdm6gfh88jvs5nqgr3sgrnc";
611 sha256 = "0j7ji699fwswm4vg6w1v07fkbf8dkzdm6gfh88jvs5nqgr3sgrnc";
612 };
612 };
613 meta = {
613 meta = {
614 license = [ pkgs.lib.licenses.bsdOriginal ];
614 license = [ pkgs.lib.licenses.bsdOriginal ];
615 };
615 };
616 };
616 };
617 "formencode" = super.buildPythonPackage {
617 "formencode" = super.buildPythonPackage {
618 name = "formencode-1.2.4";
618 name = "formencode-1.2.4";
619 doCheck = false;
619 doCheck = false;
620 src = fetchurl {
620 src = fetchurl {
621 url = "https://files.pythonhosted.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
621 url = "https://files.pythonhosted.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
622 sha256 = "1fgy04sdy4yry5xcjls3x3xy30dqwj58ycnkndim819jx0788w42";
622 sha256 = "1fgy04sdy4yry5xcjls3x3xy30dqwj58ycnkndim819jx0788w42";
623 };
623 };
624 meta = {
624 meta = {
625 license = [ pkgs.lib.licenses.psfl ];
625 license = [ pkgs.lib.licenses.psfl ];
626 };
626 };
627 };
627 };
628 "funcsigs" = super.buildPythonPackage {
628 "funcsigs" = super.buildPythonPackage {
629 name = "funcsigs-1.0.2";
629 name = "funcsigs-1.0.2";
630 doCheck = false;
630 doCheck = false;
631 src = fetchurl {
631 src = fetchurl {
632 url = "https://files.pythonhosted.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz";
632 url = "https://files.pythonhosted.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz";
633 sha256 = "0l4g5818ffyfmfs1a924811azhjj8ax9xd1cffr1mzd3ycn0zfx7";
633 sha256 = "0l4g5818ffyfmfs1a924811azhjj8ax9xd1cffr1mzd3ycn0zfx7";
634 };
634 };
635 meta = {
635 meta = {
636 license = [ { fullName = "ASL"; } pkgs.lib.licenses.asl20 ];
636 license = [ { fullName = "ASL"; } pkgs.lib.licenses.asl20 ];
637 };
637 };
638 };
638 };
639 "functools32" = super.buildPythonPackage {
639 "functools32" = super.buildPythonPackage {
640 name = "functools32-3.2.3.post2";
640 name = "functools32-3.2.3.post2";
641 doCheck = false;
641 doCheck = false;
642 src = fetchurl {
642 src = fetchurl {
643 url = "https://files.pythonhosted.org/packages/c5/60/6ac26ad05857c601308d8fb9e87fa36d0ebf889423f47c3502ef034365db/functools32-3.2.3-2.tar.gz";
643 url = "https://files.pythonhosted.org/packages/c5/60/6ac26ad05857c601308d8fb9e87fa36d0ebf889423f47c3502ef034365db/functools32-3.2.3-2.tar.gz";
644 sha256 = "0v8ya0b58x47wp216n1zamimv4iw57cxz3xxhzix52jkw3xks9gn";
644 sha256 = "0v8ya0b58x47wp216n1zamimv4iw57cxz3xxhzix52jkw3xks9gn";
645 };
645 };
646 meta = {
646 meta = {
647 license = [ pkgs.lib.licenses.psfl ];
647 license = [ pkgs.lib.licenses.psfl ];
648 };
648 };
649 };
649 };
650 "future" = super.buildPythonPackage {
650 "future" = super.buildPythonPackage {
651 name = "future-0.14.3";
651 name = "future-0.14.3";
652 doCheck = false;
652 doCheck = false;
653 src = fetchurl {
653 src = fetchurl {
654 url = "https://files.pythonhosted.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
654 url = "https://files.pythonhosted.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
655 sha256 = "1savk7jx7hal032f522c5ajhh8fra6gmnadrj9adv5qxi18pv1b2";
655 sha256 = "1savk7jx7hal032f522c5ajhh8fra6gmnadrj9adv5qxi18pv1b2";
656 };
656 };
657 meta = {
657 meta = {
658 license = [ { fullName = "OSI Approved"; } pkgs.lib.licenses.mit ];
658 license = [ { fullName = "OSI Approved"; } pkgs.lib.licenses.mit ];
659 };
659 };
660 };
660 };
661 "futures" = super.buildPythonPackage {
661 "futures" = super.buildPythonPackage {
662 name = "futures-3.0.2";
662 name = "futures-3.0.2";
663 doCheck = false;
663 doCheck = false;
664 src = fetchurl {
664 src = fetchurl {
665 url = "https://files.pythonhosted.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
665 url = "https://files.pythonhosted.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
666 sha256 = "0mz2pbgxbc2nbib1szifi07whjbfs4r02pv2z390z7p410awjgyw";
666 sha256 = "0mz2pbgxbc2nbib1szifi07whjbfs4r02pv2z390z7p410awjgyw";
667 };
667 };
668 meta = {
668 meta = {
669 license = [ pkgs.lib.licenses.bsdOriginal ];
669 license = [ pkgs.lib.licenses.bsdOriginal ];
670 };
670 };
671 };
671 };
672 "gevent" = super.buildPythonPackage {
672 "gevent" = super.buildPythonPackage {
673 name = "gevent-1.5.0";
673 name = "gevent-1.5.0";
674 doCheck = false;
674 doCheck = false;
675 propagatedBuildInputs = [
675 propagatedBuildInputs = [
676 self."greenlet"
676 self."greenlet"
677 ];
677 ];
678 src = fetchurl {
678 src = fetchurl {
679 url = "https://files.pythonhosted.org/packages/5a/79/2c63d385d017b5dd7d70983a463dfd25befae70c824fedb857df6e72eff2/gevent-1.5.0.tar.gz";
679 url = "https://files.pythonhosted.org/packages/5a/79/2c63d385d017b5dd7d70983a463dfd25befae70c824fedb857df6e72eff2/gevent-1.5.0.tar.gz";
680 sha256 = "0aac3d4vhv5n4rsb6cqzq0d1xx9immqz4fmpddw35yxkwdc450dj";
680 sha256 = "0aac3d4vhv5n4rsb6cqzq0d1xx9immqz4fmpddw35yxkwdc450dj";
681 };
681 };
682 meta = {
682 meta = {
683 license = [ pkgs.lib.licenses.mit ];
683 license = [ pkgs.lib.licenses.mit ];
684 };
684 };
685 };
685 };
686 "gnureadline" = super.buildPythonPackage {
686 "gnureadline" = super.buildPythonPackage {
687 name = "gnureadline-6.3.8";
687 name = "gnureadline-6.3.8";
688 doCheck = false;
688 doCheck = false;
689 src = fetchurl {
689 src = fetchurl {
690 url = "https://files.pythonhosted.org/packages/50/64/86085c823cd78f9df9d8e33dce0baa71618016f8860460b82cf6610e1eb3/gnureadline-6.3.8.tar.gz";
690 url = "https://files.pythonhosted.org/packages/50/64/86085c823cd78f9df9d8e33dce0baa71618016f8860460b82cf6610e1eb3/gnureadline-6.3.8.tar.gz";
691 sha256 = "0ddhj98x2nv45iz4aadk4b9m0b1kpsn1xhcbypn5cd556knhiqjq";
691 sha256 = "0ddhj98x2nv45iz4aadk4b9m0b1kpsn1xhcbypn5cd556knhiqjq";
692 };
692 };
693 meta = {
693 meta = {
694 license = [ { fullName = "GNU General Public License v3 (GPLv3)"; } pkgs.lib.licenses.gpl1 ];
694 license = [ { fullName = "GNU General Public License v3 (GPLv3)"; } pkgs.lib.licenses.gpl1 ];
695 };
695 };
696 };
696 };
697 "gprof2dot" = super.buildPythonPackage {
697 "gprof2dot" = super.buildPythonPackage {
698 name = "gprof2dot-2017.9.19";
698 name = "gprof2dot-2017.9.19";
699 doCheck = false;
699 doCheck = false;
700 src = fetchurl {
700 src = fetchurl {
701 url = "https://files.pythonhosted.org/packages/9d/36/f977122502979f3dfb50704979c9ed70e6b620787942b089bf1af15f5aba/gprof2dot-2017.9.19.tar.gz";
701 url = "https://files.pythonhosted.org/packages/9d/36/f977122502979f3dfb50704979c9ed70e6b620787942b089bf1af15f5aba/gprof2dot-2017.9.19.tar.gz";
702 sha256 = "17ih23ld2nzgc3xwgbay911l6lh96jp1zshmskm17n1gg2i7mg6f";
702 sha256 = "17ih23ld2nzgc3xwgbay911l6lh96jp1zshmskm17n1gg2i7mg6f";
703 };
703 };
704 meta = {
704 meta = {
705 license = [ { fullName = "GNU Lesser General Public License v3 or later (LGPLv3+)"; } { fullName = "LGPL"; } ];
705 license = [ { fullName = "GNU Lesser General Public License v3 or later (LGPLv3+)"; } { fullName = "LGPL"; } ];
706 };
706 };
707 };
707 };
708 "greenlet" = super.buildPythonPackage {
708 "greenlet" = super.buildPythonPackage {
709 name = "greenlet-0.4.15";
709 name = "greenlet-0.4.15";
710 doCheck = false;
710 doCheck = false;
711 src = fetchurl {
711 src = fetchurl {
712 url = "https://files.pythonhosted.org/packages/f8/e8/b30ae23b45f69aa3f024b46064c0ac8e5fcb4f22ace0dca8d6f9c8bbe5e7/greenlet-0.4.15.tar.gz";
712 url = "https://files.pythonhosted.org/packages/f8/e8/b30ae23b45f69aa3f024b46064c0ac8e5fcb4f22ace0dca8d6f9c8bbe5e7/greenlet-0.4.15.tar.gz";
713 sha256 = "1g4g1wwc472ds89zmqlpyan3fbnzpa8qm48z3z1y6mlk44z485ll";
713 sha256 = "1g4g1wwc472ds89zmqlpyan3fbnzpa8qm48z3z1y6mlk44z485ll";
714 };
714 };
715 meta = {
715 meta = {
716 license = [ pkgs.lib.licenses.mit ];
716 license = [ pkgs.lib.licenses.mit ];
717 };
717 };
718 };
718 };
719 "gunicorn" = super.buildPythonPackage {
719 "gunicorn" = super.buildPythonPackage {
720 name = "gunicorn-19.9.0";
720 name = "gunicorn-19.9.0";
721 doCheck = false;
721 doCheck = false;
722 src = fetchurl {
722 src = fetchurl {
723 url = "https://files.pythonhosted.org/packages/47/52/68ba8e5e8ba251e54006a49441f7ccabca83b6bef5aedacb4890596c7911/gunicorn-19.9.0.tar.gz";
723 url = "https://files.pythonhosted.org/packages/47/52/68ba8e5e8ba251e54006a49441f7ccabca83b6bef5aedacb4890596c7911/gunicorn-19.9.0.tar.gz";
724 sha256 = "1wzlf4xmn6qjirh5w81l6i6kqjnab1n1qqkh7zsj1yb6gh4n49ps";
724 sha256 = "1wzlf4xmn6qjirh5w81l6i6kqjnab1n1qqkh7zsj1yb6gh4n49ps";
725 };
725 };
726 meta = {
726 meta = {
727 license = [ pkgs.lib.licenses.mit ];
727 license = [ pkgs.lib.licenses.mit ];
728 };
728 };
729 };
729 };
730 "hupper" = super.buildPythonPackage {
730 "hupper" = super.buildPythonPackage {
731 name = "hupper-1.10.2";
731 name = "hupper-1.10.2";
732 doCheck = false;
732 doCheck = false;
733 src = fetchurl {
733 src = fetchurl {
734 url = "https://files.pythonhosted.org/packages/41/24/ea90fef04706e54bd1635c05c50dc9cf87cda543c59303a03e7aa7dda0ce/hupper-1.10.2.tar.gz";
734 url = "https://files.pythonhosted.org/packages/41/24/ea90fef04706e54bd1635c05c50dc9cf87cda543c59303a03e7aa7dda0ce/hupper-1.10.2.tar.gz";
735 sha256 = "0am0p6g5cz6xmcaf04xq8q6dzdd9qz0phj6gcmpsckf2mcyza61q";
735 sha256 = "0am0p6g5cz6xmcaf04xq8q6dzdd9qz0phj6gcmpsckf2mcyza61q";
736 };
736 };
737 meta = {
737 meta = {
738 license = [ pkgs.lib.licenses.mit ];
738 license = [ pkgs.lib.licenses.mit ];
739 };
739 };
740 };
740 };
741 "idna" = super.buildPythonPackage {
741 "idna" = super.buildPythonPackage {
742 name = "idna-2.8";
742 name = "idna-2.8";
743 doCheck = false;
743 doCheck = false;
744 src = fetchurl {
744 src = fetchurl {
745 url = "https://files.pythonhosted.org/packages/ad/13/eb56951b6f7950cadb579ca166e448ba77f9d24efc03edd7e55fa57d04b7/idna-2.8.tar.gz";
745 url = "https://files.pythonhosted.org/packages/ad/13/eb56951b6f7950cadb579ca166e448ba77f9d24efc03edd7e55fa57d04b7/idna-2.8.tar.gz";
746 sha256 = "01rlkigdxg17sf9yar1jl8n18ls59367wqh59hnawlyg53vb6my3";
746 sha256 = "01rlkigdxg17sf9yar1jl8n18ls59367wqh59hnawlyg53vb6my3";
747 };
747 };
748 meta = {
748 meta = {
749 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "BSD-like"; } ];
749 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "BSD-like"; } ];
750 };
750 };
751 };
751 };
752 "importlib-metadata" = super.buildPythonPackage {
752 "importlib-metadata" = super.buildPythonPackage {
753 name = "importlib-metadata-1.6.0";
753 name = "importlib-metadata-1.6.0";
754 doCheck = false;
754 doCheck = false;
755 propagatedBuildInputs = [
755 propagatedBuildInputs = [
756 self."zipp"
756 self."zipp"
757 self."pathlib2"
757 self."pathlib2"
758 self."contextlib2"
758 self."contextlib2"
759 self."configparser"
759 self."configparser"
760 ];
760 ];
761 src = fetchurl {
761 src = fetchurl {
762 url = "https://files.pythonhosted.org/packages/b4/1b/baab42e3cd64c9d5caac25a9d6c054f8324cdc38975a44d600569f1f7158/importlib_metadata-1.6.0.tar.gz";
762 url = "https://files.pythonhosted.org/packages/b4/1b/baab42e3cd64c9d5caac25a9d6c054f8324cdc38975a44d600569f1f7158/importlib_metadata-1.6.0.tar.gz";
763 sha256 = "07icyggasn38yv2swdrd8z6i0plazmc9adavsdkbqqj91j53ll9l";
763 sha256 = "07icyggasn38yv2swdrd8z6i0plazmc9adavsdkbqqj91j53ll9l";
764 };
764 };
765 meta = {
765 meta = {
766 license = [ pkgs.lib.licenses.asl20 ];
766 license = [ pkgs.lib.licenses.asl20 ];
767 };
767 };
768 };
768 };
769 "infrae.cache" = super.buildPythonPackage {
769 "infrae.cache" = super.buildPythonPackage {
770 name = "infrae.cache-1.0.1";
770 name = "infrae.cache-1.0.1";
771 doCheck = false;
771 doCheck = false;
772 propagatedBuildInputs = [
772 propagatedBuildInputs = [
773 self."beaker"
773 self."beaker"
774 self."repoze.lru"
774 self."repoze.lru"
775 ];
775 ];
776 src = fetchurl {
776 src = fetchurl {
777 url = "https://files.pythonhosted.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
777 url = "https://files.pythonhosted.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
778 sha256 = "1dvqsjn8vw253wz9d1pz17j79mf4bs53dvp2qxck2qdp1am1njw4";
778 sha256 = "1dvqsjn8vw253wz9d1pz17j79mf4bs53dvp2qxck2qdp1am1njw4";
779 };
779 };
780 meta = {
780 meta = {
781 license = [ pkgs.lib.licenses.zpl21 ];
781 license = [ pkgs.lib.licenses.zpl21 ];
782 };
782 };
783 };
783 };
784 "invoke" = super.buildPythonPackage {
784 "invoke" = super.buildPythonPackage {
785 name = "invoke-0.13.0";
785 name = "invoke-0.13.0";
786 doCheck = false;
786 doCheck = false;
787 src = fetchurl {
787 src = fetchurl {
788 url = "https://files.pythonhosted.org/packages/47/bf/d07ef52fa1ac645468858bbac7cb95b246a972a045e821493d17d89c81be/invoke-0.13.0.tar.gz";
788 url = "https://files.pythonhosted.org/packages/47/bf/d07ef52fa1ac645468858bbac7cb95b246a972a045e821493d17d89c81be/invoke-0.13.0.tar.gz";
789 sha256 = "0794vhgxfmkh0vzkkg5cfv1w82g3jc3xr18wim29far9qpx9468s";
789 sha256 = "0794vhgxfmkh0vzkkg5cfv1w82g3jc3xr18wim29far9qpx9468s";
790 };
790 };
791 meta = {
791 meta = {
792 license = [ pkgs.lib.licenses.bsdOriginal ];
792 license = [ pkgs.lib.licenses.bsdOriginal ];
793 };
793 };
794 };
794 };
795 "ipaddress" = super.buildPythonPackage {
795 "ipaddress" = super.buildPythonPackage {
796 name = "ipaddress-1.0.23";
796 name = "ipaddress-1.0.23";
797 doCheck = false;
797 doCheck = false;
798 src = fetchurl {
798 src = fetchurl {
799 url = "https://files.pythonhosted.org/packages/b9/9a/3e9da40ea28b8210dd6504d3fe9fe7e013b62bf45902b458d1cdc3c34ed9/ipaddress-1.0.23.tar.gz";
799 url = "https://files.pythonhosted.org/packages/b9/9a/3e9da40ea28b8210dd6504d3fe9fe7e013b62bf45902b458d1cdc3c34ed9/ipaddress-1.0.23.tar.gz";
800 sha256 = "1qp743h30s04m3cg3yk3fycad930jv17q7dsslj4mfw0jlvf1y5p";
800 sha256 = "1qp743h30s04m3cg3yk3fycad930jv17q7dsslj4mfw0jlvf1y5p";
801 };
801 };
802 meta = {
802 meta = {
803 license = [ pkgs.lib.licenses.psfl ];
803 license = [ pkgs.lib.licenses.psfl ];
804 };
804 };
805 };
805 };
806 "ipdb" = super.buildPythonPackage {
806 "ipdb" = super.buildPythonPackage {
807 name = "ipdb-0.13.2";
807 name = "ipdb-0.13.2";
808 doCheck = false;
808 doCheck = false;
809 propagatedBuildInputs = [
809 propagatedBuildInputs = [
810 self."setuptools"
810 self."setuptools"
811 self."ipython"
811 self."ipython"
812 ];
812 ];
813 src = fetchurl {
813 src = fetchurl {
814 url = "https://files.pythonhosted.org/packages/2c/bb/a3e1a441719ebd75c6dac8170d3ddba884b7ee8a5c0f9aefa7297386627a/ipdb-0.13.2.tar.gz";
814 url = "https://files.pythonhosted.org/packages/2c/bb/a3e1a441719ebd75c6dac8170d3ddba884b7ee8a5c0f9aefa7297386627a/ipdb-0.13.2.tar.gz";
815 sha256 = "0jcd849rx30y3wcgzsqbn06v0yjlzvb9x3076q0yxpycdwm1ryvp";
815 sha256 = "0jcd849rx30y3wcgzsqbn06v0yjlzvb9x3076q0yxpycdwm1ryvp";
816 };
816 };
817 meta = {
817 meta = {
818 license = [ pkgs.lib.licenses.bsdOriginal ];
818 license = [ pkgs.lib.licenses.bsdOriginal ];
819 };
819 };
820 };
820 };
821 "ipython" = super.buildPythonPackage {
821 "ipython" = super.buildPythonPackage {
822 name = "ipython-5.1.0";
822 name = "ipython-5.1.0";
823 doCheck = false;
823 doCheck = false;
824 propagatedBuildInputs = [
824 propagatedBuildInputs = [
825 self."setuptools"
825 self."setuptools"
826 self."decorator"
826 self."decorator"
827 self."pickleshare"
827 self."pickleshare"
828 self."simplegeneric"
828 self."simplegeneric"
829 self."traitlets"
829 self."traitlets"
830 self."prompt-toolkit"
830 self."prompt-toolkit"
831 self."pygments"
831 self."pygments"
832 self."pexpect"
832 self."pexpect"
833 self."backports.shutil-get-terminal-size"
833 self."backports.shutil-get-terminal-size"
834 self."pathlib2"
834 self."pathlib2"
835 self."pexpect"
835 self."pexpect"
836 ];
836 ];
837 src = fetchurl {
837 src = fetchurl {
838 url = "https://files.pythonhosted.org/packages/89/63/a9292f7cd9d0090a0f995e1167f3f17d5889dcbc9a175261719c513b9848/ipython-5.1.0.tar.gz";
838 url = "https://files.pythonhosted.org/packages/89/63/a9292f7cd9d0090a0f995e1167f3f17d5889dcbc9a175261719c513b9848/ipython-5.1.0.tar.gz";
839 sha256 = "0qdrf6aj9kvjczd5chj1my8y2iq09am9l8bb2a1334a52d76kx3y";
839 sha256 = "0qdrf6aj9kvjczd5chj1my8y2iq09am9l8bb2a1334a52d76kx3y";
840 };
840 };
841 meta = {
841 meta = {
842 license = [ pkgs.lib.licenses.bsdOriginal ];
842 license = [ pkgs.lib.licenses.bsdOriginal ];
843 };
843 };
844 };
844 };
845 "ipython-genutils" = super.buildPythonPackage {
845 "ipython-genutils" = super.buildPythonPackage {
846 name = "ipython-genutils-0.2.0";
846 name = "ipython-genutils-0.2.0";
847 doCheck = false;
847 doCheck = false;
848 src = fetchurl {
848 src = fetchurl {
849 url = "https://files.pythonhosted.org/packages/e8/69/fbeffffc05236398ebfcfb512b6d2511c622871dca1746361006da310399/ipython_genutils-0.2.0.tar.gz";
849 url = "https://files.pythonhosted.org/packages/e8/69/fbeffffc05236398ebfcfb512b6d2511c622871dca1746361006da310399/ipython_genutils-0.2.0.tar.gz";
850 sha256 = "1a4bc9y8hnvq6cp08qs4mckgm6i6ajpndp4g496rvvzcfmp12bpb";
850 sha256 = "1a4bc9y8hnvq6cp08qs4mckgm6i6ajpndp4g496rvvzcfmp12bpb";
851 };
851 };
852 meta = {
852 meta = {
853 license = [ pkgs.lib.licenses.bsdOriginal ];
853 license = [ pkgs.lib.licenses.bsdOriginal ];
854 };
854 };
855 };
855 };
856 "iso8601" = super.buildPythonPackage {
856 "iso8601" = super.buildPythonPackage {
857 name = "iso8601-0.1.12";
857 name = "iso8601-0.1.12";
858 doCheck = false;
858 doCheck = false;
859 src = fetchurl {
859 src = fetchurl {
860 url = "https://files.pythonhosted.org/packages/45/13/3db24895497345fb44c4248c08b16da34a9eb02643cea2754b21b5ed08b0/iso8601-0.1.12.tar.gz";
860 url = "https://files.pythonhosted.org/packages/45/13/3db24895497345fb44c4248c08b16da34a9eb02643cea2754b21b5ed08b0/iso8601-0.1.12.tar.gz";
861 sha256 = "10nyvvnrhw2w3p09v1ica4lgj6f4g9j3kkfx17qmraiq3w7b5i29";
861 sha256 = "10nyvvnrhw2w3p09v1ica4lgj6f4g9j3kkfx17qmraiq3w7b5i29";
862 };
862 };
863 meta = {
863 meta = {
864 license = [ pkgs.lib.licenses.mit ];
864 license = [ pkgs.lib.licenses.mit ];
865 };
865 };
866 };
866 };
867 "isodate" = super.buildPythonPackage {
867 "isodate" = super.buildPythonPackage {
868 name = "isodate-0.6.0";
868 name = "isodate-0.6.0";
869 doCheck = false;
869 doCheck = false;
870 propagatedBuildInputs = [
870 propagatedBuildInputs = [
871 self."six"
871 self."six"
872 ];
872 ];
873 src = fetchurl {
873 src = fetchurl {
874 url = "https://files.pythonhosted.org/packages/b1/80/fb8c13a4cd38eb5021dc3741a9e588e4d1de88d895c1910c6fc8a08b7a70/isodate-0.6.0.tar.gz";
874 url = "https://files.pythonhosted.org/packages/b1/80/fb8c13a4cd38eb5021dc3741a9e588e4d1de88d895c1910c6fc8a08b7a70/isodate-0.6.0.tar.gz";
875 sha256 = "1n7jkz68kk5pwni540pr5zdh99bf6ywydk1p5pdrqisrawylldif";
875 sha256 = "1n7jkz68kk5pwni540pr5zdh99bf6ywydk1p5pdrqisrawylldif";
876 };
876 };
877 meta = {
877 meta = {
878 license = [ pkgs.lib.licenses.bsdOriginal ];
878 license = [ pkgs.lib.licenses.bsdOriginal ];
879 };
879 };
880 };
880 };
881 "itsdangerous" = super.buildPythonPackage {
881 "itsdangerous" = super.buildPythonPackage {
882 name = "itsdangerous-1.1.0";
882 name = "itsdangerous-1.1.0";
883 doCheck = false;
883 doCheck = false;
884 src = fetchurl {
884 src = fetchurl {
885 url = "https://files.pythonhosted.org/packages/68/1a/f27de07a8a304ad5fa817bbe383d1238ac4396da447fa11ed937039fa04b/itsdangerous-1.1.0.tar.gz";
885 url = "https://files.pythonhosted.org/packages/68/1a/f27de07a8a304ad5fa817bbe383d1238ac4396da447fa11ed937039fa04b/itsdangerous-1.1.0.tar.gz";
886 sha256 = "068zpbksq5q2z4dckh2k1zbcq43ay74ylqn77rni797j0wyh66rj";
886 sha256 = "068zpbksq5q2z4dckh2k1zbcq43ay74ylqn77rni797j0wyh66rj";
887 };
887 };
888 meta = {
888 meta = {
889 license = [ pkgs.lib.licenses.bsdOriginal ];
889 license = [ pkgs.lib.licenses.bsdOriginal ];
890 };
890 };
891 };
891 };
892 "jinja2" = super.buildPythonPackage {
892 "jinja2" = super.buildPythonPackage {
893 name = "jinja2-2.9.6";
893 name = "jinja2-2.9.6";
894 doCheck = false;
894 doCheck = false;
895 propagatedBuildInputs = [
895 propagatedBuildInputs = [
896 self."markupsafe"
896 self."markupsafe"
897 ];
897 ];
898 src = fetchurl {
898 src = fetchurl {
899 url = "https://files.pythonhosted.org/packages/90/61/f820ff0076a2599dd39406dcb858ecb239438c02ce706c8e91131ab9c7f1/Jinja2-2.9.6.tar.gz";
899 url = "https://files.pythonhosted.org/packages/90/61/f820ff0076a2599dd39406dcb858ecb239438c02ce706c8e91131ab9c7f1/Jinja2-2.9.6.tar.gz";
900 sha256 = "1zzrkywhziqffrzks14kzixz7nd4yh2vc0fb04a68vfd2ai03anx";
900 sha256 = "1zzrkywhziqffrzks14kzixz7nd4yh2vc0fb04a68vfd2ai03anx";
901 };
901 };
902 meta = {
902 meta = {
903 license = [ pkgs.lib.licenses.bsdOriginal ];
903 license = [ pkgs.lib.licenses.bsdOriginal ];
904 };
904 };
905 };
905 };
906 "jsonschema" = super.buildPythonPackage {
906 "jsonschema" = super.buildPythonPackage {
907 name = "jsonschema-2.6.0";
907 name = "jsonschema-2.6.0";
908 doCheck = false;
908 doCheck = false;
909 propagatedBuildInputs = [
909 propagatedBuildInputs = [
910 self."functools32"
910 self."functools32"
911 ];
911 ];
912 src = fetchurl {
912 src = fetchurl {
913 url = "https://files.pythonhosted.org/packages/58/b9/171dbb07e18c6346090a37f03c7e74410a1a56123f847efed59af260a298/jsonschema-2.6.0.tar.gz";
913 url = "https://files.pythonhosted.org/packages/58/b9/171dbb07e18c6346090a37f03c7e74410a1a56123f847efed59af260a298/jsonschema-2.6.0.tar.gz";
914 sha256 = "00kf3zmpp9ya4sydffpifn0j0mzm342a2vzh82p6r0vh10cg7xbg";
914 sha256 = "00kf3zmpp9ya4sydffpifn0j0mzm342a2vzh82p6r0vh10cg7xbg";
915 };
915 };
916 meta = {
916 meta = {
917 license = [ pkgs.lib.licenses.mit ];
917 license = [ pkgs.lib.licenses.mit ];
918 };
918 };
919 };
919 };
920 "jupyter-client" = super.buildPythonPackage {
920 "jupyter-client" = super.buildPythonPackage {
921 name = "jupyter-client-5.0.0";
921 name = "jupyter-client-5.0.0";
922 doCheck = false;
922 doCheck = false;
923 propagatedBuildInputs = [
923 propagatedBuildInputs = [
924 self."traitlets"
924 self."traitlets"
925 self."jupyter-core"
925 self."jupyter-core"
926 self."pyzmq"
926 self."pyzmq"
927 self."python-dateutil"
927 self."python-dateutil"
928 ];
928 ];
929 src = fetchurl {
929 src = fetchurl {
930 url = "https://files.pythonhosted.org/packages/e5/6f/65412ed462202b90134b7e761b0b7e7f949e07a549c1755475333727b3d0/jupyter_client-5.0.0.tar.gz";
930 url = "https://files.pythonhosted.org/packages/e5/6f/65412ed462202b90134b7e761b0b7e7f949e07a549c1755475333727b3d0/jupyter_client-5.0.0.tar.gz";
931 sha256 = "0nxw4rqk4wsjhc87gjqd7pv89cb9dnimcfnmcmp85bmrvv1gjri7";
931 sha256 = "0nxw4rqk4wsjhc87gjqd7pv89cb9dnimcfnmcmp85bmrvv1gjri7";
932 };
932 };
933 meta = {
933 meta = {
934 license = [ pkgs.lib.licenses.bsdOriginal ];
934 license = [ pkgs.lib.licenses.bsdOriginal ];
935 };
935 };
936 };
936 };
937 "jupyter-core" = super.buildPythonPackage {
937 "jupyter-core" = super.buildPythonPackage {
938 name = "jupyter-core-4.5.0";
938 name = "jupyter-core-4.5.0";
939 doCheck = false;
939 doCheck = false;
940 propagatedBuildInputs = [
940 propagatedBuildInputs = [
941 self."traitlets"
941 self."traitlets"
942 ];
942 ];
943 src = fetchurl {
943 src = fetchurl {
944 url = "https://files.pythonhosted.org/packages/4a/de/ff4ca734656d17ebe0450807b59d728f45277e2e7f4b82bc9aae6cb82961/jupyter_core-4.5.0.tar.gz";
944 url = "https://files.pythonhosted.org/packages/4a/de/ff4ca734656d17ebe0450807b59d728f45277e2e7f4b82bc9aae6cb82961/jupyter_core-4.5.0.tar.gz";
945 sha256 = "1xr4pbghwk5hayn5wwnhb7z95380r45p79gf5if5pi1akwg7qvic";
945 sha256 = "1xr4pbghwk5hayn5wwnhb7z95380r45p79gf5if5pi1akwg7qvic";
946 };
946 };
947 meta = {
947 meta = {
948 license = [ pkgs.lib.licenses.bsdOriginal ];
948 license = [ pkgs.lib.licenses.bsdOriginal ];
949 };
949 };
950 };
950 };
951 "kombu" = super.buildPythonPackage {
951 "kombu" = super.buildPythonPackage {
952 name = "kombu-4.6.6";
952 name = "kombu-4.6.6";
953 doCheck = false;
953 doCheck = false;
954 propagatedBuildInputs = [
954 propagatedBuildInputs = [
955 self."amqp"
955 self."amqp"
956 self."importlib-metadata"
956 self."importlib-metadata"
957 ];
957 ];
958 src = fetchurl {
958 src = fetchurl {
959 url = "https://files.pythonhosted.org/packages/20/e6/bc2d9affba6138a1dc143f77fef253e9e08e238fa7c0688d917c09005e96/kombu-4.6.6.tar.gz";
959 url = "https://files.pythonhosted.org/packages/20/e6/bc2d9affba6138a1dc143f77fef253e9e08e238fa7c0688d917c09005e96/kombu-4.6.6.tar.gz";
960 sha256 = "11mxpcy8mg1l35bgbhba70v29bydr2hrhdbdlb4lg98m3m5vaq0p";
960 sha256 = "11mxpcy8mg1l35bgbhba70v29bydr2hrhdbdlb4lg98m3m5vaq0p";
961 };
961 };
962 meta = {
962 meta = {
963 license = [ pkgs.lib.licenses.bsdOriginal ];
963 license = [ pkgs.lib.licenses.bsdOriginal ];
964 };
964 };
965 };
965 };
966 "lxml" = super.buildPythonPackage {
966 "lxml" = super.buildPythonPackage {
967 name = "lxml-4.2.5";
967 name = "lxml-4.2.5";
968 doCheck = false;
968 doCheck = false;
969 src = fetchurl {
969 src = fetchurl {
970 url = "https://files.pythonhosted.org/packages/4b/20/ddf5eb3bd5c57582d2b4652b4bbcf8da301bdfe5d805cb94e805f4d7464d/lxml-4.2.5.tar.gz";
970 url = "https://files.pythonhosted.org/packages/4b/20/ddf5eb3bd5c57582d2b4652b4bbcf8da301bdfe5d805cb94e805f4d7464d/lxml-4.2.5.tar.gz";
971 sha256 = "0zw0y9hs0nflxhl9cs6ipwwh53szi3w2x06wl0k9cylyqac0cwin";
971 sha256 = "0zw0y9hs0nflxhl9cs6ipwwh53szi3w2x06wl0k9cylyqac0cwin";
972 };
972 };
973 meta = {
973 meta = {
974 license = [ pkgs.lib.licenses.bsdOriginal ];
974 license = [ pkgs.lib.licenses.bsdOriginal ];
975 };
975 };
976 };
976 };
977 "mako" = super.buildPythonPackage {
977 "mako" = super.buildPythonPackage {
978 name = "mako-1.1.0";
978 name = "mako-1.1.0";
979 doCheck = false;
979 doCheck = false;
980 propagatedBuildInputs = [
980 propagatedBuildInputs = [
981 self."markupsafe"
981 self."markupsafe"
982 ];
982 ];
983 src = fetchurl {
983 src = fetchurl {
984 url = "https://files.pythonhosted.org/packages/b0/3c/8dcd6883d009f7cae0f3157fb53e9afb05a0d3d33b3db1268ec2e6f4a56b/Mako-1.1.0.tar.gz";
984 url = "https://files.pythonhosted.org/packages/b0/3c/8dcd6883d009f7cae0f3157fb53e9afb05a0d3d33b3db1268ec2e6f4a56b/Mako-1.1.0.tar.gz";
985 sha256 = "0jqa3qfpykyn4fmkn0kh6043sfls7br8i2bsdbccazcvk9cijsd3";
985 sha256 = "0jqa3qfpykyn4fmkn0kh6043sfls7br8i2bsdbccazcvk9cijsd3";
986 };
986 };
987 meta = {
987 meta = {
988 license = [ pkgs.lib.licenses.mit ];
988 license = [ pkgs.lib.licenses.mit ];
989 };
989 };
990 };
990 };
991 "markdown" = super.buildPythonPackage {
991 "markdown" = super.buildPythonPackage {
992 name = "markdown-2.6.11";
992 name = "markdown-2.6.11";
993 doCheck = false;
993 doCheck = false;
994 src = fetchurl {
994 src = fetchurl {
995 url = "https://files.pythonhosted.org/packages/b3/73/fc5c850f44af5889192dff783b7b0d8f3fe8d30b65c8e3f78f8f0265fecf/Markdown-2.6.11.tar.gz";
995 url = "https://files.pythonhosted.org/packages/b3/73/fc5c850f44af5889192dff783b7b0d8f3fe8d30b65c8e3f78f8f0265fecf/Markdown-2.6.11.tar.gz";
996 sha256 = "108g80ryzykh8bj0i7jfp71510wrcixdi771lf2asyghgyf8cmm8";
996 sha256 = "108g80ryzykh8bj0i7jfp71510wrcixdi771lf2asyghgyf8cmm8";
997 };
997 };
998 meta = {
998 meta = {
999 license = [ pkgs.lib.licenses.bsdOriginal ];
999 license = [ pkgs.lib.licenses.bsdOriginal ];
1000 };
1000 };
1001 };
1001 };
1002 "markupsafe" = super.buildPythonPackage {
1002 "markupsafe" = super.buildPythonPackage {
1003 name = "markupsafe-1.1.1";
1003 name = "markupsafe-1.1.1";
1004 doCheck = false;
1004 doCheck = false;
1005 src = fetchurl {
1005 src = fetchurl {
1006 url = "https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz";
1006 url = "https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz";
1007 sha256 = "0sqipg4fk7xbixqd8kq6rlkxj664d157bdwbh93farcphf92x1r9";
1007 sha256 = "0sqipg4fk7xbixqd8kq6rlkxj664d157bdwbh93farcphf92x1r9";
1008 };
1008 };
1009 meta = {
1009 meta = {
1010 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd3 ];
1010 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd3 ];
1011 };
1011 };
1012 };
1012 };
1013 "marshmallow" = super.buildPythonPackage {
1013 "marshmallow" = super.buildPythonPackage {
1014 name = "marshmallow-2.18.0";
1014 name = "marshmallow-2.18.0";
1015 doCheck = false;
1015 doCheck = false;
1016 src = fetchurl {
1016 src = fetchurl {
1017 url = "https://files.pythonhosted.org/packages/ad/0b/5799965d1c6d5f608d684e2c0dce8a828e0309a3bfe8327d9418a89f591c/marshmallow-2.18.0.tar.gz";
1017 url = "https://files.pythonhosted.org/packages/ad/0b/5799965d1c6d5f608d684e2c0dce8a828e0309a3bfe8327d9418a89f591c/marshmallow-2.18.0.tar.gz";
1018 sha256 = "1g0aafpjn7yaxq06yndy8c7rs9n42adxkqq1ayhlr869pr06d3lm";
1018 sha256 = "1g0aafpjn7yaxq06yndy8c7rs9n42adxkqq1ayhlr869pr06d3lm";
1019 };
1019 };
1020 meta = {
1020 meta = {
1021 license = [ pkgs.lib.licenses.mit ];
1021 license = [ pkgs.lib.licenses.mit ];
1022 };
1022 };
1023 };
1023 };
1024 "mistune" = super.buildPythonPackage {
1024 "mistune" = super.buildPythonPackage {
1025 name = "mistune-0.8.4";
1025 name = "mistune-0.8.4";
1026 doCheck = false;
1026 doCheck = false;
1027 src = fetchurl {
1027 src = fetchurl {
1028 url = "https://files.pythonhosted.org/packages/2d/a4/509f6e7783ddd35482feda27bc7f72e65b5e7dc910eca4ab2164daf9c577/mistune-0.8.4.tar.gz";
1028 url = "https://files.pythonhosted.org/packages/2d/a4/509f6e7783ddd35482feda27bc7f72e65b5e7dc910eca4ab2164daf9c577/mistune-0.8.4.tar.gz";
1029 sha256 = "0vkmsh0x480rni51lhyvigfdf06b9247z868pk3bal1wnnfl58sr";
1029 sha256 = "0vkmsh0x480rni51lhyvigfdf06b9247z868pk3bal1wnnfl58sr";
1030 };
1030 };
1031 meta = {
1031 meta = {
1032 license = [ pkgs.lib.licenses.bsdOriginal ];
1032 license = [ pkgs.lib.licenses.bsdOriginal ];
1033 };
1033 };
1034 };
1034 };
1035 "mock" = super.buildPythonPackage {
1035 "mock" = super.buildPythonPackage {
1036 name = "mock-3.0.5";
1036 name = "mock-3.0.5";
1037 doCheck = false;
1037 doCheck = false;
1038 propagatedBuildInputs = [
1038 propagatedBuildInputs = [
1039 self."six"
1039 self."six"
1040 self."funcsigs"
1040 self."funcsigs"
1041 ];
1041 ];
1042 src = fetchurl {
1042 src = fetchurl {
1043 url = "https://files.pythonhosted.org/packages/2e/ab/4fe657d78b270aa6a32f027849513b829b41b0f28d9d8d7f8c3d29ea559a/mock-3.0.5.tar.gz";
1043 url = "https://files.pythonhosted.org/packages/2e/ab/4fe657d78b270aa6a32f027849513b829b41b0f28d9d8d7f8c3d29ea559a/mock-3.0.5.tar.gz";
1044 sha256 = "1hrp6j0yrx2xzylfv02qa8kph661m6yq4p0mc8fnimch9j4psrc3";
1044 sha256 = "1hrp6j0yrx2xzylfv02qa8kph661m6yq4p0mc8fnimch9j4psrc3";
1045 };
1045 };
1046 meta = {
1046 meta = {
1047 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "OSI Approved :: BSD License"; } ];
1047 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "OSI Approved :: BSD License"; } ];
1048 };
1048 };
1049 };
1049 };
1050 "more-itertools" = super.buildPythonPackage {
1050 "more-itertools" = super.buildPythonPackage {
1051 name = "more-itertools-5.0.0";
1051 name = "more-itertools-5.0.0";
1052 doCheck = false;
1052 doCheck = false;
1053 propagatedBuildInputs = [
1053 propagatedBuildInputs = [
1054 self."six"
1054 self."six"
1055 ];
1055 ];
1056 src = fetchurl {
1056 src = fetchurl {
1057 url = "https://files.pythonhosted.org/packages/dd/26/30fc0d541d9fdf55faf5ba4b0fd68f81d5bd2447579224820ad525934178/more-itertools-5.0.0.tar.gz";
1057 url = "https://files.pythonhosted.org/packages/dd/26/30fc0d541d9fdf55faf5ba4b0fd68f81d5bd2447579224820ad525934178/more-itertools-5.0.0.tar.gz";
1058 sha256 = "1r12cm6mcdwdzz7d47a6g4l437xsvapdlgyhqay3i2nrlv03da9q";
1058 sha256 = "1r12cm6mcdwdzz7d47a6g4l437xsvapdlgyhqay3i2nrlv03da9q";
1059 };
1059 };
1060 meta = {
1060 meta = {
1061 license = [ pkgs.lib.licenses.mit ];
1061 license = [ pkgs.lib.licenses.mit ];
1062 };
1062 };
1063 };
1063 };
1064 "msgpack-python" = super.buildPythonPackage {
1064 "msgpack-python" = super.buildPythonPackage {
1065 name = "msgpack-python-0.5.6";
1065 name = "msgpack-python-0.5.6";
1066 doCheck = false;
1066 doCheck = false;
1067 src = fetchurl {
1067 src = fetchurl {
1068 url = "https://files.pythonhosted.org/packages/8a/20/6eca772d1a5830336f84aca1d8198e5a3f4715cd1c7fc36d3cc7f7185091/msgpack-python-0.5.6.tar.gz";
1068 url = "https://files.pythonhosted.org/packages/8a/20/6eca772d1a5830336f84aca1d8198e5a3f4715cd1c7fc36d3cc7f7185091/msgpack-python-0.5.6.tar.gz";
1069 sha256 = "16wh8qgybmfh4pjp8vfv78mdlkxfmcasg78lzlnm6nslsfkci31p";
1069 sha256 = "16wh8qgybmfh4pjp8vfv78mdlkxfmcasg78lzlnm6nslsfkci31p";
1070 };
1070 };
1071 meta = {
1071 meta = {
1072 license = [ pkgs.lib.licenses.asl20 ];
1072 license = [ pkgs.lib.licenses.asl20 ];
1073 };
1073 };
1074 };
1074 };
1075 "mysql-python" = super.buildPythonPackage {
1075 "mysql-python" = super.buildPythonPackage {
1076 name = "mysql-python-1.2.5";
1076 name = "mysql-python-1.2.5";
1077 doCheck = false;
1077 doCheck = false;
1078 src = fetchurl {
1078 src = fetchurl {
1079 url = "https://files.pythonhosted.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
1079 url = "https://files.pythonhosted.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
1080 sha256 = "0x0c2jg0bb3pp84njaqiic050qkyd7ymwhfvhipnimg58yv40441";
1080 sha256 = "0x0c2jg0bb3pp84njaqiic050qkyd7ymwhfvhipnimg58yv40441";
1081 };
1081 };
1082 meta = {
1082 meta = {
1083 license = [ pkgs.lib.licenses.gpl1 ];
1083 license = [ pkgs.lib.licenses.gpl1 ];
1084 };
1084 };
1085 };
1085 };
1086 "nbconvert" = super.buildPythonPackage {
1086 "nbconvert" = super.buildPythonPackage {
1087 name = "nbconvert-5.3.1";
1087 name = "nbconvert-5.3.1";
1088 doCheck = false;
1088 doCheck = false;
1089 propagatedBuildInputs = [
1089 propagatedBuildInputs = [
1090 self."mistune"
1090 self."mistune"
1091 self."jinja2"
1091 self."jinja2"
1092 self."pygments"
1092 self."pygments"
1093 self."traitlets"
1093 self."traitlets"
1094 self."jupyter-core"
1094 self."jupyter-core"
1095 self."nbformat"
1095 self."nbformat"
1096 self."entrypoints"
1096 self."entrypoints"
1097 self."bleach"
1097 self."bleach"
1098 self."pandocfilters"
1098 self."pandocfilters"
1099 self."testpath"
1099 self."testpath"
1100 ];
1100 ];
1101 src = fetchurl {
1101 src = fetchurl {
1102 url = "https://files.pythonhosted.org/packages/b9/a4/d0a0938ad6f5eeb4dea4e73d255c617ef94b0b2849d51194c9bbdb838412/nbconvert-5.3.1.tar.gz";
1102 url = "https://files.pythonhosted.org/packages/b9/a4/d0a0938ad6f5eeb4dea4e73d255c617ef94b0b2849d51194c9bbdb838412/nbconvert-5.3.1.tar.gz";
1103 sha256 = "1f9dkvpx186xjm4xab0qbph588mncp4vqk3fmxrsnqs43mks9c8j";
1103 sha256 = "1f9dkvpx186xjm4xab0qbph588mncp4vqk3fmxrsnqs43mks9c8j";
1104 };
1104 };
1105 meta = {
1105 meta = {
1106 license = [ pkgs.lib.licenses.bsdOriginal ];
1106 license = [ pkgs.lib.licenses.bsdOriginal ];
1107 };
1107 };
1108 };
1108 };
1109 "nbformat" = super.buildPythonPackage {
1109 "nbformat" = super.buildPythonPackage {
1110 name = "nbformat-4.4.0";
1110 name = "nbformat-4.4.0";
1111 doCheck = false;
1111 doCheck = false;
1112 propagatedBuildInputs = [
1112 propagatedBuildInputs = [
1113 self."ipython-genutils"
1113 self."ipython-genutils"
1114 self."traitlets"
1114 self."traitlets"
1115 self."jsonschema"
1115 self."jsonschema"
1116 self."jupyter-core"
1116 self."jupyter-core"
1117 ];
1117 ];
1118 src = fetchurl {
1118 src = fetchurl {
1119 url = "https://files.pythonhosted.org/packages/6e/0e/160754f7ae3e984863f585a3743b0ed1702043a81245907c8fae2d537155/nbformat-4.4.0.tar.gz";
1119 url = "https://files.pythonhosted.org/packages/6e/0e/160754f7ae3e984863f585a3743b0ed1702043a81245907c8fae2d537155/nbformat-4.4.0.tar.gz";
1120 sha256 = "00nlf08h8yc4q73nphfvfhxrcnilaqanb8z0mdy6nxk0vzq4wjgp";
1120 sha256 = "00nlf08h8yc4q73nphfvfhxrcnilaqanb8z0mdy6nxk0vzq4wjgp";
1121 };
1121 };
1122 meta = {
1122 meta = {
1123 license = [ pkgs.lib.licenses.bsdOriginal ];
1123 license = [ pkgs.lib.licenses.bsdOriginal ];
1124 };
1124 };
1125 };
1125 };
1126 "packaging" = super.buildPythonPackage {
1126 "packaging" = super.buildPythonPackage {
1127 name = "packaging-20.3";
1127 name = "packaging-20.3";
1128 doCheck = false;
1128 doCheck = false;
1129 propagatedBuildInputs = [
1129 propagatedBuildInputs = [
1130 self."pyparsing"
1130 self."pyparsing"
1131 self."six"
1131 self."six"
1132 ];
1132 ];
1133 src = fetchurl {
1133 src = fetchurl {
1134 url = "https://files.pythonhosted.org/packages/65/37/83e3f492eb52d771e2820e88105f605335553fe10422cba9d256faeb1702/packaging-20.3.tar.gz";
1134 url = "https://files.pythonhosted.org/packages/65/37/83e3f492eb52d771e2820e88105f605335553fe10422cba9d256faeb1702/packaging-20.3.tar.gz";
1135 sha256 = "18xpablq278janh03bai9xd4kz9b0yfp6vflazn725ns9x3jna9w";
1135 sha256 = "18xpablq278janh03bai9xd4kz9b0yfp6vflazn725ns9x3jna9w";
1136 };
1136 };
1137 meta = {
1137 meta = {
1138 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "BSD or Apache License, Version 2.0"; } pkgs.lib.licenses.asl20 ];
1138 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "BSD or Apache License, Version 2.0"; } pkgs.lib.licenses.asl20 ];
1139 };
1139 };
1140 };
1140 };
1141 "pandocfilters" = super.buildPythonPackage {
1141 "pandocfilters" = super.buildPythonPackage {
1142 name = "pandocfilters-1.4.2";
1142 name = "pandocfilters-1.4.2";
1143 doCheck = false;
1143 doCheck = false;
1144 src = fetchurl {
1144 src = fetchurl {
1145 url = "https://files.pythonhosted.org/packages/4c/ea/236e2584af67bb6df960832731a6e5325fd4441de001767da328c33368ce/pandocfilters-1.4.2.tar.gz";
1145 url = "https://files.pythonhosted.org/packages/4c/ea/236e2584af67bb6df960832731a6e5325fd4441de001767da328c33368ce/pandocfilters-1.4.2.tar.gz";
1146 sha256 = "1a8d9b7s48gmq9zj0pmbyv2sivn5i7m6mybgpkk4jm5vd7hp1pdk";
1146 sha256 = "1a8d9b7s48gmq9zj0pmbyv2sivn5i7m6mybgpkk4jm5vd7hp1pdk";
1147 };
1147 };
1148 meta = {
1148 meta = {
1149 license = [ pkgs.lib.licenses.bsdOriginal ];
1149 license = [ pkgs.lib.licenses.bsdOriginal ];
1150 };
1150 };
1151 };
1151 };
1152 "paste" = super.buildPythonPackage {
1152 "paste" = super.buildPythonPackage {
1153 name = "paste-3.4.0";
1153 name = "paste-3.4.0";
1154 doCheck = false;
1154 doCheck = false;
1155 propagatedBuildInputs = [
1155 propagatedBuildInputs = [
1156 self."six"
1156 self."six"
1157 ];
1157 ];
1158 src = fetchurl {
1158 src = fetchurl {
1159 url = "https://files.pythonhosted.org/packages/79/4a/45821b71dd40000507549afd1491546afad8279c0a87527c88776a794158/Paste-3.4.0.tar.gz";
1159 url = "https://files.pythonhosted.org/packages/79/4a/45821b71dd40000507549afd1491546afad8279c0a87527c88776a794158/Paste-3.4.0.tar.gz";
1160 sha256 = "16sichvhyci1gaarkjs35mai8vphh7b244qm14hj1isw38nx4c03";
1160 sha256 = "16sichvhyci1gaarkjs35mai8vphh7b244qm14hj1isw38nx4c03";
1161 };
1161 };
1162 meta = {
1162 meta = {
1163 license = [ pkgs.lib.licenses.mit ];
1163 license = [ pkgs.lib.licenses.mit ];
1164 };
1164 };
1165 };
1165 };
1166 "pastedeploy" = super.buildPythonPackage {
1166 "pastedeploy" = super.buildPythonPackage {
1167 name = "pastedeploy-2.1.0";
1167 name = "pastedeploy-2.1.0";
1168 doCheck = false;
1168 doCheck = false;
1169 src = fetchurl {
1169 src = fetchurl {
1170 url = "https://files.pythonhosted.org/packages/c4/e9/972a1c20318b3ae9edcab11a6cef64308fbae5d0d45ab52c6f8b2b8f35b8/PasteDeploy-2.1.0.tar.gz";
1170 url = "https://files.pythonhosted.org/packages/c4/e9/972a1c20318b3ae9edcab11a6cef64308fbae5d0d45ab52c6f8b2b8f35b8/PasteDeploy-2.1.0.tar.gz";
1171 sha256 = "16qsq5y6mryslmbp5pn35x4z8z3ndp5rpgl42h226879nrw9hmg7";
1171 sha256 = "16qsq5y6mryslmbp5pn35x4z8z3ndp5rpgl42h226879nrw9hmg7";
1172 };
1172 };
1173 meta = {
1173 meta = {
1174 license = [ pkgs.lib.licenses.mit ];
1174 license = [ pkgs.lib.licenses.mit ];
1175 };
1175 };
1176 };
1176 };
1177 "pastescript" = super.buildPythonPackage {
1177 "pastescript" = super.buildPythonPackage {
1178 name = "pastescript-3.2.0";
1178 name = "pastescript-3.2.0";
1179 doCheck = false;
1179 doCheck = false;
1180 propagatedBuildInputs = [
1180 propagatedBuildInputs = [
1181 self."paste"
1181 self."paste"
1182 self."pastedeploy"
1182 self."pastedeploy"
1183 self."six"
1183 self."six"
1184 ];
1184 ];
1185 src = fetchurl {
1185 src = fetchurl {
1186 url = "https://files.pythonhosted.org/packages/ff/47/45c6f5a3cb8f5abf786fea98dbb8d02400a55768a9b623afb7df12346c61/PasteScript-3.2.0.tar.gz";
1186 url = "https://files.pythonhosted.org/packages/ff/47/45c6f5a3cb8f5abf786fea98dbb8d02400a55768a9b623afb7df12346c61/PasteScript-3.2.0.tar.gz";
1187 sha256 = "1b3jq7xh383nvrrlblk05m37345bv97xrhx77wshllba3h7mq3wv";
1187 sha256 = "1b3jq7xh383nvrrlblk05m37345bv97xrhx77wshllba3h7mq3wv";
1188 };
1188 };
1189 meta = {
1189 meta = {
1190 license = [ pkgs.lib.licenses.mit ];
1190 license = [ pkgs.lib.licenses.mit ];
1191 };
1191 };
1192 };
1192 };
1193 "pathlib2" = super.buildPythonPackage {
1193 "pathlib2" = super.buildPythonPackage {
1194 name = "pathlib2-2.3.5";
1194 name = "pathlib2-2.3.5";
1195 doCheck = false;
1195 doCheck = false;
1196 propagatedBuildInputs = [
1196 propagatedBuildInputs = [
1197 self."six"
1197 self."six"
1198 self."scandir"
1198 self."scandir"
1199 ];
1199 ];
1200 src = fetchurl {
1200 src = fetchurl {
1201 url = "https://files.pythonhosted.org/packages/94/d8/65c86584e7e97ef824a1845c72bbe95d79f5b306364fa778a3c3e401b309/pathlib2-2.3.5.tar.gz";
1201 url = "https://files.pythonhosted.org/packages/94/d8/65c86584e7e97ef824a1845c72bbe95d79f5b306364fa778a3c3e401b309/pathlib2-2.3.5.tar.gz";
1202 sha256 = "0s4qa8c082fdkb17izh4mfgwrjd1n5pya18wvrbwqdvvb5xs9nbc";
1202 sha256 = "0s4qa8c082fdkb17izh4mfgwrjd1n5pya18wvrbwqdvvb5xs9nbc";
1203 };
1203 };
1204 meta = {
1204 meta = {
1205 license = [ pkgs.lib.licenses.mit ];
1205 license = [ pkgs.lib.licenses.mit ];
1206 };
1206 };
1207 };
1207 };
1208 "peppercorn" = super.buildPythonPackage {
1208 "peppercorn" = super.buildPythonPackage {
1209 name = "peppercorn-0.6";
1209 name = "peppercorn-0.6";
1210 doCheck = false;
1210 doCheck = false;
1211 src = fetchurl {
1211 src = fetchurl {
1212 url = "https://files.pythonhosted.org/packages/e4/77/93085de7108cdf1a0b092ff443872a8f9442c736d7ddebdf2f27627935f4/peppercorn-0.6.tar.gz";
1212 url = "https://files.pythonhosted.org/packages/e4/77/93085de7108cdf1a0b092ff443872a8f9442c736d7ddebdf2f27627935f4/peppercorn-0.6.tar.gz";
1213 sha256 = "1ip4bfwcpwkq9hz2dai14k2cyabvwrnvcvrcmzxmqm04g8fnimwn";
1213 sha256 = "1ip4bfwcpwkq9hz2dai14k2cyabvwrnvcvrcmzxmqm04g8fnimwn";
1214 };
1214 };
1215 meta = {
1215 meta = {
1216 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1216 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1217 };
1217 };
1218 };
1218 };
1219 "pexpect" = super.buildPythonPackage {
1219 "pexpect" = super.buildPythonPackage {
1220 name = "pexpect-4.8.0";
1220 name = "pexpect-4.8.0";
1221 doCheck = false;
1221 doCheck = false;
1222 propagatedBuildInputs = [
1222 propagatedBuildInputs = [
1223 self."ptyprocess"
1223 self."ptyprocess"
1224 ];
1224 ];
1225 src = fetchurl {
1225 src = fetchurl {
1226 url = "https://files.pythonhosted.org/packages/e5/9b/ff402e0e930e70467a7178abb7c128709a30dfb22d8777c043e501bc1b10/pexpect-4.8.0.tar.gz";
1226 url = "https://files.pythonhosted.org/packages/e5/9b/ff402e0e930e70467a7178abb7c128709a30dfb22d8777c043e501bc1b10/pexpect-4.8.0.tar.gz";
1227 sha256 = "032cg337h8awydgypz6f4wx848lw8dyrj4zy988x0lyib4ws8rgw";
1227 sha256 = "032cg337h8awydgypz6f4wx848lw8dyrj4zy988x0lyib4ws8rgw";
1228 };
1228 };
1229 meta = {
1229 meta = {
1230 license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ];
1230 license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ];
1231 };
1231 };
1232 };
1232 };
1233 "pickleshare" = super.buildPythonPackage {
1233 "pickleshare" = super.buildPythonPackage {
1234 name = "pickleshare-0.7.5";
1234 name = "pickleshare-0.7.5";
1235 doCheck = false;
1235 doCheck = false;
1236 propagatedBuildInputs = [
1236 propagatedBuildInputs = [
1237 self."pathlib2"
1237 self."pathlib2"
1238 ];
1238 ];
1239 src = fetchurl {
1239 src = fetchurl {
1240 url = "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz";
1240 url = "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz";
1241 sha256 = "1jmghg3c53yp1i8cm6pcrm280ayi8621rwyav9fac7awjr3kss47";
1241 sha256 = "1jmghg3c53yp1i8cm6pcrm280ayi8621rwyav9fac7awjr3kss47";
1242 };
1242 };
1243 meta = {
1243 meta = {
1244 license = [ pkgs.lib.licenses.mit ];
1244 license = [ pkgs.lib.licenses.mit ];
1245 };
1245 };
1246 };
1246 };
1247 "plaster" = super.buildPythonPackage {
1247 "plaster" = super.buildPythonPackage {
1248 name = "plaster-1.0";
1248 name = "plaster-1.0";
1249 doCheck = false;
1249 doCheck = false;
1250 propagatedBuildInputs = [
1250 propagatedBuildInputs = [
1251 self."setuptools"
1251 self."setuptools"
1252 ];
1252 ];
1253 src = fetchurl {
1253 src = fetchurl {
1254 url = "https://files.pythonhosted.org/packages/37/e1/56d04382d718d32751017d32f351214384e529b794084eee20bb52405563/plaster-1.0.tar.gz";
1254 url = "https://files.pythonhosted.org/packages/37/e1/56d04382d718d32751017d32f351214384e529b794084eee20bb52405563/plaster-1.0.tar.gz";
1255 sha256 = "1hy8k0nv2mxq94y5aysk6hjk9ryb4bsd13g83m60hcyzxz3wflc3";
1255 sha256 = "1hy8k0nv2mxq94y5aysk6hjk9ryb4bsd13g83m60hcyzxz3wflc3";
1256 };
1256 };
1257 meta = {
1257 meta = {
1258 license = [ pkgs.lib.licenses.mit ];
1258 license = [ pkgs.lib.licenses.mit ];
1259 };
1259 };
1260 };
1260 };
1261 "plaster-pastedeploy" = super.buildPythonPackage {
1261 "plaster-pastedeploy" = super.buildPythonPackage {
1262 name = "plaster-pastedeploy-0.7";
1262 name = "plaster-pastedeploy-0.7";
1263 doCheck = false;
1263 doCheck = false;
1264 propagatedBuildInputs = [
1264 propagatedBuildInputs = [
1265 self."pastedeploy"
1265 self."pastedeploy"
1266 self."plaster"
1266 self."plaster"
1267 ];
1267 ];
1268 src = fetchurl {
1268 src = fetchurl {
1269 url = "https://files.pythonhosted.org/packages/99/69/2d3bc33091249266a1bd3cf24499e40ab31d54dffb4a7d76fe647950b98c/plaster_pastedeploy-0.7.tar.gz";
1269 url = "https://files.pythonhosted.org/packages/99/69/2d3bc33091249266a1bd3cf24499e40ab31d54dffb4a7d76fe647950b98c/plaster_pastedeploy-0.7.tar.gz";
1270 sha256 = "1zg7gcsvc1kzay1ry5p699rg2qavfsxqwl17mqxzr0gzw6j9679r";
1270 sha256 = "1zg7gcsvc1kzay1ry5p699rg2qavfsxqwl17mqxzr0gzw6j9679r";
1271 };
1271 };
1272 meta = {
1272 meta = {
1273 license = [ pkgs.lib.licenses.mit ];
1273 license = [ pkgs.lib.licenses.mit ];
1274 };
1274 };
1275 };
1275 };
1276 "pluggy" = super.buildPythonPackage {
1276 "pluggy" = super.buildPythonPackage {
1277 name = "pluggy-0.13.1";
1277 name = "pluggy-0.13.1";
1278 doCheck = false;
1278 doCheck = false;
1279 propagatedBuildInputs = [
1279 propagatedBuildInputs = [
1280 self."importlib-metadata"
1280 self."importlib-metadata"
1281 ];
1281 ];
1282 src = fetchurl {
1282 src = fetchurl {
1283 url = "https://files.pythonhosted.org/packages/f8/04/7a8542bed4b16a65c2714bf76cf5a0b026157da7f75e87cc88774aa10b14/pluggy-0.13.1.tar.gz";
1283 url = "https://files.pythonhosted.org/packages/f8/04/7a8542bed4b16a65c2714bf76cf5a0b026157da7f75e87cc88774aa10b14/pluggy-0.13.1.tar.gz";
1284 sha256 = "1c35qyhvy27q9ih9n899f3h4sdnpgq027dbiilly2qb5cvgarchm";
1284 sha256 = "1c35qyhvy27q9ih9n899f3h4sdnpgq027dbiilly2qb5cvgarchm";
1285 };
1285 };
1286 meta = {
1286 meta = {
1287 license = [ pkgs.lib.licenses.mit ];
1287 license = [ pkgs.lib.licenses.mit ];
1288 };
1288 };
1289 };
1289 };
1290 "premailer" = super.buildPythonPackage {
1290 "premailer" = super.buildPythonPackage {
1291 name = "premailer-3.6.1";
1291 name = "premailer-3.6.1";
1292 doCheck = false;
1292 doCheck = false;
1293 propagatedBuildInputs = [
1293 propagatedBuildInputs = [
1294 self."lxml"
1294 self."lxml"
1295 self."cssselect"
1295 self."cssselect"
1296 self."cssutils"
1296 self."cssutils"
1297 self."requests"
1297 self."requests"
1298 self."cachetools"
1298 self."cachetools"
1299 ];
1299 ];
1300 src = fetchurl {
1300 src = fetchurl {
1301 url = "https://files.pythonhosted.org/packages/62/da/2f43cdf9d3d79c80c4856a12389a1f257d65fe9ccc44bc6b4383c8a18e33/premailer-3.6.1.tar.gz";
1301 url = "https://files.pythonhosted.org/packages/62/da/2f43cdf9d3d79c80c4856a12389a1f257d65fe9ccc44bc6b4383c8a18e33/premailer-3.6.1.tar.gz";
1302 sha256 = "08pshx7a110k4ll20x0xhpvyn3kkipkrbgxjjn7ncdxs54ihdhgw";
1302 sha256 = "08pshx7a110k4ll20x0xhpvyn3kkipkrbgxjjn7ncdxs54ihdhgw";
1303 };
1303 };
1304 meta = {
1304 meta = {
1305 license = [ pkgs.lib.licenses.psfl { fullName = "Python"; } ];
1305 license = [ pkgs.lib.licenses.psfl { fullName = "Python"; } ];
1306 };
1306 };
1307 };
1307 };
1308 "prompt-toolkit" = super.buildPythonPackage {
1308 "prompt-toolkit" = super.buildPythonPackage {
1309 name = "prompt-toolkit-1.0.18";
1309 name = "prompt-toolkit-1.0.18";
1310 doCheck = false;
1310 doCheck = false;
1311 propagatedBuildInputs = [
1311 propagatedBuildInputs = [
1312 self."six"
1312 self."six"
1313 self."wcwidth"
1313 self."wcwidth"
1314 ];
1314 ];
1315 src = fetchurl {
1315 src = fetchurl {
1316 url = "https://files.pythonhosted.org/packages/c5/64/c170e5b1913b540bf0c8ab7676b21fdd1d25b65ddeb10025c6ca43cccd4c/prompt_toolkit-1.0.18.tar.gz";
1316 url = "https://files.pythonhosted.org/packages/c5/64/c170e5b1913b540bf0c8ab7676b21fdd1d25b65ddeb10025c6ca43cccd4c/prompt_toolkit-1.0.18.tar.gz";
1317 sha256 = "09h1153wgr5x2ny7ds0w2m81n3bb9j8hjb8sjfnrg506r01clkyx";
1317 sha256 = "09h1153wgr5x2ny7ds0w2m81n3bb9j8hjb8sjfnrg506r01clkyx";
1318 };
1318 };
1319 meta = {
1319 meta = {
1320 license = [ pkgs.lib.licenses.bsdOriginal ];
1320 license = [ pkgs.lib.licenses.bsdOriginal ];
1321 };
1321 };
1322 };
1322 };
1323 "psutil" = super.buildPythonPackage {
1323 "psutil" = super.buildPythonPackage {
1324 name = "psutil-5.7.0";
1324 name = "psutil-5.7.0";
1325 doCheck = false;
1325 doCheck = false;
1326 src = fetchurl {
1326 src = fetchurl {
1327 url = "https://files.pythonhosted.org/packages/c4/b8/3512f0e93e0db23a71d82485ba256071ebef99b227351f0f5540f744af41/psutil-5.7.0.tar.gz";
1327 url = "https://files.pythonhosted.org/packages/c4/b8/3512f0e93e0db23a71d82485ba256071ebef99b227351f0f5540f744af41/psutil-5.7.0.tar.gz";
1328 sha256 = "03jykdi3dgf1cdal9bv4fq9zjvzj9l9bs99gi5ar81sdl5nc2pk8";
1328 sha256 = "03jykdi3dgf1cdal9bv4fq9zjvzj9l9bs99gi5ar81sdl5nc2pk8";
1329 };
1329 };
1330 meta = {
1330 meta = {
1331 license = [ pkgs.lib.licenses.bsdOriginal ];
1331 license = [ pkgs.lib.licenses.bsdOriginal ];
1332 };
1332 };
1333 };
1333 };
1334 "psycopg2" = super.buildPythonPackage {
1334 "psycopg2" = super.buildPythonPackage {
1335 name = "psycopg2-2.8.4";
1335 name = "psycopg2-2.8.4";
1336 doCheck = false;
1336 doCheck = false;
1337 src = fetchurl {
1337 src = fetchurl {
1338 url = "https://files.pythonhosted.org/packages/84/d7/6a93c99b5ba4d4d22daa3928b983cec66df4536ca50b22ce5dcac65e4e71/psycopg2-2.8.4.tar.gz";
1338 url = "https://files.pythonhosted.org/packages/84/d7/6a93c99b5ba4d4d22daa3928b983cec66df4536ca50b22ce5dcac65e4e71/psycopg2-2.8.4.tar.gz";
1339 sha256 = "1djvh98pi4hjd8rxbq8qzc63bg8v78k33yg6pl99wak61b6fb67q";
1339 sha256 = "1djvh98pi4hjd8rxbq8qzc63bg8v78k33yg6pl99wak61b6fb67q";
1340 };
1340 };
1341 meta = {
1341 meta = {
1342 license = [ pkgs.lib.licenses.zpl21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
1342 license = [ pkgs.lib.licenses.zpl21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
1343 };
1343 };
1344 };
1344 };
1345 "ptyprocess" = super.buildPythonPackage {
1345 "ptyprocess" = super.buildPythonPackage {
1346 name = "ptyprocess-0.6.0";
1346 name = "ptyprocess-0.6.0";
1347 doCheck = false;
1347 doCheck = false;
1348 src = fetchurl {
1348 src = fetchurl {
1349 url = "https://files.pythonhosted.org/packages/7d/2d/e4b8733cf79b7309d84c9081a4ab558c89d8c89da5961bf4ddb050ca1ce0/ptyprocess-0.6.0.tar.gz";
1349 url = "https://files.pythonhosted.org/packages/7d/2d/e4b8733cf79b7309d84c9081a4ab558c89d8c89da5961bf4ddb050ca1ce0/ptyprocess-0.6.0.tar.gz";
1350 sha256 = "1h4lcd3w5nrxnsk436ar7fwkiy5rfn5wj2xwy9l0r4mdqnf2jgwj";
1350 sha256 = "1h4lcd3w5nrxnsk436ar7fwkiy5rfn5wj2xwy9l0r4mdqnf2jgwj";
1351 };
1351 };
1352 meta = {
1352 meta = {
1353 license = [ ];
1353 license = [ ];
1354 };
1354 };
1355 };
1355 };
1356 "py" = super.buildPythonPackage {
1356 "py" = super.buildPythonPackage {
1357 name = "py-1.8.0";
1357 name = "py-1.8.0";
1358 doCheck = false;
1358 doCheck = false;
1359 src = fetchurl {
1359 src = fetchurl {
1360 url = "https://files.pythonhosted.org/packages/f1/5a/87ca5909f400a2de1561f1648883af74345fe96349f34f737cdfc94eba8c/py-1.8.0.tar.gz";
1360 url = "https://files.pythonhosted.org/packages/f1/5a/87ca5909f400a2de1561f1648883af74345fe96349f34f737cdfc94eba8c/py-1.8.0.tar.gz";
1361 sha256 = "0lsy1gajva083pzc7csj1cvbmminb7b4l6a0prdzyb3fd829nqyw";
1361 sha256 = "0lsy1gajva083pzc7csj1cvbmminb7b4l6a0prdzyb3fd829nqyw";
1362 };
1362 };
1363 meta = {
1363 meta = {
1364 license = [ pkgs.lib.licenses.mit ];
1364 license = [ pkgs.lib.licenses.mit ];
1365 };
1365 };
1366 };
1366 };
1367 "py-bcrypt" = super.buildPythonPackage {
1367 "py-bcrypt" = super.buildPythonPackage {
1368 name = "py-bcrypt-0.4";
1368 name = "py-bcrypt-0.4";
1369 doCheck = false;
1369 doCheck = false;
1370 src = fetchurl {
1370 src = fetchurl {
1371 url = "https://files.pythonhosted.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
1371 url = "https://files.pythonhosted.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
1372 sha256 = "0y6smdggwi5s72v6p1nn53dg6w05hna3d264cq6kas0lap73p8az";
1372 sha256 = "0y6smdggwi5s72v6p1nn53dg6w05hna3d264cq6kas0lap73p8az";
1373 };
1373 };
1374 meta = {
1374 meta = {
1375 license = [ pkgs.lib.licenses.bsdOriginal ];
1375 license = [ pkgs.lib.licenses.bsdOriginal ];
1376 };
1376 };
1377 };
1377 };
1378 "py-gfm" = super.buildPythonPackage {
1378 "py-gfm" = super.buildPythonPackage {
1379 name = "py-gfm-0.1.4";
1379 name = "py-gfm-0.1.4";
1380 doCheck = false;
1380 doCheck = false;
1381 propagatedBuildInputs = [
1381 propagatedBuildInputs = [
1382 self."setuptools"
1382 self."setuptools"
1383 self."markdown"
1383 self."markdown"
1384 ];
1384 ];
1385 src = fetchurl {
1385 src = fetchurl {
1386 url = "https://files.pythonhosted.org/packages/06/ee/004a03a1d92bb386dae44f6dd087db541bc5093374f1637d4d4ae5596cc2/py-gfm-0.1.4.tar.gz";
1386 url = "https://files.pythonhosted.org/packages/06/ee/004a03a1d92bb386dae44f6dd087db541bc5093374f1637d4d4ae5596cc2/py-gfm-0.1.4.tar.gz";
1387 sha256 = "0zip06g2isivx8fzgqd4n9qzsa22c25jas1rsb7m2rnjg72m0rzg";
1387 sha256 = "0zip06g2isivx8fzgqd4n9qzsa22c25jas1rsb7m2rnjg72m0rzg";
1388 };
1388 };
1389 meta = {
1389 meta = {
1390 license = [ pkgs.lib.licenses.bsdOriginal ];
1390 license = [ pkgs.lib.licenses.bsdOriginal ];
1391 };
1391 };
1392 };
1392 };
1393 "pyasn1" = super.buildPythonPackage {
1393 "pyasn1" = super.buildPythonPackage {
1394 name = "pyasn1-0.4.8";
1394 name = "pyasn1-0.4.8";
1395 doCheck = false;
1395 doCheck = false;
1396 src = fetchurl {
1396 src = fetchurl {
1397 url = "https://files.pythonhosted.org/packages/a4/db/fffec68299e6d7bad3d504147f9094830b704527a7fc098b721d38cc7fa7/pyasn1-0.4.8.tar.gz";
1397 url = "https://files.pythonhosted.org/packages/a4/db/fffec68299e6d7bad3d504147f9094830b704527a7fc098b721d38cc7fa7/pyasn1-0.4.8.tar.gz";
1398 sha256 = "1fnhbi3rmk47l9851gbik0flfr64vs5j0hbqx24cafjap6gprxxf";
1398 sha256 = "1fnhbi3rmk47l9851gbik0flfr64vs5j0hbqx24cafjap6gprxxf";
1399 };
1399 };
1400 meta = {
1400 meta = {
1401 license = [ pkgs.lib.licenses.bsdOriginal ];
1401 license = [ pkgs.lib.licenses.bsdOriginal ];
1402 };
1402 };
1403 };
1403 };
1404 "pyasn1-modules" = super.buildPythonPackage {
1404 "pyasn1-modules" = super.buildPythonPackage {
1405 name = "pyasn1-modules-0.2.6";
1405 name = "pyasn1-modules-0.2.6";
1406 doCheck = false;
1406 doCheck = false;
1407 propagatedBuildInputs = [
1407 propagatedBuildInputs = [
1408 self."pyasn1"
1408 self."pyasn1"
1409 ];
1409 ];
1410 src = fetchurl {
1410 src = fetchurl {
1411 url = "https://files.pythonhosted.org/packages/f1/a9/a1ef72a0e43feff643cf0130a08123dea76205e7a0dda37e3efb5f054a31/pyasn1-modules-0.2.6.tar.gz";
1411 url = "https://files.pythonhosted.org/packages/f1/a9/a1ef72a0e43feff643cf0130a08123dea76205e7a0dda37e3efb5f054a31/pyasn1-modules-0.2.6.tar.gz";
1412 sha256 = "08hph9j1r018drnrny29l7dl2q0cin78csswrhwrh8jmq61pmha3";
1412 sha256 = "08hph9j1r018drnrny29l7dl2q0cin78csswrhwrh8jmq61pmha3";
1413 };
1413 };
1414 meta = {
1414 meta = {
1415 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
1415 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
1416 };
1416 };
1417 };
1417 };
1418 "pycparser" = super.buildPythonPackage {
1418 "pycparser" = super.buildPythonPackage {
1419 name = "pycparser-2.20";
1419 name = "pycparser-2.20";
1420 doCheck = false;
1420 doCheck = false;
1421 src = fetchurl {
1421 src = fetchurl {
1422 url = "https://files.pythonhosted.org/packages/0f/86/e19659527668d70be91d0369aeaa055b4eb396b0f387a4f92293a20035bd/pycparser-2.20.tar.gz";
1422 url = "https://files.pythonhosted.org/packages/0f/86/e19659527668d70be91d0369aeaa055b4eb396b0f387a4f92293a20035bd/pycparser-2.20.tar.gz";
1423 sha256 = "1w0m3xvlrzq4lkbvd1ngfm8mdw64r1yxy6n7djlw6qj5d0km6ird";
1423 sha256 = "1w0m3xvlrzq4lkbvd1ngfm8mdw64r1yxy6n7djlw6qj5d0km6ird";
1424 };
1424 };
1425 meta = {
1425 meta = {
1426 license = [ pkgs.lib.licenses.bsdOriginal ];
1426 license = [ pkgs.lib.licenses.bsdOriginal ];
1427 };
1427 };
1428 };
1428 };
1429 "pycrypto" = super.buildPythonPackage {
1429 "pycrypto" = super.buildPythonPackage {
1430 name = "pycrypto-2.6.1";
1430 name = "pycrypto-2.6.1";
1431 doCheck = false;
1431 doCheck = false;
1432 src = fetchurl {
1432 src = fetchurl {
1433 url = "https://files.pythonhosted.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
1433 url = "https://files.pythonhosted.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
1434 sha256 = "0g0ayql5b9mkjam8hym6zyg6bv77lbh66rv1fyvgqb17kfc1xkpj";
1434 sha256 = "0g0ayql5b9mkjam8hym6zyg6bv77lbh66rv1fyvgqb17kfc1xkpj";
1435 };
1435 };
1436 meta = {
1436 meta = {
1437 license = [ pkgs.lib.licenses.publicDomain ];
1437 license = [ pkgs.lib.licenses.publicDomain ];
1438 };
1438 };
1439 };
1439 };
1440 "pycurl" = super.buildPythonPackage {
1440 "pycurl" = super.buildPythonPackage {
1441 name = "pycurl-7.43.0.3";
1441 name = "pycurl-7.43.0.3";
1442 doCheck = false;
1442 doCheck = false;
1443 src = fetchurl {
1443 src = fetchurl {
1444 url = "https://files.pythonhosted.org/packages/ac/b3/0f3979633b7890bab6098d84c84467030b807a1e2b31f5d30103af5a71ca/pycurl-7.43.0.3.tar.gz";
1444 url = "https://files.pythonhosted.org/packages/ac/b3/0f3979633b7890bab6098d84c84467030b807a1e2b31f5d30103af5a71ca/pycurl-7.43.0.3.tar.gz";
1445 sha256 = "13nsvqhvnmnvfk75s8iynqsgszyv06cjp4drd3psi7zpbh63623g";
1445 sha256 = "13nsvqhvnmnvfk75s8iynqsgszyv06cjp4drd3psi7zpbh63623g";
1446 };
1446 };
1447 meta = {
1447 meta = {
1448 license = [ pkgs.lib.licenses.mit { fullName = "LGPL/MIT"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1448 license = [ pkgs.lib.licenses.mit { fullName = "LGPL/MIT"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1449 };
1449 };
1450 };
1450 };
1451 "pygments" = super.buildPythonPackage {
1451 "pygments" = super.buildPythonPackage {
1452 name = "pygments-2.4.2";
1452 name = "pygments-2.4.2";
1453 doCheck = false;
1453 doCheck = false;
1454 src = fetchurl {
1454 src = fetchurl {
1455 url = "https://files.pythonhosted.org/packages/7e/ae/26808275fc76bf2832deb10d3a3ed3107bc4de01b85dcccbe525f2cd6d1e/Pygments-2.4.2.tar.gz";
1455 url = "https://files.pythonhosted.org/packages/7e/ae/26808275fc76bf2832deb10d3a3ed3107bc4de01b85dcccbe525f2cd6d1e/Pygments-2.4.2.tar.gz";
1456 sha256 = "15v2sqm5g12bqa0c7wikfh9ck2nl97ayizy1hpqhmws5gqalq748";
1456 sha256 = "15v2sqm5g12bqa0c7wikfh9ck2nl97ayizy1hpqhmws5gqalq748";
1457 };
1457 };
1458 meta = {
1458 meta = {
1459 license = [ pkgs.lib.licenses.bsdOriginal ];
1459 license = [ pkgs.lib.licenses.bsdOriginal ];
1460 };
1460 };
1461 };
1461 };
1462 "pymysql" = super.buildPythonPackage {
1462 "pymysql" = super.buildPythonPackage {
1463 name = "pymysql-0.8.1";
1463 name = "pymysql-0.8.1";
1464 doCheck = false;
1464 doCheck = false;
1465 src = fetchurl {
1465 src = fetchurl {
1466 url = "https://files.pythonhosted.org/packages/44/39/6bcb83cae0095a31b6be4511707fdf2009d3e29903a55a0494d3a9a2fac0/PyMySQL-0.8.1.tar.gz";
1466 url = "https://files.pythonhosted.org/packages/44/39/6bcb83cae0095a31b6be4511707fdf2009d3e29903a55a0494d3a9a2fac0/PyMySQL-0.8.1.tar.gz";
1467 sha256 = "0a96crz55bw4h6myh833skrli7b0ck89m3x673y2z2ryy7zrpq9l";
1467 sha256 = "0a96crz55bw4h6myh833skrli7b0ck89m3x673y2z2ryy7zrpq9l";
1468 };
1468 };
1469 meta = {
1469 meta = {
1470 license = [ pkgs.lib.licenses.mit ];
1470 license = [ pkgs.lib.licenses.mit ];
1471 };
1471 };
1472 };
1472 };
1473 "pyotp" = super.buildPythonPackage {
1473 "pyotp" = super.buildPythonPackage {
1474 name = "pyotp-2.3.0";
1474 name = "pyotp-2.3.0";
1475 doCheck = false;
1475 doCheck = false;
1476 src = fetchurl {
1476 src = fetchurl {
1477 url = "https://files.pythonhosted.org/packages/f7/15/395c4945ea6bc37e8811280bb675615cb4c2b2c1cd70bdc43329da91a386/pyotp-2.3.0.tar.gz";
1477 url = "https://files.pythonhosted.org/packages/f7/15/395c4945ea6bc37e8811280bb675615cb4c2b2c1cd70bdc43329da91a386/pyotp-2.3.0.tar.gz";
1478 sha256 = "18d13ikra1iq0xyfqfm72zhgwxi2qi9ps6z1a6zmqp4qrn57wlzw";
1478 sha256 = "18d13ikra1iq0xyfqfm72zhgwxi2qi9ps6z1a6zmqp4qrn57wlzw";
1479 };
1479 };
1480 meta = {
1480 meta = {
1481 license = [ pkgs.lib.licenses.mit ];
1481 license = [ pkgs.lib.licenses.mit ];
1482 };
1482 };
1483 };
1483 };
1484 "pyparsing" = super.buildPythonPackage {
1484 "pyparsing" = super.buildPythonPackage {
1485 name = "pyparsing-2.4.7";
1485 name = "pyparsing-2.4.7";
1486 doCheck = false;
1486 doCheck = false;
1487 src = fetchurl {
1487 src = fetchurl {
1488 url = "https://files.pythonhosted.org/packages/c1/47/dfc9c342c9842bbe0036c7f763d2d6686bcf5eb1808ba3e170afdb282210/pyparsing-2.4.7.tar.gz";
1488 url = "https://files.pythonhosted.org/packages/c1/47/dfc9c342c9842bbe0036c7f763d2d6686bcf5eb1808ba3e170afdb282210/pyparsing-2.4.7.tar.gz";
1489 sha256 = "1hgc8qrbq1ymxbwfbjghv01fm3fbpjwpjwi0bcailxxzhf3yq0y2";
1489 sha256 = "1hgc8qrbq1ymxbwfbjghv01fm3fbpjwpjwi0bcailxxzhf3yq0y2";
1490 };
1490 };
1491 meta = {
1491 meta = {
1492 license = [ pkgs.lib.licenses.mit ];
1492 license = [ pkgs.lib.licenses.mit ];
1493 };
1493 };
1494 };
1494 };
1495 "pyramid" = super.buildPythonPackage {
1495 "pyramid" = super.buildPythonPackage {
1496 name = "pyramid-1.10.4";
1496 name = "pyramid-1.10.4";
1497 doCheck = false;
1497 doCheck = false;
1498 propagatedBuildInputs = [
1498 propagatedBuildInputs = [
1499 self."hupper"
1499 self."hupper"
1500 self."plaster"
1500 self."plaster"
1501 self."plaster-pastedeploy"
1501 self."plaster-pastedeploy"
1502 self."setuptools"
1502 self."setuptools"
1503 self."translationstring"
1503 self."translationstring"
1504 self."venusian"
1504 self."venusian"
1505 self."webob"
1505 self."webob"
1506 self."zope.deprecation"
1506 self."zope.deprecation"
1507 self."zope.interface"
1507 self."zope.interface"
1508 self."repoze.lru"
1508 self."repoze.lru"
1509 ];
1509 ];
1510 src = fetchurl {
1510 src = fetchurl {
1511 url = "https://files.pythonhosted.org/packages/c2/43/1ae701c9c6bb3a434358e678a5e72c96e8aa55cf4cb1d2fa2041b5dd38b7/pyramid-1.10.4.tar.gz";
1511 url = "https://files.pythonhosted.org/packages/c2/43/1ae701c9c6bb3a434358e678a5e72c96e8aa55cf4cb1d2fa2041b5dd38b7/pyramid-1.10.4.tar.gz";
1512 sha256 = "0rkxs1ajycg2zh1c94xlmls56mx5m161sn8112skj0amza6cn36q";
1512 sha256 = "0rkxs1ajycg2zh1c94xlmls56mx5m161sn8112skj0amza6cn36q";
1513 };
1513 };
1514 meta = {
1514 meta = {
1515 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1515 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1516 };
1516 };
1517 };
1517 };
1518 "pyramid-debugtoolbar" = super.buildPythonPackage {
1518 "pyramid-debugtoolbar" = super.buildPythonPackage {
1519 name = "pyramid-debugtoolbar-4.6.1";
1519 name = "pyramid-debugtoolbar-4.6.1";
1520 doCheck = false;
1520 doCheck = false;
1521 propagatedBuildInputs = [
1521 propagatedBuildInputs = [
1522 self."pyramid"
1522 self."pyramid"
1523 self."pyramid-mako"
1523 self."pyramid-mako"
1524 self."repoze.lru"
1524 self."repoze.lru"
1525 self."pygments"
1525 self."pygments"
1526 self."ipaddress"
1526 self."ipaddress"
1527 ];
1527 ];
1528 src = fetchurl {
1528 src = fetchurl {
1529 url = "https://files.pythonhosted.org/packages/99/f6/b8603f82c18275be293921bc3a2184205056ca505747bf64ab8a0c08e124/pyramid_debugtoolbar-4.6.1.tar.gz";
1529 url = "https://files.pythonhosted.org/packages/99/f6/b8603f82c18275be293921bc3a2184205056ca505747bf64ab8a0c08e124/pyramid_debugtoolbar-4.6.1.tar.gz";
1530 sha256 = "185z7q8n959ga5331iczwra2iljwkidfx4qn6bbd7vm3rm4w6llv";
1530 sha256 = "185z7q8n959ga5331iczwra2iljwkidfx4qn6bbd7vm3rm4w6llv";
1531 };
1531 };
1532 meta = {
1532 meta = {
1533 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1533 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1534 };
1534 };
1535 };
1535 };
1536 "pyramid-jinja2" = super.buildPythonPackage {
1536 "pyramid-jinja2" = super.buildPythonPackage {
1537 name = "pyramid-jinja2-2.7";
1537 name = "pyramid-jinja2-2.7";
1538 doCheck = false;
1538 doCheck = false;
1539 propagatedBuildInputs = [
1539 propagatedBuildInputs = [
1540 self."pyramid"
1540 self."pyramid"
1541 self."zope.deprecation"
1541 self."zope.deprecation"
1542 self."jinja2"
1542 self."jinja2"
1543 self."markupsafe"
1543 self."markupsafe"
1544 ];
1544 ];
1545 src = fetchurl {
1545 src = fetchurl {
1546 url = "https://files.pythonhosted.org/packages/d8/80/d60a7233823de22ce77bd864a8a83736a1fe8b49884b08303a2e68b2c853/pyramid_jinja2-2.7.tar.gz";
1546 url = "https://files.pythonhosted.org/packages/d8/80/d60a7233823de22ce77bd864a8a83736a1fe8b49884b08303a2e68b2c853/pyramid_jinja2-2.7.tar.gz";
1547 sha256 = "1sz5s0pp5jqhf4w22w9527yz8hgdi4mhr6apd6vw1gm5clghh8aw";
1547 sha256 = "1sz5s0pp5jqhf4w22w9527yz8hgdi4mhr6apd6vw1gm5clghh8aw";
1548 };
1548 };
1549 meta = {
1549 meta = {
1550 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1550 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1551 };
1551 };
1552 };
1552 };
1553 "pyramid-apispec" = super.buildPythonPackage {
1553 "pyramid-apispec" = super.buildPythonPackage {
1554 name = "pyramid-apispec-0.3.2";
1554 name = "pyramid-apispec-0.3.2";
1555 doCheck = false;
1555 doCheck = false;
1556 propagatedBuildInputs = [
1556 propagatedBuildInputs = [
1557 self."apispec"
1557 self."apispec"
1558 ];
1558 ];
1559 src = fetchurl {
1559 src = fetchurl {
1560 url = "https://files.pythonhosted.org/packages/2a/30/1dea5d81ea635449572ba60ec3148310d75ae4530c3c695f54b0991bb8c7/pyramid_apispec-0.3.2.tar.gz";
1560 url = "https://files.pythonhosted.org/packages/2a/30/1dea5d81ea635449572ba60ec3148310d75ae4530c3c695f54b0991bb8c7/pyramid_apispec-0.3.2.tar.gz";
1561 sha256 = "0ffrcqp9dkykivhfcq0v9lgy6w0qhwl6x78925vfjmayly9r8da0";
1561 sha256 = "0ffrcqp9dkykivhfcq0v9lgy6w0qhwl6x78925vfjmayly9r8da0";
1562 };
1562 };
1563 meta = {
1563 meta = {
1564 license = [ pkgs.lib.licenses.bsdOriginal ];
1564 license = [ pkgs.lib.licenses.bsdOriginal ];
1565 };
1565 };
1566 };
1566 };
1567 "pyramid-mailer" = super.buildPythonPackage {
1567 "pyramid-mailer" = super.buildPythonPackage {
1568 name = "pyramid-mailer-0.15.1";
1568 name = "pyramid-mailer-0.15.1";
1569 doCheck = false;
1569 doCheck = false;
1570 propagatedBuildInputs = [
1570 propagatedBuildInputs = [
1571 self."pyramid"
1571 self."pyramid"
1572 self."repoze.sendmail"
1572 self."repoze.sendmail"
1573 self."transaction"
1573 self."transaction"
1574 ];
1574 ];
1575 src = fetchurl {
1575 src = fetchurl {
1576 url = "https://files.pythonhosted.org/packages/a0/f2/6febf5459dff4d7e653314d575469ad2e11b9d2af2c3606360e1c67202f2/pyramid_mailer-0.15.1.tar.gz";
1576 url = "https://files.pythonhosted.org/packages/a0/f2/6febf5459dff4d7e653314d575469ad2e11b9d2af2c3606360e1c67202f2/pyramid_mailer-0.15.1.tar.gz";
1577 sha256 = "16vg8jb203jgb7b0hd6wllfqvp542qh2ry1gjai2m6qpv5agy2pc";
1577 sha256 = "16vg8jb203jgb7b0hd6wllfqvp542qh2ry1gjai2m6qpv5agy2pc";
1578 };
1578 };
1579 meta = {
1579 meta = {
1580 license = [ pkgs.lib.licenses.bsdOriginal ];
1580 license = [ pkgs.lib.licenses.bsdOriginal ];
1581 };
1581 };
1582 };
1582 };
1583 "pyramid-mako" = super.buildPythonPackage {
1583 "pyramid-mako" = super.buildPythonPackage {
1584 name = "pyramid-mako-1.1.0";
1584 name = "pyramid-mako-1.1.0";
1585 doCheck = false;
1585 doCheck = false;
1586 propagatedBuildInputs = [
1586 propagatedBuildInputs = [
1587 self."pyramid"
1587 self."pyramid"
1588 self."mako"
1588 self."mako"
1589 ];
1589 ];
1590 src = fetchurl {
1590 src = fetchurl {
1591 url = "https://files.pythonhosted.org/packages/63/7b/5e2af68f675071a6bad148c1c393928f0ef5fcd94e95cbf53b89d6471a83/pyramid_mako-1.1.0.tar.gz";
1591 url = "https://files.pythonhosted.org/packages/63/7b/5e2af68f675071a6bad148c1c393928f0ef5fcd94e95cbf53b89d6471a83/pyramid_mako-1.1.0.tar.gz";
1592 sha256 = "1qj0m091mnii86j2q1d82yir22nha361rvhclvg3s70z8iiwhrh0";
1592 sha256 = "1qj0m091mnii86j2q1d82yir22nha361rvhclvg3s70z8iiwhrh0";
1593 };
1593 };
1594 meta = {
1594 meta = {
1595 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1595 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1596 };
1596 };
1597 };
1597 };
1598 "pysqlite" = super.buildPythonPackage {
1598 "pysqlite" = super.buildPythonPackage {
1599 name = "pysqlite-2.8.3";
1599 name = "pysqlite-2.8.3";
1600 doCheck = false;
1600 doCheck = false;
1601 src = fetchurl {
1601 src = fetchurl {
1602 url = "https://files.pythonhosted.org/packages/42/02/981b6703e3c83c5b25a829c6e77aad059f9481b0bbacb47e6e8ca12bd731/pysqlite-2.8.3.tar.gz";
1602 url = "https://files.pythonhosted.org/packages/42/02/981b6703e3c83c5b25a829c6e77aad059f9481b0bbacb47e6e8ca12bd731/pysqlite-2.8.3.tar.gz";
1603 sha256 = "1424gwq9sil2ffmnizk60q36vydkv8rxs6m7xs987kz8cdc37lqp";
1603 sha256 = "1424gwq9sil2ffmnizk60q36vydkv8rxs6m7xs987kz8cdc37lqp";
1604 };
1604 };
1605 meta = {
1605 meta = {
1606 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1606 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1607 };
1607 };
1608 };
1608 };
1609 "pytest" = super.buildPythonPackage {
1609 "pytest" = super.buildPythonPackage {
1610 name = "pytest-4.6.5";
1610 name = "pytest-4.6.5";
1611 doCheck = false;
1611 doCheck = false;
1612 propagatedBuildInputs = [
1612 propagatedBuildInputs = [
1613 self."py"
1613 self."py"
1614 self."six"
1614 self."six"
1615 self."packaging"
1615 self."packaging"
1616 self."attrs"
1616 self."attrs"
1617 self."atomicwrites"
1617 self."atomicwrites"
1618 self."pluggy"
1618 self."pluggy"
1619 self."importlib-metadata"
1619 self."importlib-metadata"
1620 self."wcwidth"
1620 self."wcwidth"
1621 self."funcsigs"
1621 self."funcsigs"
1622 self."pathlib2"
1622 self."pathlib2"
1623 self."more-itertools"
1623 self."more-itertools"
1624 ];
1624 ];
1625 src = fetchurl {
1625 src = fetchurl {
1626 url = "https://files.pythonhosted.org/packages/2a/c6/1d1f32f6a5009900521b12e6560fb6b7245b0d4bc3fb771acd63d10e30e1/pytest-4.6.5.tar.gz";
1626 url = "https://files.pythonhosted.org/packages/2a/c6/1d1f32f6a5009900521b12e6560fb6b7245b0d4bc3fb771acd63d10e30e1/pytest-4.6.5.tar.gz";
1627 sha256 = "0iykwwfp4h181nd7rsihh2120b0rkawlw7rvbl19sgfspncr3hwg";
1627 sha256 = "0iykwwfp4h181nd7rsihh2120b0rkawlw7rvbl19sgfspncr3hwg";
1628 };
1628 };
1629 meta = {
1629 meta = {
1630 license = [ pkgs.lib.licenses.mit ];
1630 license = [ pkgs.lib.licenses.mit ];
1631 };
1631 };
1632 };
1632 };
1633 "pytest-cov" = super.buildPythonPackage {
1633 "pytest-cov" = super.buildPythonPackage {
1634 name = "pytest-cov-2.7.1";
1634 name = "pytest-cov-2.7.1";
1635 doCheck = false;
1635 doCheck = false;
1636 propagatedBuildInputs = [
1636 propagatedBuildInputs = [
1637 self."pytest"
1637 self."pytest"
1638 self."coverage"
1638 self."coverage"
1639 ];
1639 ];
1640 src = fetchurl {
1640 src = fetchurl {
1641 url = "https://files.pythonhosted.org/packages/bb/0f/3db7ff86801883b21d5353b258c994b1b8e2abbc804e2273b8d0fd19004b/pytest-cov-2.7.1.tar.gz";
1641 url = "https://files.pythonhosted.org/packages/bb/0f/3db7ff86801883b21d5353b258c994b1b8e2abbc804e2273b8d0fd19004b/pytest-cov-2.7.1.tar.gz";
1642 sha256 = "0filvmmyqm715azsl09ql8hy2x7h286n6d8z5x42a1wpvvys83p0";
1642 sha256 = "0filvmmyqm715azsl09ql8hy2x7h286n6d8z5x42a1wpvvys83p0";
1643 };
1643 };
1644 meta = {
1644 meta = {
1645 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ];
1645 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ];
1646 };
1646 };
1647 };
1647 };
1648 "pytest-profiling" = super.buildPythonPackage {
1648 "pytest-profiling" = super.buildPythonPackage {
1649 name = "pytest-profiling-1.7.0";
1649 name = "pytest-profiling-1.7.0";
1650 doCheck = false;
1650 doCheck = false;
1651 propagatedBuildInputs = [
1651 propagatedBuildInputs = [
1652 self."six"
1652 self."six"
1653 self."pytest"
1653 self."pytest"
1654 self."gprof2dot"
1654 self."gprof2dot"
1655 ];
1655 ];
1656 src = fetchurl {
1656 src = fetchurl {
1657 url = "https://files.pythonhosted.org/packages/39/70/22a4b33739f07f1732a63e33bbfbf68e0fa58cfba9d200e76d01921eddbf/pytest-profiling-1.7.0.tar.gz";
1657 url = "https://files.pythonhosted.org/packages/39/70/22a4b33739f07f1732a63e33bbfbf68e0fa58cfba9d200e76d01921eddbf/pytest-profiling-1.7.0.tar.gz";
1658 sha256 = "0abz9gi26jpcfdzgsvwad91555lpgdc8kbymicmms8k2fqa8z4wk";
1658 sha256 = "0abz9gi26jpcfdzgsvwad91555lpgdc8kbymicmms8k2fqa8z4wk";
1659 };
1659 };
1660 meta = {
1660 meta = {
1661 license = [ pkgs.lib.licenses.mit ];
1661 license = [ pkgs.lib.licenses.mit ];
1662 };
1662 };
1663 };
1663 };
1664 "pytest-runner" = super.buildPythonPackage {
1664 "pytest-runner" = super.buildPythonPackage {
1665 name = "pytest-runner-5.1";
1665 name = "pytest-runner-5.1";
1666 doCheck = false;
1666 doCheck = false;
1667 src = fetchurl {
1667 src = fetchurl {
1668 url = "https://files.pythonhosted.org/packages/d9/6d/4b41a74b31720e25abd4799be72d54811da4b4d0233e38b75864dcc1f7ad/pytest-runner-5.1.tar.gz";
1668 url = "https://files.pythonhosted.org/packages/d9/6d/4b41a74b31720e25abd4799be72d54811da4b4d0233e38b75864dcc1f7ad/pytest-runner-5.1.tar.gz";
1669 sha256 = "0ykfcnpp8c22winj63qzc07l5axwlc9ikl8vn05sc32gv3417815";
1669 sha256 = "0ykfcnpp8c22winj63qzc07l5axwlc9ikl8vn05sc32gv3417815";
1670 };
1670 };
1671 meta = {
1671 meta = {
1672 license = [ pkgs.lib.licenses.mit ];
1672 license = [ pkgs.lib.licenses.mit ];
1673 };
1673 };
1674 };
1674 };
1675 "pytest-sugar" = super.buildPythonPackage {
1675 "pytest-sugar" = super.buildPythonPackage {
1676 name = "pytest-sugar-0.9.2";
1676 name = "pytest-sugar-0.9.2";
1677 doCheck = false;
1677 doCheck = false;
1678 propagatedBuildInputs = [
1678 propagatedBuildInputs = [
1679 self."pytest"
1679 self."pytest"
1680 self."termcolor"
1680 self."termcolor"
1681 self."packaging"
1681 self."packaging"
1682 ];
1682 ];
1683 src = fetchurl {
1683 src = fetchurl {
1684 url = "https://files.pythonhosted.org/packages/55/59/f02f78d1c80f7e03e23177f60624c8106d4f23d124c921df103f65692464/pytest-sugar-0.9.2.tar.gz";
1684 url = "https://files.pythonhosted.org/packages/55/59/f02f78d1c80f7e03e23177f60624c8106d4f23d124c921df103f65692464/pytest-sugar-0.9.2.tar.gz";
1685 sha256 = "1asq7yc4g8bx2sn7yy974mhc9ywvaihasjab4inkirdwn9s7mn7w";
1685 sha256 = "1asq7yc4g8bx2sn7yy974mhc9ywvaihasjab4inkirdwn9s7mn7w";
1686 };
1686 };
1687 meta = {
1687 meta = {
1688 license = [ pkgs.lib.licenses.bsdOriginal ];
1688 license = [ pkgs.lib.licenses.bsdOriginal ];
1689 };
1689 };
1690 };
1690 };
1691 "pytest-timeout" = super.buildPythonPackage {
1691 "pytest-timeout" = super.buildPythonPackage {
1692 name = "pytest-timeout-1.3.3";
1692 name = "pytest-timeout-1.3.3";
1693 doCheck = false;
1693 doCheck = false;
1694 propagatedBuildInputs = [
1694 propagatedBuildInputs = [
1695 self."pytest"
1695 self."pytest"
1696 ];
1696 ];
1697 src = fetchurl {
1697 src = fetchurl {
1698 url = "https://files.pythonhosted.org/packages/13/48/7a166eaa29c1dca6cc253e3ba5773ff2e4aa4f567c1ea3905808e95ac5c1/pytest-timeout-1.3.3.tar.gz";
1698 url = "https://files.pythonhosted.org/packages/13/48/7a166eaa29c1dca6cc253e3ba5773ff2e4aa4f567c1ea3905808e95ac5c1/pytest-timeout-1.3.3.tar.gz";
1699 sha256 = "1cczcjhw4xx5sjkhxlhc5c1bkr7x6fcyx12wrnvwfckshdvblc2a";
1699 sha256 = "1cczcjhw4xx5sjkhxlhc5c1bkr7x6fcyx12wrnvwfckshdvblc2a";
1700 };
1700 };
1701 meta = {
1701 meta = {
1702 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
1702 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
1703 };
1703 };
1704 };
1704 };
1705 "python-dateutil" = super.buildPythonPackage {
1705 "python-dateutil" = super.buildPythonPackage {
1706 name = "python-dateutil-2.8.1";
1706 name = "python-dateutil-2.8.1";
1707 doCheck = false;
1707 doCheck = false;
1708 propagatedBuildInputs = [
1708 propagatedBuildInputs = [
1709 self."six"
1709 self."six"
1710 ];
1710 ];
1711 src = fetchurl {
1711 src = fetchurl {
1712 url = "https://files.pythonhosted.org/packages/be/ed/5bbc91f03fa4c839c4c7360375da77f9659af5f7086b7a7bdda65771c8e0/python-dateutil-2.8.1.tar.gz";
1712 url = "https://files.pythonhosted.org/packages/be/ed/5bbc91f03fa4c839c4c7360375da77f9659af5f7086b7a7bdda65771c8e0/python-dateutil-2.8.1.tar.gz";
1713 sha256 = "0g42w7k5007iv9dam6gnja2ry8ydwirh99mgdll35s12pyfzxsvk";
1713 sha256 = "0g42w7k5007iv9dam6gnja2ry8ydwirh99mgdll35s12pyfzxsvk";
1714 };
1714 };
1715 meta = {
1715 meta = {
1716 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.asl20 { fullName = "Dual License"; } ];
1716 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.asl20 { fullName = "Dual License"; } ];
1717 };
1717 };
1718 };
1718 };
1719 "python-editor" = super.buildPythonPackage {
1719 "python-editor" = super.buildPythonPackage {
1720 name = "python-editor-1.0.4";
1720 name = "python-editor-1.0.4";
1721 doCheck = false;
1721 doCheck = false;
1722 src = fetchurl {
1722 src = fetchurl {
1723 url = "https://files.pythonhosted.org/packages/0a/85/78f4a216d28343a67b7397c99825cff336330893f00601443f7c7b2f2234/python-editor-1.0.4.tar.gz";
1723 url = "https://files.pythonhosted.org/packages/0a/85/78f4a216d28343a67b7397c99825cff336330893f00601443f7c7b2f2234/python-editor-1.0.4.tar.gz";
1724 sha256 = "0yrjh8w72ivqxi4i7xsg5b1vz15x8fg51xra7c3bgfyxqnyadzai";
1724 sha256 = "0yrjh8w72ivqxi4i7xsg5b1vz15x8fg51xra7c3bgfyxqnyadzai";
1725 };
1725 };
1726 meta = {
1726 meta = {
1727 license = [ pkgs.lib.licenses.asl20 { fullName = "Apache"; } ];
1727 license = [ pkgs.lib.licenses.asl20 { fullName = "Apache"; } ];
1728 };
1728 };
1729 };
1729 };
1730 "python-ldap" = super.buildPythonPackage {
1730 "python-ldap" = super.buildPythonPackage {
1731 name = "python-ldap-3.2.0";
1731 name = "python-ldap-3.2.0";
1732 doCheck = false;
1732 doCheck = false;
1733 propagatedBuildInputs = [
1733 propagatedBuildInputs = [
1734 self."pyasn1"
1734 self."pyasn1"
1735 self."pyasn1-modules"
1735 self."pyasn1-modules"
1736 ];
1736 ];
1737 src = fetchurl {
1737 src = fetchurl {
1738 url = "https://files.pythonhosted.org/packages/ea/93/596f875e003c770447f4b99267820a0c769dd2dc3ae3ed19afe460fcbad0/python-ldap-3.2.0.tar.gz";
1738 url = "https://files.pythonhosted.org/packages/ea/93/596f875e003c770447f4b99267820a0c769dd2dc3ae3ed19afe460fcbad0/python-ldap-3.2.0.tar.gz";
1739 sha256 = "13nvrhp85yr0jyxixcjj012iw8l9wynxxlykm9j3alss6waln73x";
1739 sha256 = "13nvrhp85yr0jyxixcjj012iw8l9wynxxlykm9j3alss6waln73x";
1740 };
1740 };
1741 meta = {
1741 meta = {
1742 license = [ pkgs.lib.licenses.psfl ];
1742 license = [ pkgs.lib.licenses.psfl ];
1743 };
1743 };
1744 };
1744 };
1745 "python-memcached" = super.buildPythonPackage {
1745 "python-memcached" = super.buildPythonPackage {
1746 name = "python-memcached-1.59";
1746 name = "python-memcached-1.59";
1747 doCheck = false;
1747 doCheck = false;
1748 propagatedBuildInputs = [
1748 propagatedBuildInputs = [
1749 self."six"
1749 self."six"
1750 ];
1750 ];
1751 src = fetchurl {
1751 src = fetchurl {
1752 url = "https://files.pythonhosted.org/packages/90/59/5faf6e3cd8a568dd4f737ddae4f2e54204fd8c51f90bf8df99aca6c22318/python-memcached-1.59.tar.gz";
1752 url = "https://files.pythonhosted.org/packages/90/59/5faf6e3cd8a568dd4f737ddae4f2e54204fd8c51f90bf8df99aca6c22318/python-memcached-1.59.tar.gz";
1753 sha256 = "0kvyapavbirk2x3n1jx4yb9nyigrj1s3x15nm3qhpvhkpqvqdqm2";
1753 sha256 = "0kvyapavbirk2x3n1jx4yb9nyigrj1s3x15nm3qhpvhkpqvqdqm2";
1754 };
1754 };
1755 meta = {
1755 meta = {
1756 license = [ pkgs.lib.licenses.psfl ];
1756 license = [ pkgs.lib.licenses.psfl ];
1757 };
1757 };
1758 };
1758 };
1759 "python-pam" = super.buildPythonPackage {
1759 "python-pam" = super.buildPythonPackage {
1760 name = "python-pam-1.8.4";
1760 name = "python-pam-1.8.4";
1761 doCheck = false;
1761 doCheck = false;
1762 src = fetchurl {
1762 src = fetchurl {
1763 url = "https://files.pythonhosted.org/packages/01/16/544d01cae9f28e0292dbd092b6b8b0bf222b528f362ee768a5bed2140111/python-pam-1.8.4.tar.gz";
1763 url = "https://files.pythonhosted.org/packages/01/16/544d01cae9f28e0292dbd092b6b8b0bf222b528f362ee768a5bed2140111/python-pam-1.8.4.tar.gz";
1764 sha256 = "16whhc0vr7gxsbzvsnq65nq8fs3wwmx755cavm8kkczdkz4djmn8";
1764 sha256 = "16whhc0vr7gxsbzvsnq65nq8fs3wwmx755cavm8kkczdkz4djmn8";
1765 };
1765 };
1766 meta = {
1766 meta = {
1767 license = [ { fullName = "License :: OSI Approved :: MIT License"; } pkgs.lib.licenses.mit ];
1767 license = [ { fullName = "License :: OSI Approved :: MIT License"; } pkgs.lib.licenses.mit ];
1768 };
1768 };
1769 };
1769 };
1770 "python-saml" = super.buildPythonPackage {
1770 "python-saml" = super.buildPythonPackage {
1771 name = "python-saml-2.4.2";
1771 name = "python-saml-2.4.2";
1772 doCheck = false;
1772 doCheck = false;
1773 propagatedBuildInputs = [
1773 propagatedBuildInputs = [
1774 self."dm.xmlsec.binding"
1774 self."dm.xmlsec.binding"
1775 self."isodate"
1775 self."isodate"
1776 self."defusedxml"
1776 self."defusedxml"
1777 ];
1777 ];
1778 src = fetchurl {
1778 src = fetchurl {
1779 url = "https://files.pythonhosted.org/packages/79/a8/a6611017e0883102fd5e2b73c9d90691b8134e38247c04ee1531d3dc647c/python-saml-2.4.2.tar.gz";
1779 url = "https://files.pythonhosted.org/packages/79/a8/a6611017e0883102fd5e2b73c9d90691b8134e38247c04ee1531d3dc647c/python-saml-2.4.2.tar.gz";
1780 sha256 = "0dls4hwvf13yg7x5yfjrghbywg8g38vn5vr0rsf70hli3ydbfm43";
1780 sha256 = "0dls4hwvf13yg7x5yfjrghbywg8g38vn5vr0rsf70hli3ydbfm43";
1781 };
1781 };
1782 meta = {
1782 meta = {
1783 license = [ pkgs.lib.licenses.mit ];
1783 license = [ pkgs.lib.licenses.mit ];
1784 };
1784 };
1785 };
1785 };
1786 "pytz" = super.buildPythonPackage {
1786 "pytz" = super.buildPythonPackage {
1787 name = "pytz-2019.3";
1787 name = "pytz-2019.3";
1788 doCheck = false;
1788 doCheck = false;
1789 src = fetchurl {
1789 src = fetchurl {
1790 url = "https://files.pythonhosted.org/packages/82/c3/534ddba230bd4fbbd3b7a3d35f3341d014cca213f369a9940925e7e5f691/pytz-2019.3.tar.gz";
1790 url = "https://files.pythonhosted.org/packages/82/c3/534ddba230bd4fbbd3b7a3d35f3341d014cca213f369a9940925e7e5f691/pytz-2019.3.tar.gz";
1791 sha256 = "1ghrk1wg45d3nymj7bf4zj03n3bh64xmczhk4pfi577hdkdhcb5h";
1791 sha256 = "1ghrk1wg45d3nymj7bf4zj03n3bh64xmczhk4pfi577hdkdhcb5h";
1792 };
1792 };
1793 meta = {
1793 meta = {
1794 license = [ pkgs.lib.licenses.mit ];
1794 license = [ pkgs.lib.licenses.mit ];
1795 };
1795 };
1796 };
1796 };
1797 "pyzmq" = super.buildPythonPackage {
1797 "pyzmq" = super.buildPythonPackage {
1798 name = "pyzmq-14.6.0";
1798 name = "pyzmq-14.6.0";
1799 doCheck = false;
1799 doCheck = false;
1800 src = fetchurl {
1800 src = fetchurl {
1801 url = "https://files.pythonhosted.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1801 url = "https://files.pythonhosted.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1802 sha256 = "1frmbjykvhmdg64g7sn20c9fpamrsfxwci1nhhg8q7jgz5pq0ikp";
1802 sha256 = "1frmbjykvhmdg64g7sn20c9fpamrsfxwci1nhhg8q7jgz5pq0ikp";
1803 };
1803 };
1804 meta = {
1804 meta = {
1805 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1805 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1806 };
1806 };
1807 };
1807 };
1808 "PyYAML" = super.buildPythonPackage {
1808 "PyYAML" = super.buildPythonPackage {
1809 name = "PyYAML-5.3.1";
1809 name = "PyYAML-5.3.1";
1810 doCheck = false;
1810 doCheck = false;
1811 src = fetchurl {
1811 src = fetchurl {
1812 url = "https://files.pythonhosted.org/packages/64/c2/b80047c7ac2478f9501676c988a5411ed5572f35d1beff9cae07d321512c/PyYAML-5.3.1.tar.gz";
1812 url = "https://files.pythonhosted.org/packages/64/c2/b80047c7ac2478f9501676c988a5411ed5572f35d1beff9cae07d321512c/PyYAML-5.3.1.tar.gz";
1813 sha256 = "0pb4zvkfxfijkpgd1b86xjsqql97ssf1knbd1v53wkg1qm9cgsmq";
1813 sha256 = "0pb4zvkfxfijkpgd1b86xjsqql97ssf1knbd1v53wkg1qm9cgsmq";
1814 };
1814 };
1815 meta = {
1815 meta = {
1816 license = [ pkgs.lib.licenses.mit ];
1816 license = [ pkgs.lib.licenses.mit ];
1817 };
1817 };
1818 };
1818 };
1819 "regex" = super.buildPythonPackage {
1819 "regex" = super.buildPythonPackage {
1820 name = "regex-2020.9.27";
1820 name = "regex-2020.9.27";
1821 doCheck = false;
1821 doCheck = false;
1822 src = fetchurl {
1822 src = fetchurl {
1823 url = "https://files.pythonhosted.org/packages/93/8c/17f45cdfb39b13d4b5f909e4b4c2917abcbdef9c0036919a0399769148cf/regex-2020.9.27.tar.gz";
1823 url = "https://files.pythonhosted.org/packages/93/8c/17f45cdfb39b13d4b5f909e4b4c2917abcbdef9c0036919a0399769148cf/regex-2020.9.27.tar.gz";
1824 sha256 = "179ngfzwbsjvn5vhyzdahvmg0f7acahkwwy9bpjy1pv08bm2mwx6";
1824 sha256 = "179ngfzwbsjvn5vhyzdahvmg0f7acahkwwy9bpjy1pv08bm2mwx6";
1825 };
1825 };
1826 meta = {
1826 meta = {
1827 license = [ pkgs.lib.licenses.psfl ];
1827 license = [ pkgs.lib.licenses.psfl ];
1828 };
1828 };
1829 };
1829 };
1830 "redis" = super.buildPythonPackage {
1830 "redis" = super.buildPythonPackage {
1831 name = "redis-3.4.1";
1831 name = "redis-3.4.1";
1832 doCheck = false;
1832 doCheck = false;
1833 src = fetchurl {
1833 src = fetchurl {
1834 url = "https://files.pythonhosted.org/packages/ef/2e/2c0f59891db7db087a7eeaa79bc7c7f2c039e71a2b5b0a41391e9d462926/redis-3.4.1.tar.gz";
1834 url = "https://files.pythonhosted.org/packages/ef/2e/2c0f59891db7db087a7eeaa79bc7c7f2c039e71a2b5b0a41391e9d462926/redis-3.4.1.tar.gz";
1835 sha256 = "07yaj0j9fs7xdkg5bg926fa990khyigjbp31si8ai20vj8sv7kqd";
1835 sha256 = "07yaj0j9fs7xdkg5bg926fa990khyigjbp31si8ai20vj8sv7kqd";
1836 };
1836 };
1837 meta = {
1837 meta = {
1838 license = [ pkgs.lib.licenses.mit ];
1838 license = [ pkgs.lib.licenses.mit ];
1839 };
1839 };
1840 };
1840 };
1841 "repoze.lru" = super.buildPythonPackage {
1841 "repoze.lru" = super.buildPythonPackage {
1842 name = "repoze.lru-0.7";
1842 name = "repoze.lru-0.7";
1843 doCheck = false;
1843 doCheck = false;
1844 src = fetchurl {
1844 src = fetchurl {
1845 url = "https://files.pythonhosted.org/packages/12/bc/595a77c4b5e204847fdf19268314ef59c85193a9dc9f83630fc459c0fee5/repoze.lru-0.7.tar.gz";
1845 url = "https://files.pythonhosted.org/packages/12/bc/595a77c4b5e204847fdf19268314ef59c85193a9dc9f83630fc459c0fee5/repoze.lru-0.7.tar.gz";
1846 sha256 = "0xzz1aw2smy8hdszrq8yhnklx6w1r1mf55061kalw3iq35gafa84";
1846 sha256 = "0xzz1aw2smy8hdszrq8yhnklx6w1r1mf55061kalw3iq35gafa84";
1847 };
1847 };
1848 meta = {
1848 meta = {
1849 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1849 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1850 };
1850 };
1851 };
1851 };
1852 "repoze.sendmail" = super.buildPythonPackage {
1852 "repoze.sendmail" = super.buildPythonPackage {
1853 name = "repoze.sendmail-4.4.1";
1853 name = "repoze.sendmail-4.4.1";
1854 doCheck = false;
1854 doCheck = false;
1855 propagatedBuildInputs = [
1855 propagatedBuildInputs = [
1856 self."setuptools"
1856 self."setuptools"
1857 self."zope.interface"
1857 self."zope.interface"
1858 self."transaction"
1858 self."transaction"
1859 ];
1859 ];
1860 src = fetchurl {
1860 src = fetchurl {
1861 url = "https://files.pythonhosted.org/packages/12/4e/8ef1fd5c42765d712427b9c391419a77bd48877886d2cbc5e9f23c8cad9b/repoze.sendmail-4.4.1.tar.gz";
1861 url = "https://files.pythonhosted.org/packages/12/4e/8ef1fd5c42765d712427b9c391419a77bd48877886d2cbc5e9f23c8cad9b/repoze.sendmail-4.4.1.tar.gz";
1862 sha256 = "096ln02jr2afk7ab9j2czxqv2ryqq7m86ah572nqplx52iws73ks";
1862 sha256 = "096ln02jr2afk7ab9j2czxqv2ryqq7m86ah572nqplx52iws73ks";
1863 };
1863 };
1864 meta = {
1864 meta = {
1865 license = [ pkgs.lib.licenses.zpl21 ];
1865 license = [ pkgs.lib.licenses.zpl21 ];
1866 };
1866 };
1867 };
1867 };
1868 "requests" = super.buildPythonPackage {
1868 "requests" = super.buildPythonPackage {
1869 name = "requests-2.22.0";
1869 name = "requests-2.22.0";
1870 doCheck = false;
1870 doCheck = false;
1871 propagatedBuildInputs = [
1871 propagatedBuildInputs = [
1872 self."chardet"
1872 self."chardet"
1873 self."idna"
1873 self."idna"
1874 self."urllib3"
1874 self."urllib3"
1875 self."certifi"
1875 self."certifi"
1876 ];
1876 ];
1877 src = fetchurl {
1877 src = fetchurl {
1878 url = "https://files.pythonhosted.org/packages/01/62/ddcf76d1d19885e8579acb1b1df26a852b03472c0e46d2b959a714c90608/requests-2.22.0.tar.gz";
1878 url = "https://files.pythonhosted.org/packages/01/62/ddcf76d1d19885e8579acb1b1df26a852b03472c0e46d2b959a714c90608/requests-2.22.0.tar.gz";
1879 sha256 = "1d5ybh11jr5sm7xp6mz8fyc7vrp4syifds91m7sj60xalal0gq0i";
1879 sha256 = "1d5ybh11jr5sm7xp6mz8fyc7vrp4syifds91m7sj60xalal0gq0i";
1880 };
1880 };
1881 meta = {
1881 meta = {
1882 license = [ pkgs.lib.licenses.asl20 ];
1882 license = [ pkgs.lib.licenses.asl20 ];
1883 };
1883 };
1884 };
1884 };
1885 "rhodecode-enterprise-ce" = super.buildPythonPackage {
1885 "rhodecode-enterprise-ce" = super.buildPythonPackage {
1886 name = "rhodecode-enterprise-ce-4.23.0";
1886 name = "rhodecode-enterprise-ce-4.23.2";
1887 buildInputs = [
1887 buildInputs = [
1888 self."pytest"
1888 self."pytest"
1889 self."py"
1889 self."py"
1890 self."pytest-cov"
1890 self."pytest-cov"
1891 self."pytest-sugar"
1891 self."pytest-sugar"
1892 self."pytest-runner"
1892 self."pytest-runner"
1893 self."pytest-profiling"
1893 self."pytest-profiling"
1894 self."pytest-timeout"
1894 self."pytest-timeout"
1895 self."gprof2dot"
1895 self."gprof2dot"
1896 self."mock"
1896 self."mock"
1897 self."cov-core"
1897 self."cov-core"
1898 self."coverage"
1898 self."coverage"
1899 self."webtest"
1899 self."webtest"
1900 self."beautifulsoup4"
1900 self."beautifulsoup4"
1901 self."configobj"
1901 self."configobj"
1902 ];
1902 ];
1903 doCheck = true;
1903 doCheck = true;
1904 propagatedBuildInputs = [
1904 propagatedBuildInputs = [
1905 self."amqp"
1905 self."amqp"
1906 self."babel"
1906 self."babel"
1907 self."beaker"
1907 self."beaker"
1908 self."bleach"
1908 self."bleach"
1909 self."celery"
1909 self."celery"
1910 self."channelstream"
1910 self."channelstream"
1911 self."click"
1911 self."click"
1912 self."colander"
1912 self."colander"
1913 self."configobj"
1913 self."configobj"
1914 self."cssselect"
1914 self."cssselect"
1915 self."cryptography"
1915 self."cryptography"
1916 self."decorator"
1916 self."decorator"
1917 self."deform"
1917 self."deform"
1918 self."docutils"
1918 self."docutils"
1919 self."dogpile.cache"
1919 self."dogpile.cache"
1920 self."dogpile.core"
1920 self."dogpile.core"
1921 self."formencode"
1921 self."formencode"
1922 self."future"
1922 self."future"
1923 self."futures"
1923 self."futures"
1924 self."infrae.cache"
1924 self."infrae.cache"
1925 self."iso8601"
1925 self."iso8601"
1926 self."itsdangerous"
1926 self."itsdangerous"
1927 self."kombu"
1927 self."kombu"
1928 self."lxml"
1928 self."lxml"
1929 self."mako"
1929 self."mako"
1930 self."markdown"
1930 self."markdown"
1931 self."markupsafe"
1931 self."markupsafe"
1932 self."msgpack-python"
1932 self."msgpack-python"
1933 self."pyotp"
1933 self."pyotp"
1934 self."packaging"
1934 self."packaging"
1935 self."pathlib2"
1935 self."pathlib2"
1936 self."paste"
1936 self."paste"
1937 self."pastedeploy"
1937 self."pastedeploy"
1938 self."pastescript"
1938 self."pastescript"
1939 self."peppercorn"
1939 self."peppercorn"
1940 self."premailer"
1940 self."premailer"
1941 self."psutil"
1941 self."psutil"
1942 self."py-bcrypt"
1942 self."py-bcrypt"
1943 self."pycurl"
1943 self."pycurl"
1944 self."pycrypto"
1944 self."pycrypto"
1945 self."pygments"
1945 self."pygments"
1946 self."pyparsing"
1946 self."pyparsing"
1947 self."pyramid-debugtoolbar"
1947 self."pyramid-debugtoolbar"
1948 self."pyramid-mako"
1948 self."pyramid-mako"
1949 self."pyramid"
1949 self."pyramid"
1950 self."pyramid-mailer"
1950 self."pyramid-mailer"
1951 self."python-dateutil"
1951 self."python-dateutil"
1952 self."python-ldap"
1952 self."python-ldap"
1953 self."python-memcached"
1953 self."python-memcached"
1954 self."python-pam"
1954 self."python-pam"
1955 self."python-saml"
1955 self."python-saml"
1956 self."pytz"
1956 self."pytz"
1957 self."tzlocal"
1957 self."tzlocal"
1958 self."pyzmq"
1958 self."pyzmq"
1959 self."py-gfm"
1959 self."py-gfm"
1960 self."regex"
1960 self."regex"
1961 self."redis"
1961 self."redis"
1962 self."repoze.lru"
1962 self."repoze.lru"
1963 self."requests"
1963 self."requests"
1964 self."routes"
1964 self."routes"
1965 self."simplejson"
1965 self."simplejson"
1966 self."six"
1966 self."six"
1967 self."sqlalchemy"
1967 self."sqlalchemy"
1968 self."sshpubkeys"
1968 self."sshpubkeys"
1969 self."subprocess32"
1969 self."subprocess32"
1970 self."supervisor"
1970 self."supervisor"
1971 self."translationstring"
1971 self."translationstring"
1972 self."urllib3"
1972 self."urllib3"
1973 self."urlobject"
1973 self."urlobject"
1974 self."venusian"
1974 self."venusian"
1975 self."weberror"
1975 self."weberror"
1976 self."webhelpers2"
1976 self."webhelpers2"
1977 self."webob"
1977 self."webob"
1978 self."whoosh"
1978 self."whoosh"
1979 self."wsgiref"
1979 self."wsgiref"
1980 self."zope.cachedescriptors"
1980 self."zope.cachedescriptors"
1981 self."zope.deprecation"
1981 self."zope.deprecation"
1982 self."zope.event"
1982 self."zope.event"
1983 self."zope.interface"
1983 self."zope.interface"
1984 self."mysql-python"
1984 self."mysql-python"
1985 self."pymysql"
1985 self."pymysql"
1986 self."pysqlite"
1986 self."pysqlite"
1987 self."psycopg2"
1987 self."psycopg2"
1988 self."nbconvert"
1988 self."nbconvert"
1989 self."nbformat"
1989 self."nbformat"
1990 self."jupyter-client"
1990 self."jupyter-client"
1991 self."jupyter-core"
1991 self."jupyter-core"
1992 self."alembic"
1992 self."alembic"
1993 self."invoke"
1993 self."invoke"
1994 self."bumpversion"
1994 self."bumpversion"
1995 self."gevent"
1995 self."gevent"
1996 self."greenlet"
1996 self."greenlet"
1997 self."gunicorn"
1997 self."gunicorn"
1998 self."waitress"
1998 self."waitress"
1999 self."ipdb"
1999 self."ipdb"
2000 self."ipython"
2000 self."ipython"
2001 self."rhodecode-tools"
2001 self."rhodecode-tools"
2002 self."appenlight-client"
2002 self."appenlight-client"
2003 self."pytest"
2003 self."pytest"
2004 self."py"
2004 self."py"
2005 self."pytest-cov"
2005 self."pytest-cov"
2006 self."pytest-sugar"
2006 self."pytest-sugar"
2007 self."pytest-runner"
2007 self."pytest-runner"
2008 self."pytest-profiling"
2008 self."pytest-profiling"
2009 self."pytest-timeout"
2009 self."pytest-timeout"
2010 self."gprof2dot"
2010 self."gprof2dot"
2011 self."mock"
2011 self."mock"
2012 self."cov-core"
2012 self."cov-core"
2013 self."coverage"
2013 self."coverage"
2014 self."webtest"
2014 self."webtest"
2015 self."beautifulsoup4"
2015 self."beautifulsoup4"
2016 ];
2016 ];
2017 src = ./.;
2017 src = ./.;
2018 meta = {
2018 meta = {
2019 license = [ { fullName = "Affero GNU General Public License v3 or later (AGPLv3+)"; } { fullName = "AGPLv3, and Commercial License"; } ];
2019 license = [ { fullName = "Affero GNU General Public License v3 or later (AGPLv3+)"; } { fullName = "AGPLv3, and Commercial License"; } ];
2020 };
2020 };
2021 };
2021 };
2022 "rhodecode-tools" = super.buildPythonPackage {
2022 "rhodecode-tools" = super.buildPythonPackage {
2023 name = "rhodecode-tools-1.4.0";
2023 name = "rhodecode-tools-1.4.0";
2024 doCheck = false;
2024 doCheck = false;
2025 propagatedBuildInputs = [
2025 propagatedBuildInputs = [
2026 self."click"
2026 self."click"
2027 self."future"
2027 self."future"
2028 self."six"
2028 self."six"
2029 self."mako"
2029 self."mako"
2030 self."markupsafe"
2030 self."markupsafe"
2031 self."requests"
2031 self."requests"
2032 self."urllib3"
2032 self."urllib3"
2033 self."whoosh"
2033 self."whoosh"
2034 self."elasticsearch"
2034 self."elasticsearch"
2035 self."elasticsearch-dsl"
2035 self."elasticsearch-dsl"
2036 self."elasticsearch2"
2036 self."elasticsearch2"
2037 self."elasticsearch1-dsl"
2037 self."elasticsearch1-dsl"
2038 ];
2038 ];
2039 src = fetchurl {
2039 src = fetchurl {
2040 url = "https://code.rhodecode.com/rhodecode-tools-ce/artifacts/download/0-ed54e749-2ef5-4bc7-ae7f-7900e3c2aa15.tar.gz?sha256=76f024bad3a1e55fdb3d64f13f5b77ff21a12fee699918de2110fe21effd5a3a";
2040 url = "https://code.rhodecode.com/rhodecode-tools-ce/artifacts/download/0-ed54e749-2ef5-4bc7-ae7f-7900e3c2aa15.tar.gz?sha256=76f024bad3a1e55fdb3d64f13f5b77ff21a12fee699918de2110fe21effd5a3a";
2041 sha256 = "0fjszppj3zhh47g1i6b9xqps28gzfxdkzwb47pdmzrd1sfx29w3n";
2041 sha256 = "0fjszppj3zhh47g1i6b9xqps28gzfxdkzwb47pdmzrd1sfx29w3n";
2042 };
2042 };
2043 meta = {
2043 meta = {
2044 license = [ { fullName = "Apache 2.0 and Proprietary"; } ];
2044 license = [ { fullName = "Apache 2.0 and Proprietary"; } ];
2045 };
2045 };
2046 };
2046 };
2047 "routes" = super.buildPythonPackage {
2047 "routes" = super.buildPythonPackage {
2048 name = "routes-2.4.1";
2048 name = "routes-2.4.1";
2049 doCheck = false;
2049 doCheck = false;
2050 propagatedBuildInputs = [
2050 propagatedBuildInputs = [
2051 self."six"
2051 self."six"
2052 self."repoze.lru"
2052 self."repoze.lru"
2053 ];
2053 ];
2054 src = fetchurl {
2054 src = fetchurl {
2055 url = "https://files.pythonhosted.org/packages/33/38/ea827837e68d9c7dde4cff7ec122a93c319f0effc08ce92a17095576603f/Routes-2.4.1.tar.gz";
2055 url = "https://files.pythonhosted.org/packages/33/38/ea827837e68d9c7dde4cff7ec122a93c319f0effc08ce92a17095576603f/Routes-2.4.1.tar.gz";
2056 sha256 = "1zamff3m0kc4vyfniyhxpkkcqv1rrgnmh37ykxv34nna1ws47vi6";
2056 sha256 = "1zamff3m0kc4vyfniyhxpkkcqv1rrgnmh37ykxv34nna1ws47vi6";
2057 };
2057 };
2058 meta = {
2058 meta = {
2059 license = [ pkgs.lib.licenses.mit ];
2059 license = [ pkgs.lib.licenses.mit ];
2060 };
2060 };
2061 };
2061 };
2062 "scandir" = super.buildPythonPackage {
2062 "scandir" = super.buildPythonPackage {
2063 name = "scandir-1.10.0";
2063 name = "scandir-1.10.0";
2064 doCheck = false;
2064 doCheck = false;
2065 src = fetchurl {
2065 src = fetchurl {
2066 url = "https://files.pythonhosted.org/packages/df/f5/9c052db7bd54d0cbf1bc0bb6554362bba1012d03e5888950a4f5c5dadc4e/scandir-1.10.0.tar.gz";
2066 url = "https://files.pythonhosted.org/packages/df/f5/9c052db7bd54d0cbf1bc0bb6554362bba1012d03e5888950a4f5c5dadc4e/scandir-1.10.0.tar.gz";
2067 sha256 = "1bkqwmf056pkchf05ywbnf659wqlp6lljcdb0y88wr9f0vv32ijd";
2067 sha256 = "1bkqwmf056pkchf05ywbnf659wqlp6lljcdb0y88wr9f0vv32ijd";
2068 };
2068 };
2069 meta = {
2069 meta = {
2070 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "New BSD License"; } ];
2070 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "New BSD License"; } ];
2071 };
2071 };
2072 };
2072 };
2073 "setproctitle" = super.buildPythonPackage {
2073 "setproctitle" = super.buildPythonPackage {
2074 name = "setproctitle-1.1.10";
2074 name = "setproctitle-1.1.10";
2075 doCheck = false;
2075 doCheck = false;
2076 src = fetchurl {
2076 src = fetchurl {
2077 url = "https://files.pythonhosted.org/packages/5a/0d/dc0d2234aacba6cf1a729964383e3452c52096dc695581248b548786f2b3/setproctitle-1.1.10.tar.gz";
2077 url = "https://files.pythonhosted.org/packages/5a/0d/dc0d2234aacba6cf1a729964383e3452c52096dc695581248b548786f2b3/setproctitle-1.1.10.tar.gz";
2078 sha256 = "163kplw9dcrw0lffq1bvli5yws3rngpnvrxrzdw89pbphjjvg0v2";
2078 sha256 = "163kplw9dcrw0lffq1bvli5yws3rngpnvrxrzdw89pbphjjvg0v2";
2079 };
2079 };
2080 meta = {
2080 meta = {
2081 license = [ pkgs.lib.licenses.bsdOriginal ];
2081 license = [ pkgs.lib.licenses.bsdOriginal ];
2082 };
2082 };
2083 };
2083 };
2084 "setuptools" = super.buildPythonPackage {
2084 "setuptools" = super.buildPythonPackage {
2085 name = "setuptools-44.1.0";
2085 name = "setuptools-44.1.0";
2086 doCheck = false;
2086 doCheck = false;
2087 src = fetchurl {
2087 src = fetchurl {
2088 url = "https://files.pythonhosted.org/packages/ed/7b/bbf89ca71e722b7f9464ebffe4b5ee20a9e5c9a555a56e2d3914bb9119a6/setuptools-44.1.0.zip";
2088 url = "https://files.pythonhosted.org/packages/ed/7b/bbf89ca71e722b7f9464ebffe4b5ee20a9e5c9a555a56e2d3914bb9119a6/setuptools-44.1.0.zip";
2089 sha256 = "1jja896zvd1ppccnjbhkgagxbwchgq6vfamp6qn1hvywq6q9cjkr";
2089 sha256 = "1jja896zvd1ppccnjbhkgagxbwchgq6vfamp6qn1hvywq6q9cjkr";
2090 };
2090 };
2091 meta = {
2091 meta = {
2092 license = [ pkgs.lib.licenses.mit ];
2092 license = [ pkgs.lib.licenses.mit ];
2093 };
2093 };
2094 };
2094 };
2095 "simplegeneric" = super.buildPythonPackage {
2095 "simplegeneric" = super.buildPythonPackage {
2096 name = "simplegeneric-0.8.1";
2096 name = "simplegeneric-0.8.1";
2097 doCheck = false;
2097 doCheck = false;
2098 src = fetchurl {
2098 src = fetchurl {
2099 url = "https://files.pythonhosted.org/packages/3d/57/4d9c9e3ae9a255cd4e1106bb57e24056d3d0709fc01b2e3e345898e49d5b/simplegeneric-0.8.1.zip";
2099 url = "https://files.pythonhosted.org/packages/3d/57/4d9c9e3ae9a255cd4e1106bb57e24056d3d0709fc01b2e3e345898e49d5b/simplegeneric-0.8.1.zip";
2100 sha256 = "0wwi1c6md4vkbcsfsf8dklf3vr4mcdj4mpxkanwgb6jb1432x5yw";
2100 sha256 = "0wwi1c6md4vkbcsfsf8dklf3vr4mcdj4mpxkanwgb6jb1432x5yw";
2101 };
2101 };
2102 meta = {
2102 meta = {
2103 license = [ pkgs.lib.licenses.zpl21 ];
2103 license = [ pkgs.lib.licenses.zpl21 ];
2104 };
2104 };
2105 };
2105 };
2106 "simplejson" = super.buildPythonPackage {
2106 "simplejson" = super.buildPythonPackage {
2107 name = "simplejson-3.16.0";
2107 name = "simplejson-3.16.0";
2108 doCheck = false;
2108 doCheck = false;
2109 src = fetchurl {
2109 src = fetchurl {
2110 url = "https://files.pythonhosted.org/packages/e3/24/c35fb1c1c315fc0fffe61ea00d3f88e85469004713dab488dee4f35b0aff/simplejson-3.16.0.tar.gz";
2110 url = "https://files.pythonhosted.org/packages/e3/24/c35fb1c1c315fc0fffe61ea00d3f88e85469004713dab488dee4f35b0aff/simplejson-3.16.0.tar.gz";
2111 sha256 = "19cws1syk8jzq2pw43878dv6fjkb0ifvjpx0i9aajix6kc9jkwxi";
2111 sha256 = "19cws1syk8jzq2pw43878dv6fjkb0ifvjpx0i9aajix6kc9jkwxi";
2112 };
2112 };
2113 meta = {
2113 meta = {
2114 license = [ { fullName = "Academic Free License (AFL)"; } pkgs.lib.licenses.mit ];
2114 license = [ { fullName = "Academic Free License (AFL)"; } pkgs.lib.licenses.mit ];
2115 };
2115 };
2116 };
2116 };
2117 "six" = super.buildPythonPackage {
2117 "six" = super.buildPythonPackage {
2118 name = "six-1.11.0";
2118 name = "six-1.11.0";
2119 doCheck = false;
2119 doCheck = false;
2120 src = fetchurl {
2120 src = fetchurl {
2121 url = "https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six-1.11.0.tar.gz";
2121 url = "https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six-1.11.0.tar.gz";
2122 sha256 = "1scqzwc51c875z23phj48gircqjgnn3af8zy2izjwmnlxrxsgs3h";
2122 sha256 = "1scqzwc51c875z23phj48gircqjgnn3af8zy2izjwmnlxrxsgs3h";
2123 };
2123 };
2124 meta = {
2124 meta = {
2125 license = [ pkgs.lib.licenses.mit ];
2125 license = [ pkgs.lib.licenses.mit ];
2126 };
2126 };
2127 };
2127 };
2128 "sqlalchemy" = super.buildPythonPackage {
2128 "sqlalchemy" = super.buildPythonPackage {
2129 name = "sqlalchemy-1.3.15";
2129 name = "sqlalchemy-1.3.15";
2130 doCheck = false;
2130 doCheck = false;
2131 src = fetchurl {
2131 src = fetchurl {
2132 url = "https://files.pythonhosted.org/packages/8c/30/4134e726dd5ed13728ff814fa91fc01c447ad8700504653fe99d91fdd34b/SQLAlchemy-1.3.15.tar.gz";
2132 url = "https://files.pythonhosted.org/packages/8c/30/4134e726dd5ed13728ff814fa91fc01c447ad8700504653fe99d91fdd34b/SQLAlchemy-1.3.15.tar.gz";
2133 sha256 = "0iglkvymfp35zm5pxy5kzqvcv96kkas0chqdx7xpla86sspa9k64";
2133 sha256 = "0iglkvymfp35zm5pxy5kzqvcv96kkas0chqdx7xpla86sspa9k64";
2134 };
2134 };
2135 meta = {
2135 meta = {
2136 license = [ pkgs.lib.licenses.mit ];
2136 license = [ pkgs.lib.licenses.mit ];
2137 };
2137 };
2138 };
2138 };
2139 "sshpubkeys" = super.buildPythonPackage {
2139 "sshpubkeys" = super.buildPythonPackage {
2140 name = "sshpubkeys-3.1.0";
2140 name = "sshpubkeys-3.1.0";
2141 doCheck = false;
2141 doCheck = false;
2142 propagatedBuildInputs = [
2142 propagatedBuildInputs = [
2143 self."cryptography"
2143 self."cryptography"
2144 self."ecdsa"
2144 self."ecdsa"
2145 ];
2145 ];
2146 src = fetchurl {
2146 src = fetchurl {
2147 url = "https://files.pythonhosted.org/packages/00/23/f7508a12007c96861c3da811992f14283d79c819d71a217b3e12d5196649/sshpubkeys-3.1.0.tar.gz";
2147 url = "https://files.pythonhosted.org/packages/00/23/f7508a12007c96861c3da811992f14283d79c819d71a217b3e12d5196649/sshpubkeys-3.1.0.tar.gz";
2148 sha256 = "105g2li04nm1hb15a2y6hm9m9k7fbrkd5l3gy12w3kgcmsf3k25k";
2148 sha256 = "105g2li04nm1hb15a2y6hm9m9k7fbrkd5l3gy12w3kgcmsf3k25k";
2149 };
2149 };
2150 meta = {
2150 meta = {
2151 license = [ pkgs.lib.licenses.bsdOriginal ];
2151 license = [ pkgs.lib.licenses.bsdOriginal ];
2152 };
2152 };
2153 };
2153 };
2154 "subprocess32" = super.buildPythonPackage {
2154 "subprocess32" = super.buildPythonPackage {
2155 name = "subprocess32-3.5.4";
2155 name = "subprocess32-3.5.4";
2156 doCheck = false;
2156 doCheck = false;
2157 src = fetchurl {
2157 src = fetchurl {
2158 url = "https://files.pythonhosted.org/packages/32/c8/564be4d12629b912ea431f1a50eb8b3b9d00f1a0b1ceff17f266be190007/subprocess32-3.5.4.tar.gz";
2158 url = "https://files.pythonhosted.org/packages/32/c8/564be4d12629b912ea431f1a50eb8b3b9d00f1a0b1ceff17f266be190007/subprocess32-3.5.4.tar.gz";
2159 sha256 = "17f7mvwx2271s1wrl0qac3wjqqnrqag866zs3qc8v5wp0k43fagb";
2159 sha256 = "17f7mvwx2271s1wrl0qac3wjqqnrqag866zs3qc8v5wp0k43fagb";
2160 };
2160 };
2161 meta = {
2161 meta = {
2162 license = [ pkgs.lib.licenses.psfl ];
2162 license = [ pkgs.lib.licenses.psfl ];
2163 };
2163 };
2164 };
2164 };
2165 "supervisor" = super.buildPythonPackage {
2165 "supervisor" = super.buildPythonPackage {
2166 name = "supervisor-4.1.0";
2166 name = "supervisor-4.1.0";
2167 doCheck = false;
2167 doCheck = false;
2168 src = fetchurl {
2168 src = fetchurl {
2169 url = "https://files.pythonhosted.org/packages/de/87/ee1ad8fa533a4b5f2c7623f4a2b585d3c1947af7bed8e65bc7772274320e/supervisor-4.1.0.tar.gz";
2169 url = "https://files.pythonhosted.org/packages/de/87/ee1ad8fa533a4b5f2c7623f4a2b585d3c1947af7bed8e65bc7772274320e/supervisor-4.1.0.tar.gz";
2170 sha256 = "10q36sa1jqljyyyl7cif52akpygl5kmlqq9x91hmx53f8zh6zj1d";
2170 sha256 = "10q36sa1jqljyyyl7cif52akpygl5kmlqq9x91hmx53f8zh6zj1d";
2171 };
2171 };
2172 meta = {
2172 meta = {
2173 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
2173 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
2174 };
2174 };
2175 };
2175 };
2176 "tempita" = super.buildPythonPackage {
2176 "tempita" = super.buildPythonPackage {
2177 name = "tempita-0.5.2";
2177 name = "tempita-0.5.2";
2178 doCheck = false;
2178 doCheck = false;
2179 src = fetchurl {
2179 src = fetchurl {
2180 url = "https://files.pythonhosted.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
2180 url = "https://files.pythonhosted.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
2181 sha256 = "177wwq45slfyajd8csy477bmdmzipyw0dm7i85k3akb7m85wzkna";
2181 sha256 = "177wwq45slfyajd8csy477bmdmzipyw0dm7i85k3akb7m85wzkna";
2182 };
2182 };
2183 meta = {
2183 meta = {
2184 license = [ pkgs.lib.licenses.mit ];
2184 license = [ pkgs.lib.licenses.mit ];
2185 };
2185 };
2186 };
2186 };
2187 "termcolor" = super.buildPythonPackage {
2187 "termcolor" = super.buildPythonPackage {
2188 name = "termcolor-1.1.0";
2188 name = "termcolor-1.1.0";
2189 doCheck = false;
2189 doCheck = false;
2190 src = fetchurl {
2190 src = fetchurl {
2191 url = "https://files.pythonhosted.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz";
2191 url = "https://files.pythonhosted.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz";
2192 sha256 = "0fv1vq14rpqwgazxg4981904lfyp84mnammw7y046491cv76jv8x";
2192 sha256 = "0fv1vq14rpqwgazxg4981904lfyp84mnammw7y046491cv76jv8x";
2193 };
2193 };
2194 meta = {
2194 meta = {
2195 license = [ pkgs.lib.licenses.mit ];
2195 license = [ pkgs.lib.licenses.mit ];
2196 };
2196 };
2197 };
2197 };
2198 "testpath" = super.buildPythonPackage {
2198 "testpath" = super.buildPythonPackage {
2199 name = "testpath-0.4.4";
2199 name = "testpath-0.4.4";
2200 doCheck = false;
2200 doCheck = false;
2201 src = fetchurl {
2201 src = fetchurl {
2202 url = "https://files.pythonhosted.org/packages/2c/b3/5d57205e896d8998d77ad12aa42ebce75cd97d8b9a97d00ba078c4c9ffeb/testpath-0.4.4.tar.gz";
2202 url = "https://files.pythonhosted.org/packages/2c/b3/5d57205e896d8998d77ad12aa42ebce75cd97d8b9a97d00ba078c4c9ffeb/testpath-0.4.4.tar.gz";
2203 sha256 = "0zpcmq22dz79ipvvsfnw1ykpjcaj6xyzy7ws77s5b5ql3hka7q30";
2203 sha256 = "0zpcmq22dz79ipvvsfnw1ykpjcaj6xyzy7ws77s5b5ql3hka7q30";
2204 };
2204 };
2205 meta = {
2205 meta = {
2206 license = [ ];
2206 license = [ ];
2207 };
2207 };
2208 };
2208 };
2209 "traitlets" = super.buildPythonPackage {
2209 "traitlets" = super.buildPythonPackage {
2210 name = "traitlets-4.3.3";
2210 name = "traitlets-4.3.3";
2211 doCheck = false;
2211 doCheck = false;
2212 propagatedBuildInputs = [
2212 propagatedBuildInputs = [
2213 self."ipython-genutils"
2213 self."ipython-genutils"
2214 self."six"
2214 self."six"
2215 self."decorator"
2215 self."decorator"
2216 self."enum34"
2216 self."enum34"
2217 ];
2217 ];
2218 src = fetchurl {
2218 src = fetchurl {
2219 url = "https://files.pythonhosted.org/packages/75/b0/43deb021bc943f18f07cbe3dac1d681626a48997b7ffa1e7fb14ef922b21/traitlets-4.3.3.tar.gz";
2219 url = "https://files.pythonhosted.org/packages/75/b0/43deb021bc943f18f07cbe3dac1d681626a48997b7ffa1e7fb14ef922b21/traitlets-4.3.3.tar.gz";
2220 sha256 = "1xsrwgivpkxlbr4dfndfsi098s29yqgswgjc1qqn69yxklvfw8yh";
2220 sha256 = "1xsrwgivpkxlbr4dfndfsi098s29yqgswgjc1qqn69yxklvfw8yh";
2221 };
2221 };
2222 meta = {
2222 meta = {
2223 license = [ pkgs.lib.licenses.bsdOriginal ];
2223 license = [ pkgs.lib.licenses.bsdOriginal ];
2224 };
2224 };
2225 };
2225 };
2226 "transaction" = super.buildPythonPackage {
2226 "transaction" = super.buildPythonPackage {
2227 name = "transaction-2.4.0";
2227 name = "transaction-2.4.0";
2228 doCheck = false;
2228 doCheck = false;
2229 propagatedBuildInputs = [
2229 propagatedBuildInputs = [
2230 self."zope.interface"
2230 self."zope.interface"
2231 ];
2231 ];
2232 src = fetchurl {
2232 src = fetchurl {
2233 url = "https://files.pythonhosted.org/packages/9d/7d/0e8af0d059e052b9dcf2bb5a08aad20ae3e238746bdd3f8701a60969b363/transaction-2.4.0.tar.gz";
2233 url = "https://files.pythonhosted.org/packages/9d/7d/0e8af0d059e052b9dcf2bb5a08aad20ae3e238746bdd3f8701a60969b363/transaction-2.4.0.tar.gz";
2234 sha256 = "17wz1y524ca07vr03yddy8dv0gbscs06dbdywmllxv5rc725jq3j";
2234 sha256 = "17wz1y524ca07vr03yddy8dv0gbscs06dbdywmllxv5rc725jq3j";
2235 };
2235 };
2236 meta = {
2236 meta = {
2237 license = [ pkgs.lib.licenses.zpl21 ];
2237 license = [ pkgs.lib.licenses.zpl21 ];
2238 };
2238 };
2239 };
2239 };
2240 "translationstring" = super.buildPythonPackage {
2240 "translationstring" = super.buildPythonPackage {
2241 name = "translationstring-1.3";
2241 name = "translationstring-1.3";
2242 doCheck = false;
2242 doCheck = false;
2243 src = fetchurl {
2243 src = fetchurl {
2244 url = "https://files.pythonhosted.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
2244 url = "https://files.pythonhosted.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
2245 sha256 = "0bdpcnd9pv0131dl08h4zbcwmgc45lyvq3pa224xwan5b3x4rr2f";
2245 sha256 = "0bdpcnd9pv0131dl08h4zbcwmgc45lyvq3pa224xwan5b3x4rr2f";
2246 };
2246 };
2247 meta = {
2247 meta = {
2248 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
2248 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
2249 };
2249 };
2250 };
2250 };
2251 "tzlocal" = super.buildPythonPackage {
2251 "tzlocal" = super.buildPythonPackage {
2252 name = "tzlocal-1.5.1";
2252 name = "tzlocal-1.5.1";
2253 doCheck = false;
2253 doCheck = false;
2254 propagatedBuildInputs = [
2254 propagatedBuildInputs = [
2255 self."pytz"
2255 self."pytz"
2256 ];
2256 ];
2257 src = fetchurl {
2257 src = fetchurl {
2258 url = "https://files.pythonhosted.org/packages/cb/89/e3687d3ed99bc882793f82634e9824e62499fdfdc4b1ae39e211c5b05017/tzlocal-1.5.1.tar.gz";
2258 url = "https://files.pythonhosted.org/packages/cb/89/e3687d3ed99bc882793f82634e9824e62499fdfdc4b1ae39e211c5b05017/tzlocal-1.5.1.tar.gz";
2259 sha256 = "0kiciwiqx0bv0fbc913idxibc4ygg4cb7f8rcpd9ij2shi4bigjf";
2259 sha256 = "0kiciwiqx0bv0fbc913idxibc4ygg4cb7f8rcpd9ij2shi4bigjf";
2260 };
2260 };
2261 meta = {
2261 meta = {
2262 license = [ pkgs.lib.licenses.mit ];
2262 license = [ pkgs.lib.licenses.mit ];
2263 };
2263 };
2264 };
2264 };
2265 "urllib3" = super.buildPythonPackage {
2265 "urllib3" = super.buildPythonPackage {
2266 name = "urllib3-1.25.2";
2266 name = "urllib3-1.25.2";
2267 doCheck = false;
2267 doCheck = false;
2268 src = fetchurl {
2268 src = fetchurl {
2269 url = "https://files.pythonhosted.org/packages/9a/8b/ea6d2beb2da6e331e9857d0a60b79ed4f72dcbc4e2c7f2d2521b0480fda2/urllib3-1.25.2.tar.gz";
2269 url = "https://files.pythonhosted.org/packages/9a/8b/ea6d2beb2da6e331e9857d0a60b79ed4f72dcbc4e2c7f2d2521b0480fda2/urllib3-1.25.2.tar.gz";
2270 sha256 = "1nq2k4pss1ihsjh02r41sqpjpm5rfqkjfysyq7g7n2i1p7c66c55";
2270 sha256 = "1nq2k4pss1ihsjh02r41sqpjpm5rfqkjfysyq7g7n2i1p7c66c55";
2271 };
2271 };
2272 meta = {
2272 meta = {
2273 license = [ pkgs.lib.licenses.mit ];
2273 license = [ pkgs.lib.licenses.mit ];
2274 };
2274 };
2275 };
2275 };
2276 "urlobject" = super.buildPythonPackage {
2276 "urlobject" = super.buildPythonPackage {
2277 name = "urlobject-2.4.3";
2277 name = "urlobject-2.4.3";
2278 doCheck = false;
2278 doCheck = false;
2279 src = fetchurl {
2279 src = fetchurl {
2280 url = "https://files.pythonhosted.org/packages/e2/b8/1d0a916f4b34c4618846e6da0e4eeaa8fcb4a2f39e006434fe38acb74b34/URLObject-2.4.3.tar.gz";
2280 url = "https://files.pythonhosted.org/packages/e2/b8/1d0a916f4b34c4618846e6da0e4eeaa8fcb4a2f39e006434fe38acb74b34/URLObject-2.4.3.tar.gz";
2281 sha256 = "1ahc8ficzfvr2avln71immfh4ls0zyv6cdaa5xmkdj5rd87f5cj7";
2281 sha256 = "1ahc8ficzfvr2avln71immfh4ls0zyv6cdaa5xmkdj5rd87f5cj7";
2282 };
2282 };
2283 meta = {
2283 meta = {
2284 license = [ pkgs.lib.licenses.publicDomain ];
2284 license = [ pkgs.lib.licenses.publicDomain ];
2285 };
2285 };
2286 };
2286 };
2287 "venusian" = super.buildPythonPackage {
2287 "venusian" = super.buildPythonPackage {
2288 name = "venusian-1.2.0";
2288 name = "venusian-1.2.0";
2289 doCheck = false;
2289 doCheck = false;
2290 src = fetchurl {
2290 src = fetchurl {
2291 url = "https://files.pythonhosted.org/packages/7e/6f/40a9d43ac77cb51cb62be5b5662d170f43f8037bdc4eab56336c4ca92bb7/venusian-1.2.0.tar.gz";
2291 url = "https://files.pythonhosted.org/packages/7e/6f/40a9d43ac77cb51cb62be5b5662d170f43f8037bdc4eab56336c4ca92bb7/venusian-1.2.0.tar.gz";
2292 sha256 = "0ghyx66g8ikx9nx1mnwqvdcqm11i1vlq0hnvwl50s48bp22q5v34";
2292 sha256 = "0ghyx66g8ikx9nx1mnwqvdcqm11i1vlq0hnvwl50s48bp22q5v34";
2293 };
2293 };
2294 meta = {
2294 meta = {
2295 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
2295 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
2296 };
2296 };
2297 };
2297 };
2298 "vine" = super.buildPythonPackage {
2298 "vine" = super.buildPythonPackage {
2299 name = "vine-1.3.0";
2299 name = "vine-1.3.0";
2300 doCheck = false;
2300 doCheck = false;
2301 src = fetchurl {
2301 src = fetchurl {
2302 url = "https://files.pythonhosted.org/packages/1c/e1/79fb8046e607dd6c2ad05c9b8ebac9d0bd31d086a08f02699e96fc5b3046/vine-1.3.0.tar.gz";
2302 url = "https://files.pythonhosted.org/packages/1c/e1/79fb8046e607dd6c2ad05c9b8ebac9d0bd31d086a08f02699e96fc5b3046/vine-1.3.0.tar.gz";
2303 sha256 = "11ydsbhl1vabndc2r979dv61s6j2b0giq6dgvryifvq1m7bycghk";
2303 sha256 = "11ydsbhl1vabndc2r979dv61s6j2b0giq6dgvryifvq1m7bycghk";
2304 };
2304 };
2305 meta = {
2305 meta = {
2306 license = [ pkgs.lib.licenses.bsdOriginal ];
2306 license = [ pkgs.lib.licenses.bsdOriginal ];
2307 };
2307 };
2308 };
2308 };
2309 "waitress" = super.buildPythonPackage {
2309 "waitress" = super.buildPythonPackage {
2310 name = "waitress-1.3.1";
2310 name = "waitress-1.3.1";
2311 doCheck = false;
2311 doCheck = false;
2312 src = fetchurl {
2312 src = fetchurl {
2313 url = "https://files.pythonhosted.org/packages/a6/e6/708da7bba65898e5d759ade8391b1077e49d07be0b0223c39f5be04def56/waitress-1.3.1.tar.gz";
2313 url = "https://files.pythonhosted.org/packages/a6/e6/708da7bba65898e5d759ade8391b1077e49d07be0b0223c39f5be04def56/waitress-1.3.1.tar.gz";
2314 sha256 = "1iysl8ka3l4cdrr0r19fh1cv28q41mwpvgsb81ji7k4shkb0k3i7";
2314 sha256 = "1iysl8ka3l4cdrr0r19fh1cv28q41mwpvgsb81ji7k4shkb0k3i7";
2315 };
2315 };
2316 meta = {
2316 meta = {
2317 license = [ pkgs.lib.licenses.zpl21 ];
2317 license = [ pkgs.lib.licenses.zpl21 ];
2318 };
2318 };
2319 };
2319 };
2320 "wcwidth" = super.buildPythonPackage {
2320 "wcwidth" = super.buildPythonPackage {
2321 name = "wcwidth-0.1.9";
2321 name = "wcwidth-0.1.9";
2322 doCheck = false;
2322 doCheck = false;
2323 src = fetchurl {
2323 src = fetchurl {
2324 url = "https://files.pythonhosted.org/packages/25/9d/0acbed6e4a4be4fc99148f275488580968f44ddb5e69b8ceb53fc9df55a0/wcwidth-0.1.9.tar.gz";
2324 url = "https://files.pythonhosted.org/packages/25/9d/0acbed6e4a4be4fc99148f275488580968f44ddb5e69b8ceb53fc9df55a0/wcwidth-0.1.9.tar.gz";
2325 sha256 = "1wf5ycjx8s066rdvr0fgz4xds9a8zhs91c4jzxvvymm1c8l8cwzf";
2325 sha256 = "1wf5ycjx8s066rdvr0fgz4xds9a8zhs91c4jzxvvymm1c8l8cwzf";
2326 };
2326 };
2327 meta = {
2327 meta = {
2328 license = [ pkgs.lib.licenses.mit ];
2328 license = [ pkgs.lib.licenses.mit ];
2329 };
2329 };
2330 };
2330 };
2331 "webencodings" = super.buildPythonPackage {
2331 "webencodings" = super.buildPythonPackage {
2332 name = "webencodings-0.5.1";
2332 name = "webencodings-0.5.1";
2333 doCheck = false;
2333 doCheck = false;
2334 src = fetchurl {
2334 src = fetchurl {
2335 url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz";
2335 url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz";
2336 sha256 = "08qrgrc4hrximb2gqnl69g01s93rhf2842jfxdjljc1dbwj1qsmk";
2336 sha256 = "08qrgrc4hrximb2gqnl69g01s93rhf2842jfxdjljc1dbwj1qsmk";
2337 };
2337 };
2338 meta = {
2338 meta = {
2339 license = [ pkgs.lib.licenses.bsdOriginal ];
2339 license = [ pkgs.lib.licenses.bsdOriginal ];
2340 };
2340 };
2341 };
2341 };
2342 "weberror" = super.buildPythonPackage {
2342 "weberror" = super.buildPythonPackage {
2343 name = "weberror-0.13.1";
2343 name = "weberror-0.13.1";
2344 doCheck = false;
2344 doCheck = false;
2345 propagatedBuildInputs = [
2345 propagatedBuildInputs = [
2346 self."webob"
2346 self."webob"
2347 self."tempita"
2347 self."tempita"
2348 self."pygments"
2348 self."pygments"
2349 self."paste"
2349 self."paste"
2350 ];
2350 ];
2351 src = fetchurl {
2351 src = fetchurl {
2352 url = "https://files.pythonhosted.org/packages/07/0a/09ca5eb0fab5c0d17b380026babe81c96ecebb13f2b06c3203432dd7be72/WebError-0.13.1.tar.gz";
2352 url = "https://files.pythonhosted.org/packages/07/0a/09ca5eb0fab5c0d17b380026babe81c96ecebb13f2b06c3203432dd7be72/WebError-0.13.1.tar.gz";
2353 sha256 = "0r4qvnf2r92gfnpa1kwygh4j2x6j3axg2i4an6hyxwg2gpaqp7y1";
2353 sha256 = "0r4qvnf2r92gfnpa1kwygh4j2x6j3axg2i4an6hyxwg2gpaqp7y1";
2354 };
2354 };
2355 meta = {
2355 meta = {
2356 license = [ pkgs.lib.licenses.mit ];
2356 license = [ pkgs.lib.licenses.mit ];
2357 };
2357 };
2358 };
2358 };
2359 "webhelpers2" = super.buildPythonPackage {
2359 "webhelpers2" = super.buildPythonPackage {
2360 name = "webhelpers2-2.0";
2360 name = "webhelpers2-2.0";
2361 doCheck = false;
2361 doCheck = false;
2362 propagatedBuildInputs = [
2362 propagatedBuildInputs = [
2363 self."markupsafe"
2363 self."markupsafe"
2364 self."six"
2364 self."six"
2365 ];
2365 ];
2366 src = fetchurl {
2366 src = fetchurl {
2367 url = "https://files.pythonhosted.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
2367 url = "https://files.pythonhosted.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
2368 sha256 = "0aphva1qmxh83n01p53f5fd43m4srzbnfbz5ajvbx9aj2aipwmcs";
2368 sha256 = "0aphva1qmxh83n01p53f5fd43m4srzbnfbz5ajvbx9aj2aipwmcs";
2369 };
2369 };
2370 meta = {
2370 meta = {
2371 license = [ pkgs.lib.licenses.mit ];
2371 license = [ pkgs.lib.licenses.mit ];
2372 };
2372 };
2373 };
2373 };
2374 "webob" = super.buildPythonPackage {
2374 "webob" = super.buildPythonPackage {
2375 name = "webob-1.8.5";
2375 name = "webob-1.8.5";
2376 doCheck = false;
2376 doCheck = false;
2377 src = fetchurl {
2377 src = fetchurl {
2378 url = "https://files.pythonhosted.org/packages/9d/1a/0c89c070ee2829c934cb6c7082287c822e28236a4fcf90063e6be7c35532/WebOb-1.8.5.tar.gz";
2378 url = "https://files.pythonhosted.org/packages/9d/1a/0c89c070ee2829c934cb6c7082287c822e28236a4fcf90063e6be7c35532/WebOb-1.8.5.tar.gz";
2379 sha256 = "11khpzaxc88q31v25ic330gsf56fwmbdc9b30br8mvp0fmwspah5";
2379 sha256 = "11khpzaxc88q31v25ic330gsf56fwmbdc9b30br8mvp0fmwspah5";
2380 };
2380 };
2381 meta = {
2381 meta = {
2382 license = [ pkgs.lib.licenses.mit ];
2382 license = [ pkgs.lib.licenses.mit ];
2383 };
2383 };
2384 };
2384 };
2385 "webtest" = super.buildPythonPackage {
2385 "webtest" = super.buildPythonPackage {
2386 name = "webtest-2.0.34";
2386 name = "webtest-2.0.34";
2387 doCheck = false;
2387 doCheck = false;
2388 propagatedBuildInputs = [
2388 propagatedBuildInputs = [
2389 self."six"
2389 self."six"
2390 self."webob"
2390 self."webob"
2391 self."waitress"
2391 self."waitress"
2392 self."beautifulsoup4"
2392 self."beautifulsoup4"
2393 ];
2393 ];
2394 src = fetchurl {
2394 src = fetchurl {
2395 url = "https://files.pythonhosted.org/packages/2c/74/a0e63feee438735d628631e2b70d82280276a930637ac535479e5fad9427/WebTest-2.0.34.tar.gz";
2395 url = "https://files.pythonhosted.org/packages/2c/74/a0e63feee438735d628631e2b70d82280276a930637ac535479e5fad9427/WebTest-2.0.34.tar.gz";
2396 sha256 = "0x1y2c8z4fmpsny4hbp6ka37si2g10r5r2jwxhvv5mx7g3blq4bi";
2396 sha256 = "0x1y2c8z4fmpsny4hbp6ka37si2g10r5r2jwxhvv5mx7g3blq4bi";
2397 };
2397 };
2398 meta = {
2398 meta = {
2399 license = [ pkgs.lib.licenses.mit ];
2399 license = [ pkgs.lib.licenses.mit ];
2400 };
2400 };
2401 };
2401 };
2402 "whoosh" = super.buildPythonPackage {
2402 "whoosh" = super.buildPythonPackage {
2403 name = "whoosh-2.7.4";
2403 name = "whoosh-2.7.4";
2404 doCheck = false;
2404 doCheck = false;
2405 src = fetchurl {
2405 src = fetchurl {
2406 url = "https://files.pythonhosted.org/packages/25/2b/6beed2107b148edc1321da0d489afc4617b9ed317ef7b72d4993cad9b684/Whoosh-2.7.4.tar.gz";
2406 url = "https://files.pythonhosted.org/packages/25/2b/6beed2107b148edc1321da0d489afc4617b9ed317ef7b72d4993cad9b684/Whoosh-2.7.4.tar.gz";
2407 sha256 = "10qsqdjpbc85fykc1vgcs8xwbgn4l2l52c8d83xf1q59pwyn79bw";
2407 sha256 = "10qsqdjpbc85fykc1vgcs8xwbgn4l2l52c8d83xf1q59pwyn79bw";
2408 };
2408 };
2409 meta = {
2409 meta = {
2410 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
2410 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
2411 };
2411 };
2412 };
2412 };
2413 "ws4py" = super.buildPythonPackage {
2413 "ws4py" = super.buildPythonPackage {
2414 name = "ws4py-0.5.1";
2414 name = "ws4py-0.5.1";
2415 doCheck = false;
2415 doCheck = false;
2416 src = fetchurl {
2416 src = fetchurl {
2417 url = "https://files.pythonhosted.org/packages/53/20/4019a739b2eefe9282d3822ef6a225250af964b117356971bd55e274193c/ws4py-0.5.1.tar.gz";
2417 url = "https://files.pythonhosted.org/packages/53/20/4019a739b2eefe9282d3822ef6a225250af964b117356971bd55e274193c/ws4py-0.5.1.tar.gz";
2418 sha256 = "10slbbf2jm4hpr92jx7kh7mhf48sjl01v2w4d8z3f1p0ybbp7l19";
2418 sha256 = "10slbbf2jm4hpr92jx7kh7mhf48sjl01v2w4d8z3f1p0ybbp7l19";
2419 };
2419 };
2420 meta = {
2420 meta = {
2421 license = [ pkgs.lib.licenses.bsdOriginal ];
2421 license = [ pkgs.lib.licenses.bsdOriginal ];
2422 };
2422 };
2423 };
2423 };
2424 "wsgiref" = super.buildPythonPackage {
2424 "wsgiref" = super.buildPythonPackage {
2425 name = "wsgiref-0.1.2";
2425 name = "wsgiref-0.1.2";
2426 doCheck = false;
2426 doCheck = false;
2427 src = fetchurl {
2427 src = fetchurl {
2428 url = "https://files.pythonhosted.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
2428 url = "https://files.pythonhosted.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
2429 sha256 = "0y8fyjmpq7vwwm4x732w97qbkw78rjwal5409k04cw4m03411rn7";
2429 sha256 = "0y8fyjmpq7vwwm4x732w97qbkw78rjwal5409k04cw4m03411rn7";
2430 };
2430 };
2431 meta = {
2431 meta = {
2432 license = [ { fullName = "PSF or ZPL"; } ];
2432 license = [ { fullName = "PSF or ZPL"; } ];
2433 };
2433 };
2434 };
2434 };
2435 "zipp" = super.buildPythonPackage {
2435 "zipp" = super.buildPythonPackage {
2436 name = "zipp-1.2.0";
2436 name = "zipp-1.2.0";
2437 doCheck = false;
2437 doCheck = false;
2438 propagatedBuildInputs = [
2438 propagatedBuildInputs = [
2439 self."contextlib2"
2439 self."contextlib2"
2440 ];
2440 ];
2441 src = fetchurl {
2441 src = fetchurl {
2442 url = "https://files.pythonhosted.org/packages/78/08/d52f0ea643bc1068d6dc98b412f4966a9b63255d20911a23ac3220c033c4/zipp-1.2.0.tar.gz";
2442 url = "https://files.pythonhosted.org/packages/78/08/d52f0ea643bc1068d6dc98b412f4966a9b63255d20911a23ac3220c033c4/zipp-1.2.0.tar.gz";
2443 sha256 = "1c91lnv1bxjimh8as27hz7bghsjkkbxn1d37xq7in9c82iai0167";
2443 sha256 = "1c91lnv1bxjimh8as27hz7bghsjkkbxn1d37xq7in9c82iai0167";
2444 };
2444 };
2445 meta = {
2445 meta = {
2446 license = [ pkgs.lib.licenses.mit ];
2446 license = [ pkgs.lib.licenses.mit ];
2447 };
2447 };
2448 };
2448 };
2449 "zope.cachedescriptors" = super.buildPythonPackage {
2449 "zope.cachedescriptors" = super.buildPythonPackage {
2450 name = "zope.cachedescriptors-4.3.1";
2450 name = "zope.cachedescriptors-4.3.1";
2451 doCheck = false;
2451 doCheck = false;
2452 propagatedBuildInputs = [
2452 propagatedBuildInputs = [
2453 self."setuptools"
2453 self."setuptools"
2454 ];
2454 ];
2455 src = fetchurl {
2455 src = fetchurl {
2456 url = "https://files.pythonhosted.org/packages/2f/89/ebe1890cc6d3291ebc935558fa764d5fffe571018dbbee200e9db78762cb/zope.cachedescriptors-4.3.1.tar.gz";
2456 url = "https://files.pythonhosted.org/packages/2f/89/ebe1890cc6d3291ebc935558fa764d5fffe571018dbbee200e9db78762cb/zope.cachedescriptors-4.3.1.tar.gz";
2457 sha256 = "0jhr3m5p74c6r7k8iv0005b8bfsialih9d7zl5vx38rf5xq1lk8z";
2457 sha256 = "0jhr3m5p74c6r7k8iv0005b8bfsialih9d7zl5vx38rf5xq1lk8z";
2458 };
2458 };
2459 meta = {
2459 meta = {
2460 license = [ pkgs.lib.licenses.zpl21 ];
2460 license = [ pkgs.lib.licenses.zpl21 ];
2461 };
2461 };
2462 };
2462 };
2463 "zope.deprecation" = super.buildPythonPackage {
2463 "zope.deprecation" = super.buildPythonPackage {
2464 name = "zope.deprecation-4.4.0";
2464 name = "zope.deprecation-4.4.0";
2465 doCheck = false;
2465 doCheck = false;
2466 propagatedBuildInputs = [
2466 propagatedBuildInputs = [
2467 self."setuptools"
2467 self."setuptools"
2468 ];
2468 ];
2469 src = fetchurl {
2469 src = fetchurl {
2470 url = "https://files.pythonhosted.org/packages/34/da/46e92d32d545dd067b9436279d84c339e8b16de2ca393d7b892bc1e1e9fd/zope.deprecation-4.4.0.tar.gz";
2470 url = "https://files.pythonhosted.org/packages/34/da/46e92d32d545dd067b9436279d84c339e8b16de2ca393d7b892bc1e1e9fd/zope.deprecation-4.4.0.tar.gz";
2471 sha256 = "1pz2cv7gv9y1r3m0bdv7ks1alagmrn5msm5spwdzkb2by0w36i8d";
2471 sha256 = "1pz2cv7gv9y1r3m0bdv7ks1alagmrn5msm5spwdzkb2by0w36i8d";
2472 };
2472 };
2473 meta = {
2473 meta = {
2474 license = [ pkgs.lib.licenses.zpl21 ];
2474 license = [ pkgs.lib.licenses.zpl21 ];
2475 };
2475 };
2476 };
2476 };
2477 "zope.event" = super.buildPythonPackage {
2477 "zope.event" = super.buildPythonPackage {
2478 name = "zope.event-4.4";
2478 name = "zope.event-4.4";
2479 doCheck = false;
2479 doCheck = false;
2480 propagatedBuildInputs = [
2480 propagatedBuildInputs = [
2481 self."setuptools"
2481 self."setuptools"
2482 ];
2482 ];
2483 src = fetchurl {
2483 src = fetchurl {
2484 url = "https://files.pythonhosted.org/packages/4c/b2/51c0369adcf5be2334280eed230192ab3b03f81f8efda9ddea6f65cc7b32/zope.event-4.4.tar.gz";
2484 url = "https://files.pythonhosted.org/packages/4c/b2/51c0369adcf5be2334280eed230192ab3b03f81f8efda9ddea6f65cc7b32/zope.event-4.4.tar.gz";
2485 sha256 = "1ksbc726av9xacml6jhcfyn828hlhb9xlddpx6fcvnlvmpmpvhk9";
2485 sha256 = "1ksbc726av9xacml6jhcfyn828hlhb9xlddpx6fcvnlvmpmpvhk9";
2486 };
2486 };
2487 meta = {
2487 meta = {
2488 license = [ pkgs.lib.licenses.zpl21 ];
2488 license = [ pkgs.lib.licenses.zpl21 ];
2489 };
2489 };
2490 };
2490 };
2491 "zope.interface" = super.buildPythonPackage {
2491 "zope.interface" = super.buildPythonPackage {
2492 name = "zope.interface-4.6.0";
2492 name = "zope.interface-4.6.0";
2493 doCheck = false;
2493 doCheck = false;
2494 propagatedBuildInputs = [
2494 propagatedBuildInputs = [
2495 self."setuptools"
2495 self."setuptools"
2496 ];
2496 ];
2497 src = fetchurl {
2497 src = fetchurl {
2498 url = "https://files.pythonhosted.org/packages/4e/d0/c9d16bd5b38de44a20c6dc5d5ed80a49626fafcb3db9f9efdc2a19026db6/zope.interface-4.6.0.tar.gz";
2498 url = "https://files.pythonhosted.org/packages/4e/d0/c9d16bd5b38de44a20c6dc5d5ed80a49626fafcb3db9f9efdc2a19026db6/zope.interface-4.6.0.tar.gz";
2499 sha256 = "1rgh2x3rcl9r0v0499kf78xy86rnmanajf4ywmqb943wpk50sg8v";
2499 sha256 = "1rgh2x3rcl9r0v0499kf78xy86rnmanajf4ywmqb943wpk50sg8v";
2500 };
2500 };
2501 meta = {
2501 meta = {
2502 license = [ pkgs.lib.licenses.zpl21 ];
2502 license = [ pkgs.lib.licenses.zpl21 ];
2503 };
2503 };
2504 };
2504 };
2505
2505
2506 ### Test requirements
2506 ### Test requirements
2507
2507
2508
2508
2509 }
2509 }
@@ -1,555 +1,564 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2020 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import itertools
21 import itertools
22 import logging
22 import logging
23 import sys
23 import sys
24 import types
24 import types
25 import fnmatch
25 import fnmatch
26
26
27 import decorator
27 import decorator
28 import venusian
28 import venusian
29 from collections import OrderedDict
29 from collections import OrderedDict
30
30
31 from pyramid.exceptions import ConfigurationError
31 from pyramid.exceptions import ConfigurationError
32 from pyramid.renderers import render
32 from pyramid.renderers import render
33 from pyramid.response import Response
33 from pyramid.response import Response
34 from pyramid.httpexceptions import HTTPNotFound
34 from pyramid.httpexceptions import HTTPNotFound
35
35
36 from rhodecode.api.exc import (
36 from rhodecode.api.exc import (
37 JSONRPCBaseError, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
37 JSONRPCBaseError, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
38 from rhodecode.apps._base import TemplateArgs
38 from rhodecode.apps._base import TemplateArgs
39 from rhodecode.lib.auth import AuthUser
39 from rhodecode.lib.auth import AuthUser
40 from rhodecode.lib.base import get_ip_addr, attach_context_attributes
40 from rhodecode.lib.base import get_ip_addr, attach_context_attributes
41 from rhodecode.lib.exc_tracking import store_exception
41 from rhodecode.lib.exc_tracking import store_exception
42 from rhodecode.lib.ext_json import json
42 from rhodecode.lib.ext_json import json
43 from rhodecode.lib.utils2 import safe_str
43 from rhodecode.lib.utils2 import safe_str
44 from rhodecode.lib.plugins.utils import get_plugin_settings
44 from rhodecode.lib.plugins.utils import get_plugin_settings
45 from rhodecode.model.db import User, UserApiKeys
45 from rhodecode.model.db import User, UserApiKeys
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49 DEFAULT_RENDERER = 'jsonrpc_renderer'
49 DEFAULT_RENDERER = 'jsonrpc_renderer'
50 DEFAULT_URL = '/_admin/apiv2'
50 DEFAULT_URL = '/_admin/apiv2'
51
51
52
52
53 def find_methods(jsonrpc_methods, pattern):
53 def find_methods(jsonrpc_methods, pattern):
54 matches = OrderedDict()
54 matches = OrderedDict()
55 if not isinstance(pattern, (list, tuple)):
55 if not isinstance(pattern, (list, tuple)):
56 pattern = [pattern]
56 pattern = [pattern]
57
57
58 for single_pattern in pattern:
58 for single_pattern in pattern:
59 for method_name, method in jsonrpc_methods.items():
59 for method_name, method in jsonrpc_methods.items():
60 if fnmatch.fnmatch(method_name, single_pattern):
60 if fnmatch.fnmatch(method_name, single_pattern):
61 matches[method_name] = method
61 matches[method_name] = method
62 return matches
62 return matches
63
63
64
64
65 class ExtJsonRenderer(object):
65 class ExtJsonRenderer(object):
66 """
66 """
67 Custom renderer that mkaes use of our ext_json lib
67 Custom renderer that mkaes use of our ext_json lib
68
68
69 """
69 """
70
70
71 def __init__(self, serializer=json.dumps, **kw):
71 def __init__(self, serializer=json.dumps, **kw):
72 """ Any keyword arguments will be passed to the ``serializer``
72 """ Any keyword arguments will be passed to the ``serializer``
73 function."""
73 function."""
74 self.serializer = serializer
74 self.serializer = serializer
75 self.kw = kw
75 self.kw = kw
76
76
77 def __call__(self, info):
77 def __call__(self, info):
78 """ Returns a plain JSON-encoded string with content-type
78 """ Returns a plain JSON-encoded string with content-type
79 ``application/json``. The content-type may be overridden by
79 ``application/json``. The content-type may be overridden by
80 setting ``request.response.content_type``."""
80 setting ``request.response.content_type``."""
81
81
82 def _render(value, system):
82 def _render(value, system):
83 request = system.get('request')
83 request = system.get('request')
84 if request is not None:
84 if request is not None:
85 response = request.response
85 response = request.response
86 ct = response.content_type
86 ct = response.content_type
87 if ct == response.default_content_type:
87 if ct == response.default_content_type:
88 response.content_type = 'application/json'
88 response.content_type = 'application/json'
89
89
90 return self.serializer(value, **self.kw)
90 return self.serializer(value, **self.kw)
91
91
92 return _render
92 return _render
93
93
94
94
95 def jsonrpc_response(request, result):
95 def jsonrpc_response(request, result):
96 rpc_id = getattr(request, 'rpc_id', None)
96 rpc_id = getattr(request, 'rpc_id', None)
97 response = request.response
97 response = request.response
98
98
99 # store content_type before render is called
99 # store content_type before render is called
100 ct = response.content_type
100 ct = response.content_type
101
101
102 ret_value = ''
102 ret_value = ''
103 if rpc_id:
103 if rpc_id:
104 ret_value = {
104 ret_value = {
105 'id': rpc_id,
105 'id': rpc_id,
106 'result': result,
106 'result': result,
107 'error': None,
107 'error': None,
108 }
108 }
109
109
110 # fetch deprecation warnings, and store it inside results
110 # fetch deprecation warnings, and store it inside results
111 deprecation = getattr(request, 'rpc_deprecation', None)
111 deprecation = getattr(request, 'rpc_deprecation', None)
112 if deprecation:
112 if deprecation:
113 ret_value['DEPRECATION_WARNING'] = deprecation
113 ret_value['DEPRECATION_WARNING'] = deprecation
114
114
115 raw_body = render(DEFAULT_RENDERER, ret_value, request=request)
115 raw_body = render(DEFAULT_RENDERER, ret_value, request=request)
116 response.body = safe_str(raw_body, response.charset)
116 response.body = safe_str(raw_body, response.charset)
117
117
118 if ct == response.default_content_type:
118 if ct == response.default_content_type:
119 response.content_type = 'application/json'
119 response.content_type = 'application/json'
120
120
121 return response
121 return response
122
122
123
123
124 def jsonrpc_error(request, message, retid=None, code=None, headers=None):
124 def jsonrpc_error(request, message, retid=None, code=None, headers=None):
125 """
125 """
126 Generate a Response object with a JSON-RPC error body
126 Generate a Response object with a JSON-RPC error body
127
127
128 :param code:
128 :param code:
129 :param retid:
129 :param retid:
130 :param message:
130 :param message:
131 """
131 """
132 err_dict = {'id': retid, 'result': None, 'error': message}
132 err_dict = {'id': retid, 'result': None, 'error': message}
133 body = render(DEFAULT_RENDERER, err_dict, request=request).encode('utf-8')
133 body = render(DEFAULT_RENDERER, err_dict, request=request).encode('utf-8')
134
134
135 return Response(
135 return Response(
136 body=body,
136 body=body,
137 status=code,
137 status=code,
138 content_type='application/json',
138 content_type='application/json',
139 headerlist=headers
139 headerlist=headers
140 )
140 )
141
141
142
142
143 def exception_view(exc, request):
143 def exception_view(exc, request):
144 rpc_id = getattr(request, 'rpc_id', None)
144 rpc_id = getattr(request, 'rpc_id', None)
145
145
146 if isinstance(exc, JSONRPCError):
146 if isinstance(exc, JSONRPCError):
147 fault_message = safe_str(exc.message)
147 fault_message = safe_str(exc.message)
148 log.debug('json-rpc error rpc_id:%s "%s"', rpc_id, fault_message)
148 log.debug('json-rpc error rpc_id:%s "%s"', rpc_id, fault_message)
149 elif isinstance(exc, JSONRPCValidationError):
149 elif isinstance(exc, JSONRPCValidationError):
150 colander_exc = exc.colander_exception
150 colander_exc = exc.colander_exception
151 # TODO(marcink): think maybe of nicer way to serialize errors ?
151 # TODO(marcink): think maybe of nicer way to serialize errors ?
152 fault_message = colander_exc.asdict()
152 fault_message = colander_exc.asdict()
153 log.debug('json-rpc colander error rpc_id:%s "%s"', rpc_id, fault_message)
153 log.debug('json-rpc colander error rpc_id:%s "%s"', rpc_id, fault_message)
154 elif isinstance(exc, JSONRPCForbidden):
154 elif isinstance(exc, JSONRPCForbidden):
155 fault_message = 'Access was denied to this resource.'
155 fault_message = 'Access was denied to this resource.'
156 log.warning('json-rpc forbidden call rpc_id:%s "%s"', rpc_id, fault_message)
156 log.warning('json-rpc forbidden call rpc_id:%s "%s"', rpc_id, fault_message)
157 elif isinstance(exc, HTTPNotFound):
157 elif isinstance(exc, HTTPNotFound):
158 method = request.rpc_method
158 method = request.rpc_method
159 log.debug('json-rpc method `%s` not found in list of '
159 log.debug('json-rpc method `%s` not found in list of '
160 'api calls: %s, rpc_id:%s',
160 'api calls: %s, rpc_id:%s',
161 method, request.registry.jsonrpc_methods.keys(), rpc_id)
161 method, request.registry.jsonrpc_methods.keys(), rpc_id)
162
162
163 similar = 'none'
163 similar = 'none'
164 try:
164 try:
165 similar_paterns = ['*{}*'.format(x) for x in method.split('_')]
165 similar_paterns = ['*{}*'.format(x) for x in method.split('_')]
166 similar_found = find_methods(
166 similar_found = find_methods(
167 request.registry.jsonrpc_methods, similar_paterns)
167 request.registry.jsonrpc_methods, similar_paterns)
168 similar = ', '.join(similar_found.keys()) or similar
168 similar = ', '.join(similar_found.keys()) or similar
169 except Exception:
169 except Exception:
170 # make the whole above block safe
170 # make the whole above block safe
171 pass
171 pass
172
172
173 fault_message = "No such method: {}. Similar methods: {}".format(
173 fault_message = "No such method: {}. Similar methods: {}".format(
174 method, similar)
174 method, similar)
175 else:
175 else:
176 fault_message = 'undefined error'
176 fault_message = 'undefined error'
177 exc_info = exc.exc_info()
177 exc_info = exc.exc_info()
178 store_exception(id(exc_info), exc_info, prefix='rhodecode-api')
178 store_exception(id(exc_info), exc_info, prefix='rhodecode-api')
179
179
180 return jsonrpc_error(request, fault_message, rpc_id)
180 return jsonrpc_error(request, fault_message, rpc_id)
181
181
182
182
183 def request_view(request):
183 def request_view(request):
184 """
184 """
185 Main request handling method. It handles all logic to call a specific
185 Main request handling method. It handles all logic to call a specific
186 exposed method
186 exposed method
187 """
187 """
188 # cython compatible inspect
188 # cython compatible inspect
189 from rhodecode.config.patches import inspect_getargspec
189 from rhodecode.config.patches import inspect_getargspec
190 inspect = inspect_getargspec()
190 inspect = inspect_getargspec()
191
191
192 # check if we can find this session using api_key, get_by_auth_token
192 # check if we can find this session using api_key, get_by_auth_token
193 # search not expired tokens only
193 # search not expired tokens only
194 try:
194 try:
195 api_user = User.get_by_auth_token(request.rpc_api_key)
195 api_user = User.get_by_auth_token(request.rpc_api_key)
196
196
197 if api_user is None:
197 if api_user is None:
198 return jsonrpc_error(
198 return jsonrpc_error(
199 request, retid=request.rpc_id, message='Invalid API KEY')
199 request, retid=request.rpc_id, message='Invalid API KEY')
200
200
201 if not api_user.active:
201 if not api_user.active:
202 return jsonrpc_error(
202 return jsonrpc_error(
203 request, retid=request.rpc_id,
203 request, retid=request.rpc_id,
204 message='Request from this user not allowed')
204 message='Request from this user not allowed')
205
205
206 # check if we are allowed to use this IP
206 # check if we are allowed to use this IP
207 auth_u = AuthUser(
207 auth_u = AuthUser(
208 api_user.user_id, request.rpc_api_key, ip_addr=request.rpc_ip_addr)
208 api_user.user_id, request.rpc_api_key, ip_addr=request.rpc_ip_addr)
209 if not auth_u.ip_allowed:
209 if not auth_u.ip_allowed:
210 return jsonrpc_error(
210 return jsonrpc_error(
211 request, retid=request.rpc_id,
211 request, retid=request.rpc_id,
212 message='Request from IP:%s not allowed' % (
212 message='Request from IP:%s not allowed' % (
213 request.rpc_ip_addr,))
213 request.rpc_ip_addr,))
214 else:
214 else:
215 log.info('Access for IP:%s allowed', request.rpc_ip_addr)
215 log.info('Access for IP:%s allowed', request.rpc_ip_addr)
216
216
217 # register our auth-user
217 # register our auth-user
218 request.rpc_user = auth_u
218 request.rpc_user = auth_u
219 request.environ['rc_auth_user_id'] = auth_u.user_id
219 request.environ['rc_auth_user_id'] = auth_u.user_id
220
220
221 # now check if token is valid for API
221 # now check if token is valid for API
222 auth_token = request.rpc_api_key
222 auth_token = request.rpc_api_key
223 token_match = api_user.authenticate_by_token(
223 token_match = api_user.authenticate_by_token(
224 auth_token, roles=[UserApiKeys.ROLE_API])
224 auth_token, roles=[UserApiKeys.ROLE_API])
225 invalid_token = not token_match
225 invalid_token = not token_match
226
226
227 log.debug('Checking if API KEY is valid with proper role')
227 log.debug('Checking if API KEY is valid with proper role')
228 if invalid_token:
228 if invalid_token:
229 return jsonrpc_error(
229 return jsonrpc_error(
230 request, retid=request.rpc_id,
230 request, retid=request.rpc_id,
231 message='API KEY invalid or, has bad role for an API call')
231 message='API KEY invalid or, has bad role for an API call')
232
232
233 except Exception:
233 except Exception:
234 log.exception('Error on API AUTH')
234 log.exception('Error on API AUTH')
235 return jsonrpc_error(
235 return jsonrpc_error(
236 request, retid=request.rpc_id, message='Invalid API KEY')
236 request, retid=request.rpc_id, message='Invalid API KEY')
237
237
238 method = request.rpc_method
238 method = request.rpc_method
239 func = request.registry.jsonrpc_methods[method]
239 func = request.registry.jsonrpc_methods[method]
240
240
241 # now that we have a method, add request._req_params to
241 # now that we have a method, add request._req_params to
242 # self.kargs and dispatch control to WGIController
242 # self.kargs and dispatch control to WGIController
243 argspec = inspect.getargspec(func)
243 argspec = inspect.getargspec(func)
244 arglist = argspec[0]
244 arglist = argspec[0]
245 defaults = map(type, argspec[3] or [])
245 defaults = map(type, argspec[3] or [])
246 default_empty = types.NotImplementedType
246 default_empty = types.NotImplementedType
247
247
248 # kw arguments required by this method
248 # kw arguments required by this method
249 func_kwargs = dict(itertools.izip_longest(
249 func_kwargs = dict(itertools.izip_longest(
250 reversed(arglist), reversed(defaults), fillvalue=default_empty))
250 reversed(arglist), reversed(defaults), fillvalue=default_empty))
251
251
252 # This attribute will need to be first param of a method that uses
252 # This attribute will need to be first param of a method that uses
253 # api_key, which is translated to instance of user at that name
253 # api_key, which is translated to instance of user at that name
254 user_var = 'apiuser'
254 user_var = 'apiuser'
255 request_var = 'request'
255 request_var = 'request'
256
256
257 for arg in [user_var, request_var]:
257 for arg in [user_var, request_var]:
258 if arg not in arglist:
258 if arg not in arglist:
259 return jsonrpc_error(
259 return jsonrpc_error(
260 request,
260 request,
261 retid=request.rpc_id,
261 retid=request.rpc_id,
262 message='This method [%s] does not support '
262 message='This method [%s] does not support '
263 'required parameter `%s`' % (func.__name__, arg))
263 'required parameter `%s`' % (func.__name__, arg))
264
264
265 # get our arglist and check if we provided them as args
265 # get our arglist and check if we provided them as args
266 for arg, default in func_kwargs.items():
266 for arg, default in func_kwargs.items():
267 if arg in [user_var, request_var]:
267 if arg in [user_var, request_var]:
268 # user_var and request_var are pre-hardcoded parameters and we
268 # user_var and request_var are pre-hardcoded parameters and we
269 # don't need to do any translation
269 # don't need to do any translation
270 continue
270 continue
271
271
272 # skip the required param check if it's default value is
272 # skip the required param check if it's default value is
273 # NotImplementedType (default_empty)
273 # NotImplementedType (default_empty)
274 if default == default_empty and arg not in request.rpc_params:
274 if default == default_empty and arg not in request.rpc_params:
275 return jsonrpc_error(
275 return jsonrpc_error(
276 request,
276 request,
277 retid=request.rpc_id,
277 retid=request.rpc_id,
278 message=('Missing non optional `%s` arg in JSON DATA' % arg)
278 message=('Missing non optional `%s` arg in JSON DATA' % arg)
279 )
279 )
280
280
281 # sanitize extra passed arguments
281 # sanitize extra passed arguments
282 for k in request.rpc_params.keys()[:]:
282 for k in request.rpc_params.keys()[:]:
283 if k not in func_kwargs:
283 if k not in func_kwargs:
284 del request.rpc_params[k]
284 del request.rpc_params[k]
285
285
286 call_params = request.rpc_params
286 call_params = request.rpc_params
287 call_params.update({
287 call_params.update({
288 'request': request,
288 'request': request,
289 'apiuser': auth_u
289 'apiuser': auth_u
290 })
290 })
291
291
292 # register some common functions for usage
292 # register some common functions for usage
293 attach_context_attributes(TemplateArgs(), request, request.rpc_user.user_id)
293 attach_context_attributes(TemplateArgs(), request, request.rpc_user.user_id)
294
294
295 try:
295 try:
296 ret_value = func(**call_params)
296 ret_value = func(**call_params)
297 return jsonrpc_response(request, ret_value)
297 return jsonrpc_response(request, ret_value)
298 except JSONRPCBaseError:
298 except JSONRPCBaseError:
299 raise
299 raise
300 except Exception:
300 except Exception:
301 log.exception('Unhandled exception occurred on api call: %s', func)
301 log.exception('Unhandled exception occurred on api call: %s', func)
302 exc_info = sys.exc_info()
302 exc_info = sys.exc_info()
303 exc_id, exc_type_name = store_exception(
303 exc_id, exc_type_name = store_exception(
304 id(exc_info), exc_info, prefix='rhodecode-api')
304 id(exc_info), exc_info, prefix='rhodecode-api')
305 error_headers = [('RhodeCode-Exception-Id', str(exc_id)),
305 error_headers = [('RhodeCode-Exception-Id', str(exc_id)),
306 ('RhodeCode-Exception-Type', str(exc_type_name))]
306 ('RhodeCode-Exception-Type', str(exc_type_name))]
307 return jsonrpc_error(
307 return jsonrpc_error(
308 request, retid=request.rpc_id, message='Internal server error',
308 request, retid=request.rpc_id, message='Internal server error',
309 headers=error_headers)
309 headers=error_headers)
310
310
311
311
312 def setup_request(request):
312 def setup_request(request):
313 """
313 """
314 Parse a JSON-RPC request body. It's used inside the predicates method
314 Parse a JSON-RPC request body. It's used inside the predicates method
315 to validate and bootstrap requests for usage in rpc calls.
315 to validate and bootstrap requests for usage in rpc calls.
316
316
317 We need to raise JSONRPCError here if we want to return some errors back to
317 We need to raise JSONRPCError here if we want to return some errors back to
318 user.
318 user.
319 """
319 """
320
320
321 log.debug('Executing setup request: %r', request)
321 log.debug('Executing setup request: %r', request)
322 request.rpc_ip_addr = get_ip_addr(request.environ)
322 request.rpc_ip_addr = get_ip_addr(request.environ)
323 # TODO(marcink): deprecate GET at some point
323 # TODO(marcink): deprecate GET at some point
324 if request.method not in ['POST', 'GET']:
324 if request.method not in ['POST', 'GET']:
325 log.debug('unsupported request method "%s"', request.method)
325 log.debug('unsupported request method "%s"', request.method)
326 raise JSONRPCError(
326 raise JSONRPCError(
327 'unsupported request method "%s". Please use POST' % request.method)
327 'unsupported request method "%s". Please use POST' % request.method)
328
328
329 if 'CONTENT_LENGTH' not in request.environ:
329 if 'CONTENT_LENGTH' not in request.environ:
330 log.debug("No Content-Length")
330 log.debug("No Content-Length")
331 raise JSONRPCError("Empty body, No Content-Length in request")
331 raise JSONRPCError("Empty body, No Content-Length in request")
332
332
333 else:
333 else:
334 length = request.environ['CONTENT_LENGTH']
334 length = request.environ['CONTENT_LENGTH']
335 log.debug('Content-Length: %s', length)
335 log.debug('Content-Length: %s', length)
336
336
337 if length == 0:
337 if length == 0:
338 log.debug("Content-Length is 0")
338 log.debug("Content-Length is 0")
339 raise JSONRPCError("Content-Length is 0")
339 raise JSONRPCError("Content-Length is 0")
340
340
341 raw_body = request.body
341 raw_body = request.body
342 log.debug("Loading JSON body now")
342 log.debug("Loading JSON body now")
343 try:
343 try:
344 json_body = json.loads(raw_body)
344 json_body = json.loads(raw_body)
345 except ValueError as e:
345 except ValueError as e:
346 # catch JSON errors Here
346 # catch JSON errors Here
347 raise JSONRPCError("JSON parse error ERR:%s RAW:%r" % (e, raw_body))
347 raise JSONRPCError("JSON parse error ERR:%s RAW:%r" % (e, raw_body))
348
348
349 request.rpc_id = json_body.get('id')
349 request.rpc_id = json_body.get('id')
350 request.rpc_method = json_body.get('method')
350 request.rpc_method = json_body.get('method')
351
351
352 # check required base parameters
352 # check required base parameters
353 try:
353 try:
354 api_key = json_body.get('api_key')
354 api_key = json_body.get('api_key')
355 if not api_key:
355 if not api_key:
356 api_key = json_body.get('auth_token')
356 api_key = json_body.get('auth_token')
357
357
358 if not api_key:
358 if not api_key:
359 raise KeyError('api_key or auth_token')
359 raise KeyError('api_key or auth_token')
360
360
361 # TODO(marcink): support passing in token in request header
361 # TODO(marcink): support passing in token in request header
362
362
363 request.rpc_api_key = api_key
363 request.rpc_api_key = api_key
364 request.rpc_id = json_body['id']
364 request.rpc_id = json_body['id']
365 request.rpc_method = json_body['method']
365 request.rpc_method = json_body['method']
366 request.rpc_params = json_body['args'] \
366 request.rpc_params = json_body['args'] \
367 if isinstance(json_body['args'], dict) else {}
367 if isinstance(json_body['args'], dict) else {}
368
368
369 log.debug('method: %s, params: %.10240r', request.rpc_method, request.rpc_params)
369 log.debug('method: %s, params: %.10240r', request.rpc_method, request.rpc_params)
370 except KeyError as e:
370 except KeyError as e:
371 raise JSONRPCError('Incorrect JSON data. Missing %s' % e)
371 raise JSONRPCError('Incorrect JSON data. Missing %s' % e)
372
372
373 log.debug('setup complete, now handling method:%s rpcid:%s',
373 log.debug('setup complete, now handling method:%s rpcid:%s',
374 request.rpc_method, request.rpc_id, )
374 request.rpc_method, request.rpc_id, )
375
375
376
376
377 class RoutePredicate(object):
377 class RoutePredicate(object):
378 def __init__(self, val, config):
378 def __init__(self, val, config):
379 self.val = val
379 self.val = val
380
380
381 def text(self):
381 def text(self):
382 return 'jsonrpc route = %s' % self.val
382 return 'jsonrpc route = %s' % self.val
383
383
384 phash = text
384 phash = text
385
385
386 def __call__(self, info, request):
386 def __call__(self, info, request):
387 if self.val:
387 if self.val:
388 # potentially setup and bootstrap our call
388 # potentially setup and bootstrap our call
389 setup_request(request)
389 setup_request(request)
390
390
391 # Always return True so that even if it isn't a valid RPC it
391 # Always return True so that even if it isn't a valid RPC it
392 # will fall through to the underlaying handlers like notfound_view
392 # will fall through to the underlaying handlers like notfound_view
393 return True
393 return True
394
394
395
395
396 class NotFoundPredicate(object):
396 class NotFoundPredicate(object):
397 def __init__(self, val, config):
397 def __init__(self, val, config):
398 self.val = val
398 self.val = val
399 self.methods = config.registry.jsonrpc_methods
399 self.methods = config.registry.jsonrpc_methods
400
400
401 def text(self):
401 def text(self):
402 return 'jsonrpc method not found = {}.'.format(self.val)
402 return 'jsonrpc method not found = {}.'.format(self.val)
403
403
404 phash = text
404 phash = text
405
405
406 def __call__(self, info, request):
406 def __call__(self, info, request):
407 return hasattr(request, 'rpc_method')
407 return hasattr(request, 'rpc_method')
408
408
409
409
410 class MethodPredicate(object):
410 class MethodPredicate(object):
411 def __init__(self, val, config):
411 def __init__(self, val, config):
412 self.method = val
412 self.method = val
413
413
414 def text(self):
414 def text(self):
415 return 'jsonrpc method = %s' % self.method
415 return 'jsonrpc method = %s' % self.method
416
416
417 phash = text
417 phash = text
418
418
419 def __call__(self, context, request):
419 def __call__(self, context, request):
420 # we need to explicitly return False here, so pyramid doesn't try to
420 # we need to explicitly return False here, so pyramid doesn't try to
421 # execute our view directly. We need our main handler to execute things
421 # execute our view directly. We need our main handler to execute things
422 return getattr(request, 'rpc_method') == self.method
422 return getattr(request, 'rpc_method') == self.method
423
423
424
424
425 def add_jsonrpc_method(config, view, **kwargs):
425 def add_jsonrpc_method(config, view, **kwargs):
426 # pop the method name
426 # pop the method name
427 method = kwargs.pop('method', None)
427 method = kwargs.pop('method', None)
428
428
429 if method is None:
429 if method is None:
430 raise ConfigurationError(
430 raise ConfigurationError(
431 'Cannot register a JSON-RPC method without specifying the "method"')
431 'Cannot register a JSON-RPC method without specifying the "method"')
432
432
433 # we define custom predicate, to enable to detect conflicting methods,
433 # we define custom predicate, to enable to detect conflicting methods,
434 # those predicates are kind of "translation" from the decorator variables
434 # those predicates are kind of "translation" from the decorator variables
435 # to internal predicates names
435 # to internal predicates names
436
436
437 kwargs['jsonrpc_method'] = method
437 kwargs['jsonrpc_method'] = method
438
438
439 # register our view into global view store for validation
439 # register our view into global view store for validation
440 config.registry.jsonrpc_methods[method] = view
440 config.registry.jsonrpc_methods[method] = view
441
441
442 # we're using our main request_view handler, here, so each method
442 # we're using our main request_view handler, here, so each method
443 # has a unified handler for itself
443 # has a unified handler for itself
444 config.add_view(request_view, route_name='apiv2', **kwargs)
444 config.add_view(request_view, route_name='apiv2', **kwargs)
445
445
446
446
447 class jsonrpc_method(object):
447 class jsonrpc_method(object):
448 """
448 """
449 decorator that works similar to @add_view_config decorator,
449 decorator that works similar to @add_view_config decorator,
450 but tailored for our JSON RPC
450 but tailored for our JSON RPC
451 """
451 """
452
452
453 venusian = venusian # for testing injection
453 venusian = venusian # for testing injection
454
454
455 def __init__(self, method=None, **kwargs):
455 def __init__(self, method=None, **kwargs):
456 self.method = method
456 self.method = method
457 self.kwargs = kwargs
457 self.kwargs = kwargs
458
458
459 def __call__(self, wrapped):
459 def __call__(self, wrapped):
460 kwargs = self.kwargs.copy()
460 kwargs = self.kwargs.copy()
461 kwargs['method'] = self.method or wrapped.__name__
461 kwargs['method'] = self.method or wrapped.__name__
462 depth = kwargs.pop('_depth', 0)
462 depth = kwargs.pop('_depth', 0)
463
463
464 def callback(context, name, ob):
464 def callback(context, name, ob):
465 config = context.config.with_package(info.module)
465 config = context.config.with_package(info.module)
466 config.add_jsonrpc_method(view=ob, **kwargs)
466 config.add_jsonrpc_method(view=ob, **kwargs)
467
467
468 info = venusian.attach(wrapped, callback, category='pyramid',
468 info = venusian.attach(wrapped, callback, category='pyramid',
469 depth=depth + 1)
469 depth=depth + 1)
470 if info.scope == 'class':
470 if info.scope == 'class':
471 # ensure that attr is set if decorating a class method
471 # ensure that attr is set if decorating a class method
472 kwargs.setdefault('attr', wrapped.__name__)
472 kwargs.setdefault('attr', wrapped.__name__)
473
473
474 kwargs['_info'] = info.codeinfo # fbo action_method
474 kwargs['_info'] = info.codeinfo # fbo action_method
475 return wrapped
475 return wrapped
476
476
477
477
478 class jsonrpc_deprecated_method(object):
478 class jsonrpc_deprecated_method(object):
479 """
479 """
480 Marks method as deprecated, adds log.warning, and inject special key to
480 Marks method as deprecated, adds log.warning, and inject special key to
481 the request variable to mark method as deprecated.
481 the request variable to mark method as deprecated.
482 Also injects special docstring that extract_docs will catch to mark
482 Also injects special docstring that extract_docs will catch to mark
483 method as deprecated.
483 method as deprecated.
484
484
485 :param use_method: specify which method should be used instead of
485 :param use_method: specify which method should be used instead of
486 the decorated one
486 the decorated one
487
487
488 Use like::
488 Use like::
489
489
490 @jsonrpc_method()
490 @jsonrpc_method()
491 @jsonrpc_deprecated_method(use_method='new_func', deprecated_at_version='3.0.0')
491 @jsonrpc_deprecated_method(use_method='new_func', deprecated_at_version='3.0.0')
492 def old_func(request, apiuser, arg1, arg2):
492 def old_func(request, apiuser, arg1, arg2):
493 ...
493 ...
494 """
494 """
495
495
496 def __init__(self, use_method, deprecated_at_version):
496 def __init__(self, use_method, deprecated_at_version):
497 self.use_method = use_method
497 self.use_method = use_method
498 self.deprecated_at_version = deprecated_at_version
498 self.deprecated_at_version = deprecated_at_version
499 self.deprecated_msg = ''
499 self.deprecated_msg = ''
500
500
501 def __call__(self, func):
501 def __call__(self, func):
502 self.deprecated_msg = 'Please use method `{method}` instead.'.format(
502 self.deprecated_msg = 'Please use method `{method}` instead.'.format(
503 method=self.use_method)
503 method=self.use_method)
504
504
505 docstring = """\n
505 docstring = """\n
506 .. deprecated:: {version}
506 .. deprecated:: {version}
507
507
508 {deprecation_message}
508 {deprecation_message}
509
509
510 {original_docstring}
510 {original_docstring}
511 """
511 """
512 func.__doc__ = docstring.format(
512 func.__doc__ = docstring.format(
513 version=self.deprecated_at_version,
513 version=self.deprecated_at_version,
514 deprecation_message=self.deprecated_msg,
514 deprecation_message=self.deprecated_msg,
515 original_docstring=func.__doc__)
515 original_docstring=func.__doc__)
516 return decorator.decorator(self.__wrapper, func)
516 return decorator.decorator(self.__wrapper, func)
517
517
518 def __wrapper(self, func, *fargs, **fkwargs):
518 def __wrapper(self, func, *fargs, **fkwargs):
519 log.warning('DEPRECATED API CALL on function %s, please '
519 log.warning('DEPRECATED API CALL on function %s, please '
520 'use `%s` instead', func, self.use_method)
520 'use `%s` instead', func, self.use_method)
521 # alter function docstring to mark as deprecated, this is picked up
521 # alter function docstring to mark as deprecated, this is picked up
522 # via fabric file that generates API DOC.
522 # via fabric file that generates API DOC.
523 result = func(*fargs, **fkwargs)
523 result = func(*fargs, **fkwargs)
524
524
525 request = fargs[0]
525 request = fargs[0]
526 request.rpc_deprecation = 'DEPRECATED METHOD ' + self.deprecated_msg
526 request.rpc_deprecation = 'DEPRECATED METHOD ' + self.deprecated_msg
527 return result
527 return result
528
528
529
529
530 def add_api_methods(config):
531 from rhodecode.api.views import (
532 deprecated_api, gist_api, pull_request_api, repo_api, repo_group_api,
533 server_api, search_api, testing_api, user_api, user_group_api)
534
535 config.scan('rhodecode.api.views')
536
537
530 def includeme(config):
538 def includeme(config):
531 plugin_module = 'rhodecode.api'
539 plugin_module = 'rhodecode.api'
532 plugin_settings = get_plugin_settings(
540 plugin_settings = get_plugin_settings(
533 plugin_module, config.registry.settings)
541 plugin_module, config.registry.settings)
534
542
535 if not hasattr(config.registry, 'jsonrpc_methods'):
543 if not hasattr(config.registry, 'jsonrpc_methods'):
536 config.registry.jsonrpc_methods = OrderedDict()
544 config.registry.jsonrpc_methods = OrderedDict()
537
545
538 # match filter by given method only
546 # match filter by given method only
539 config.add_view_predicate('jsonrpc_method', MethodPredicate)
547 config.add_view_predicate('jsonrpc_method', MethodPredicate)
540 config.add_view_predicate('jsonrpc_method_not_found', NotFoundPredicate)
548 config.add_view_predicate('jsonrpc_method_not_found', NotFoundPredicate)
541
549
542 config.add_renderer(DEFAULT_RENDERER, ExtJsonRenderer(
550 config.add_renderer(DEFAULT_RENDERER, ExtJsonRenderer(
543 serializer=json.dumps, indent=4))
551 serializer=json.dumps, indent=4))
544 config.add_directive('add_jsonrpc_method', add_jsonrpc_method)
552 config.add_directive('add_jsonrpc_method', add_jsonrpc_method)
545
553
546 config.add_route_predicate(
554 config.add_route_predicate(
547 'jsonrpc_call', RoutePredicate)
555 'jsonrpc_call', RoutePredicate)
548
556
549 config.add_route(
557 config.add_route(
550 'apiv2', plugin_settings.get('url', DEFAULT_URL), jsonrpc_call=True)
558 'apiv2', plugin_settings.get('url', DEFAULT_URL), jsonrpc_call=True)
551
559
552 config.scan(plugin_module, ignore='rhodecode.api.tests')
553 # register some exception handling view
560 # register some exception handling view
554 config.add_view(exception_view, context=JSONRPCBaseError)
561 config.add_view(exception_view, context=JSONRPCBaseError)
555 config.add_notfound_view(exception_view, jsonrpc_method_not_found=True)
562 config.add_notfound_view(exception_view, jsonrpc_method_not_found=True)
563
564 add_api_methods(config)
@@ -1,146 +1,148 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from zope.interface import implementer
25 from zope.interface import implementer
26
26
27 from rhodecode.apps._base.interfaces import IAdminNavigationRegistry
27 from rhodecode.apps._base.interfaces import IAdminNavigationRegistry
28 from rhodecode.lib.utils2 import str2bool
28 from rhodecode.lib.utils2 import str2bool
29 from rhodecode.translation import _
29 from rhodecode.translation import _
30
30
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34 NavListEntry = collections.namedtuple(
34 NavListEntry = collections.namedtuple(
35 'NavListEntry', ['key', 'name', 'url', 'active_list'])
35 'NavListEntry', ['key', 'name', 'url', 'active_list'])
36
36
37
37
38 class NavEntry(object):
38 class NavEntry(object):
39 """
39 """
40 Represents an entry in the admin navigation.
40 Represents an entry in the admin navigation.
41
41
42 :param key: Unique identifier used to store reference in an OrderedDict.
42 :param key: Unique identifier used to store reference in an OrderedDict.
43 :param name: Display name, usually a translation string.
43 :param name: Display name, usually a translation string.
44 :param view_name: Name of the view, used generate the URL.
44 :param view_name: Name of the view, used generate the URL.
45 :param active_list: list of urls that we select active for this element
45 :param active_list: list of urls that we select active for this element
46 """
46 """
47
47
48 def __init__(self, key, name, view_name, active_list=None):
48 def __init__(self, key, name, view_name, active_list=None):
49 self.key = key
49 self.key = key
50 self.name = name
50 self.name = name
51 self.view_name = view_name
51 self.view_name = view_name
52 self._active_list = active_list or []
52 self._active_list = active_list or []
53
53
54 def generate_url(self, request):
54 def generate_url(self, request):
55 return request.route_path(self.view_name)
55 return request.route_path(self.view_name)
56
56
57 def get_localized_name(self, request):
57 def get_localized_name(self, request):
58 return request.translate(self.name)
58 return request.translate(self.name)
59
59
60 @property
60 @property
61 def active_list(self):
61 def active_list(self):
62 active_list = [self.key]
62 active_list = [self.key]
63 if self._active_list:
63 if self._active_list:
64 active_list = self._active_list
64 active_list = self._active_list
65 return active_list
65 return active_list
66
66
67
67
68 @implementer(IAdminNavigationRegistry)
68 @implementer(IAdminNavigationRegistry)
69 class NavigationRegistry(object):
69 class NavigationRegistry(object):
70
70
71 _base_entries = [
71 _base_entries = [
72 NavEntry('global', _('Global'),
72 NavEntry('global', _('Global'),
73 'admin_settings_global'),
73 'admin_settings_global'),
74 NavEntry('vcs', _('VCS'),
74 NavEntry('vcs', _('VCS'),
75 'admin_settings_vcs'),
75 'admin_settings_vcs'),
76 NavEntry('visual', _('Visual'),
76 NavEntry('visual', _('Visual'),
77 'admin_settings_visual'),
77 'admin_settings_visual'),
78 NavEntry('mapping', _('Remap and Rescan'),
78 NavEntry('mapping', _('Remap and Rescan'),
79 'admin_settings_mapping'),
79 'admin_settings_mapping'),
80 NavEntry('issuetracker', _('Issue Tracker'),
80 NavEntry('issuetracker', _('Issue Tracker'),
81 'admin_settings_issuetracker'),
81 'admin_settings_issuetracker'),
82 NavEntry('email', _('Email'),
82 NavEntry('email', _('Email'),
83 'admin_settings_email'),
83 'admin_settings_email'),
84 NavEntry('hooks', _('Hooks'),
84 NavEntry('hooks', _('Hooks'),
85 'admin_settings_hooks'),
85 'admin_settings_hooks'),
86 NavEntry('search', _('Full Text Search'),
86 NavEntry('search', _('Full Text Search'),
87 'admin_settings_search'),
87 'admin_settings_search'),
88 NavEntry('system', _('System Info'),
88 NavEntry('system', _('System Info'),
89 'admin_settings_system'),
89 'admin_settings_system'),
90 NavEntry('exceptions', _('Exceptions Tracker'),
90 NavEntry('exceptions', _('Exceptions Tracker'),
91 'admin_settings_exception_tracker',
91 'admin_settings_exception_tracker',
92 active_list=['exceptions', 'exceptions_browse']),
92 active_list=['exceptions', 'exceptions_browse']),
93 NavEntry('process_management', _('Processes'),
93 NavEntry('process_management', _('Processes'),
94 'admin_settings_process_management'),
94 'admin_settings_process_management'),
95 NavEntry('sessions', _('User Sessions'),
95 NavEntry('sessions', _('User Sessions'),
96 'admin_settings_sessions'),
96 'admin_settings_sessions'),
97 NavEntry('open_source', _('Open Source Licenses'),
97 NavEntry('open_source', _('Open Source Licenses'),
98 'admin_settings_open_source'),
98 'admin_settings_open_source'),
99 NavEntry('automation', _('Automation'),
99 NavEntry('automation', _('Automation'),
100 'admin_settings_automation')
100 'admin_settings_automation')
101 ]
101 ]
102
102
103 _labs_entry = NavEntry('labs', _('Labs'),
103 _labs_entry = NavEntry('labs', _('Labs'),
104 'admin_settings_labs')
104 'admin_settings_labs')
105
105
106 def __init__(self, labs_active=False):
106 def __init__(self, labs_active=False):
107 self._registered_entries = collections.OrderedDict()
107 self._registered_entries = collections.OrderedDict()
108 for item in self.__class__._base_entries:
108 for item in self.__class__._base_entries:
109 self._registered_entries[item.key] = item
109 self._registered_entries[item.key] = item
110
110
111 if labs_active:
111 if labs_active:
112 self.add_entry(self._labs_entry)
112 self.add_entry(self._labs_entry)
113
113
114 def add_entry(self, entry):
114 def add_entry(self, entry):
115 self._registered_entries[entry.key] = entry
115 self._registered_entries[entry.key] = entry
116
116
117 def get_navlist(self, request):
117 def get_navlist(self, request):
118 nav_list = [
118 nav_list = [
119 NavListEntry(i.key, i.get_localized_name(request),
119 NavListEntry(i.key, i.get_localized_name(request),
120 i.generate_url(request), i.active_list)
120 i.generate_url(request), i.active_list)
121 for i in self._registered_entries.values()]
121 for i in self._registered_entries.values()]
122 return nav_list
122 return nav_list
123
123
124
124
125 def navigation_registry(request, registry=None):
125 def navigation_registry(request, registry=None):
126 """
126 """
127 Helper that returns the admin navigation registry.
127 Helper that returns the admin navigation registry.
128 """
128 """
129 pyramid_registry = registry or request.registry
129 pyramid_registry = registry or request.registry
130 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
130 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
131 return nav_registry
131 return nav_registry
132
132
133
133
134 def navigation_list(request):
134 def navigation_list(request):
135 """
135 """
136 Helper that returns the admin navigation as list of NavListEntry objects.
136 Helper that returns the admin navigation as list of NavListEntry objects.
137 """
137 """
138 return navigation_registry(request).get_navlist(request)
138 return navigation_registry(request).get_navlist(request)
139
139
140
140
141 def includeme(config):
141 def includeme(config):
142 # Create admin navigation registry and add it to the pyramid registry.
142 # Create admin navigation registry and add it to the pyramid registry.
143 settings = config.get_settings()
143 settings = config.get_settings()
144 labs_active = str2bool(settings.get('labs_settings_active', False))
144 labs_active = str2bool(settings.get('labs_settings_active', False))
145 navigation_registry_instance = NavigationRegistry(labs_active=labs_active)
145 navigation_registry_instance = NavigationRegistry(labs_active=labs_active)
146 config.registry.registerUtility(navigation_registry_instance)
146 config.registry.registerUtility(navigation_registry_instance)
147 log.debug('Created new nabigation instance, %s', navigation_registry_instance)
148
This diff has been collapsed as it changes many lines, (619 lines changed) Show them Hide them
@@ -1,466 +1,1055 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 from rhodecode.apps._base import ADMIN_PREFIX
22 from rhodecode.apps._base import ADMIN_PREFIX
23
23
24
24
25 def admin_routes(config):
25 def admin_routes(config):
26 """
26 """
27 Admin prefixed routes
27 Admin prefixed routes
28 """
28 """
29 from rhodecode.apps.admin.views.audit_logs import AdminAuditLogsView
30 from rhodecode.apps.admin.views.defaults import AdminDefaultSettingsView
31 from rhodecode.apps.admin.views.exception_tracker import ExceptionsTrackerView
32 from rhodecode.apps.admin.views.main_views import AdminMainView
33 from rhodecode.apps.admin.views.open_source_licenses import OpenSourceLicensesAdminSettingsView
34 from rhodecode.apps.admin.views.permissions import AdminPermissionsView
35 from rhodecode.apps.admin.views.process_management import AdminProcessManagementView
36 from rhodecode.apps.admin.views.repo_groups import AdminRepoGroupsView
37 from rhodecode.apps.admin.views.repositories import AdminReposView
38 from rhodecode.apps.admin.views.sessions import AdminSessionSettingsView
39 from rhodecode.apps.admin.views.settings import AdminSettingsView
40 from rhodecode.apps.admin.views.svn_config import AdminSvnConfigView
41 from rhodecode.apps.admin.views.system_info import AdminSystemInfoSettingsView
42 from rhodecode.apps.admin.views.user_groups import AdminUserGroupsView
43 from rhodecode.apps.admin.views.users import AdminUsersView, UsersView
44
29 config.add_route(
45 config.add_route(
30 name='admin_audit_logs',
46 name='admin_audit_logs',
31 pattern='/audit_logs')
47 pattern='/audit_logs')
48 config.add_view(
49 AdminAuditLogsView,
50 attr='admin_audit_logs',
51 route_name='admin_audit_logs', request_method='GET',
52 renderer='rhodecode:templates/admin/admin_audit_logs.mako')
32
53
33 config.add_route(
54 config.add_route(
34 name='admin_audit_log_entry',
55 name='admin_audit_log_entry',
35 pattern='/audit_logs/{audit_log_id}')
56 pattern='/audit_logs/{audit_log_id}')
36
57 config.add_view(
37 config.add_route(
58 AdminAuditLogsView,
38 name='pull_requests_global_0', # backward compat
59 attr='admin_audit_log_entry',
39 pattern='/pull_requests/{pull_request_id:\d+}')
60 route_name='admin_audit_log_entry', request_method='GET',
40 config.add_route(
61 renderer='rhodecode:templates/admin/admin_audit_log_entry.mako')
41 name='pull_requests_global_1', # backward compat
42 pattern='/pull-requests/{pull_request_id:\d+}')
43 config.add_route(
44 name='pull_requests_global',
45 pattern='/pull-request/{pull_request_id:\d+}')
46
62
47 config.add_route(
63 config.add_route(
48 name='admin_settings_open_source',
64 name='admin_settings_open_source',
49 pattern='/settings/open_source')
65 pattern='/settings/open_source')
66 config.add_view(
67 OpenSourceLicensesAdminSettingsView,
68 attr='open_source_licenses',
69 route_name='admin_settings_open_source', request_method='GET',
70 renderer='rhodecode:templates/admin/settings/settings.mako')
71
50 config.add_route(
72 config.add_route(
51 name='admin_settings_vcs_svn_generate_cfg',
73 name='admin_settings_vcs_svn_generate_cfg',
52 pattern='/settings/vcs/svn_generate_cfg')
74 pattern='/settings/vcs/svn_generate_cfg')
75 config.add_view(
76 AdminSvnConfigView,
77 attr='vcs_svn_generate_config',
78 route_name='admin_settings_vcs_svn_generate_cfg',
79 request_method='POST', renderer='json')
53
80
54 config.add_route(
81 config.add_route(
55 name='admin_settings_system',
82 name='admin_settings_system',
56 pattern='/settings/system')
83 pattern='/settings/system')
84 config.add_view(
85 AdminSystemInfoSettingsView,
86 attr='settings_system_info',
87 route_name='admin_settings_system', request_method='GET',
88 renderer='rhodecode:templates/admin/settings/settings.mako')
89
57 config.add_route(
90 config.add_route(
58 name='admin_settings_system_update',
91 name='admin_settings_system_update',
59 pattern='/settings/system/updates')
92 pattern='/settings/system/updates')
93 config.add_view(
94 AdminSystemInfoSettingsView,
95 attr='settings_system_info_check_update',
96 route_name='admin_settings_system_update', request_method='GET',
97 renderer='rhodecode:templates/admin/settings/settings_system_update.mako')
60
98
61 config.add_route(
99 config.add_route(
62 name='admin_settings_exception_tracker',
100 name='admin_settings_exception_tracker',
63 pattern='/settings/exceptions')
101 pattern='/settings/exceptions')
102 config.add_view(
103 ExceptionsTrackerView,
104 attr='browse_exceptions',
105 route_name='admin_settings_exception_tracker', request_method='GET',
106 renderer='rhodecode:templates/admin/settings/settings.mako')
107
64 config.add_route(
108 config.add_route(
65 name='admin_settings_exception_tracker_delete_all',
109 name='admin_settings_exception_tracker_delete_all',
66 pattern='/settings/exceptions/delete')
110 pattern='/settings/exceptions_delete_all')
111 config.add_view(
112 ExceptionsTrackerView,
113 attr='exception_delete_all',
114 route_name='admin_settings_exception_tracker_delete_all', request_method='POST',
115 renderer='rhodecode:templates/admin/settings/settings.mako')
116
67 config.add_route(
117 config.add_route(
68 name='admin_settings_exception_tracker_show',
118 name='admin_settings_exception_tracker_show',
69 pattern='/settings/exceptions/{exception_id}')
119 pattern='/settings/exceptions/{exception_id}')
120 config.add_view(
121 ExceptionsTrackerView,
122 attr='exception_show',
123 route_name='admin_settings_exception_tracker_show', request_method='GET',
124 renderer='rhodecode:templates/admin/settings/settings.mako')
125
70 config.add_route(
126 config.add_route(
71 name='admin_settings_exception_tracker_delete',
127 name='admin_settings_exception_tracker_delete',
72 pattern='/settings/exceptions/{exception_id}/delete')
128 pattern='/settings/exceptions/{exception_id}/delete')
129 config.add_view(
130 ExceptionsTrackerView,
131 attr='exception_delete',
132 route_name='admin_settings_exception_tracker_delete', request_method='POST',
133 renderer='rhodecode:templates/admin/settings/settings.mako')
73
134
74 config.add_route(
135 config.add_route(
75 name='admin_settings_sessions',
136 name='admin_settings_sessions',
76 pattern='/settings/sessions')
137 pattern='/settings/sessions')
138 config.add_view(
139 AdminSessionSettingsView,
140 attr='settings_sessions',
141 route_name='admin_settings_sessions', request_method='GET',
142 renderer='rhodecode:templates/admin/settings/settings.mako')
143
77 config.add_route(
144 config.add_route(
78 name='admin_settings_sessions_cleanup',
145 name='admin_settings_sessions_cleanup',
79 pattern='/settings/sessions/cleanup')
146 pattern='/settings/sessions/cleanup')
147 config.add_view(
148 AdminSessionSettingsView,
149 attr='settings_sessions_cleanup',
150 route_name='admin_settings_sessions_cleanup', request_method='POST')
80
151
81 config.add_route(
152 config.add_route(
82 name='admin_settings_process_management',
153 name='admin_settings_process_management',
83 pattern='/settings/process_management')
154 pattern='/settings/process_management')
155 config.add_view(
156 AdminProcessManagementView,
157 attr='process_management',
158 route_name='admin_settings_process_management', request_method='GET',
159 renderer='rhodecode:templates/admin/settings/settings.mako')
160
84 config.add_route(
161 config.add_route(
85 name='admin_settings_process_management_data',
162 name='admin_settings_process_management_data',
86 pattern='/settings/process_management/data')
163 pattern='/settings/process_management/data')
164 config.add_view(
165 AdminProcessManagementView,
166 attr='process_management_data',
167 route_name='admin_settings_process_management_data', request_method='GET',
168 renderer='rhodecode:templates/admin/settings/settings_process_management_data.mako')
169
87 config.add_route(
170 config.add_route(
88 name='admin_settings_process_management_signal',
171 name='admin_settings_process_management_signal',
89 pattern='/settings/process_management/signal')
172 pattern='/settings/process_management/signal')
173 config.add_view(
174 AdminProcessManagementView,
175 attr='process_management_signal',
176 route_name='admin_settings_process_management_signal',
177 request_method='POST', renderer='json_ext')
178
90 config.add_route(
179 config.add_route(
91 name='admin_settings_process_management_master_signal',
180 name='admin_settings_process_management_master_signal',
92 pattern='/settings/process_management/master_signal')
181 pattern='/settings/process_management/master_signal')
182 config.add_view(
183 AdminProcessManagementView,
184 attr='process_management_master_signal',
185 route_name='admin_settings_process_management_master_signal',
186 request_method='POST', renderer='json_ext')
93
187
94 # default settings
188 # default settings
95 config.add_route(
189 config.add_route(
96 name='admin_defaults_repositories',
190 name='admin_defaults_repositories',
97 pattern='/defaults/repositories')
191 pattern='/defaults/repositories')
192 config.add_view(
193 AdminDefaultSettingsView,
194 attr='defaults_repository_show',
195 route_name='admin_defaults_repositories', request_method='GET',
196 renderer='rhodecode:templates/admin/defaults/defaults.mako')
197
98 config.add_route(
198 config.add_route(
99 name='admin_defaults_repositories_update',
199 name='admin_defaults_repositories_update',
100 pattern='/defaults/repositories/update')
200 pattern='/defaults/repositories/update')
201 config.add_view(
202 AdminDefaultSettingsView,
203 attr='defaults_repository_update',
204 route_name='admin_defaults_repositories_update', request_method='POST',
205 renderer='rhodecode:templates/admin/defaults/defaults.mako')
101
206
102 # admin settings
207 # admin settings
103
208
104 config.add_route(
209 config.add_route(
105 name='admin_settings',
210 name='admin_settings',
106 pattern='/settings')
211 pattern='/settings')
212 config.add_view(
213 AdminSettingsView,
214 attr='settings_global',
215 route_name='admin_settings', request_method='GET',
216 renderer='rhodecode:templates/admin/settings/settings.mako')
217
107 config.add_route(
218 config.add_route(
108 name='admin_settings_update',
219 name='admin_settings_update',
109 pattern='/settings/update')
220 pattern='/settings/update')
221 config.add_view(
222 AdminSettingsView,
223 attr='settings_global_update',
224 route_name='admin_settings_update', request_method='POST',
225 renderer='rhodecode:templates/admin/settings/settings.mako')
110
226
111 config.add_route(
227 config.add_route(
112 name='admin_settings_global',
228 name='admin_settings_global',
113 pattern='/settings/global')
229 pattern='/settings/global')
230 config.add_view(
231 AdminSettingsView,
232 attr='settings_global',
233 route_name='admin_settings_global', request_method='GET',
234 renderer='rhodecode:templates/admin/settings/settings.mako')
235
114 config.add_route(
236 config.add_route(
115 name='admin_settings_global_update',
237 name='admin_settings_global_update',
116 pattern='/settings/global/update')
238 pattern='/settings/global/update')
239 config.add_view(
240 AdminSettingsView,
241 attr='settings_global_update',
242 route_name='admin_settings_global_update', request_method='POST',
243 renderer='rhodecode:templates/admin/settings/settings.mako')
117
244
118 config.add_route(
245 config.add_route(
119 name='admin_settings_vcs',
246 name='admin_settings_vcs',
120 pattern='/settings/vcs')
247 pattern='/settings/vcs')
248 config.add_view(
249 AdminSettingsView,
250 attr='settings_vcs',
251 route_name='admin_settings_vcs', request_method='GET',
252 renderer='rhodecode:templates/admin/settings/settings.mako')
253
121 config.add_route(
254 config.add_route(
122 name='admin_settings_vcs_update',
255 name='admin_settings_vcs_update',
123 pattern='/settings/vcs/update')
256 pattern='/settings/vcs/update')
257 config.add_view(
258 AdminSettingsView,
259 attr='settings_vcs_update',
260 route_name='admin_settings_vcs_update', request_method='POST',
261 renderer='rhodecode:templates/admin/settings/settings.mako')
262
124 config.add_route(
263 config.add_route(
125 name='admin_settings_vcs_svn_pattern_delete',
264 name='admin_settings_vcs_svn_pattern_delete',
126 pattern='/settings/vcs/svn_pattern_delete')
265 pattern='/settings/vcs/svn_pattern_delete')
266 config.add_view(
267 AdminSettingsView,
268 attr='settings_vcs_delete_svn_pattern',
269 route_name='admin_settings_vcs_svn_pattern_delete', request_method='POST',
270 renderer='json_ext', xhr=True)
127
271
128 config.add_route(
272 config.add_route(
129 name='admin_settings_mapping',
273 name='admin_settings_mapping',
130 pattern='/settings/mapping')
274 pattern='/settings/mapping')
275 config.add_view(
276 AdminSettingsView,
277 attr='settings_mapping',
278 route_name='admin_settings_mapping', request_method='GET',
279 renderer='rhodecode:templates/admin/settings/settings.mako')
280
131 config.add_route(
281 config.add_route(
132 name='admin_settings_mapping_update',
282 name='admin_settings_mapping_update',
133 pattern='/settings/mapping/update')
283 pattern='/settings/mapping/update')
284 config.add_view(
285 AdminSettingsView,
286 attr='settings_mapping_update',
287 route_name='admin_settings_mapping_update', request_method='POST',
288 renderer='rhodecode:templates/admin/settings/settings.mako')
134
289
135 config.add_route(
290 config.add_route(
136 name='admin_settings_visual',
291 name='admin_settings_visual',
137 pattern='/settings/visual')
292 pattern='/settings/visual')
293 config.add_view(
294 AdminSettingsView,
295 attr='settings_visual',
296 route_name='admin_settings_visual', request_method='GET',
297 renderer='rhodecode:templates/admin/settings/settings.mako')
298
138 config.add_route(
299 config.add_route(
139 name='admin_settings_visual_update',
300 name='admin_settings_visual_update',
140 pattern='/settings/visual/update')
301 pattern='/settings/visual/update')
302 config.add_view(
303 AdminSettingsView,
304 attr='settings_visual_update',
305 route_name='admin_settings_visual_update', request_method='POST',
306 renderer='rhodecode:templates/admin/settings/settings.mako')
141
307
142 config.add_route(
308 config.add_route(
143 name='admin_settings_issuetracker',
309 name='admin_settings_issuetracker',
144 pattern='/settings/issue-tracker')
310 pattern='/settings/issue-tracker')
311 config.add_view(
312 AdminSettingsView,
313 attr='settings_issuetracker',
314 route_name='admin_settings_issuetracker', request_method='GET',
315 renderer='rhodecode:templates/admin/settings/settings.mako')
316
145 config.add_route(
317 config.add_route(
146 name='admin_settings_issuetracker_update',
318 name='admin_settings_issuetracker_update',
147 pattern='/settings/issue-tracker/update')
319 pattern='/settings/issue-tracker/update')
320 config.add_view(
321 AdminSettingsView,
322 attr='settings_issuetracker_update',
323 route_name='admin_settings_issuetracker_update', request_method='POST',
324 renderer='rhodecode:templates/admin/settings/settings.mako')
325
148 config.add_route(
326 config.add_route(
149 name='admin_settings_issuetracker_test',
327 name='admin_settings_issuetracker_test',
150 pattern='/settings/issue-tracker/test')
328 pattern='/settings/issue-tracker/test')
329 config.add_view(
330 AdminSettingsView,
331 attr='settings_issuetracker_test',
332 route_name='admin_settings_issuetracker_test', request_method='POST',
333 renderer='string', xhr=True)
334
151 config.add_route(
335 config.add_route(
152 name='admin_settings_issuetracker_delete',
336 name='admin_settings_issuetracker_delete',
153 pattern='/settings/issue-tracker/delete')
337 pattern='/settings/issue-tracker/delete')
338 config.add_view(
339 AdminSettingsView,
340 attr='settings_issuetracker_delete',
341 route_name='admin_settings_issuetracker_delete', request_method='POST',
342 renderer='json_ext', xhr=True)
154
343
155 config.add_route(
344 config.add_route(
156 name='admin_settings_email',
345 name='admin_settings_email',
157 pattern='/settings/email')
346 pattern='/settings/email')
347 config.add_view(
348 AdminSettingsView,
349 attr='settings_email',
350 route_name='admin_settings_email', request_method='GET',
351 renderer='rhodecode:templates/admin/settings/settings.mako')
352
158 config.add_route(
353 config.add_route(
159 name='admin_settings_email_update',
354 name='admin_settings_email_update',
160 pattern='/settings/email/update')
355 pattern='/settings/email/update')
356 config.add_view(
357 AdminSettingsView,
358 attr='settings_email_update',
359 route_name='admin_settings_email_update', request_method='POST',
360 renderer='rhodecode:templates/admin/settings/settings.mako')
161
361
162 config.add_route(
362 config.add_route(
163 name='admin_settings_hooks',
363 name='admin_settings_hooks',
164 pattern='/settings/hooks')
364 pattern='/settings/hooks')
365 config.add_view(
366 AdminSettingsView,
367 attr='settings_hooks',
368 route_name='admin_settings_hooks', request_method='GET',
369 renderer='rhodecode:templates/admin/settings/settings.mako')
370
165 config.add_route(
371 config.add_route(
166 name='admin_settings_hooks_update',
372 name='admin_settings_hooks_update',
167 pattern='/settings/hooks/update')
373 pattern='/settings/hooks/update')
374 config.add_view(
375 AdminSettingsView,
376 attr='settings_hooks_update',
377 route_name='admin_settings_hooks_update', request_method='POST',
378 renderer='rhodecode:templates/admin/settings/settings.mako')
379
168 config.add_route(
380 config.add_route(
169 name='admin_settings_hooks_delete',
381 name='admin_settings_hooks_delete',
170 pattern='/settings/hooks/delete')
382 pattern='/settings/hooks/delete')
383 config.add_view(
384 AdminSettingsView,
385 attr='settings_hooks_update',
386 route_name='admin_settings_hooks_delete', request_method='POST',
387 renderer='rhodecode:templates/admin/settings/settings.mako')
171
388
172 config.add_route(
389 config.add_route(
173 name='admin_settings_search',
390 name='admin_settings_search',
174 pattern='/settings/search')
391 pattern='/settings/search')
392 config.add_view(
393 AdminSettingsView,
394 attr='settings_search',
395 route_name='admin_settings_search', request_method='GET',
396 renderer='rhodecode:templates/admin/settings/settings.mako')
175
397
176 config.add_route(
398 config.add_route(
177 name='admin_settings_labs',
399 name='admin_settings_labs',
178 pattern='/settings/labs')
400 pattern='/settings/labs')
401 config.add_view(
402 AdminSettingsView,
403 attr='settings_labs',
404 route_name='admin_settings_labs', request_method='GET',
405 renderer='rhodecode:templates/admin/settings/settings.mako')
406
179 config.add_route(
407 config.add_route(
180 name='admin_settings_labs_update',
408 name='admin_settings_labs_update',
181 pattern='/settings/labs/update')
409 pattern='/settings/labs/update')
410 config.add_view(
411 AdminSettingsView,
412 attr='settings_labs_update',
413 route_name='admin_settings_labs_update', request_method='POST',
414 renderer='rhodecode:templates/admin/settings/settings.mako')
182
415
183 # Automation EE feature
416 # Automation EE feature
184 config.add_route(
417 config.add_route(
185 'admin_settings_automation',
418 'admin_settings_automation',
186 pattern=ADMIN_PREFIX + '/settings/automation')
419 pattern=ADMIN_PREFIX + '/settings/automation')
420 config.add_view(
421 AdminSettingsView,
422 attr='settings_automation',
423 route_name='admin_settings_automation', request_method='GET',
424 renderer='rhodecode:templates/admin/settings/settings.mako')
187
425
188 # global permissions
426 # global permissions
189
427
190 config.add_route(
428 config.add_route(
191 name='admin_permissions_application',
429 name='admin_permissions_application',
192 pattern='/permissions/application')
430 pattern='/permissions/application')
431 config.add_view(
432 AdminPermissionsView,
433 attr='permissions_application',
434 route_name='admin_permissions_application', request_method='GET',
435 renderer='rhodecode:templates/admin/permissions/permissions.mako')
436
193 config.add_route(
437 config.add_route(
194 name='admin_permissions_application_update',
438 name='admin_permissions_application_update',
195 pattern='/permissions/application/update')
439 pattern='/permissions/application/update')
440 config.add_view(
441 AdminPermissionsView,
442 attr='permissions_application_update',
443 route_name='admin_permissions_application_update', request_method='POST',
444 renderer='rhodecode:templates/admin/permissions/permissions.mako')
196
445
197 config.add_route(
446 config.add_route(
198 name='admin_permissions_global',
447 name='admin_permissions_global',
199 pattern='/permissions/global')
448 pattern='/permissions/global')
449 config.add_view(
450 AdminPermissionsView,
451 attr='permissions_global',
452 route_name='admin_permissions_global', request_method='GET',
453 renderer='rhodecode:templates/admin/permissions/permissions.mako')
454
200 config.add_route(
455 config.add_route(
201 name='admin_permissions_global_update',
456 name='admin_permissions_global_update',
202 pattern='/permissions/global/update')
457 pattern='/permissions/global/update')
458 config.add_view(
459 AdminPermissionsView,
460 attr='permissions_global_update',
461 route_name='admin_permissions_global_update', request_method='POST',
462 renderer='rhodecode:templates/admin/permissions/permissions.mako')
203
463
204 config.add_route(
464 config.add_route(
205 name='admin_permissions_object',
465 name='admin_permissions_object',
206 pattern='/permissions/object')
466 pattern='/permissions/object')
467 config.add_view(
468 AdminPermissionsView,
469 attr='permissions_objects',
470 route_name='admin_permissions_object', request_method='GET',
471 renderer='rhodecode:templates/admin/permissions/permissions.mako')
472
207 config.add_route(
473 config.add_route(
208 name='admin_permissions_object_update',
474 name='admin_permissions_object_update',
209 pattern='/permissions/object/update')
475 pattern='/permissions/object/update')
476 config.add_view(
477 AdminPermissionsView,
478 attr='permissions_objects_update',
479 route_name='admin_permissions_object_update', request_method='POST',
480 renderer='rhodecode:templates/admin/permissions/permissions.mako')
210
481
211 # Branch perms EE feature
482 # Branch perms EE feature
212 config.add_route(
483 config.add_route(
213 name='admin_permissions_branch',
484 name='admin_permissions_branch',
214 pattern='/permissions/branch')
485 pattern='/permissions/branch')
486 config.add_view(
487 AdminPermissionsView,
488 attr='permissions_branch',
489 route_name='admin_permissions_branch', request_method='GET',
490 renderer='rhodecode:templates/admin/permissions/permissions.mako')
215
491
216 config.add_route(
492 config.add_route(
217 name='admin_permissions_ips',
493 name='admin_permissions_ips',
218 pattern='/permissions/ips')
494 pattern='/permissions/ips')
495 config.add_view(
496 AdminPermissionsView,
497 attr='permissions_ips',
498 route_name='admin_permissions_ips', request_method='GET',
499 renderer='rhodecode:templates/admin/permissions/permissions.mako')
219
500
220 config.add_route(
501 config.add_route(
221 name='admin_permissions_overview',
502 name='admin_permissions_overview',
222 pattern='/permissions/overview')
503 pattern='/permissions/overview')
504 config.add_view(
505 AdminPermissionsView,
506 attr='permissions_overview',
507 route_name='admin_permissions_overview', request_method='GET',
508 renderer='rhodecode:templates/admin/permissions/permissions.mako')
223
509
224 config.add_route(
510 config.add_route(
225 name='admin_permissions_auth_token_access',
511 name='admin_permissions_auth_token_access',
226 pattern='/permissions/auth_token_access')
512 pattern='/permissions/auth_token_access')
513 config.add_view(
514 AdminPermissionsView,
515 attr='auth_token_access',
516 route_name='admin_permissions_auth_token_access', request_method='GET',
517 renderer='rhodecode:templates/admin/permissions/permissions.mako')
227
518
228 config.add_route(
519 config.add_route(
229 name='admin_permissions_ssh_keys',
520 name='admin_permissions_ssh_keys',
230 pattern='/permissions/ssh_keys')
521 pattern='/permissions/ssh_keys')
522 config.add_view(
523 AdminPermissionsView,
524 attr='ssh_keys',
525 route_name='admin_permissions_ssh_keys', request_method='GET',
526 renderer='rhodecode:templates/admin/permissions/permissions.mako')
527
231 config.add_route(
528 config.add_route(
232 name='admin_permissions_ssh_keys_data',
529 name='admin_permissions_ssh_keys_data',
233 pattern='/permissions/ssh_keys/data')
530 pattern='/permissions/ssh_keys/data')
531 config.add_view(
532 AdminPermissionsView,
533 attr='ssh_keys_data',
534 route_name='admin_permissions_ssh_keys_data', request_method='GET',
535 renderer='json_ext', xhr=True)
536
234 config.add_route(
537 config.add_route(
235 name='admin_permissions_ssh_keys_update',
538 name='admin_permissions_ssh_keys_update',
236 pattern='/permissions/ssh_keys/update')
539 pattern='/permissions/ssh_keys/update')
540 config.add_view(
541 AdminPermissionsView,
542 attr='ssh_keys_update',
543 route_name='admin_permissions_ssh_keys_update', request_method='POST',
544 renderer='rhodecode:templates/admin/permissions/permissions.mako')
237
545
238 # users admin
546 # users admin
239 config.add_route(
547 config.add_route(
240 name='users',
548 name='users',
241 pattern='/users')
549 pattern='/users')
550 config.add_view(
551 AdminUsersView,
552 attr='users_list',
553 route_name='users', request_method='GET',
554 renderer='rhodecode:templates/admin/users/users.mako')
242
555
243 config.add_route(
556 config.add_route(
244 name='users_data',
557 name='users_data',
245 pattern='/users_data')
558 pattern='/users_data')
559 config.add_view(
560 AdminUsersView,
561 attr='users_list_data',
562 # renderer defined below
563 route_name='users_data', request_method='GET',
564 renderer='json_ext', xhr=True)
246
565
247 config.add_route(
566 config.add_route(
248 name='users_create',
567 name='users_create',
249 pattern='/users/create')
568 pattern='/users/create')
569 config.add_view(
570 AdminUsersView,
571 attr='users_create',
572 route_name='users_create', request_method='POST',
573 renderer='rhodecode:templates/admin/users/user_add.mako')
250
574
251 config.add_route(
575 config.add_route(
252 name='users_new',
576 name='users_new',
253 pattern='/users/new')
577 pattern='/users/new')
578 config.add_view(
579 AdminUsersView,
580 attr='users_new',
581 route_name='users_new', request_method='GET',
582 renderer='rhodecode:templates/admin/users/user_add.mako')
254
583
255 # user management
584 # user management
256 config.add_route(
585 config.add_route(
257 name='user_edit',
586 name='user_edit',
258 pattern='/users/{user_id:\d+}/edit',
587 pattern='/users/{user_id:\d+}/edit',
259 user_route=True)
588 user_route=True)
589 config.add_view(
590 UsersView,
591 attr='user_edit',
592 route_name='user_edit', request_method='GET',
593 renderer='rhodecode:templates/admin/users/user_edit.mako')
594
260 config.add_route(
595 config.add_route(
261 name='user_edit_advanced',
596 name='user_edit_advanced',
262 pattern='/users/{user_id:\d+}/edit/advanced',
597 pattern='/users/{user_id:\d+}/edit/advanced',
263 user_route=True)
598 user_route=True)
599 config.add_view(
600 UsersView,
601 attr='user_edit_advanced',
602 route_name='user_edit_advanced', request_method='GET',
603 renderer='rhodecode:templates/admin/users/user_edit.mako')
604
264 config.add_route(
605 config.add_route(
265 name='user_edit_global_perms',
606 name='user_edit_global_perms',
266 pattern='/users/{user_id:\d+}/edit/global_permissions',
607 pattern='/users/{user_id:\d+}/edit/global_permissions',
267 user_route=True)
608 user_route=True)
609 config.add_view(
610 UsersView,
611 attr='user_edit_global_perms',
612 route_name='user_edit_global_perms', request_method='GET',
613 renderer='rhodecode:templates/admin/users/user_edit.mako')
614
268 config.add_route(
615 config.add_route(
269 name='user_edit_global_perms_update',
616 name='user_edit_global_perms_update',
270 pattern='/users/{user_id:\d+}/edit/global_permissions/update',
617 pattern='/users/{user_id:\d+}/edit/global_permissions/update',
271 user_route=True)
618 user_route=True)
619 config.add_view(
620 UsersView,
621 attr='user_edit_global_perms_update',
622 route_name='user_edit_global_perms_update', request_method='POST',
623 renderer='rhodecode:templates/admin/users/user_edit.mako')
624
272 config.add_route(
625 config.add_route(
273 name='user_update',
626 name='user_update',
274 pattern='/users/{user_id:\d+}/update',
627 pattern='/users/{user_id:\d+}/update',
275 user_route=True)
628 user_route=True)
629 config.add_view(
630 UsersView,
631 attr='user_update',
632 route_name='user_update', request_method='POST',
633 renderer='rhodecode:templates/admin/users/user_edit.mako')
634
276 config.add_route(
635 config.add_route(
277 name='user_delete',
636 name='user_delete',
278 pattern='/users/{user_id:\d+}/delete',
637 pattern='/users/{user_id:\d+}/delete',
279 user_route=True)
638 user_route=True)
639 config.add_view(
640 UsersView,
641 attr='user_delete',
642 route_name='user_delete', request_method='POST',
643 renderer='rhodecode:templates/admin/users/user_edit.mako')
644
280 config.add_route(
645 config.add_route(
281 name='user_enable_force_password_reset',
646 name='user_enable_force_password_reset',
282 pattern='/users/{user_id:\d+}/password_reset_enable',
647 pattern='/users/{user_id:\d+}/password_reset_enable',
283 user_route=True)
648 user_route=True)
649 config.add_view(
650 UsersView,
651 attr='user_enable_force_password_reset',
652 route_name='user_enable_force_password_reset', request_method='POST',
653 renderer='rhodecode:templates/admin/users/user_edit.mako')
654
284 config.add_route(
655 config.add_route(
285 name='user_disable_force_password_reset',
656 name='user_disable_force_password_reset',
286 pattern='/users/{user_id:\d+}/password_reset_disable',
657 pattern='/users/{user_id:\d+}/password_reset_disable',
287 user_route=True)
658 user_route=True)
659 config.add_view(
660 UsersView,
661 attr='user_disable_force_password_reset',
662 route_name='user_disable_force_password_reset', request_method='POST',
663 renderer='rhodecode:templates/admin/users/user_edit.mako')
664
288 config.add_route(
665 config.add_route(
289 name='user_create_personal_repo_group',
666 name='user_create_personal_repo_group',
290 pattern='/users/{user_id:\d+}/create_repo_group',
667 pattern='/users/{user_id:\d+}/create_repo_group',
291 user_route=True)
668 user_route=True)
669 config.add_view(
670 UsersView,
671 attr='user_create_personal_repo_group',
672 route_name='user_create_personal_repo_group', request_method='POST',
673 renderer='rhodecode:templates/admin/users/user_edit.mako')
292
674
293 # user notice
675 # user notice
294 config.add_route(
676 config.add_route(
295 name='user_notice_dismiss',
677 name='user_notice_dismiss',
296 pattern='/users/{user_id:\d+}/notice_dismiss',
678 pattern='/users/{user_id:\d+}/notice_dismiss',
297 user_route=True)
679 user_route=True)
680 config.add_view(
681 UsersView,
682 attr='user_notice_dismiss',
683 route_name='user_notice_dismiss', request_method='POST',
684 renderer='json_ext', xhr=True)
298
685
299 # user auth tokens
686 # user auth tokens
300 config.add_route(
687 config.add_route(
301 name='edit_user_auth_tokens',
688 name='edit_user_auth_tokens',
302 pattern='/users/{user_id:\d+}/edit/auth_tokens',
689 pattern='/users/{user_id:\d+}/edit/auth_tokens',
303 user_route=True)
690 user_route=True)
691 config.add_view(
692 UsersView,
693 attr='auth_tokens',
694 route_name='edit_user_auth_tokens', request_method='GET',
695 renderer='rhodecode:templates/admin/users/user_edit.mako')
696
304 config.add_route(
697 config.add_route(
305 name='edit_user_auth_tokens_view',
698 name='edit_user_auth_tokens_view',
306 pattern='/users/{user_id:\d+}/edit/auth_tokens/view',
699 pattern='/users/{user_id:\d+}/edit/auth_tokens/view',
307 user_route=True)
700 user_route=True)
701 config.add_view(
702 UsersView,
703 attr='auth_tokens_view',
704 route_name='edit_user_auth_tokens_view', request_method='POST',
705 renderer='json_ext', xhr=True)
706
308 config.add_route(
707 config.add_route(
309 name='edit_user_auth_tokens_add',
708 name='edit_user_auth_tokens_add',
310 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
709 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
311 user_route=True)
710 user_route=True)
711 config.add_view(
712 UsersView,
713 attr='auth_tokens_add',
714 route_name='edit_user_auth_tokens_add', request_method='POST')
715
312 config.add_route(
716 config.add_route(
313 name='edit_user_auth_tokens_delete',
717 name='edit_user_auth_tokens_delete',
314 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete',
718 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete',
315 user_route=True)
719 user_route=True)
720 config.add_view(
721 UsersView,
722 attr='auth_tokens_delete',
723 route_name='edit_user_auth_tokens_delete', request_method='POST')
316
724
317 # user ssh keys
725 # user ssh keys
318 config.add_route(
726 config.add_route(
319 name='edit_user_ssh_keys',
727 name='edit_user_ssh_keys',
320 pattern='/users/{user_id:\d+}/edit/ssh_keys',
728 pattern='/users/{user_id:\d+}/edit/ssh_keys',
321 user_route=True)
729 user_route=True)
730 config.add_view(
731 UsersView,
732 attr='ssh_keys',
733 route_name='edit_user_ssh_keys', request_method='GET',
734 renderer='rhodecode:templates/admin/users/user_edit.mako')
735
322 config.add_route(
736 config.add_route(
323 name='edit_user_ssh_keys_generate_keypair',
737 name='edit_user_ssh_keys_generate_keypair',
324 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate',
738 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate',
325 user_route=True)
739 user_route=True)
740 config.add_view(
741 UsersView,
742 attr='ssh_keys_generate_keypair',
743 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
744 renderer='rhodecode:templates/admin/users/user_edit.mako')
745
326 config.add_route(
746 config.add_route(
327 name='edit_user_ssh_keys_add',
747 name='edit_user_ssh_keys_add',
328 pattern='/users/{user_id:\d+}/edit/ssh_keys/new',
748 pattern='/users/{user_id:\d+}/edit/ssh_keys/new',
329 user_route=True)
749 user_route=True)
750 config.add_view(
751 UsersView,
752 attr='ssh_keys_add',
753 route_name='edit_user_ssh_keys_add', request_method='POST')
754
330 config.add_route(
755 config.add_route(
331 name='edit_user_ssh_keys_delete',
756 name='edit_user_ssh_keys_delete',
332 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete',
757 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete',
333 user_route=True)
758 user_route=True)
759 config.add_view(
760 UsersView,
761 attr='ssh_keys_delete',
762 route_name='edit_user_ssh_keys_delete', request_method='POST')
334
763
335 # user emails
764 # user emails
336 config.add_route(
765 config.add_route(
337 name='edit_user_emails',
766 name='edit_user_emails',
338 pattern='/users/{user_id:\d+}/edit/emails',
767 pattern='/users/{user_id:\d+}/edit/emails',
339 user_route=True)
768 user_route=True)
769 config.add_view(
770 UsersView,
771 attr='emails',
772 route_name='edit_user_emails', request_method='GET',
773 renderer='rhodecode:templates/admin/users/user_edit.mako')
774
340 config.add_route(
775 config.add_route(
341 name='edit_user_emails_add',
776 name='edit_user_emails_add',
342 pattern='/users/{user_id:\d+}/edit/emails/new',
777 pattern='/users/{user_id:\d+}/edit/emails/new',
343 user_route=True)
778 user_route=True)
779 config.add_view(
780 UsersView,
781 attr='emails_add',
782 route_name='edit_user_emails_add', request_method='POST')
783
344 config.add_route(
784 config.add_route(
345 name='edit_user_emails_delete',
785 name='edit_user_emails_delete',
346 pattern='/users/{user_id:\d+}/edit/emails/delete',
786 pattern='/users/{user_id:\d+}/edit/emails/delete',
347 user_route=True)
787 user_route=True)
788 config.add_view(
789 UsersView,
790 attr='emails_delete',
791 route_name='edit_user_emails_delete', request_method='POST')
348
792
349 # user IPs
793 # user IPs
350 config.add_route(
794 config.add_route(
351 name='edit_user_ips',
795 name='edit_user_ips',
352 pattern='/users/{user_id:\d+}/edit/ips',
796 pattern='/users/{user_id:\d+}/edit/ips',
353 user_route=True)
797 user_route=True)
798 config.add_view(
799 UsersView,
800 attr='ips',
801 route_name='edit_user_ips', request_method='GET',
802 renderer='rhodecode:templates/admin/users/user_edit.mako')
803
354 config.add_route(
804 config.add_route(
355 name='edit_user_ips_add',
805 name='edit_user_ips_add',
356 pattern='/users/{user_id:\d+}/edit/ips/new',
806 pattern='/users/{user_id:\d+}/edit/ips/new',
357 user_route_with_default=True) # enabled for default user too
807 user_route_with_default=True) # enabled for default user too
808 config.add_view(
809 UsersView,
810 attr='ips_add',
811 route_name='edit_user_ips_add', request_method='POST')
812
358 config.add_route(
813 config.add_route(
359 name='edit_user_ips_delete',
814 name='edit_user_ips_delete',
360 pattern='/users/{user_id:\d+}/edit/ips/delete',
815 pattern='/users/{user_id:\d+}/edit/ips/delete',
361 user_route_with_default=True) # enabled for default user too
816 user_route_with_default=True) # enabled for default user too
817 config.add_view(
818 UsersView,
819 attr='ips_delete',
820 route_name='edit_user_ips_delete', request_method='POST')
362
821
363 # user perms
822 # user perms
364 config.add_route(
823 config.add_route(
365 name='edit_user_perms_summary',
824 name='edit_user_perms_summary',
366 pattern='/users/{user_id:\d+}/edit/permissions_summary',
825 pattern='/users/{user_id:\d+}/edit/permissions_summary',
367 user_route=True)
826 user_route=True)
827 config.add_view(
828 UsersView,
829 attr='user_perms_summary',
830 route_name='edit_user_perms_summary', request_method='GET',
831 renderer='rhodecode:templates/admin/users/user_edit.mako')
832
368 config.add_route(
833 config.add_route(
369 name='edit_user_perms_summary_json',
834 name='edit_user_perms_summary_json',
370 pattern='/users/{user_id:\d+}/edit/permissions_summary/json',
835 pattern='/users/{user_id:\d+}/edit/permissions_summary/json',
371 user_route=True)
836 user_route=True)
837 config.add_view(
838 UsersView,
839 attr='user_perms_summary_json',
840 route_name='edit_user_perms_summary_json', request_method='GET',
841 renderer='json_ext')
372
842
373 # user user groups management
843 # user user groups management
374 config.add_route(
844 config.add_route(
375 name='edit_user_groups_management',
845 name='edit_user_groups_management',
376 pattern='/users/{user_id:\d+}/edit/groups_management',
846 pattern='/users/{user_id:\d+}/edit/groups_management',
377 user_route=True)
847 user_route=True)
848 config.add_view(
849 UsersView,
850 attr='groups_management',
851 route_name='edit_user_groups_management', request_method='GET',
852 renderer='rhodecode:templates/admin/users/user_edit.mako')
378
853
379 config.add_route(
854 config.add_route(
380 name='edit_user_groups_management_updates',
855 name='edit_user_groups_management_updates',
381 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates',
856 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates',
382 user_route=True)
857 user_route=True)
858 config.add_view(
859 UsersView,
860 attr='groups_management_updates',
861 route_name='edit_user_groups_management_updates', request_method='POST')
383
862
384 # user audit logs
863 # user audit logs
385 config.add_route(
864 config.add_route(
386 name='edit_user_audit_logs',
865 name='edit_user_audit_logs',
387 pattern='/users/{user_id:\d+}/edit/audit', user_route=True)
866 pattern='/users/{user_id:\d+}/edit/audit', user_route=True)
867 config.add_view(
868 UsersView,
869 attr='user_audit_logs',
870 route_name='edit_user_audit_logs', request_method='GET',
871 renderer='rhodecode:templates/admin/users/user_edit.mako')
388
872
389 config.add_route(
873 config.add_route(
390 name='edit_user_audit_logs_download',
874 name='edit_user_audit_logs_download',
391 pattern='/users/{user_id:\d+}/edit/audit/download', user_route=True)
875 pattern='/users/{user_id:\d+}/edit/audit/download', user_route=True)
876 config.add_view(
877 UsersView,
878 attr='user_audit_logs_download',
879 route_name='edit_user_audit_logs_download', request_method='GET',
880 renderer='string')
392
881
393 # user caches
882 # user caches
394 config.add_route(
883 config.add_route(
395 name='edit_user_caches',
884 name='edit_user_caches',
396 pattern='/users/{user_id:\d+}/edit/caches',
885 pattern='/users/{user_id:\d+}/edit/caches',
397 user_route=True)
886 user_route=True)
887 config.add_view(
888 UsersView,
889 attr='user_caches',
890 route_name='edit_user_caches', request_method='GET',
891 renderer='rhodecode:templates/admin/users/user_edit.mako')
892
398 config.add_route(
893 config.add_route(
399 name='edit_user_caches_update',
894 name='edit_user_caches_update',
400 pattern='/users/{user_id:\d+}/edit/caches/update',
895 pattern='/users/{user_id:\d+}/edit/caches/update',
401 user_route=True)
896 user_route=True)
897 config.add_view(
898 UsersView,
899 attr='user_caches_update',
900 route_name='edit_user_caches_update', request_method='POST')
402
901
403 # user-groups admin
902 # user-groups admin
404 config.add_route(
903 config.add_route(
405 name='user_groups',
904 name='user_groups',
406 pattern='/user_groups')
905 pattern='/user_groups')
906 config.add_view(
907 AdminUserGroupsView,
908 attr='user_groups_list',
909 route_name='user_groups', request_method='GET',
910 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
407
911
408 config.add_route(
912 config.add_route(
409 name='user_groups_data',
913 name='user_groups_data',
410 pattern='/user_groups_data')
914 pattern='/user_groups_data')
915 config.add_view(
916 AdminUserGroupsView,
917 attr='user_groups_list_data',
918 route_name='user_groups_data', request_method='GET',
919 renderer='json_ext', xhr=True)
411
920
412 config.add_route(
921 config.add_route(
413 name='user_groups_new',
922 name='user_groups_new',
414 pattern='/user_groups/new')
923 pattern='/user_groups/new')
924 config.add_view(
925 AdminUserGroupsView,
926 attr='user_groups_new',
927 route_name='user_groups_new', request_method='GET',
928 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
415
929
416 config.add_route(
930 config.add_route(
417 name='user_groups_create',
931 name='user_groups_create',
418 pattern='/user_groups/create')
932 pattern='/user_groups/create')
933 config.add_view(
934 AdminUserGroupsView,
935 attr='user_groups_create',
936 route_name='user_groups_create', request_method='POST',
937 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
419
938
420 # repos admin
939 # repos admin
421 config.add_route(
940 config.add_route(
422 name='repos',
941 name='repos',
423 pattern='/repos')
942 pattern='/repos')
943 config.add_view(
944 AdminReposView,
945 attr='repository_list',
946 route_name='repos', request_method='GET',
947 renderer='rhodecode:templates/admin/repos/repos.mako')
424
948
425 config.add_route(
949 config.add_route(
426 name='repos_data',
950 name='repos_data',
427 pattern='/repos_data')
951 pattern='/repos_data')
952 config.add_view(
953 AdminReposView,
954 attr='repository_list_data',
955 route_name='repos_data', request_method='GET',
956 renderer='json_ext', xhr=True)
428
957
429 config.add_route(
958 config.add_route(
430 name='repo_new',
959 name='repo_new',
431 pattern='/repos/new')
960 pattern='/repos/new')
961 config.add_view(
962 AdminReposView,
963 attr='repository_new',
964 route_name='repo_new', request_method='GET',
965 renderer='rhodecode:templates/admin/repos/repo_add.mako')
432
966
433 config.add_route(
967 config.add_route(
434 name='repo_create',
968 name='repo_create',
435 pattern='/repos/create')
969 pattern='/repos/create')
970 config.add_view(
971 AdminReposView,
972 attr='repository_create',
973 route_name='repo_create', request_method='POST',
974 renderer='rhodecode:templates/admin/repos/repos.mako')
436
975
437 # repo groups admin
976 # repo groups admin
438 config.add_route(
977 config.add_route(
439 name='repo_groups',
978 name='repo_groups',
440 pattern='/repo_groups')
979 pattern='/repo_groups')
980 config.add_view(
981 AdminRepoGroupsView,
982 attr='repo_group_list',
983 route_name='repo_groups', request_method='GET',
984 renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako')
441
985
442 config.add_route(
986 config.add_route(
443 name='repo_groups_data',
987 name='repo_groups_data',
444 pattern='/repo_groups_data')
988 pattern='/repo_groups_data')
989 config.add_view(
990 AdminRepoGroupsView,
991 attr='repo_group_list_data',
992 route_name='repo_groups_data', request_method='GET',
993 renderer='json_ext', xhr=True)
445
994
446 config.add_route(
995 config.add_route(
447 name='repo_group_new',
996 name='repo_group_new',
448 pattern='/repo_group/new')
997 pattern='/repo_group/new')
998 config.add_view(
999 AdminRepoGroupsView,
1000 attr='repo_group_new',
1001 route_name='repo_group_new', request_method='GET',
1002 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
449
1003
450 config.add_route(
1004 config.add_route(
451 name='repo_group_create',
1005 name='repo_group_create',
452 pattern='/repo_group/create')
1006 pattern='/repo_group/create')
1007 config.add_view(
1008 AdminRepoGroupsView,
1009 attr='repo_group_create',
1010 route_name='repo_group_create', request_method='POST',
1011 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
453
1012
454
1013
455 def includeme(config):
1014 def includeme(config):
456 from rhodecode.apps._base.navigation import includeme as nav_includeme
1015 from rhodecode.apps._base.navigation import includeme as nav_includeme
1016 from rhodecode.apps.admin.views.main_views import AdminMainView
457
1017
458 # Create admin navigation registry and add it to the pyramid registry.
1018 # Create admin navigation registry and add it to the pyramid registry.
459 nav_includeme(config)
1019 nav_includeme(config)
460
1020
461 # main admin routes
1021 # main admin routes
462 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
1022 config.add_route(
463 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
1023 name='admin_home', pattern=ADMIN_PREFIX)
1024 config.add_view(
1025 AdminMainView,
1026 attr='admin_main',
1027 route_name='admin_home', request_method='GET',
1028 renderer='rhodecode:templates/admin/main.mako')
1029
1030 # pr global redirect
1031 config.add_route(
1032 name='pull_requests_global_0', # backward compat
1033 pattern=ADMIN_PREFIX + '/pull_requests/{pull_request_id:\d+}')
1034 config.add_view(
1035 AdminMainView,
1036 attr='pull_requests',
1037 route_name='pull_requests_global_0', request_method='GET')
464
1038
465 # Scan module for configuration decorators.
1039 config.add_route(
466 config.scan('.views', ignore='.tests')
1040 name='pull_requests_global_1', # backward compat
1041 pattern=ADMIN_PREFIX + '/pull-requests/{pull_request_id:\d+}')
1042 config.add_view(
1043 AdminMainView,
1044 attr='pull_requests',
1045 route_name='pull_requests_global_1', request_method='GET')
1046
1047 config.add_route(
1048 name='pull_requests_global',
1049 pattern=ADMIN_PREFIX + '/pull-request/{pull_request_id:\d+}')
1050 config.add_view(
1051 AdminMainView,
1052 attr='pull_requests',
1053 route_name='pull_requests_global', request_method='GET')
1054
1055 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
@@ -1,93 +1,87 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from pyramid.httpexceptions import HTTPNotFound
23 from pyramid.httpexceptions import HTTPNotFound
24 from pyramid.view import view_config
25
24
26 from rhodecode.apps._base import BaseAppView
25 from rhodecode.apps._base import BaseAppView
27 from rhodecode.model.db import joinedload, UserLog
26 from rhodecode.model.db import joinedload, UserLog
28 from rhodecode.lib.user_log_filter import user_log_filter
27 from rhodecode.lib.user_log_filter import user_log_filter
29 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
28 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
30 from rhodecode.lib.utils2 import safe_int
29 from rhodecode.lib.utils2 import safe_int
31 from rhodecode.lib.helpers import SqlPage
30 from rhodecode.lib.helpers import SqlPage
32
31
33 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
34
33
35
34
36 class AdminAuditLogsView(BaseAppView):
35 class AdminAuditLogsView(BaseAppView):
36
37 def load_default_context(self):
37 def load_default_context(self):
38 c = self._get_local_tmpl_context()
38 c = self._get_local_tmpl_context()
39 return c
39 return c
40
40
41 @LoginRequired()
41 @LoginRequired()
42 @HasPermissionAllDecorator('hg.admin')
42 @HasPermissionAllDecorator('hg.admin')
43 @view_config(
44 route_name='admin_audit_logs', request_method='GET',
45 renderer='rhodecode:templates/admin/admin_audit_logs.mako')
46 def admin_audit_logs(self):
43 def admin_audit_logs(self):
47 c = self.load_default_context()
44 c = self.load_default_context()
48
45
49 users_log = UserLog.query()\
46 users_log = UserLog.query()\
50 .options(joinedload(UserLog.user))\
47 .options(joinedload(UserLog.user))\
51 .options(joinedload(UserLog.repository))
48 .options(joinedload(UserLog.repository))
52
49
53 # FILTERING
50 # FILTERING
54 c.search_term = self.request.GET.get('filter')
51 c.search_term = self.request.GET.get('filter')
55 try:
52 try:
56 users_log = user_log_filter(users_log, c.search_term)
53 users_log = user_log_filter(users_log, c.search_term)
57 except Exception:
54 except Exception:
58 # we want this to crash for now
55 # we want this to crash for now
59 raise
56 raise
60
57
61 users_log = users_log.order_by(UserLog.action_date.desc())
58 users_log = users_log.order_by(UserLog.action_date.desc())
62
59
63 p = safe_int(self.request.GET.get('page', 1), 1)
60 p = safe_int(self.request.GET.get('page', 1), 1)
64
61
65 def url_generator(page_num):
62 def url_generator(page_num):
66 query_params = {
63 query_params = {
67 'page': page_num
64 'page': page_num
68 }
65 }
69 if c.search_term:
66 if c.search_term:
70 query_params['filter'] = c.search_term
67 query_params['filter'] = c.search_term
71 return self.request.current_route_path(_query=query_params)
68 return self.request.current_route_path(_query=query_params)
72
69
73 c.audit_logs = SqlPage(users_log, page=p, items_per_page=10,
70 c.audit_logs = SqlPage(users_log, page=p, items_per_page=10,
74 url_maker=url_generator)
71 url_maker=url_generator)
75 return self._get_template_context(c)
72 return self._get_template_context(c)
76
73
77 @LoginRequired()
74 @LoginRequired()
78 @HasPermissionAllDecorator('hg.admin')
75 @HasPermissionAllDecorator('hg.admin')
79 @view_config(
80 route_name='admin_audit_log_entry', request_method='GET',
81 renderer='rhodecode:templates/admin/admin_audit_log_entry.mako')
82 def admin_audit_log_entry(self):
76 def admin_audit_log_entry(self):
83 c = self.load_default_context()
77 c = self.load_default_context()
84 audit_log_id = self.request.matchdict['audit_log_id']
78 audit_log_id = self.request.matchdict['audit_log_id']
85
79
86 c.audit_log_entry = UserLog.query()\
80 c.audit_log_entry = UserLog.query()\
87 .options(joinedload(UserLog.user))\
81 .options(joinedload(UserLog.user))\
88 .options(joinedload(UserLog.repository))\
82 .options(joinedload(UserLog.repository))\
89 .filter(UserLog.user_log_id == audit_log_id).scalar()
83 .filter(UserLog.user_log_id == audit_log_id).scalar()
90 if not c.audit_log_entry:
84 if not c.audit_log_entry:
91 raise HTTPNotFound()
85 raise HTTPNotFound()
92
86
93 return self._get_template_context(c)
87 return self._get_template_context(c)
@@ -1,111 +1,103 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.view import view_config
27 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
28 from pyramid.renderers import render
27 from pyramid.renderers import render
29 from pyramid.response import Response
28 from pyramid.response import Response
30
29
31 from rhodecode.apps._base import BaseAppView
30 from rhodecode.apps._base import BaseAppView
32 from rhodecode.lib.auth import (
31 from rhodecode.lib.auth import (
33 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
32 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
34 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
35 from rhodecode.model.forms import DefaultsForm
34 from rhodecode.model.forms import DefaultsForm
36 from rhodecode.model.meta import Session
35 from rhodecode.model.meta import Session
37 from rhodecode import BACKENDS
36 from rhodecode import BACKENDS
38 from rhodecode.model.settings import SettingsModel
37 from rhodecode.model.settings import SettingsModel
39
38
40 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
41
40
42
41
43 class AdminDefaultSettingsView(BaseAppView):
42 class AdminDefaultSettingsView(BaseAppView):
43
44 def load_default_context(self):
44 def load_default_context(self):
45 c = self._get_local_tmpl_context()
45 c = self._get_local_tmpl_context()
46
47
48 return c
46 return c
49
47
50 @LoginRequired()
48 @LoginRequired()
51 @HasPermissionAllDecorator('hg.admin')
49 @HasPermissionAllDecorator('hg.admin')
52 @view_config(
53 route_name='admin_defaults_repositories', request_method='GET',
54 renderer='rhodecode:templates/admin/defaults/defaults.mako')
55 def defaults_repository_show(self):
50 def defaults_repository_show(self):
56 c = self.load_default_context()
51 c = self.load_default_context()
57 c.backends = BACKENDS.keys()
52 c.backends = BACKENDS.keys()
58 c.active = 'repositories'
53 c.active = 'repositories'
59 defaults = SettingsModel().get_default_repo_settings()
54 defaults = SettingsModel().get_default_repo_settings()
60
55
61 data = render(
56 data = render(
62 'rhodecode:templates/admin/defaults/defaults.mako',
57 'rhodecode:templates/admin/defaults/defaults.mako',
63 self._get_template_context(c), self.request)
58 self._get_template_context(c), self.request)
64 html = formencode.htmlfill.render(
59 html = formencode.htmlfill.render(
65 data,
60 data,
66 defaults=defaults,
61 defaults=defaults,
67 encoding="UTF-8",
62 encoding="UTF-8",
68 force_defaults=False
63 force_defaults=False
69 )
64 )
70 return Response(html)
65 return Response(html)
71
66
72 @LoginRequired()
67 @LoginRequired()
73 @HasPermissionAllDecorator('hg.admin')
68 @HasPermissionAllDecorator('hg.admin')
74 @CSRFRequired()
69 @CSRFRequired()
75 @view_config(
76 route_name='admin_defaults_repositories_update', request_method='POST',
77 renderer='rhodecode:templates/admin/defaults/defaults.mako')
78 def defaults_repository_update(self):
70 def defaults_repository_update(self):
79 _ = self.request.translate
71 _ = self.request.translate
80 c = self.load_default_context()
72 c = self.load_default_context()
81 c.active = 'repositories'
73 c.active = 'repositories'
82 form = DefaultsForm(self.request.translate)()
74 form = DefaultsForm(self.request.translate)()
83
75
84 try:
76 try:
85 form_result = form.to_python(dict(self.request.POST))
77 form_result = form.to_python(dict(self.request.POST))
86 for k, v in form_result.iteritems():
78 for k, v in form_result.iteritems():
87 setting = SettingsModel().create_or_update_setting(k, v)
79 setting = SettingsModel().create_or_update_setting(k, v)
88 Session().add(setting)
80 Session().add(setting)
89 Session().commit()
81 Session().commit()
90 h.flash(_('Default settings updated successfully'),
82 h.flash(_('Default settings updated successfully'),
91 category='success')
83 category='success')
92
84
93 except formencode.Invalid as errors:
85 except formencode.Invalid as errors:
94 data = render(
86 data = render(
95 'rhodecode:templates/admin/defaults/defaults.mako',
87 'rhodecode:templates/admin/defaults/defaults.mako',
96 self._get_template_context(c), self.request)
88 self._get_template_context(c), self.request)
97 html = formencode.htmlfill.render(
89 html = formencode.htmlfill.render(
98 data,
90 data,
99 defaults=errors.value,
91 defaults=errors.value,
100 errors=errors.error_dict or {},
92 errors=errors.error_dict or {},
101 prefix_error=False,
93 prefix_error=False,
102 encoding="UTF-8",
94 encoding="UTF-8",
103 force_defaults=False
95 force_defaults=False
104 )
96 )
105 return Response(html)
97 return Response(html)
106 except Exception:
98 except Exception:
107 log.exception('Exception in update action')
99 log.exception('Exception in update action')
108 h.flash(_('Error occurred during update of default values'),
100 h.flash(_('Error occurred during update of default values'),
109 category='error')
101 category='error')
110
102
111 raise HTTPFound(h.route_path('admin_defaults_repositories'))
103 raise HTTPFound(h.route_path('admin_defaults_repositories'))
@@ -1,174 +1,161 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2018-2020 RhodeCode GmbH
3 # Copyright (C) 2018-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import os
20 import os
21 import logging
21 import logging
22
22
23 from pyramid.httpexceptions import HTTPFound
23 from pyramid.httpexceptions import HTTPFound
24 from pyramid.view import view_config
25
24
26 from rhodecode.apps._base import BaseAppView
25 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base.navigation import navigation_list
26 from rhodecode.apps._base.navigation import navigation_list
28 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
29 from rhodecode.lib.auth import (
28 from rhodecode.lib.auth import (
30 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
29 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
31 from rhodecode.lib.utils2 import time_to_utcdatetime, safe_int
30 from rhodecode.lib.utils2 import time_to_utcdatetime, safe_int
32 from rhodecode.lib import exc_tracking
31 from rhodecode.lib import exc_tracking
33
32
34 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
35
34
36
35
37 class ExceptionsTrackerView(BaseAppView):
36 class ExceptionsTrackerView(BaseAppView):
38 def load_default_context(self):
37 def load_default_context(self):
39 c = self._get_local_tmpl_context()
38 c = self._get_local_tmpl_context()
40 c.navlist = navigation_list(self.request)
39 c.navlist = navigation_list(self.request)
41 return c
40 return c
42
41
43 def count_all_exceptions(self):
42 def count_all_exceptions(self):
44 exc_store_path = exc_tracking.get_exc_store()
43 exc_store_path = exc_tracking.get_exc_store()
45 count = 0
44 count = 0
46 for fname in os.listdir(exc_store_path):
45 for fname in os.listdir(exc_store_path):
47 parts = fname.split('_', 2)
46 parts = fname.split('_', 2)
48 if not len(parts) == 3:
47 if not len(parts) == 3:
49 continue
48 continue
50 count +=1
49 count +=1
51 return count
50 return count
52
51
53 def get_all_exceptions(self, read_metadata=False, limit=None, type_filter=None):
52 def get_all_exceptions(self, read_metadata=False, limit=None, type_filter=None):
54 exc_store_path = exc_tracking.get_exc_store()
53 exc_store_path = exc_tracking.get_exc_store()
55 exception_list = []
54 exception_list = []
56
55
57 def key_sorter(val):
56 def key_sorter(val):
58 try:
57 try:
59 return val.split('_')[-1]
58 return val.split('_')[-1]
60 except Exception:
59 except Exception:
61 return 0
60 return 0
62
61
63 for fname in reversed(sorted(os.listdir(exc_store_path), key=key_sorter)):
62 for fname in reversed(sorted(os.listdir(exc_store_path), key=key_sorter)):
64
63
65 parts = fname.split('_', 2)
64 parts = fname.split('_', 2)
66 if not len(parts) == 3:
65 if not len(parts) == 3:
67 continue
66 continue
68
67
69 exc_id, app_type, exc_timestamp = parts
68 exc_id, app_type, exc_timestamp = parts
70
69
71 exc = {'exc_id': exc_id, 'app_type': app_type, 'exc_type': 'unknown',
70 exc = {'exc_id': exc_id, 'app_type': app_type, 'exc_type': 'unknown',
72 'exc_utc_date': '', 'exc_timestamp': exc_timestamp}
71 'exc_utc_date': '', 'exc_timestamp': exc_timestamp}
73
72
74 if read_metadata:
73 if read_metadata:
75 full_path = os.path.join(exc_store_path, fname)
74 full_path = os.path.join(exc_store_path, fname)
76 if not os.path.isfile(full_path):
75 if not os.path.isfile(full_path):
77 continue
76 continue
78 try:
77 try:
79 # we can read our metadata
78 # we can read our metadata
80 with open(full_path, 'rb') as f:
79 with open(full_path, 'rb') as f:
81 exc_metadata = exc_tracking.exc_unserialize(f.read())
80 exc_metadata = exc_tracking.exc_unserialize(f.read())
82 exc.update(exc_metadata)
81 exc.update(exc_metadata)
83 except Exception:
82 except Exception:
84 log.exception('Failed to read exc data from:{}'.format(full_path))
83 log.exception('Failed to read exc data from:{}'.format(full_path))
85 pass
84 pass
86 # convert our timestamp to a date obj, for nicer representation
85 # convert our timestamp to a date obj, for nicer representation
87 exc['exc_utc_date'] = time_to_utcdatetime(exc['exc_timestamp'])
86 exc['exc_utc_date'] = time_to_utcdatetime(exc['exc_timestamp'])
88
87
89 type_present = exc.get('exc_type')
88 type_present = exc.get('exc_type')
90 if type_filter:
89 if type_filter:
91 if type_present and type_present == type_filter:
90 if type_present and type_present == type_filter:
92 exception_list.append(exc)
91 exception_list.append(exc)
93 else:
92 else:
94 exception_list.append(exc)
93 exception_list.append(exc)
95
94
96 if limit and len(exception_list) >= limit:
95 if limit and len(exception_list) >= limit:
97 break
96 break
98 return exception_list
97 return exception_list
99
98
100 @LoginRequired()
99 @LoginRequired()
101 @HasPermissionAllDecorator('hg.admin')
100 @HasPermissionAllDecorator('hg.admin')
102 @view_config(
103 route_name='admin_settings_exception_tracker', request_method='GET',
104 renderer='rhodecode:templates/admin/settings/settings.mako')
105 def browse_exceptions(self):
101 def browse_exceptions(self):
106 _ = self.request.translate
102 _ = self.request.translate
107 c = self.load_default_context()
103 c = self.load_default_context()
108 c.active = 'exceptions_browse'
104 c.active = 'exceptions_browse'
109 c.limit = safe_int(self.request.GET.get('limit')) or 50
105 c.limit = safe_int(self.request.GET.get('limit')) or 50
110 c.type_filter = self.request.GET.get('type_filter')
106 c.type_filter = self.request.GET.get('type_filter')
111 c.next_limit = c.limit + 50
107 c.next_limit = c.limit + 50
112 c.exception_list = self.get_all_exceptions(
108 c.exception_list = self.get_all_exceptions(
113 read_metadata=True, limit=c.limit, type_filter=c.type_filter)
109 read_metadata=True, limit=c.limit, type_filter=c.type_filter)
114 c.exception_list_count = self.count_all_exceptions()
110 c.exception_list_count = self.count_all_exceptions()
115 c.exception_store_dir = exc_tracking.get_exc_store()
111 c.exception_store_dir = exc_tracking.get_exc_store()
116 return self._get_template_context(c)
112 return self._get_template_context(c)
117
113
118 @LoginRequired()
114 @LoginRequired()
119 @HasPermissionAllDecorator('hg.admin')
115 @HasPermissionAllDecorator('hg.admin')
120 @view_config(
121 route_name='admin_settings_exception_tracker_show', request_method='GET',
122 renderer='rhodecode:templates/admin/settings/settings.mako')
123 def exception_show(self):
116 def exception_show(self):
124 _ = self.request.translate
117 _ = self.request.translate
125 c = self.load_default_context()
118 c = self.load_default_context()
126
119
127 c.active = 'exceptions'
120 c.active = 'exceptions'
128 c.exception_id = self.request.matchdict['exception_id']
121 c.exception_id = self.request.matchdict['exception_id']
129 c.traceback = exc_tracking.read_exception(c.exception_id, prefix=None)
122 c.traceback = exc_tracking.read_exception(c.exception_id, prefix=None)
130 return self._get_template_context(c)
123 return self._get_template_context(c)
131
124
132 @LoginRequired()
125 @LoginRequired()
133 @HasPermissionAllDecorator('hg.admin')
126 @HasPermissionAllDecorator('hg.admin')
134 @CSRFRequired()
127 @CSRFRequired()
135 @view_config(
136 route_name='admin_settings_exception_tracker_delete_all', request_method='POST',
137 renderer='rhodecode:templates/admin/settings/settings.mako')
138 def exception_delete_all(self):
128 def exception_delete_all(self):
139 _ = self.request.translate
129 _ = self.request.translate
140 c = self.load_default_context()
130 c = self.load_default_context()
141 type_filter = self.request.POST.get('type_filter')
131 type_filter = self.request.POST.get('type_filter')
142
132
143 c.active = 'exceptions'
133 c.active = 'exceptions'
144 all_exc = self.get_all_exceptions(read_metadata=bool(type_filter), type_filter=type_filter)
134 all_exc = self.get_all_exceptions(read_metadata=bool(type_filter), type_filter=type_filter)
145 exc_count = 0
135 exc_count = 0
146
136
147 for exc in all_exc:
137 for exc in all_exc:
148 if type_filter:
138 if type_filter:
149 if exc.get('exc_type') == type_filter:
139 if exc.get('exc_type') == type_filter:
150 exc_tracking.delete_exception(exc['exc_id'], prefix=None)
140 exc_tracking.delete_exception(exc['exc_id'], prefix=None)
151 exc_count += 1
141 exc_count += 1
152 else:
142 else:
153 exc_tracking.delete_exception(exc['exc_id'], prefix=None)
143 exc_tracking.delete_exception(exc['exc_id'], prefix=None)
154 exc_count += 1
144 exc_count += 1
155
145
156 h.flash(_('Removed {} Exceptions').format(exc_count), category='success')
146 h.flash(_('Removed {} Exceptions').format(exc_count), category='success')
157 raise HTTPFound(h.route_path('admin_settings_exception_tracker'))
147 raise HTTPFound(h.route_path('admin_settings_exception_tracker'))
158
148
159 @LoginRequired()
149 @LoginRequired()
160 @HasPermissionAllDecorator('hg.admin')
150 @HasPermissionAllDecorator('hg.admin')
161 @CSRFRequired()
151 @CSRFRequired()
162 @view_config(
163 route_name='admin_settings_exception_tracker_delete', request_method='POST',
164 renderer='rhodecode:templates/admin/settings/settings.mako')
165 def exception_delete(self):
152 def exception_delete(self):
166 _ = self.request.translate
153 _ = self.request.translate
167 c = self.load_default_context()
154 c = self.load_default_context()
168
155
169 c.active = 'exceptions'
156 c.active = 'exceptions'
170 c.exception_id = self.request.matchdict['exception_id']
157 c.exception_id = self.request.matchdict['exception_id']
171 exc_tracking.delete_exception(c.exception_id, prefix=None)
158 exc_tracking.delete_exception(c.exception_id, prefix=None)
172
159
173 h.flash(_('Removed Exception {}').format(c.exception_id), category='success')
160 h.flash(_('Removed Exception {}').format(c.exception_id), category='success')
174 raise HTTPFound(h.route_path('admin_settings_exception_tracker'))
161 raise HTTPFound(h.route_path('admin_settings_exception_tracker'))
@@ -1,79 +1,72 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
23 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
24 from pyramid.view import view_config
25
24
26 from rhodecode.apps._base import BaseAppView
25 from rhodecode.apps._base import BaseAppView
27 from rhodecode.lib import helpers as h
26 from rhodecode.lib import helpers as h
28 from rhodecode.lib.auth import (LoginRequired, NotAnonymous, HasRepoPermissionAny)
27 from rhodecode.lib.auth import (LoginRequired, NotAnonymous, HasRepoPermissionAny)
29 from rhodecode.model.db import PullRequest
28 from rhodecode.model.db import PullRequest
30
29
31
30
32 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
33
32
34
33
35 class AdminMainView(BaseAppView):
34 class AdminMainView(BaseAppView):
36 def load_default_context(self):
35 def load_default_context(self):
37 c = self._get_local_tmpl_context()
36 c = self._get_local_tmpl_context()
38 return c
37 return c
39
38
40 @LoginRequired()
39 @LoginRequired()
41 @NotAnonymous()
40 @NotAnonymous()
42 @view_config(
43 route_name='admin_home', request_method='GET',
44 renderer='rhodecode:templates/admin/main.mako')
45 def admin_main(self):
41 def admin_main(self):
46 c = self.load_default_context()
42 c = self.load_default_context()
47 c.active = 'admin'
43 c.active = 'admin'
48
44
49 if not (c.is_super_admin or c.is_delegated_admin):
45 if not (c.is_super_admin or c.is_delegated_admin):
50 raise HTTPNotFound()
46 raise HTTPNotFound()
51
47
52 return self._get_template_context(c)
48 return self._get_template_context(c)
53
49
54 @LoginRequired()
50 @LoginRequired()
55 @view_config(route_name='pull_requests_global_0', request_method='GET')
56 @view_config(route_name='pull_requests_global_1', request_method='GET')
57 @view_config(route_name='pull_requests_global', request_method='GET')
58 def pull_requests(self):
51 def pull_requests(self):
59 """
52 """
60 Global redirect for Pull Requests
53 Global redirect for Pull Requests
61 pull_request_id: id of pull requests in the system
54 pull_request_id: id of pull requests in the system
62 """
55 """
63
56
64 pull_request = PullRequest.get_or_404(
57 pull_request = PullRequest.get_or_404(
65 self.request.matchdict['pull_request_id'])
58 self.request.matchdict['pull_request_id'])
66 pull_request_id = pull_request.pull_request_id
59 pull_request_id = pull_request.pull_request_id
67
60
68 repo_name = pull_request.target_repo.repo_name
61 repo_name = pull_request.target_repo.repo_name
69 # NOTE(marcink):
62 # NOTE(marcink):
70 # check permissions so we don't redirect to repo that we don't have access to
63 # check permissions so we don't redirect to repo that we don't have access to
71 # exposing it's name
64 # exposing it's name
72 target_repo_perm = HasRepoPermissionAny(
65 target_repo_perm = HasRepoPermissionAny(
73 'repository.read', 'repository.write', 'repository.admin')(repo_name)
66 'repository.read', 'repository.write', 'repository.admin')(repo_name)
74 if not target_repo_perm:
67 if not target_repo_perm:
75 raise HTTPNotFound()
68 raise HTTPNotFound()
76
69
77 raise HTTPFound(
70 raise HTTPFound(
78 h.route_path('pullrequest_show', repo_name=repo_name,
71 h.route_path('pullrequest_show', repo_name=repo_name,
79 pull_request_id=pull_request_id))
72 pull_request_id=pull_request_id))
@@ -1,51 +1,46 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import collections
21 import collections
22 import logging
22 import logging
23
23
24 from pyramid.view import view_config
25
26 from rhodecode.apps._base import BaseAppView
24 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base.navigation import navigation_list
25 from rhodecode.apps._base.navigation import navigation_list
28 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
26 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
29 from rhodecode.lib.utils import read_opensource_licenses
27 from rhodecode.lib.utils import read_opensource_licenses
30
28
31 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
32
30
33
31
34 class OpenSourceLicensesAdminSettingsView(BaseAppView):
32 class OpenSourceLicensesAdminSettingsView(BaseAppView):
35
33
36 def load_default_context(self):
34 def load_default_context(self):
37 c = self._get_local_tmpl_context()
35 c = self._get_local_tmpl_context()
38 return c
36 return c
39
37
40 @LoginRequired()
38 @LoginRequired()
41 @HasPermissionAllDecorator('hg.admin')
39 @HasPermissionAllDecorator('hg.admin')
42 @view_config(
43 route_name='admin_settings_open_source', request_method='GET',
44 renderer='rhodecode:templates/admin/settings/settings.mako')
45 def open_source_licenses(self):
40 def open_source_licenses(self):
46 c = self.load_default_context()
41 c = self.load_default_context()
47 c.active = 'open_source'
42 c.active = 'open_source'
48 c.navlist = navigation_list(self.request)
43 c.navlist = navigation_list(self.request)
49 c.opensource_licenses = sorted(
44 c.opensource_licenses = sorted(
50 read_opensource_licenses(), key=lambda d: d["name"])
45 read_opensource_licenses(), key=lambda d: d["name"])
51 return self._get_template_context(c)
46 return self._get_template_context(c)
@@ -1,519 +1,479 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import re
21 import re
22 import logging
22 import logging
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25 import datetime
25 import datetime
26 from pyramid.interfaces import IRoutesMapper
26 from pyramid.interfaces import IRoutesMapper
27
27
28 from pyramid.view import view_config
29 from pyramid.httpexceptions import HTTPFound
28 from pyramid.httpexceptions import HTTPFound
30 from pyramid.renderers import render
29 from pyramid.renderers import render
31 from pyramid.response import Response
30 from pyramid.response import Response
32
31
33 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.apps._base import BaseAppView, DataGridAppView
34 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
35 from rhodecode import events
34 from rhodecode import events
36
35
37 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
38 from rhodecode.lib.auth import (
37 from rhodecode.lib.auth import (
39 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
38 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
40 from rhodecode.lib.utils2 import aslist, safe_unicode
39 from rhodecode.lib.utils2 import aslist, safe_unicode
41 from rhodecode.model.db import (
40 from rhodecode.model.db import (
42 or_, coalesce, User, UserIpMap, UserSshKeys)
41 or_, coalesce, User, UserIpMap, UserSshKeys)
43 from rhodecode.model.forms import (
42 from rhodecode.model.forms import (
44 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
43 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
45 from rhodecode.model.meta import Session
44 from rhodecode.model.meta import Session
46 from rhodecode.model.permission import PermissionModel
45 from rhodecode.model.permission import PermissionModel
47 from rhodecode.model.settings import SettingsModel
46 from rhodecode.model.settings import SettingsModel
48
47
49
48
50 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
51
50
52
51
53 class AdminPermissionsView(BaseAppView, DataGridAppView):
52 class AdminPermissionsView(BaseAppView, DataGridAppView):
54 def load_default_context(self):
53 def load_default_context(self):
55 c = self._get_local_tmpl_context()
54 c = self._get_local_tmpl_context()
56 PermissionModel().set_global_permission_choices(
55 PermissionModel().set_global_permission_choices(
57 c, gettext_translator=self.request.translate)
56 c, gettext_translator=self.request.translate)
58 return c
57 return c
59
58
60 @LoginRequired()
59 @LoginRequired()
61 @HasPermissionAllDecorator('hg.admin')
60 @HasPermissionAllDecorator('hg.admin')
62 @view_config(
63 route_name='admin_permissions_application', request_method='GET',
64 renderer='rhodecode:templates/admin/permissions/permissions.mako')
65 def permissions_application(self):
61 def permissions_application(self):
66 c = self.load_default_context()
62 c = self.load_default_context()
67 c.active = 'application'
63 c.active = 'application'
68
64
69 c.user = User.get_default_user(refresh=True)
65 c.user = User.get_default_user(refresh=True)
70
66
71 app_settings = c.rc_config
67 app_settings = c.rc_config
72
68
73 defaults = {
69 defaults = {
74 'anonymous': c.user.active,
70 'anonymous': c.user.active,
75 'default_register_message': app_settings.get(
71 'default_register_message': app_settings.get(
76 'rhodecode_register_message')
72 'rhodecode_register_message')
77 }
73 }
78 defaults.update(c.user.get_default_perms())
74 defaults.update(c.user.get_default_perms())
79
75
80 data = render('rhodecode:templates/admin/permissions/permissions.mako',
76 data = render('rhodecode:templates/admin/permissions/permissions.mako',
81 self._get_template_context(c), self.request)
77 self._get_template_context(c), self.request)
82 html = formencode.htmlfill.render(
78 html = formencode.htmlfill.render(
83 data,
79 data,
84 defaults=defaults,
80 defaults=defaults,
85 encoding="UTF-8",
81 encoding="UTF-8",
86 force_defaults=False
82 force_defaults=False
87 )
83 )
88 return Response(html)
84 return Response(html)
89
85
90 @LoginRequired()
86 @LoginRequired()
91 @HasPermissionAllDecorator('hg.admin')
87 @HasPermissionAllDecorator('hg.admin')
92 @CSRFRequired()
88 @CSRFRequired()
93 @view_config(
94 route_name='admin_permissions_application_update', request_method='POST',
95 renderer='rhodecode:templates/admin/permissions/permissions.mako')
96 def permissions_application_update(self):
89 def permissions_application_update(self):
97 _ = self.request.translate
90 _ = self.request.translate
98 c = self.load_default_context()
91 c = self.load_default_context()
99 c.active = 'application'
92 c.active = 'application'
100
93
101 _form = ApplicationPermissionsForm(
94 _form = ApplicationPermissionsForm(
102 self.request.translate,
95 self.request.translate,
103 [x[0] for x in c.register_choices],
96 [x[0] for x in c.register_choices],
104 [x[0] for x in c.password_reset_choices],
97 [x[0] for x in c.password_reset_choices],
105 [x[0] for x in c.extern_activate_choices])()
98 [x[0] for x in c.extern_activate_choices])()
106
99
107 try:
100 try:
108 form_result = _form.to_python(dict(self.request.POST))
101 form_result = _form.to_python(dict(self.request.POST))
109 form_result.update({'perm_user_name': User.DEFAULT_USER})
102 form_result.update({'perm_user_name': User.DEFAULT_USER})
110 PermissionModel().update_application_permissions(form_result)
103 PermissionModel().update_application_permissions(form_result)
111
104
112 settings = [
105 settings = [
113 ('register_message', 'default_register_message'),
106 ('register_message', 'default_register_message'),
114 ]
107 ]
115 for setting, form_key in settings:
108 for setting, form_key in settings:
116 sett = SettingsModel().create_or_update_setting(
109 sett = SettingsModel().create_or_update_setting(
117 setting, form_result[form_key])
110 setting, form_result[form_key])
118 Session().add(sett)
111 Session().add(sett)
119
112
120 Session().commit()
113 Session().commit()
121 h.flash(_('Application permissions updated successfully'),
114 h.flash(_('Application permissions updated successfully'),
122 category='success')
115 category='success')
123
116
124 except formencode.Invalid as errors:
117 except formencode.Invalid as errors:
125 defaults = errors.value
118 defaults = errors.value
126
119
127 data = render(
120 data = render(
128 'rhodecode:templates/admin/permissions/permissions.mako',
121 'rhodecode:templates/admin/permissions/permissions.mako',
129 self._get_template_context(c), self.request)
122 self._get_template_context(c), self.request)
130 html = formencode.htmlfill.render(
123 html = formencode.htmlfill.render(
131 data,
124 data,
132 defaults=defaults,
125 defaults=defaults,
133 errors=errors.error_dict or {},
126 errors=errors.error_dict or {},
134 prefix_error=False,
127 prefix_error=False,
135 encoding="UTF-8",
128 encoding="UTF-8",
136 force_defaults=False
129 force_defaults=False
137 )
130 )
138 return Response(html)
131 return Response(html)
139
132
140 except Exception:
133 except Exception:
141 log.exception("Exception during update of permissions")
134 log.exception("Exception during update of permissions")
142 h.flash(_('Error occurred during update of permissions'),
135 h.flash(_('Error occurred during update of permissions'),
143 category='error')
136 category='error')
144
137
145 affected_user_ids = [User.get_default_user_id()]
138 affected_user_ids = [User.get_default_user_id()]
146 PermissionModel().trigger_permission_flush(affected_user_ids)
139 PermissionModel().trigger_permission_flush(affected_user_ids)
147
140
148 raise HTTPFound(h.route_path('admin_permissions_application'))
141 raise HTTPFound(h.route_path('admin_permissions_application'))
149
142
150 @LoginRequired()
143 @LoginRequired()
151 @HasPermissionAllDecorator('hg.admin')
144 @HasPermissionAllDecorator('hg.admin')
152 @view_config(
153 route_name='admin_permissions_object', request_method='GET',
154 renderer='rhodecode:templates/admin/permissions/permissions.mako')
155 def permissions_objects(self):
145 def permissions_objects(self):
156 c = self.load_default_context()
146 c = self.load_default_context()
157 c.active = 'objects'
147 c.active = 'objects'
158
148
159 c.user = User.get_default_user(refresh=True)
149 c.user = User.get_default_user(refresh=True)
160 defaults = {}
150 defaults = {}
161 defaults.update(c.user.get_default_perms())
151 defaults.update(c.user.get_default_perms())
162
152
163 data = render(
153 data = render(
164 'rhodecode:templates/admin/permissions/permissions.mako',
154 'rhodecode:templates/admin/permissions/permissions.mako',
165 self._get_template_context(c), self.request)
155 self._get_template_context(c), self.request)
166 html = formencode.htmlfill.render(
156 html = formencode.htmlfill.render(
167 data,
157 data,
168 defaults=defaults,
158 defaults=defaults,
169 encoding="UTF-8",
159 encoding="UTF-8",
170 force_defaults=False
160 force_defaults=False
171 )
161 )
172 return Response(html)
162 return Response(html)
173
163
174 @LoginRequired()
164 @LoginRequired()
175 @HasPermissionAllDecorator('hg.admin')
165 @HasPermissionAllDecorator('hg.admin')
176 @CSRFRequired()
166 @CSRFRequired()
177 @view_config(
178 route_name='admin_permissions_object_update', request_method='POST',
179 renderer='rhodecode:templates/admin/permissions/permissions.mako')
180 def permissions_objects_update(self):
167 def permissions_objects_update(self):
181 _ = self.request.translate
168 _ = self.request.translate
182 c = self.load_default_context()
169 c = self.load_default_context()
183 c.active = 'objects'
170 c.active = 'objects'
184
171
185 _form = ObjectPermissionsForm(
172 _form = ObjectPermissionsForm(
186 self.request.translate,
173 self.request.translate,
187 [x[0] for x in c.repo_perms_choices],
174 [x[0] for x in c.repo_perms_choices],
188 [x[0] for x in c.group_perms_choices],
175 [x[0] for x in c.group_perms_choices],
189 [x[0] for x in c.user_group_perms_choices],
176 [x[0] for x in c.user_group_perms_choices],
190 )()
177 )()
191
178
192 try:
179 try:
193 form_result = _form.to_python(dict(self.request.POST))
180 form_result = _form.to_python(dict(self.request.POST))
194 form_result.update({'perm_user_name': User.DEFAULT_USER})
181 form_result.update({'perm_user_name': User.DEFAULT_USER})
195 PermissionModel().update_object_permissions(form_result)
182 PermissionModel().update_object_permissions(form_result)
196
183
197 Session().commit()
184 Session().commit()
198 h.flash(_('Object permissions updated successfully'),
185 h.flash(_('Object permissions updated successfully'),
199 category='success')
186 category='success')
200
187
201 except formencode.Invalid as errors:
188 except formencode.Invalid as errors:
202 defaults = errors.value
189 defaults = errors.value
203
190
204 data = render(
191 data = render(
205 'rhodecode:templates/admin/permissions/permissions.mako',
192 'rhodecode:templates/admin/permissions/permissions.mako',
206 self._get_template_context(c), self.request)
193 self._get_template_context(c), self.request)
207 html = formencode.htmlfill.render(
194 html = formencode.htmlfill.render(
208 data,
195 data,
209 defaults=defaults,
196 defaults=defaults,
210 errors=errors.error_dict or {},
197 errors=errors.error_dict or {},
211 prefix_error=False,
198 prefix_error=False,
212 encoding="UTF-8",
199 encoding="UTF-8",
213 force_defaults=False
200 force_defaults=False
214 )
201 )
215 return Response(html)
202 return Response(html)
216 except Exception:
203 except Exception:
217 log.exception("Exception during update of permissions")
204 log.exception("Exception during update of permissions")
218 h.flash(_('Error occurred during update of permissions'),
205 h.flash(_('Error occurred during update of permissions'),
219 category='error')
206 category='error')
220
207
221 affected_user_ids = [User.get_default_user_id()]
208 affected_user_ids = [User.get_default_user_id()]
222 PermissionModel().trigger_permission_flush(affected_user_ids)
209 PermissionModel().trigger_permission_flush(affected_user_ids)
223
210
224 raise HTTPFound(h.route_path('admin_permissions_object'))
211 raise HTTPFound(h.route_path('admin_permissions_object'))
225
212
226 @LoginRequired()
213 @LoginRequired()
227 @HasPermissionAllDecorator('hg.admin')
214 @HasPermissionAllDecorator('hg.admin')
228 @view_config(
229 route_name='admin_permissions_branch', request_method='GET',
230 renderer='rhodecode:templates/admin/permissions/permissions.mako')
231 def permissions_branch(self):
215 def permissions_branch(self):
232 c = self.load_default_context()
216 c = self.load_default_context()
233 c.active = 'branch'
217 c.active = 'branch'
234
218
235 c.user = User.get_default_user(refresh=True)
219 c.user = User.get_default_user(refresh=True)
236 defaults = {}
220 defaults = {}
237 defaults.update(c.user.get_default_perms())
221 defaults.update(c.user.get_default_perms())
238
222
239 data = render(
223 data = render(
240 'rhodecode:templates/admin/permissions/permissions.mako',
224 'rhodecode:templates/admin/permissions/permissions.mako',
241 self._get_template_context(c), self.request)
225 self._get_template_context(c), self.request)
242 html = formencode.htmlfill.render(
226 html = formencode.htmlfill.render(
243 data,
227 data,
244 defaults=defaults,
228 defaults=defaults,
245 encoding="UTF-8",
229 encoding="UTF-8",
246 force_defaults=False
230 force_defaults=False
247 )
231 )
248 return Response(html)
232 return Response(html)
249
233
250 @LoginRequired()
234 @LoginRequired()
251 @HasPermissionAllDecorator('hg.admin')
235 @HasPermissionAllDecorator('hg.admin')
252 @view_config(
253 route_name='admin_permissions_global', request_method='GET',
254 renderer='rhodecode:templates/admin/permissions/permissions.mako')
255 def permissions_global(self):
236 def permissions_global(self):
256 c = self.load_default_context()
237 c = self.load_default_context()
257 c.active = 'global'
238 c.active = 'global'
258
239
259 c.user = User.get_default_user(refresh=True)
240 c.user = User.get_default_user(refresh=True)
260 defaults = {}
241 defaults = {}
261 defaults.update(c.user.get_default_perms())
242 defaults.update(c.user.get_default_perms())
262
243
263 data = render(
244 data = render(
264 'rhodecode:templates/admin/permissions/permissions.mako',
245 'rhodecode:templates/admin/permissions/permissions.mako',
265 self._get_template_context(c), self.request)
246 self._get_template_context(c), self.request)
266 html = formencode.htmlfill.render(
247 html = formencode.htmlfill.render(
267 data,
248 data,
268 defaults=defaults,
249 defaults=defaults,
269 encoding="UTF-8",
250 encoding="UTF-8",
270 force_defaults=False
251 force_defaults=False
271 )
252 )
272 return Response(html)
253 return Response(html)
273
254
274 @LoginRequired()
255 @LoginRequired()
275 @HasPermissionAllDecorator('hg.admin')
256 @HasPermissionAllDecorator('hg.admin')
276 @CSRFRequired()
257 @CSRFRequired()
277 @view_config(
278 route_name='admin_permissions_global_update', request_method='POST',
279 renderer='rhodecode:templates/admin/permissions/permissions.mako')
280 def permissions_global_update(self):
258 def permissions_global_update(self):
281 _ = self.request.translate
259 _ = self.request.translate
282 c = self.load_default_context()
260 c = self.load_default_context()
283 c.active = 'global'
261 c.active = 'global'
284
262
285 _form = UserPermissionsForm(
263 _form = UserPermissionsForm(
286 self.request.translate,
264 self.request.translate,
287 [x[0] for x in c.repo_create_choices],
265 [x[0] for x in c.repo_create_choices],
288 [x[0] for x in c.repo_create_on_write_choices],
266 [x[0] for x in c.repo_create_on_write_choices],
289 [x[0] for x in c.repo_group_create_choices],
267 [x[0] for x in c.repo_group_create_choices],
290 [x[0] for x in c.user_group_create_choices],
268 [x[0] for x in c.user_group_create_choices],
291 [x[0] for x in c.fork_choices],
269 [x[0] for x in c.fork_choices],
292 [x[0] for x in c.inherit_default_permission_choices])()
270 [x[0] for x in c.inherit_default_permission_choices])()
293
271
294 try:
272 try:
295 form_result = _form.to_python(dict(self.request.POST))
273 form_result = _form.to_python(dict(self.request.POST))
296 form_result.update({'perm_user_name': User.DEFAULT_USER})
274 form_result.update({'perm_user_name': User.DEFAULT_USER})
297 PermissionModel().update_user_permissions(form_result)
275 PermissionModel().update_user_permissions(form_result)
298
276
299 Session().commit()
277 Session().commit()
300 h.flash(_('Global permissions updated successfully'),
278 h.flash(_('Global permissions updated successfully'),
301 category='success')
279 category='success')
302
280
303 except formencode.Invalid as errors:
281 except formencode.Invalid as errors:
304 defaults = errors.value
282 defaults = errors.value
305
283
306 data = render(
284 data = render(
307 'rhodecode:templates/admin/permissions/permissions.mako',
285 'rhodecode:templates/admin/permissions/permissions.mako',
308 self._get_template_context(c), self.request)
286 self._get_template_context(c), self.request)
309 html = formencode.htmlfill.render(
287 html = formencode.htmlfill.render(
310 data,
288 data,
311 defaults=defaults,
289 defaults=defaults,
312 errors=errors.error_dict or {},
290 errors=errors.error_dict or {},
313 prefix_error=False,
291 prefix_error=False,
314 encoding="UTF-8",
292 encoding="UTF-8",
315 force_defaults=False
293 force_defaults=False
316 )
294 )
317 return Response(html)
295 return Response(html)
318 except Exception:
296 except Exception:
319 log.exception("Exception during update of permissions")
297 log.exception("Exception during update of permissions")
320 h.flash(_('Error occurred during update of permissions'),
298 h.flash(_('Error occurred during update of permissions'),
321 category='error')
299 category='error')
322
300
323 affected_user_ids = [User.get_default_user_id()]
301 affected_user_ids = [User.get_default_user_id()]
324 PermissionModel().trigger_permission_flush(affected_user_ids)
302 PermissionModel().trigger_permission_flush(affected_user_ids)
325
303
326 raise HTTPFound(h.route_path('admin_permissions_global'))
304 raise HTTPFound(h.route_path('admin_permissions_global'))
327
305
328 @LoginRequired()
306 @LoginRequired()
329 @HasPermissionAllDecorator('hg.admin')
307 @HasPermissionAllDecorator('hg.admin')
330 @view_config(
331 route_name='admin_permissions_ips', request_method='GET',
332 renderer='rhodecode:templates/admin/permissions/permissions.mako')
333 def permissions_ips(self):
308 def permissions_ips(self):
334 c = self.load_default_context()
309 c = self.load_default_context()
335 c.active = 'ips'
310 c.active = 'ips'
336
311
337 c.user = User.get_default_user(refresh=True)
312 c.user = User.get_default_user(refresh=True)
338 c.user_ip_map = (
313 c.user_ip_map = (
339 UserIpMap.query().filter(UserIpMap.user == c.user).all())
314 UserIpMap.query().filter(UserIpMap.user == c.user).all())
340
315
341 return self._get_template_context(c)
316 return self._get_template_context(c)
342
317
343 @LoginRequired()
318 @LoginRequired()
344 @HasPermissionAllDecorator('hg.admin')
319 @HasPermissionAllDecorator('hg.admin')
345 @view_config(
346 route_name='admin_permissions_overview', request_method='GET',
347 renderer='rhodecode:templates/admin/permissions/permissions.mako')
348 def permissions_overview(self):
320 def permissions_overview(self):
349 c = self.load_default_context()
321 c = self.load_default_context()
350 c.active = 'perms'
322 c.active = 'perms'
351
323
352 c.user = User.get_default_user(refresh=True)
324 c.user = User.get_default_user(refresh=True)
353 c.perm_user = c.user.AuthUser()
325 c.perm_user = c.user.AuthUser()
354 return self._get_template_context(c)
326 return self._get_template_context(c)
355
327
356 @LoginRequired()
328 @LoginRequired()
357 @HasPermissionAllDecorator('hg.admin')
329 @HasPermissionAllDecorator('hg.admin')
358 @view_config(
359 route_name='admin_permissions_auth_token_access', request_method='GET',
360 renderer='rhodecode:templates/admin/permissions/permissions.mako')
361 def auth_token_access(self):
330 def auth_token_access(self):
362 from rhodecode import CONFIG
331 from rhodecode import CONFIG
363
332
364 c = self.load_default_context()
333 c = self.load_default_context()
365 c.active = 'auth_token_access'
334 c.active = 'auth_token_access'
366
335
367 c.user = User.get_default_user(refresh=True)
336 c.user = User.get_default_user(refresh=True)
368 c.perm_user = c.user.AuthUser()
337 c.perm_user = c.user.AuthUser()
369
338
370 mapper = self.request.registry.queryUtility(IRoutesMapper)
339 mapper = self.request.registry.queryUtility(IRoutesMapper)
371 c.view_data = []
340 c.view_data = []
372
341
373 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
342 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
374 introspector = self.request.registry.introspector
343 introspector = self.request.registry.introspector
375
344
376 view_intr = {}
345 view_intr = {}
377 for view_data in introspector.get_category('views'):
346 for view_data in introspector.get_category('views'):
378 intr = view_data['introspectable']
347 intr = view_data['introspectable']
379
348
380 if 'route_name' in intr and intr['attr']:
349 if 'route_name' in intr and intr['attr']:
381 view_intr[intr['route_name']] = '{}:{}'.format(
350 view_intr[intr['route_name']] = '{}:{}'.format(
382 str(intr['derived_callable'].func_name), intr['attr']
351 str(intr['derived_callable'].func_name), intr['attr']
383 )
352 )
384
353
385 c.whitelist_key = 'api_access_controllers_whitelist'
354 c.whitelist_key = 'api_access_controllers_whitelist'
386 c.whitelist_file = CONFIG.get('__file__')
355 c.whitelist_file = CONFIG.get('__file__')
387 whitelist_views = aslist(
356 whitelist_views = aslist(
388 CONFIG.get(c.whitelist_key), sep=',')
357 CONFIG.get(c.whitelist_key), sep=',')
389
358
390 for route_info in mapper.get_routes():
359 for route_info in mapper.get_routes():
391 if not route_info.name.startswith('__'):
360 if not route_info.name.startswith('__'):
392 routepath = route_info.pattern
361 routepath = route_info.pattern
393
362
394 def replace(matchobj):
363 def replace(matchobj):
395 if matchobj.group(1):
364 if matchobj.group(1):
396 return "{%s}" % matchobj.group(1).split(':')[0]
365 return "{%s}" % matchobj.group(1).split(':')[0]
397 else:
366 else:
398 return "{%s}" % matchobj.group(2)
367 return "{%s}" % matchobj.group(2)
399
368
400 routepath = _argument_prog.sub(replace, routepath)
369 routepath = _argument_prog.sub(replace, routepath)
401
370
402 if not routepath.startswith('/'):
371 if not routepath.startswith('/'):
403 routepath = '/' + routepath
372 routepath = '/' + routepath
404
373
405 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
374 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
406 active = view_fqn in whitelist_views
375 active = view_fqn in whitelist_views
407 c.view_data.append((route_info.name, view_fqn, routepath, active))
376 c.view_data.append((route_info.name, view_fqn, routepath, active))
408
377
409 c.whitelist_views = whitelist_views
378 c.whitelist_views = whitelist_views
410 return self._get_template_context(c)
379 return self._get_template_context(c)
411
380
412 def ssh_enabled(self):
381 def ssh_enabled(self):
413 return self.request.registry.settings.get(
382 return self.request.registry.settings.get(
414 'ssh.generate_authorized_keyfile')
383 'ssh.generate_authorized_keyfile')
415
384
416 @LoginRequired()
385 @LoginRequired()
417 @HasPermissionAllDecorator('hg.admin')
386 @HasPermissionAllDecorator('hg.admin')
418 @view_config(
419 route_name='admin_permissions_ssh_keys', request_method='GET',
420 renderer='rhodecode:templates/admin/permissions/permissions.mako')
421 def ssh_keys(self):
387 def ssh_keys(self):
422 c = self.load_default_context()
388 c = self.load_default_context()
423 c.active = 'ssh_keys'
389 c.active = 'ssh_keys'
424 c.ssh_enabled = self.ssh_enabled()
390 c.ssh_enabled = self.ssh_enabled()
425 return self._get_template_context(c)
391 return self._get_template_context(c)
426
392
427 @LoginRequired()
393 @LoginRequired()
428 @HasPermissionAllDecorator('hg.admin')
394 @HasPermissionAllDecorator('hg.admin')
429 @view_config(
430 route_name='admin_permissions_ssh_keys_data', request_method='GET',
431 renderer='json_ext', xhr=True)
432 def ssh_keys_data(self):
395 def ssh_keys_data(self):
433 _ = self.request.translate
396 _ = self.request.translate
434 self.load_default_context()
397 self.load_default_context()
435 column_map = {
398 column_map = {
436 'fingerprint': 'ssh_key_fingerprint',
399 'fingerprint': 'ssh_key_fingerprint',
437 'username': User.username
400 'username': User.username
438 }
401 }
439 draw, start, limit = self._extract_chunk(self.request)
402 draw, start, limit = self._extract_chunk(self.request)
440 search_q, order_by, order_dir = self._extract_ordering(
403 search_q, order_by, order_dir = self._extract_ordering(
441 self.request, column_map=column_map)
404 self.request, column_map=column_map)
442
405
443 ssh_keys_data_total_count = UserSshKeys.query()\
406 ssh_keys_data_total_count = UserSshKeys.query()\
444 .count()
407 .count()
445
408
446 # json generate
409 # json generate
447 base_q = UserSshKeys.query().join(UserSshKeys.user)
410 base_q = UserSshKeys.query().join(UserSshKeys.user)
448
411
449 if search_q:
412 if search_q:
450 like_expression = u'%{}%'.format(safe_unicode(search_q))
413 like_expression = u'%{}%'.format(safe_unicode(search_q))
451 base_q = base_q.filter(or_(
414 base_q = base_q.filter(or_(
452 User.username.ilike(like_expression),
415 User.username.ilike(like_expression),
453 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
416 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
454 ))
417 ))
455
418
456 users_data_total_filtered_count = base_q.count()
419 users_data_total_filtered_count = base_q.count()
457
420
458 sort_col = self._get_order_col(order_by, UserSshKeys)
421 sort_col = self._get_order_col(order_by, UserSshKeys)
459 if sort_col:
422 if sort_col:
460 if order_dir == 'asc':
423 if order_dir == 'asc':
461 # handle null values properly to order by NULL last
424 # handle null values properly to order by NULL last
462 if order_by in ['created_on']:
425 if order_by in ['created_on']:
463 sort_col = coalesce(sort_col, datetime.date.max)
426 sort_col = coalesce(sort_col, datetime.date.max)
464 sort_col = sort_col.asc()
427 sort_col = sort_col.asc()
465 else:
428 else:
466 # handle null values properly to order by NULL last
429 # handle null values properly to order by NULL last
467 if order_by in ['created_on']:
430 if order_by in ['created_on']:
468 sort_col = coalesce(sort_col, datetime.date.min)
431 sort_col = coalesce(sort_col, datetime.date.min)
469 sort_col = sort_col.desc()
432 sort_col = sort_col.desc()
470
433
471 base_q = base_q.order_by(sort_col)
434 base_q = base_q.order_by(sort_col)
472 base_q = base_q.offset(start).limit(limit)
435 base_q = base_q.offset(start).limit(limit)
473
436
474 ssh_keys = base_q.all()
437 ssh_keys = base_q.all()
475
438
476 ssh_keys_data = []
439 ssh_keys_data = []
477 for ssh_key in ssh_keys:
440 for ssh_key in ssh_keys:
478 ssh_keys_data.append({
441 ssh_keys_data.append({
479 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
442 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
480 "fingerprint": ssh_key.ssh_key_fingerprint,
443 "fingerprint": ssh_key.ssh_key_fingerprint,
481 "description": ssh_key.description,
444 "description": ssh_key.description,
482 "created_on": h.format_date(ssh_key.created_on),
445 "created_on": h.format_date(ssh_key.created_on),
483 "accessed_on": h.format_date(ssh_key.accessed_on),
446 "accessed_on": h.format_date(ssh_key.accessed_on),
484 "action": h.link_to(
447 "action": h.link_to(
485 _('Edit'), h.route_path('edit_user_ssh_keys',
448 _('Edit'), h.route_path('edit_user_ssh_keys',
486 user_id=ssh_key.user.user_id))
449 user_id=ssh_key.user.user_id))
487 })
450 })
488
451
489 data = ({
452 data = ({
490 'draw': draw,
453 'draw': draw,
491 'data': ssh_keys_data,
454 'data': ssh_keys_data,
492 'recordsTotal': ssh_keys_data_total_count,
455 'recordsTotal': ssh_keys_data_total_count,
493 'recordsFiltered': users_data_total_filtered_count,
456 'recordsFiltered': users_data_total_filtered_count,
494 })
457 })
495
458
496 return data
459 return data
497
460
498 @LoginRequired()
461 @LoginRequired()
499 @HasPermissionAllDecorator('hg.admin')
462 @HasPermissionAllDecorator('hg.admin')
500 @CSRFRequired()
463 @CSRFRequired()
501 @view_config(
502 route_name='admin_permissions_ssh_keys_update', request_method='POST',
503 renderer='rhodecode:templates/admin/permissions/permissions.mako')
504 def ssh_keys_update(self):
464 def ssh_keys_update(self):
505 _ = self.request.translate
465 _ = self.request.translate
506 self.load_default_context()
466 self.load_default_context()
507
467
508 ssh_enabled = self.ssh_enabled()
468 ssh_enabled = self.ssh_enabled()
509 key_file = self.request.registry.settings.get(
469 key_file = self.request.registry.settings.get(
510 'ssh.authorized_keys_file_path')
470 'ssh.authorized_keys_file_path')
511 if ssh_enabled:
471 if ssh_enabled:
512 events.trigger(SshKeyFileChangeEvent(), self.request.registry)
472 events.trigger(SshKeyFileChangeEvent(), self.request.registry)
513 h.flash(_('Updated SSH keys file: {}').format(key_file),
473 h.flash(_('Updated SSH keys file: {}').format(key_file),
514 category='success')
474 category='success')
515 else:
475 else:
516 h.flash(_('SSH key support is disabled in .ini file'),
476 h.flash(_('SSH key support is disabled in .ini file'),
517 category='warning')
477 category='warning')
518
478
519 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
479 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
@@ -1,182 +1,170 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 import psutil
23 import psutil
24 import signal
24 import signal
25 from pyramid.view import view_config
25
26
26
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.apps._base.navigation import navigation_list
28 from rhodecode.apps._base.navigation import navigation_list
29 from rhodecode.lib import system_info
29 from rhodecode.lib import system_info
30 from rhodecode.lib.auth import (
30 from rhodecode.lib.auth import (
31 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
31 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
32 from rhodecode.lib.utils2 import safe_int, StrictAttributeDict
32 from rhodecode.lib.utils2 import safe_int, StrictAttributeDict
33
33
34 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
35
35
36
36
37 class AdminProcessManagementView(BaseAppView):
37 class AdminProcessManagementView(BaseAppView):
38 def load_default_context(self):
38 def load_default_context(self):
39 c = self._get_local_tmpl_context()
39 c = self._get_local_tmpl_context()
40 return c
40 return c
41
41
42 def _format_proc(self, proc, with_children=False):
42 def _format_proc(self, proc, with_children=False):
43 try:
43 try:
44 mem = proc.memory_info()
44 mem = proc.memory_info()
45 proc_formatted = StrictAttributeDict({
45 proc_formatted = StrictAttributeDict({
46 'pid': proc.pid,
46 'pid': proc.pid,
47 'name': proc.name(),
47 'name': proc.name(),
48 'mem_rss': mem.rss,
48 'mem_rss': mem.rss,
49 'mem_vms': mem.vms,
49 'mem_vms': mem.vms,
50 'cpu_percent': proc.cpu_percent(interval=0.1),
50 'cpu_percent': proc.cpu_percent(interval=0.1),
51 'create_time': proc.create_time(),
51 'create_time': proc.create_time(),
52 'cmd': ' '.join(proc.cmdline()),
52 'cmd': ' '.join(proc.cmdline()),
53 })
53 })
54
54
55 if with_children:
55 if with_children:
56 proc_formatted.update({
56 proc_formatted.update({
57 'children': [self._format_proc(x)
57 'children': [self._format_proc(x)
58 for x in proc.children(recursive=True)]
58 for x in proc.children(recursive=True)]
59 })
59 })
60 except Exception:
60 except Exception:
61 log.exception('Failed to load proc')
61 log.exception('Failed to load proc')
62 proc_formatted = None
62 proc_formatted = None
63 return proc_formatted
63 return proc_formatted
64
64
65 def get_processes(self):
65 def get_processes(self):
66 proc_list = []
66 proc_list = []
67 for p in psutil.process_iter():
67 for p in psutil.process_iter():
68 if 'gunicorn' in p.name():
68 if 'gunicorn' in p.name():
69 proc = self._format_proc(p, with_children=True)
69 proc = self._format_proc(p, with_children=True)
70 if proc:
70 if proc:
71 proc_list.append(proc)
71 proc_list.append(proc)
72
72
73 return proc_list
73 return proc_list
74
74
75 def get_workers(self):
75 def get_workers(self):
76 workers = None
76 workers = None
77 try:
77 try:
78 rc_config = system_info.rhodecode_config().value['config']
78 rc_config = system_info.rhodecode_config().value['config']
79 workers = rc_config['server:main'].get('workers')
79 workers = rc_config['server:main'].get('workers')
80 except Exception:
80 except Exception:
81 pass
81 pass
82
82
83 return workers or '?'
83 return workers or '?'
84
84
85 @LoginRequired()
85 @LoginRequired()
86 @HasPermissionAllDecorator('hg.admin')
86 @HasPermissionAllDecorator('hg.admin')
87 @view_config(
88 route_name='admin_settings_process_management', request_method='GET',
89 renderer='rhodecode:templates/admin/settings/settings.mako')
90 def process_management(self):
87 def process_management(self):
91 _ = self.request.translate
88 _ = self.request.translate
92 c = self.load_default_context()
89 c = self.load_default_context()
93
90
94 c.active = 'process_management'
91 c.active = 'process_management'
95 c.navlist = navigation_list(self.request)
92 c.navlist = navigation_list(self.request)
96 c.gunicorn_processes = self.get_processes()
93 c.gunicorn_processes = self.get_processes()
97 c.gunicorn_workers = self.get_workers()
94 c.gunicorn_workers = self.get_workers()
98 return self._get_template_context(c)
95 return self._get_template_context(c)
99
96
100 @LoginRequired()
97 @LoginRequired()
101 @HasPermissionAllDecorator('hg.admin')
98 @HasPermissionAllDecorator('hg.admin')
102 @view_config(
103 route_name='admin_settings_process_management_data', request_method='GET',
104 renderer='rhodecode:templates/admin/settings/settings_process_management_data.mako')
105 def process_management_data(self):
99 def process_management_data(self):
106 _ = self.request.translate
100 _ = self.request.translate
107 c = self.load_default_context()
101 c = self.load_default_context()
108 c.gunicorn_processes = self.get_processes()
102 c.gunicorn_processes = self.get_processes()
109 return self._get_template_context(c)
103 return self._get_template_context(c)
110
104
111 @LoginRequired()
105 @LoginRequired()
112 @HasPermissionAllDecorator('hg.admin')
106 @HasPermissionAllDecorator('hg.admin')
113 @CSRFRequired()
107 @CSRFRequired()
114 @view_config(
115 route_name='admin_settings_process_management_signal',
116 request_method='POST', renderer='json_ext')
117 def process_management_signal(self):
108 def process_management_signal(self):
118 pids = self.request.json.get('pids', [])
109 pids = self.request.json.get('pids', [])
119 result = []
110 result = []
120
111
121 def on_terminate(proc):
112 def on_terminate(proc):
122 msg = "terminated"
113 msg = "terminated"
123 result.append(msg)
114 result.append(msg)
124
115
125 procs = []
116 procs = []
126 for pid in pids:
117 for pid in pids:
127 pid = safe_int(pid)
118 pid = safe_int(pid)
128 if pid:
119 if pid:
129 try:
120 try:
130 proc = psutil.Process(pid)
121 proc = psutil.Process(pid)
131 except psutil.NoSuchProcess:
122 except psutil.NoSuchProcess:
132 continue
123 continue
133
124
134 children = proc.children(recursive=True)
125 children = proc.children(recursive=True)
135 if children:
126 if children:
136 log.warning('Wont kill Master Process')
127 log.warning('Wont kill Master Process')
137 else:
128 else:
138 procs.append(proc)
129 procs.append(proc)
139
130
140 for p in procs:
131 for p in procs:
141 try:
132 try:
142 p.terminate()
133 p.terminate()
143 except psutil.AccessDenied as e:
134 except psutil.AccessDenied as e:
144 log.warning('Access denied: {}'.format(e))
135 log.warning('Access denied: {}'.format(e))
145
136
146 gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
137 gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
147 for p in alive:
138 for p in alive:
148 try:
139 try:
149 p.kill()
140 p.kill()
150 except psutil.AccessDenied as e:
141 except psutil.AccessDenied as e:
151 log.warning('Access denied: {}'.format(e))
142 log.warning('Access denied: {}'.format(e))
152
143
153 return {'result': result}
144 return {'result': result}
154
145
155 @LoginRequired()
146 @LoginRequired()
156 @HasPermissionAllDecorator('hg.admin')
147 @HasPermissionAllDecorator('hg.admin')
157 @CSRFRequired()
148 @CSRFRequired()
158 @view_config(
159 route_name='admin_settings_process_management_master_signal',
160 request_method='POST', renderer='json_ext')
161 def process_management_master_signal(self):
149 def process_management_master_signal(self):
162 pid_data = self.request.json.get('pid_data', {})
150 pid_data = self.request.json.get('pid_data', {})
163 pid = safe_int(pid_data['pid'])
151 pid = safe_int(pid_data['pid'])
164 action = pid_data['action']
152 action = pid_data['action']
165 if pid:
153 if pid:
166 try:
154 try:
167 proc = psutil.Process(pid)
155 proc = psutil.Process(pid)
168 except psutil.NoSuchProcess:
156 except psutil.NoSuchProcess:
169 return {'result': 'failure_no_such_process'}
157 return {'result': 'failure_no_such_process'}
170
158
171 children = proc.children(recursive=True)
159 children = proc.children(recursive=True)
172 if children:
160 if children:
173 # master process
161 # master process
174 if action == '+' and len(children) <= 20:
162 if action == '+' and len(children) <= 20:
175 proc.send_signal(signal.SIGTTIN)
163 proc.send_signal(signal.SIGTTIN)
176 elif action == '-' and len(children) >= 2:
164 elif action == '-' and len(children) >= 2:
177 proc.send_signal(signal.SIGTTOU)
165 proc.send_signal(signal.SIGTTOU)
178 else:
166 else:
179 return {'result': 'failure_wrong_action'}
167 return {'result': 'failure_wrong_action'}
180 return {'result': 'success'}
168 return {'result': 'success'}
181
169
182 return {'result': 'failure_not_master'}
170 return {'result': 'failure_not_master'}
@@ -1,374 +1,362 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import datetime
20 import datetime
21 import logging
21 import logging
22 import time
22 import time
23
23
24 import formencode
24 import formencode
25 import formencode.htmlfill
25 import formencode.htmlfill
26
26
27 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
27 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
28 from pyramid.view import view_config
28
29 from pyramid.renderers import render
29 from pyramid.renderers import render
30 from pyramid.response import Response
30 from pyramid.response import Response
31
31
32 from rhodecode import events
32 from rhodecode import events
33 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 from rhodecode.apps._base import BaseAppView, DataGridAppView
34
34
35 from rhodecode.lib.auth import (
35 from rhodecode.lib.auth import (
36 LoginRequired, CSRFRequired, NotAnonymous,
36 LoginRequired, CSRFRequired, NotAnonymous,
37 HasPermissionAny, HasRepoGroupPermissionAny)
37 HasPermissionAny, HasRepoGroupPermissionAny)
38 from rhodecode.lib import helpers as h, audit_logger
38 from rhodecode.lib import helpers as h, audit_logger
39 from rhodecode.lib.utils2 import safe_int, safe_unicode, datetime_to_time
39 from rhodecode.lib.utils2 import safe_int, safe_unicode, datetime_to_time
40 from rhodecode.model.forms import RepoGroupForm
40 from rhodecode.model.forms import RepoGroupForm
41 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.permission import PermissionModel
42 from rhodecode.model.repo_group import RepoGroupModel
42 from rhodecode.model.repo_group import RepoGroupModel
43 from rhodecode.model.scm import RepoGroupList
43 from rhodecode.model.scm import RepoGroupList
44 from rhodecode.model.db import (
44 from rhodecode.model.db import (
45 or_, count, func, in_filter_generator, Session, RepoGroup, User, Repository)
45 or_, count, func, in_filter_generator, Session, RepoGroup, User, Repository)
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class AdminRepoGroupsView(BaseAppView, DataGridAppView):
50 class AdminRepoGroupsView(BaseAppView, DataGridAppView):
51
51
52 def load_default_context(self):
52 def load_default_context(self):
53 c = self._get_local_tmpl_context()
53 c = self._get_local_tmpl_context()
54
54
55 return c
55 return c
56
56
57 def _load_form_data(self, c):
57 def _load_form_data(self, c):
58 allow_empty_group = False
58 allow_empty_group = False
59
59
60 if self._can_create_repo_group():
60 if self._can_create_repo_group():
61 # we're global admin, we're ok and we can create TOP level groups
61 # we're global admin, we're ok and we can create TOP level groups
62 allow_empty_group = True
62 allow_empty_group = True
63
63
64 # override the choices for this form, we need to filter choices
64 # override the choices for this form, we need to filter choices
65 # and display only those we have ADMIN right
65 # and display only those we have ADMIN right
66 groups_with_admin_rights = RepoGroupList(
66 groups_with_admin_rights = RepoGroupList(
67 RepoGroup.query().all(),
67 RepoGroup.query().all(),
68 perm_set=['group.admin'], extra_kwargs=dict(user=self._rhodecode_user))
68 perm_set=['group.admin'], extra_kwargs=dict(user=self._rhodecode_user))
69 c.repo_groups = RepoGroup.groups_choices(
69 c.repo_groups = RepoGroup.groups_choices(
70 groups=groups_with_admin_rights,
70 groups=groups_with_admin_rights,
71 show_empty_group=allow_empty_group)
71 show_empty_group=allow_empty_group)
72 c.personal_repo_group = self._rhodecode_user.personal_repo_group
72 c.personal_repo_group = self._rhodecode_user.personal_repo_group
73
73
74 def _can_create_repo_group(self, parent_group_id=None):
74 def _can_create_repo_group(self, parent_group_id=None):
75 is_admin = HasPermissionAny('hg.admin')('group create controller')
75 is_admin = HasPermissionAny('hg.admin')('group create controller')
76 create_repo_group = HasPermissionAny(
76 create_repo_group = HasPermissionAny(
77 'hg.repogroup.create.true')('group create controller')
77 'hg.repogroup.create.true')('group create controller')
78 if is_admin or (create_repo_group and not parent_group_id):
78 if is_admin or (create_repo_group and not parent_group_id):
79 # we're global admin, or we have global repo group create
79 # we're global admin, or we have global repo group create
80 # permission
80 # permission
81 # we're ok and we can create TOP level groups
81 # we're ok and we can create TOP level groups
82 return True
82 return True
83 elif parent_group_id:
83 elif parent_group_id:
84 # we check the permission if we can write to parent group
84 # we check the permission if we can write to parent group
85 group = RepoGroup.get(parent_group_id)
85 group = RepoGroup.get(parent_group_id)
86 group_name = group.group_name if group else None
86 group_name = group.group_name if group else None
87 if HasRepoGroupPermissionAny('group.admin')(
87 if HasRepoGroupPermissionAny('group.admin')(
88 group_name, 'check if user is an admin of group'):
88 group_name, 'check if user is an admin of group'):
89 # we're an admin of passed in group, we're ok.
89 # we're an admin of passed in group, we're ok.
90 return True
90 return True
91 else:
91 else:
92 return False
92 return False
93 return False
93 return False
94
94
95 # permission check in data loading of
95 # permission check in data loading of
96 # `repo_group_list_data` via RepoGroupList
96 # `repo_group_list_data` via RepoGroupList
97 @LoginRequired()
97 @LoginRequired()
98 @NotAnonymous()
98 @NotAnonymous()
99 @view_config(
100 route_name='repo_groups', request_method='GET',
101 renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako')
102 def repo_group_list(self):
99 def repo_group_list(self):
103 c = self.load_default_context()
100 c = self.load_default_context()
104 return self._get_template_context(c)
101 return self._get_template_context(c)
105
102
106 # permission check inside
103 # permission check inside
107 @LoginRequired()
104 @LoginRequired()
108 @NotAnonymous()
105 @NotAnonymous()
109 @view_config(
110 route_name='repo_groups_data', request_method='GET',
111 renderer='json_ext', xhr=True)
112 def repo_group_list_data(self):
106 def repo_group_list_data(self):
113 self.load_default_context()
107 self.load_default_context()
114 column_map = {
108 column_map = {
115 'name': 'group_name_hash',
109 'name': 'group_name_hash',
116 'desc': 'group_description',
110 'desc': 'group_description',
117 'last_change': 'updated_on',
111 'last_change': 'updated_on',
118 'top_level_repos': 'repos_total',
112 'top_level_repos': 'repos_total',
119 'owner': 'user_username',
113 'owner': 'user_username',
120 }
114 }
121 draw, start, limit = self._extract_chunk(self.request)
115 draw, start, limit = self._extract_chunk(self.request)
122 search_q, order_by, order_dir = self._extract_ordering(
116 search_q, order_by, order_dir = self._extract_ordering(
123 self.request, column_map=column_map)
117 self.request, column_map=column_map)
124
118
125 _render = self.request.get_partial_renderer(
119 _render = self.request.get_partial_renderer(
126 'rhodecode:templates/data_table/_dt_elements.mako')
120 'rhodecode:templates/data_table/_dt_elements.mako')
127 c = _render.get_call_context()
121 c = _render.get_call_context()
128
122
129 def quick_menu(repo_group_name):
123 def quick_menu(repo_group_name):
130 return _render('quick_repo_group_menu', repo_group_name)
124 return _render('quick_repo_group_menu', repo_group_name)
131
125
132 def repo_group_lnk(repo_group_name):
126 def repo_group_lnk(repo_group_name):
133 return _render('repo_group_name', repo_group_name)
127 return _render('repo_group_name', repo_group_name)
134
128
135 def last_change(last_change):
129 def last_change(last_change):
136 if isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
130 if isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
137 ts = time.time()
131 ts = time.time()
138 utc_offset = (datetime.datetime.fromtimestamp(ts)
132 utc_offset = (datetime.datetime.fromtimestamp(ts)
139 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
133 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
140 last_change = last_change + datetime.timedelta(seconds=utc_offset)
134 last_change = last_change + datetime.timedelta(seconds=utc_offset)
141 return _render("last_change", last_change)
135 return _render("last_change", last_change)
142
136
143 def desc(desc, personal):
137 def desc(desc, personal):
144 return _render(
138 return _render(
145 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
139 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
146
140
147 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
141 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
148 return _render(
142 return _render(
149 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
143 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
150
144
151 def user_profile(username):
145 def user_profile(username):
152 return _render('user_profile', username)
146 return _render('user_profile', username)
153
147
154 _perms = ['group.admin']
148 _perms = ['group.admin']
155 allowed_ids = [-1] + self._rhodecode_user.repo_group_acl_ids_from_stack(_perms)
149 allowed_ids = [-1] + self._rhodecode_user.repo_group_acl_ids_from_stack(_perms)
156
150
157 repo_groups_data_total_count = RepoGroup.query()\
151 repo_groups_data_total_count = RepoGroup.query()\
158 .filter(or_(
152 .filter(or_(
159 # generate multiple IN to fix limitation problems
153 # generate multiple IN to fix limitation problems
160 *in_filter_generator(RepoGroup.group_id, allowed_ids)
154 *in_filter_generator(RepoGroup.group_id, allowed_ids)
161 )) \
155 )) \
162 .count()
156 .count()
163
157
164 repo_groups_data_total_inactive_count = RepoGroup.query()\
158 repo_groups_data_total_inactive_count = RepoGroup.query()\
165 .filter(RepoGroup.group_id.in_(allowed_ids))\
159 .filter(RepoGroup.group_id.in_(allowed_ids))\
166 .count()
160 .count()
167
161
168 repo_count = count(Repository.repo_id)
162 repo_count = count(Repository.repo_id)
169 base_q = Session.query(
163 base_q = Session.query(
170 RepoGroup.group_name,
164 RepoGroup.group_name,
171 RepoGroup.group_name_hash,
165 RepoGroup.group_name_hash,
172 RepoGroup.group_description,
166 RepoGroup.group_description,
173 RepoGroup.group_id,
167 RepoGroup.group_id,
174 RepoGroup.personal,
168 RepoGroup.personal,
175 RepoGroup.updated_on,
169 RepoGroup.updated_on,
176 User,
170 User,
177 repo_count.label('repos_count')
171 repo_count.label('repos_count')
178 ) \
172 ) \
179 .filter(or_(
173 .filter(or_(
180 # generate multiple IN to fix limitation problems
174 # generate multiple IN to fix limitation problems
181 *in_filter_generator(RepoGroup.group_id, allowed_ids)
175 *in_filter_generator(RepoGroup.group_id, allowed_ids)
182 )) \
176 )) \
183 .outerjoin(Repository, Repository.group_id == RepoGroup.group_id) \
177 .outerjoin(Repository, Repository.group_id == RepoGroup.group_id) \
184 .join(User, User.user_id == RepoGroup.user_id) \
178 .join(User, User.user_id == RepoGroup.user_id) \
185 .group_by(RepoGroup, User)
179 .group_by(RepoGroup, User)
186
180
187 if search_q:
181 if search_q:
188 like_expression = u'%{}%'.format(safe_unicode(search_q))
182 like_expression = u'%{}%'.format(safe_unicode(search_q))
189 base_q = base_q.filter(or_(
183 base_q = base_q.filter(or_(
190 RepoGroup.group_name.ilike(like_expression),
184 RepoGroup.group_name.ilike(like_expression),
191 ))
185 ))
192
186
193 repo_groups_data_total_filtered_count = base_q.count()
187 repo_groups_data_total_filtered_count = base_q.count()
194 # the inactive isn't really used, but we still make it same as other data grids
188 # the inactive isn't really used, but we still make it same as other data grids
195 # which use inactive (users,user groups)
189 # which use inactive (users,user groups)
196 repo_groups_data_total_filtered_inactive_count = repo_groups_data_total_filtered_count
190 repo_groups_data_total_filtered_inactive_count = repo_groups_data_total_filtered_count
197
191
198 sort_defined = False
192 sort_defined = False
199 if order_by == 'group_name':
193 if order_by == 'group_name':
200 sort_col = func.lower(RepoGroup.group_name)
194 sort_col = func.lower(RepoGroup.group_name)
201 sort_defined = True
195 sort_defined = True
202 elif order_by == 'repos_total':
196 elif order_by == 'repos_total':
203 sort_col = repo_count
197 sort_col = repo_count
204 sort_defined = True
198 sort_defined = True
205 elif order_by == 'user_username':
199 elif order_by == 'user_username':
206 sort_col = User.username
200 sort_col = User.username
207 else:
201 else:
208 sort_col = getattr(RepoGroup, order_by, None)
202 sort_col = getattr(RepoGroup, order_by, None)
209
203
210 if sort_defined or sort_col:
204 if sort_defined or sort_col:
211 if order_dir == 'asc':
205 if order_dir == 'asc':
212 sort_col = sort_col.asc()
206 sort_col = sort_col.asc()
213 else:
207 else:
214 sort_col = sort_col.desc()
208 sort_col = sort_col.desc()
215
209
216 base_q = base_q.order_by(sort_col)
210 base_q = base_q.order_by(sort_col)
217 base_q = base_q.offset(start).limit(limit)
211 base_q = base_q.offset(start).limit(limit)
218
212
219 # authenticated access to user groups
213 # authenticated access to user groups
220 auth_repo_group_list = base_q.all()
214 auth_repo_group_list = base_q.all()
221
215
222 repo_groups_data = []
216 repo_groups_data = []
223 for repo_gr in auth_repo_group_list:
217 for repo_gr in auth_repo_group_list:
224 row = {
218 row = {
225 "menu": quick_menu(repo_gr.group_name),
219 "menu": quick_menu(repo_gr.group_name),
226 "name": repo_group_lnk(repo_gr.group_name),
220 "name": repo_group_lnk(repo_gr.group_name),
227
221
228 "last_change": last_change(repo_gr.updated_on),
222 "last_change": last_change(repo_gr.updated_on),
229
223
230 "last_changeset": "",
224 "last_changeset": "",
231 "last_changeset_raw": "",
225 "last_changeset_raw": "",
232
226
233 "desc": desc(repo_gr.group_description, repo_gr.personal),
227 "desc": desc(repo_gr.group_description, repo_gr.personal),
234 "owner": user_profile(repo_gr.User.username),
228 "owner": user_profile(repo_gr.User.username),
235 "top_level_repos": repo_gr.repos_count,
229 "top_level_repos": repo_gr.repos_count,
236 "action": repo_group_actions(
230 "action": repo_group_actions(
237 repo_gr.group_id, repo_gr.group_name, repo_gr.repos_count),
231 repo_gr.group_id, repo_gr.group_name, repo_gr.repos_count),
238
232
239 }
233 }
240
234
241 repo_groups_data.append(row)
235 repo_groups_data.append(row)
242
236
243 data = ({
237 data = ({
244 'draw': draw,
238 'draw': draw,
245 'data': repo_groups_data,
239 'data': repo_groups_data,
246 'recordsTotal': repo_groups_data_total_count,
240 'recordsTotal': repo_groups_data_total_count,
247 'recordsTotalInactive': repo_groups_data_total_inactive_count,
241 'recordsTotalInactive': repo_groups_data_total_inactive_count,
248 'recordsFiltered': repo_groups_data_total_filtered_count,
242 'recordsFiltered': repo_groups_data_total_filtered_count,
249 'recordsFilteredInactive': repo_groups_data_total_filtered_inactive_count,
243 'recordsFilteredInactive': repo_groups_data_total_filtered_inactive_count,
250 })
244 })
251
245
252 return data
246 return data
253
247
254 @LoginRequired()
248 @LoginRequired()
255 @NotAnonymous()
249 @NotAnonymous()
256 # perm checks inside
250 # perm checks inside
257 @view_config(
258 route_name='repo_group_new', request_method='GET',
259 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
260 def repo_group_new(self):
251 def repo_group_new(self):
261 c = self.load_default_context()
252 c = self.load_default_context()
262
253
263 # perm check for admin, create_group perm or admin of parent_group
254 # perm check for admin, create_group perm or admin of parent_group
264 parent_group_id = safe_int(self.request.GET.get('parent_group'))
255 parent_group_id = safe_int(self.request.GET.get('parent_group'))
265 _gr = RepoGroup.get(parent_group_id)
256 _gr = RepoGroup.get(parent_group_id)
266 if not self._can_create_repo_group(parent_group_id):
257 if not self._can_create_repo_group(parent_group_id):
267 raise HTTPForbidden()
258 raise HTTPForbidden()
268
259
269 self._load_form_data(c)
260 self._load_form_data(c)
270
261
271 defaults = {} # Future proof for default of repo group
262 defaults = {} # Future proof for default of repo group
272
263
273 parent_group_choice = '-1'
264 parent_group_choice = '-1'
274 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
265 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
275 parent_group_choice = self._rhodecode_user.personal_repo_group
266 parent_group_choice = self._rhodecode_user.personal_repo_group
276
267
277 if parent_group_id and _gr:
268 if parent_group_id and _gr:
278 if parent_group_id in [x[0] for x in c.repo_groups]:
269 if parent_group_id in [x[0] for x in c.repo_groups]:
279 parent_group_choice = safe_unicode(parent_group_id)
270 parent_group_choice = safe_unicode(parent_group_id)
280
271
281 defaults.update({'group_parent_id': parent_group_choice})
272 defaults.update({'group_parent_id': parent_group_choice})
282
273
283 data = render(
274 data = render(
284 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
275 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
285 self._get_template_context(c), self.request)
276 self._get_template_context(c), self.request)
286
277
287 html = formencode.htmlfill.render(
278 html = formencode.htmlfill.render(
288 data,
279 data,
289 defaults=defaults,
280 defaults=defaults,
290 encoding="UTF-8",
281 encoding="UTF-8",
291 force_defaults=False
282 force_defaults=False
292 )
283 )
293 return Response(html)
284 return Response(html)
294
285
295 @LoginRequired()
286 @LoginRequired()
296 @NotAnonymous()
287 @NotAnonymous()
297 @CSRFRequired()
288 @CSRFRequired()
298 # perm checks inside
289 # perm checks inside
299 @view_config(
300 route_name='repo_group_create', request_method='POST',
301 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
302 def repo_group_create(self):
290 def repo_group_create(self):
303 c = self.load_default_context()
291 c = self.load_default_context()
304 _ = self.request.translate
292 _ = self.request.translate
305
293
306 parent_group_id = safe_int(self.request.POST.get('group_parent_id'))
294 parent_group_id = safe_int(self.request.POST.get('group_parent_id'))
307 can_create = self._can_create_repo_group(parent_group_id)
295 can_create = self._can_create_repo_group(parent_group_id)
308
296
309 self._load_form_data(c)
297 self._load_form_data(c)
310 # permissions for can create group based on parent_id are checked
298 # permissions for can create group based on parent_id are checked
311 # here in the Form
299 # here in the Form
312 available_groups = map(lambda k: safe_unicode(k[0]), c.repo_groups)
300 available_groups = map(lambda k: safe_unicode(k[0]), c.repo_groups)
313 repo_group_form = RepoGroupForm(
301 repo_group_form = RepoGroupForm(
314 self.request.translate, available_groups=available_groups,
302 self.request.translate, available_groups=available_groups,
315 can_create_in_root=can_create)()
303 can_create_in_root=can_create)()
316
304
317 repo_group_name = self.request.POST.get('group_name')
305 repo_group_name = self.request.POST.get('group_name')
318 try:
306 try:
319 owner = self._rhodecode_user
307 owner = self._rhodecode_user
320 form_result = repo_group_form.to_python(dict(self.request.POST))
308 form_result = repo_group_form.to_python(dict(self.request.POST))
321 copy_permissions = form_result.get('group_copy_permissions')
309 copy_permissions = form_result.get('group_copy_permissions')
322 repo_group = RepoGroupModel().create(
310 repo_group = RepoGroupModel().create(
323 group_name=form_result['group_name_full'],
311 group_name=form_result['group_name_full'],
324 group_description=form_result['group_description'],
312 group_description=form_result['group_description'],
325 owner=owner.user_id,
313 owner=owner.user_id,
326 copy_permissions=form_result['group_copy_permissions']
314 copy_permissions=form_result['group_copy_permissions']
327 )
315 )
328 Session().flush()
316 Session().flush()
329
317
330 repo_group_data = repo_group.get_api_data()
318 repo_group_data = repo_group.get_api_data()
331 audit_logger.store_web(
319 audit_logger.store_web(
332 'repo_group.create', action_data={'data': repo_group_data},
320 'repo_group.create', action_data={'data': repo_group_data},
333 user=self._rhodecode_user)
321 user=self._rhodecode_user)
334
322
335 Session().commit()
323 Session().commit()
336
324
337 _new_group_name = form_result['group_name_full']
325 _new_group_name = form_result['group_name_full']
338
326
339 repo_group_url = h.link_to(
327 repo_group_url = h.link_to(
340 _new_group_name,
328 _new_group_name,
341 h.route_path('repo_group_home', repo_group_name=_new_group_name))
329 h.route_path('repo_group_home', repo_group_name=_new_group_name))
342 h.flash(h.literal(_('Created repository group %s')
330 h.flash(h.literal(_('Created repository group %s')
343 % repo_group_url), category='success')
331 % repo_group_url), category='success')
344
332
345 except formencode.Invalid as errors:
333 except formencode.Invalid as errors:
346 data = render(
334 data = render(
347 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
335 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
348 self._get_template_context(c), self.request)
336 self._get_template_context(c), self.request)
349 html = formencode.htmlfill.render(
337 html = formencode.htmlfill.render(
350 data,
338 data,
351 defaults=errors.value,
339 defaults=errors.value,
352 errors=errors.error_dict or {},
340 errors=errors.error_dict or {},
353 prefix_error=False,
341 prefix_error=False,
354 encoding="UTF-8",
342 encoding="UTF-8",
355 force_defaults=False
343 force_defaults=False
356 )
344 )
357 return Response(html)
345 return Response(html)
358 except Exception:
346 except Exception:
359 log.exception("Exception during creation of repository group")
347 log.exception("Exception during creation of repository group")
360 h.flash(_('Error occurred during creation of repository group %s')
348 h.flash(_('Error occurred during creation of repository group %s')
361 % repo_group_name, category='error')
349 % repo_group_name, category='error')
362 raise HTTPFound(h.route_path('home'))
350 raise HTTPFound(h.route_path('home'))
363
351
364 affected_user_ids = [self._rhodecode_user.user_id]
352 affected_user_ids = [self._rhodecode_user.user_id]
365 if copy_permissions:
353 if copy_permissions:
366 user_group_perms = repo_group.permissions(expand_from_user_groups=True)
354 user_group_perms = repo_group.permissions(expand_from_user_groups=True)
367 copy_perms = [perm['user_id'] for perm in user_group_perms]
355 copy_perms = [perm['user_id'] for perm in user_group_perms]
368 # also include those newly created by copy
356 # also include those newly created by copy
369 affected_user_ids.extend(copy_perms)
357 affected_user_ids.extend(copy_perms)
370 PermissionModel().trigger_permission_flush(affected_user_ids)
358 PermissionModel().trigger_permission_flush(affected_user_ids)
371
359
372 raise HTTPFound(
360 raise HTTPFound(
373 h.route_path('repo_group_home',
361 h.route_path('repo_group_home',
374 repo_group_name=form_result['group_name_full']))
362 repo_group_name=form_result['group_name_full']))
@@ -1,266 +1,253 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import formencode
22 import formencode
23 import formencode.htmlfill
23 import formencode.htmlfill
24
24
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 from pyramid.view import view_config
26
27 from pyramid.renderers import render
27 from pyramid.renderers import render
28 from pyramid.response import Response
28 from pyramid.response import Response
29
29
30 from rhodecode import events
30 from rhodecode import events
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.lib.celerylib.utils import get_task_id
32 from rhodecode.lib.celerylib.utils import get_task_id
33
33
34 from rhodecode.lib.auth import (
34 from rhodecode.lib.auth import (
35 LoginRequired, CSRFRequired, NotAnonymous,
35 LoginRequired, CSRFRequired, NotAnonymous,
36 HasPermissionAny, HasRepoGroupPermissionAny)
36 HasPermissionAny, HasRepoGroupPermissionAny)
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.utils import repo_name_slug
38 from rhodecode.lib.utils import repo_name_slug
39 from rhodecode.lib.utils2 import safe_int, safe_unicode
39 from rhodecode.lib.utils2 import safe_int, safe_unicode
40 from rhodecode.model.forms import RepoForm
40 from rhodecode.model.forms import RepoForm
41 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.permission import PermissionModel
42 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
43 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
44 from rhodecode.model.settings import SettingsModel
44 from rhodecode.model.settings import SettingsModel
45 from rhodecode.model.db import (
45 from rhodecode.model.db import (
46 in_filter_generator, or_, func, Session, Repository, RepoGroup, User)
46 in_filter_generator, or_, func, Session, Repository, RepoGroup, User)
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50
50
51 class AdminReposView(BaseAppView, DataGridAppView):
51 class AdminReposView(BaseAppView, DataGridAppView):
52
52
53 def load_default_context(self):
53 def load_default_context(self):
54 c = self._get_local_tmpl_context()
54 c = self._get_local_tmpl_context()
55
56 return c
55 return c
57
56
58 def _load_form_data(self, c):
57 def _load_form_data(self, c):
59 acl_groups = RepoGroupList(RepoGroup.query().all(),
58 acl_groups = RepoGroupList(RepoGroup.query().all(),
60 perm_set=['group.write', 'group.admin'])
59 perm_set=['group.write', 'group.admin'])
61 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
60 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
62 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
61 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
63 c.personal_repo_group = self._rhodecode_user.personal_repo_group
62 c.personal_repo_group = self._rhodecode_user.personal_repo_group
64
63
65 @LoginRequired()
64 @LoginRequired()
66 @NotAnonymous()
65 @NotAnonymous()
67 # perms check inside
66 # perms check inside
68 @view_config(
69 route_name='repos', request_method='GET',
70 renderer='rhodecode:templates/admin/repos/repos.mako')
71 def repository_list(self):
67 def repository_list(self):
72 c = self.load_default_context()
68 c = self.load_default_context()
73 return self._get_template_context(c)
69 return self._get_template_context(c)
74
70
75 @LoginRequired()
71 @LoginRequired()
76 @NotAnonymous()
72 @NotAnonymous()
77 # perms check inside
73 # perms check inside
78 @view_config(
79 route_name='repos_data', request_method='GET',
80 renderer='json_ext', xhr=True)
81 def repository_list_data(self):
74 def repository_list_data(self):
82 self.load_default_context()
75 self.load_default_context()
83 column_map = {
76 column_map = {
84 'name': 'repo_name',
77 'name': 'repo_name',
85 'desc': 'description',
78 'desc': 'description',
86 'last_change': 'updated_on',
79 'last_change': 'updated_on',
87 'owner': 'user_username',
80 'owner': 'user_username',
88 }
81 }
89 draw, start, limit = self._extract_chunk(self.request)
82 draw, start, limit = self._extract_chunk(self.request)
90 search_q, order_by, order_dir = self._extract_ordering(
83 search_q, order_by, order_dir = self._extract_ordering(
91 self.request, column_map=column_map)
84 self.request, column_map=column_map)
92
85
93 _perms = ['repository.admin']
86 _perms = ['repository.admin']
94 allowed_ids = [-1] + self._rhodecode_user.repo_acl_ids_from_stack(_perms)
87 allowed_ids = [-1] + self._rhodecode_user.repo_acl_ids_from_stack(_perms)
95
88
96 repos_data_total_count = Repository.query() \
89 repos_data_total_count = Repository.query() \
97 .filter(or_(
90 .filter(or_(
98 # generate multiple IN to fix limitation problems
91 # generate multiple IN to fix limitation problems
99 *in_filter_generator(Repository.repo_id, allowed_ids))
92 *in_filter_generator(Repository.repo_id, allowed_ids))
100 ) \
93 ) \
101 .count()
94 .count()
102
95
103 base_q = Session.query(
96 base_q = Session.query(
104 Repository.repo_id,
97 Repository.repo_id,
105 Repository.repo_name,
98 Repository.repo_name,
106 Repository.description,
99 Repository.description,
107 Repository.repo_type,
100 Repository.repo_type,
108 Repository.repo_state,
101 Repository.repo_state,
109 Repository.private,
102 Repository.private,
110 Repository.archived,
103 Repository.archived,
111 Repository.fork,
104 Repository.fork,
112 Repository.updated_on,
105 Repository.updated_on,
113 Repository._changeset_cache,
106 Repository._changeset_cache,
114 User,
107 User,
115 ) \
108 ) \
116 .filter(or_(
109 .filter(or_(
117 # generate multiple IN to fix limitation problems
110 # generate multiple IN to fix limitation problems
118 *in_filter_generator(Repository.repo_id, allowed_ids))
111 *in_filter_generator(Repository.repo_id, allowed_ids))
119 ) \
112 ) \
120 .join(User, User.user_id == Repository.user_id) \
113 .join(User, User.user_id == Repository.user_id) \
121 .group_by(Repository, User)
114 .group_by(Repository, User)
122
115
123 if search_q:
116 if search_q:
124 like_expression = u'%{}%'.format(safe_unicode(search_q))
117 like_expression = u'%{}%'.format(safe_unicode(search_q))
125 base_q = base_q.filter(or_(
118 base_q = base_q.filter(or_(
126 Repository.repo_name.ilike(like_expression),
119 Repository.repo_name.ilike(like_expression),
127 ))
120 ))
128
121
129 repos_data_total_filtered_count = base_q.count()
122 repos_data_total_filtered_count = base_q.count()
130
123
131 sort_defined = False
124 sort_defined = False
132 if order_by == 'repo_name':
125 if order_by == 'repo_name':
133 sort_col = func.lower(Repository.repo_name)
126 sort_col = func.lower(Repository.repo_name)
134 sort_defined = True
127 sort_defined = True
135 elif order_by == 'user_username':
128 elif order_by == 'user_username':
136 sort_col = User.username
129 sort_col = User.username
137 else:
130 else:
138 sort_col = getattr(Repository, order_by, None)
131 sort_col = getattr(Repository, order_by, None)
139
132
140 if sort_defined or sort_col:
133 if sort_defined or sort_col:
141 if order_dir == 'asc':
134 if order_dir == 'asc':
142 sort_col = sort_col.asc()
135 sort_col = sort_col.asc()
143 else:
136 else:
144 sort_col = sort_col.desc()
137 sort_col = sort_col.desc()
145
138
146 base_q = base_q.order_by(sort_col)
139 base_q = base_q.order_by(sort_col)
147 base_q = base_q.offset(start).limit(limit)
140 base_q = base_q.offset(start).limit(limit)
148
141
149 repos_list = base_q.all()
142 repos_list = base_q.all()
150
143
151 repos_data = RepoModel().get_repos_as_dict(
144 repos_data = RepoModel().get_repos_as_dict(
152 repo_list=repos_list, admin=True, super_user_actions=True)
145 repo_list=repos_list, admin=True, super_user_actions=True)
153
146
154 data = ({
147 data = ({
155 'draw': draw,
148 'draw': draw,
156 'data': repos_data,
149 'data': repos_data,
157 'recordsTotal': repos_data_total_count,
150 'recordsTotal': repos_data_total_count,
158 'recordsFiltered': repos_data_total_filtered_count,
151 'recordsFiltered': repos_data_total_filtered_count,
159 })
152 })
160 return data
153 return data
161
154
162 @LoginRequired()
155 @LoginRequired()
163 @NotAnonymous()
156 @NotAnonymous()
164 # perms check inside
157 # perms check inside
165 @view_config(
166 route_name='repo_new', request_method='GET',
167 renderer='rhodecode:templates/admin/repos/repo_add.mako')
168 def repository_new(self):
158 def repository_new(self):
169 c = self.load_default_context()
159 c = self.load_default_context()
170
160
171 new_repo = self.request.GET.get('repo', '')
161 new_repo = self.request.GET.get('repo', '')
172 parent_group_id = safe_int(self.request.GET.get('parent_group'))
162 parent_group_id = safe_int(self.request.GET.get('parent_group'))
173 _gr = RepoGroup.get(parent_group_id)
163 _gr = RepoGroup.get(parent_group_id)
174
164
175 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
165 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
176 # you're not super admin nor have global create permissions,
166 # you're not super admin nor have global create permissions,
177 # but maybe you have at least write permission to a parent group ?
167 # but maybe you have at least write permission to a parent group ?
178
168
179 gr_name = _gr.group_name if _gr else None
169 gr_name = _gr.group_name if _gr else None
180 # create repositories with write permission on group is set to true
170 # create repositories with write permission on group is set to true
181 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
171 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
182 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
172 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
183 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
173 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
184 if not (group_admin or (group_write and create_on_write)):
174 if not (group_admin or (group_write and create_on_write)):
185 raise HTTPForbidden()
175 raise HTTPForbidden()
186
176
187 self._load_form_data(c)
177 self._load_form_data(c)
188 c.new_repo = repo_name_slug(new_repo)
178 c.new_repo = repo_name_slug(new_repo)
189
179
190 # apply the defaults from defaults page
180 # apply the defaults from defaults page
191 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
181 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
192 # set checkbox to autochecked
182 # set checkbox to autochecked
193 defaults['repo_copy_permissions'] = True
183 defaults['repo_copy_permissions'] = True
194
184
195 parent_group_choice = '-1'
185 parent_group_choice = '-1'
196 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
186 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
197 parent_group_choice = self._rhodecode_user.personal_repo_group
187 parent_group_choice = self._rhodecode_user.personal_repo_group
198
188
199 if parent_group_id and _gr:
189 if parent_group_id and _gr:
200 if parent_group_id in [x[0] for x in c.repo_groups]:
190 if parent_group_id in [x[0] for x in c.repo_groups]:
201 parent_group_choice = safe_unicode(parent_group_id)
191 parent_group_choice = safe_unicode(parent_group_id)
202
192
203 defaults.update({'repo_group': parent_group_choice})
193 defaults.update({'repo_group': parent_group_choice})
204
194
205 data = render('rhodecode:templates/admin/repos/repo_add.mako',
195 data = render('rhodecode:templates/admin/repos/repo_add.mako',
206 self._get_template_context(c), self.request)
196 self._get_template_context(c), self.request)
207 html = formencode.htmlfill.render(
197 html = formencode.htmlfill.render(
208 data,
198 data,
209 defaults=defaults,
199 defaults=defaults,
210 encoding="UTF-8",
200 encoding="UTF-8",
211 force_defaults=False
201 force_defaults=False
212 )
202 )
213 return Response(html)
203 return Response(html)
214
204
215 @LoginRequired()
205 @LoginRequired()
216 @NotAnonymous()
206 @NotAnonymous()
217 @CSRFRequired()
207 @CSRFRequired()
218 # perms check inside
208 # perms check inside
219 @view_config(
220 route_name='repo_create', request_method='POST',
221 renderer='rhodecode:templates/admin/repos/repos.mako')
222 def repository_create(self):
209 def repository_create(self):
223 c = self.load_default_context()
210 c = self.load_default_context()
224
211
225 form_result = {}
212 form_result = {}
226 self._load_form_data(c)
213 self._load_form_data(c)
227
214
228 try:
215 try:
229 # CanWriteToGroup validators checks permissions of this POST
216 # CanWriteToGroup validators checks permissions of this POST
230 form = RepoForm(
217 form = RepoForm(
231 self.request.translate, repo_groups=c.repo_groups_choices)()
218 self.request.translate, repo_groups=c.repo_groups_choices)()
232 form_result = form.to_python(dict(self.request.POST))
219 form_result = form.to_python(dict(self.request.POST))
233 copy_permissions = form_result.get('repo_copy_permissions')
220 copy_permissions = form_result.get('repo_copy_permissions')
234 # create is done sometimes async on celery, db transaction
221 # create is done sometimes async on celery, db transaction
235 # management is handled there.
222 # management is handled there.
236 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
223 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
237 task_id = get_task_id(task)
224 task_id = get_task_id(task)
238 except formencode.Invalid as errors:
225 except formencode.Invalid as errors:
239 data = render('rhodecode:templates/admin/repos/repo_add.mako',
226 data = render('rhodecode:templates/admin/repos/repo_add.mako',
240 self._get_template_context(c), self.request)
227 self._get_template_context(c), self.request)
241 html = formencode.htmlfill.render(
228 html = formencode.htmlfill.render(
242 data,
229 data,
243 defaults=errors.value,
230 defaults=errors.value,
244 errors=errors.error_dict or {},
231 errors=errors.error_dict or {},
245 prefix_error=False,
232 prefix_error=False,
246 encoding="UTF-8",
233 encoding="UTF-8",
247 force_defaults=False
234 force_defaults=False
248 )
235 )
249 return Response(html)
236 return Response(html)
250
237
251 except Exception as e:
238 except Exception as e:
252 msg = self._log_creation_exception(e, form_result.get('repo_name'))
239 msg = self._log_creation_exception(e, form_result.get('repo_name'))
253 h.flash(msg, category='error')
240 h.flash(msg, category='error')
254 raise HTTPFound(h.route_path('home'))
241 raise HTTPFound(h.route_path('home'))
255
242
256 repo_name = form_result.get('repo_name_full')
243 repo_name = form_result.get('repo_name_full')
257
244
258 affected_user_ids = [self._rhodecode_user.user_id]
245 affected_user_ids = [self._rhodecode_user.user_id]
259 if copy_permissions:
246 if copy_permissions:
260 # permission flush is done in repo creating
247 # permission flush is done in repo creating
261 pass
248 pass
262 PermissionModel().trigger_permission_flush(affected_user_ids)
249 PermissionModel().trigger_permission_flush(affected_user_ids)
263
250
264 raise HTTPFound(
251 raise HTTPFound(
265 h.route_path('repo_creating', repo_name=repo_name,
252 h.route_path('repo_creating', repo_name=repo_name,
266 _query=dict(task_id=task_id)))
253 _query=dict(task_id=task_id)))
@@ -1,101 +1,95 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from pyramid.view import view_config
23
24 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base.navigation import navigation_list
27 from rhodecode.apps._base.navigation import navigation_list
28 from rhodecode.lib.auth import (
28 from rhodecode.lib.auth import (
29 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
29 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
30 from rhodecode.lib.utils2 import safe_int
30 from rhodecode.lib.utils2 import safe_int
31 from rhodecode.lib import system_info
31 from rhodecode.lib import system_info
32 from rhodecode.lib import user_sessions
32 from rhodecode.lib import user_sessions
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34
34
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 class AdminSessionSettingsView(BaseAppView):
39 class AdminSessionSettingsView(BaseAppView):
40
40 def load_default_context(self):
41 def load_default_context(self):
41 c = self._get_local_tmpl_context()
42 c = self._get_local_tmpl_context()
42
43
44 return c
43 return c
45
44
46 @LoginRequired()
45 @LoginRequired()
47 @HasPermissionAllDecorator('hg.admin')
46 @HasPermissionAllDecorator('hg.admin')
48 @view_config(
49 route_name='admin_settings_sessions', request_method='GET',
50 renderer='rhodecode:templates/admin/settings/settings.mako')
51 def settings_sessions(self):
47 def settings_sessions(self):
52 c = self.load_default_context()
48 c = self.load_default_context()
53
49
54 c.active = 'sessions'
50 c.active = 'sessions'
55 c.navlist = navigation_list(self.request)
51 c.navlist = navigation_list(self.request)
56
52
57 c.cleanup_older_days = 60
53 c.cleanup_older_days = 60
58 older_than_seconds = 60 * 60 * 24 * c.cleanup_older_days
54 older_than_seconds = 60 * 60 * 24 * c.cleanup_older_days
59
55
60 config = system_info.rhodecode_config().get_value()['value']['config']
56 config = system_info.rhodecode_config().get_value()['value']['config']
61 c.session_model = user_sessions.get_session_handler(
57 c.session_model = user_sessions.get_session_handler(
62 config.get('beaker.session.type', 'memory'))(config)
58 config.get('beaker.session.type', 'memory'))(config)
63
59
64 c.session_conf = c.session_model.config
60 c.session_conf = c.session_model.config
65 c.session_count = c.session_model.get_count()
61 c.session_count = c.session_model.get_count()
66 c.session_expired_count = c.session_model.get_expired_count(
62 c.session_expired_count = c.session_model.get_expired_count(
67 older_than_seconds)
63 older_than_seconds)
68
64
69 return self._get_template_context(c)
65 return self._get_template_context(c)
70
66
71 @LoginRequired()
67 @LoginRequired()
72 @HasPermissionAllDecorator('hg.admin')
68 @HasPermissionAllDecorator('hg.admin')
73 @CSRFRequired()
69 @CSRFRequired()
74 @view_config(
75 route_name='admin_settings_sessions_cleanup', request_method='POST')
76 def settings_sessions_cleanup(self):
70 def settings_sessions_cleanup(self):
77 _ = self.request.translate
71 _ = self.request.translate
78 expire_days = safe_int(self.request.params.get('expire_days'))
72 expire_days = safe_int(self.request.params.get('expire_days'))
79
73
80 if expire_days is None:
74 if expire_days is None:
81 expire_days = 60
75 expire_days = 60
82
76
83 older_than_seconds = 60 * 60 * 24 * expire_days
77 older_than_seconds = 60 * 60 * 24 * expire_days
84
78
85 config = system_info.rhodecode_config().get_value()['value']['config']
79 config = system_info.rhodecode_config().get_value()['value']['config']
86 session_model = user_sessions.get_session_handler(
80 session_model = user_sessions.get_session_handler(
87 config.get('beaker.session.type', 'memory'))(config)
81 config.get('beaker.session.type', 'memory'))(config)
88
82
89 try:
83 try:
90 session_model.clean_sessions(
84 session_model.clean_sessions(
91 older_than_seconds=older_than_seconds)
85 older_than_seconds=older_than_seconds)
92 h.flash(_('Cleaned up old sessions'), category='success')
86 h.flash(_('Cleaned up old sessions'), category='success')
93 except user_sessions.CleanupCommand as msg:
87 except user_sessions.CleanupCommand as msg:
94 h.flash(msg.message, category='warning')
88 h.flash(msg.message, category='warning')
95 except Exception as e:
89 except Exception as e:
96 log.exception('Failed session cleanup')
90 log.exception('Failed session cleanup')
97 h.flash(_('Failed to cleanup up old sessions'), category='error')
91 h.flash(_('Failed to cleanup up old sessions'), category='error')
98
92
99 redirect_to = self.request.resource_path(
93 redirect_to = self.request.resource_path(
100 self.context, route_name='admin_settings_sessions')
94 self.context, route_name='admin_settings_sessions')
101 return HTTPFound(redirect_to)
95 return HTTPFound(redirect_to)
@@ -1,792 +1,719 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 import datetime
25 import datetime
26 import formencode
26 import formencode
27 import formencode.htmlfill
27 import formencode.htmlfill
28
28
29 import rhodecode
29 import rhodecode
30 from pyramid.view import view_config
30
31 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
31 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
32 from pyramid.renderers import render
32 from pyramid.renderers import render
33 from pyramid.response import Response
33 from pyramid.response import Response
34
34
35 from rhodecode.apps._base import BaseAppView
35 from rhodecode.apps._base import BaseAppView
36 from rhodecode.apps._base.navigation import navigation_list
36 from rhodecode.apps._base.navigation import navigation_list
37 from rhodecode.apps.svn_support.config_keys import generate_config
37 from rhodecode.apps.svn_support.config_keys import generate_config
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import (
39 from rhodecode.lib.auth import (
40 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
40 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
41 from rhodecode.lib.celerylib import tasks, run_task
41 from rhodecode.lib.celerylib import tasks, run_task
42 from rhodecode.lib.utils import repo2db_mapper
42 from rhodecode.lib.utils import repo2db_mapper
43 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
43 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
44 from rhodecode.lib.index import searcher_from_config
44 from rhodecode.lib.index import searcher_from_config
45
45
46 from rhodecode.model.db import RhodeCodeUi, Repository
46 from rhodecode.model.db import RhodeCodeUi, Repository
47 from rhodecode.model.forms import (ApplicationSettingsForm,
47 from rhodecode.model.forms import (ApplicationSettingsForm,
48 ApplicationUiSettingsForm, ApplicationVisualisationForm,
48 ApplicationUiSettingsForm, ApplicationVisualisationForm,
49 LabsSettingsForm, IssueTrackerPatternsForm)
49 LabsSettingsForm, IssueTrackerPatternsForm)
50 from rhodecode.model.permission import PermissionModel
50 from rhodecode.model.permission import PermissionModel
51 from rhodecode.model.repo_group import RepoGroupModel
51 from rhodecode.model.repo_group import RepoGroupModel
52
52
53 from rhodecode.model.scm import ScmModel
53 from rhodecode.model.scm import ScmModel
54 from rhodecode.model.notification import EmailNotificationModel
54 from rhodecode.model.notification import EmailNotificationModel
55 from rhodecode.model.meta import Session
55 from rhodecode.model.meta import Session
56 from rhodecode.model.settings import (
56 from rhodecode.model.settings import (
57 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
57 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
58 SettingsModel)
58 SettingsModel)
59
59
60
60
61 log = logging.getLogger(__name__)
61 log = logging.getLogger(__name__)
62
62
63
63
64 class AdminSettingsView(BaseAppView):
64 class AdminSettingsView(BaseAppView):
65
65
66 def load_default_context(self):
66 def load_default_context(self):
67 c = self._get_local_tmpl_context()
67 c = self._get_local_tmpl_context()
68 c.labs_active = str2bool(
68 c.labs_active = str2bool(
69 rhodecode.CONFIG.get('labs_settings_active', 'true'))
69 rhodecode.CONFIG.get('labs_settings_active', 'true'))
70 c.navlist = navigation_list(self.request)
70 c.navlist = navigation_list(self.request)
71
72 return c
71 return c
73
72
74 @classmethod
73 @classmethod
75 def _get_ui_settings(cls):
74 def _get_ui_settings(cls):
76 ret = RhodeCodeUi.query().all()
75 ret = RhodeCodeUi.query().all()
77
76
78 if not ret:
77 if not ret:
79 raise Exception('Could not get application ui settings !')
78 raise Exception('Could not get application ui settings !')
80 settings = {}
79 settings = {}
81 for each in ret:
80 for each in ret:
82 k = each.ui_key
81 k = each.ui_key
83 v = each.ui_value
82 v = each.ui_value
84 if k == '/':
83 if k == '/':
85 k = 'root_path'
84 k = 'root_path'
86
85
87 if k in ['push_ssl', 'publish', 'enabled']:
86 if k in ['push_ssl', 'publish', 'enabled']:
88 v = str2bool(v)
87 v = str2bool(v)
89
88
90 if k.find('.') != -1:
89 if k.find('.') != -1:
91 k = k.replace('.', '_')
90 k = k.replace('.', '_')
92
91
93 if each.ui_section in ['hooks', 'extensions']:
92 if each.ui_section in ['hooks', 'extensions']:
94 v = each.ui_active
93 v = each.ui_active
95
94
96 settings[each.ui_section + '_' + k] = v
95 settings[each.ui_section + '_' + k] = v
97 return settings
96 return settings
98
97
99 @classmethod
98 @classmethod
100 def _form_defaults(cls):
99 def _form_defaults(cls):
101 defaults = SettingsModel().get_all_settings()
100 defaults = SettingsModel().get_all_settings()
102 defaults.update(cls._get_ui_settings())
101 defaults.update(cls._get_ui_settings())
103
102
104 defaults.update({
103 defaults.update({
105 'new_svn_branch': '',
104 'new_svn_branch': '',
106 'new_svn_tag': '',
105 'new_svn_tag': '',
107 })
106 })
108 return defaults
107 return defaults
109
108
110 @LoginRequired()
109 @LoginRequired()
111 @HasPermissionAllDecorator('hg.admin')
110 @HasPermissionAllDecorator('hg.admin')
112 @view_config(
113 route_name='admin_settings_vcs', request_method='GET',
114 renderer='rhodecode:templates/admin/settings/settings.mako')
115 def settings_vcs(self):
111 def settings_vcs(self):
116 c = self.load_default_context()
112 c = self.load_default_context()
117 c.active = 'vcs'
113 c.active = 'vcs'
118 model = VcsSettingsModel()
114 model = VcsSettingsModel()
119 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
115 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
120 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
116 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
121
117
122 settings = self.request.registry.settings
118 settings = self.request.registry.settings
123 c.svn_proxy_generate_config = settings[generate_config]
119 c.svn_proxy_generate_config = settings[generate_config]
124
120
125 defaults = self._form_defaults()
121 defaults = self._form_defaults()
126
122
127 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
123 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
128
124
129 data = render('rhodecode:templates/admin/settings/settings.mako',
125 data = render('rhodecode:templates/admin/settings/settings.mako',
130 self._get_template_context(c), self.request)
126 self._get_template_context(c), self.request)
131 html = formencode.htmlfill.render(
127 html = formencode.htmlfill.render(
132 data,
128 data,
133 defaults=defaults,
129 defaults=defaults,
134 encoding="UTF-8",
130 encoding="UTF-8",
135 force_defaults=False
131 force_defaults=False
136 )
132 )
137 return Response(html)
133 return Response(html)
138
134
139 @LoginRequired()
135 @LoginRequired()
140 @HasPermissionAllDecorator('hg.admin')
136 @HasPermissionAllDecorator('hg.admin')
141 @CSRFRequired()
137 @CSRFRequired()
142 @view_config(
143 route_name='admin_settings_vcs_update', request_method='POST',
144 renderer='rhodecode:templates/admin/settings/settings.mako')
145 def settings_vcs_update(self):
138 def settings_vcs_update(self):
146 _ = self.request.translate
139 _ = self.request.translate
147 c = self.load_default_context()
140 c = self.load_default_context()
148 c.active = 'vcs'
141 c.active = 'vcs'
149
142
150 model = VcsSettingsModel()
143 model = VcsSettingsModel()
151 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
144 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
152 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
145 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
153
146
154 settings = self.request.registry.settings
147 settings = self.request.registry.settings
155 c.svn_proxy_generate_config = settings[generate_config]
148 c.svn_proxy_generate_config = settings[generate_config]
156
149
157 application_form = ApplicationUiSettingsForm(self.request.translate)()
150 application_form = ApplicationUiSettingsForm(self.request.translate)()
158
151
159 try:
152 try:
160 form_result = application_form.to_python(dict(self.request.POST))
153 form_result = application_form.to_python(dict(self.request.POST))
161 except formencode.Invalid as errors:
154 except formencode.Invalid as errors:
162 h.flash(
155 h.flash(
163 _("Some form inputs contain invalid data."),
156 _("Some form inputs contain invalid data."),
164 category='error')
157 category='error')
165 data = render('rhodecode:templates/admin/settings/settings.mako',
158 data = render('rhodecode:templates/admin/settings/settings.mako',
166 self._get_template_context(c), self.request)
159 self._get_template_context(c), self.request)
167 html = formencode.htmlfill.render(
160 html = formencode.htmlfill.render(
168 data,
161 data,
169 defaults=errors.value,
162 defaults=errors.value,
170 errors=errors.error_dict or {},
163 errors=errors.error_dict or {},
171 prefix_error=False,
164 prefix_error=False,
172 encoding="UTF-8",
165 encoding="UTF-8",
173 force_defaults=False
166 force_defaults=False
174 )
167 )
175 return Response(html)
168 return Response(html)
176
169
177 try:
170 try:
178 if c.visual.allow_repo_location_change:
171 if c.visual.allow_repo_location_change:
179 model.update_global_path_setting(form_result['paths_root_path'])
172 model.update_global_path_setting(form_result['paths_root_path'])
180
173
181 model.update_global_ssl_setting(form_result['web_push_ssl'])
174 model.update_global_ssl_setting(form_result['web_push_ssl'])
182 model.update_global_hook_settings(form_result)
175 model.update_global_hook_settings(form_result)
183
176
184 model.create_or_update_global_svn_settings(form_result)
177 model.create_or_update_global_svn_settings(form_result)
185 model.create_or_update_global_hg_settings(form_result)
178 model.create_or_update_global_hg_settings(form_result)
186 model.create_or_update_global_git_settings(form_result)
179 model.create_or_update_global_git_settings(form_result)
187 model.create_or_update_global_pr_settings(form_result)
180 model.create_or_update_global_pr_settings(form_result)
188 except Exception:
181 except Exception:
189 log.exception("Exception while updating settings")
182 log.exception("Exception while updating settings")
190 h.flash(_('Error occurred during updating '
183 h.flash(_('Error occurred during updating '
191 'application settings'), category='error')
184 'application settings'), category='error')
192 else:
185 else:
193 Session().commit()
186 Session().commit()
194 h.flash(_('Updated VCS settings'), category='success')
187 h.flash(_('Updated VCS settings'), category='success')
195 raise HTTPFound(h.route_path('admin_settings_vcs'))
188 raise HTTPFound(h.route_path('admin_settings_vcs'))
196
189
197 data = render('rhodecode:templates/admin/settings/settings.mako',
190 data = render('rhodecode:templates/admin/settings/settings.mako',
198 self._get_template_context(c), self.request)
191 self._get_template_context(c), self.request)
199 html = formencode.htmlfill.render(
192 html = formencode.htmlfill.render(
200 data,
193 data,
201 defaults=self._form_defaults(),
194 defaults=self._form_defaults(),
202 encoding="UTF-8",
195 encoding="UTF-8",
203 force_defaults=False
196 force_defaults=False
204 )
197 )
205 return Response(html)
198 return Response(html)
206
199
207 @LoginRequired()
200 @LoginRequired()
208 @HasPermissionAllDecorator('hg.admin')
201 @HasPermissionAllDecorator('hg.admin')
209 @CSRFRequired()
202 @CSRFRequired()
210 @view_config(
211 route_name='admin_settings_vcs_svn_pattern_delete', request_method='POST',
212 renderer='json_ext', xhr=True)
213 def settings_vcs_delete_svn_pattern(self):
203 def settings_vcs_delete_svn_pattern(self):
214 delete_pattern_id = self.request.POST.get('delete_svn_pattern')
204 delete_pattern_id = self.request.POST.get('delete_svn_pattern')
215 model = VcsSettingsModel()
205 model = VcsSettingsModel()
216 try:
206 try:
217 model.delete_global_svn_pattern(delete_pattern_id)
207 model.delete_global_svn_pattern(delete_pattern_id)
218 except SettingNotFound:
208 except SettingNotFound:
219 log.exception(
209 log.exception(
220 'Failed to delete svn_pattern with id %s', delete_pattern_id)
210 'Failed to delete svn_pattern with id %s', delete_pattern_id)
221 raise HTTPNotFound()
211 raise HTTPNotFound()
222
212
223 Session().commit()
213 Session().commit()
224 return True
214 return True
225
215
226 @LoginRequired()
216 @LoginRequired()
227 @HasPermissionAllDecorator('hg.admin')
217 @HasPermissionAllDecorator('hg.admin')
228 @view_config(
229 route_name='admin_settings_mapping', request_method='GET',
230 renderer='rhodecode:templates/admin/settings/settings.mako')
231 def settings_mapping(self):
218 def settings_mapping(self):
232 c = self.load_default_context()
219 c = self.load_default_context()
233 c.active = 'mapping'
220 c.active = 'mapping'
234
221
235 data = render('rhodecode:templates/admin/settings/settings.mako',
222 data = render('rhodecode:templates/admin/settings/settings.mako',
236 self._get_template_context(c), self.request)
223 self._get_template_context(c), self.request)
237 html = formencode.htmlfill.render(
224 html = formencode.htmlfill.render(
238 data,
225 data,
239 defaults=self._form_defaults(),
226 defaults=self._form_defaults(),
240 encoding="UTF-8",
227 encoding="UTF-8",
241 force_defaults=False
228 force_defaults=False
242 )
229 )
243 return Response(html)
230 return Response(html)
244
231
245 @LoginRequired()
232 @LoginRequired()
246 @HasPermissionAllDecorator('hg.admin')
233 @HasPermissionAllDecorator('hg.admin')
247 @CSRFRequired()
234 @CSRFRequired()
248 @view_config(
249 route_name='admin_settings_mapping_update', request_method='POST',
250 renderer='rhodecode:templates/admin/settings/settings.mako')
251 def settings_mapping_update(self):
235 def settings_mapping_update(self):
252 _ = self.request.translate
236 _ = self.request.translate
253 c = self.load_default_context()
237 c = self.load_default_context()
254 c.active = 'mapping'
238 c.active = 'mapping'
255 rm_obsolete = self.request.POST.get('destroy', False)
239 rm_obsolete = self.request.POST.get('destroy', False)
256 invalidate_cache = self.request.POST.get('invalidate', False)
240 invalidate_cache = self.request.POST.get('invalidate', False)
257 log.debug('rescanning repo location with destroy obsolete=%s', rm_obsolete)
241 log.debug('rescanning repo location with destroy obsolete=%s', rm_obsolete)
258
242
259 if invalidate_cache:
243 if invalidate_cache:
260 log.debug('invalidating all repositories cache')
244 log.debug('invalidating all repositories cache')
261 for repo in Repository.get_all():
245 for repo in Repository.get_all():
262 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
246 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
263
247
264 filesystem_repos = ScmModel().repo_scan()
248 filesystem_repos = ScmModel().repo_scan()
265 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
249 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
266 PermissionModel().trigger_permission_flush()
250 PermissionModel().trigger_permission_flush()
267
251
268 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
252 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
269 h.flash(_('Repositories successfully '
253 h.flash(_('Repositories successfully '
270 'rescanned added: %s ; removed: %s') %
254 'rescanned added: %s ; removed: %s') %
271 (_repr(added), _repr(removed)),
255 (_repr(added), _repr(removed)),
272 category='success')
256 category='success')
273 raise HTTPFound(h.route_path('admin_settings_mapping'))
257 raise HTTPFound(h.route_path('admin_settings_mapping'))
274
258
275 @LoginRequired()
259 @LoginRequired()
276 @HasPermissionAllDecorator('hg.admin')
260 @HasPermissionAllDecorator('hg.admin')
277 @view_config(
278 route_name='admin_settings', request_method='GET',
279 renderer='rhodecode:templates/admin/settings/settings.mako')
280 @view_config(
281 route_name='admin_settings_global', request_method='GET',
282 renderer='rhodecode:templates/admin/settings/settings.mako')
283 def settings_global(self):
261 def settings_global(self):
284 c = self.load_default_context()
262 c = self.load_default_context()
285 c.active = 'global'
263 c.active = 'global'
286 c.personal_repo_group_default_pattern = RepoGroupModel()\
264 c.personal_repo_group_default_pattern = RepoGroupModel()\
287 .get_personal_group_name_pattern()
265 .get_personal_group_name_pattern()
288
266
289 data = render('rhodecode:templates/admin/settings/settings.mako',
267 data = render('rhodecode:templates/admin/settings/settings.mako',
290 self._get_template_context(c), self.request)
268 self._get_template_context(c), self.request)
291 html = formencode.htmlfill.render(
269 html = formencode.htmlfill.render(
292 data,
270 data,
293 defaults=self._form_defaults(),
271 defaults=self._form_defaults(),
294 encoding="UTF-8",
272 encoding="UTF-8",
295 force_defaults=False
273 force_defaults=False
296 )
274 )
297 return Response(html)
275 return Response(html)
298
276
299 @LoginRequired()
277 @LoginRequired()
300 @HasPermissionAllDecorator('hg.admin')
278 @HasPermissionAllDecorator('hg.admin')
301 @CSRFRequired()
279 @CSRFRequired()
302 @view_config(
303 route_name='admin_settings_update', request_method='POST',
304 renderer='rhodecode:templates/admin/settings/settings.mako')
305 @view_config(
306 route_name='admin_settings_global_update', request_method='POST',
307 renderer='rhodecode:templates/admin/settings/settings.mako')
308 def settings_global_update(self):
280 def settings_global_update(self):
309 _ = self.request.translate
281 _ = self.request.translate
310 c = self.load_default_context()
282 c = self.load_default_context()
311 c.active = 'global'
283 c.active = 'global'
312 c.personal_repo_group_default_pattern = RepoGroupModel()\
284 c.personal_repo_group_default_pattern = RepoGroupModel()\
313 .get_personal_group_name_pattern()
285 .get_personal_group_name_pattern()
314 application_form = ApplicationSettingsForm(self.request.translate)()
286 application_form = ApplicationSettingsForm(self.request.translate)()
315 try:
287 try:
316 form_result = application_form.to_python(dict(self.request.POST))
288 form_result = application_form.to_python(dict(self.request.POST))
317 except formencode.Invalid as errors:
289 except formencode.Invalid as errors:
318 h.flash(
290 h.flash(
319 _("Some form inputs contain invalid data."),
291 _("Some form inputs contain invalid data."),
320 category='error')
292 category='error')
321 data = render('rhodecode:templates/admin/settings/settings.mako',
293 data = render('rhodecode:templates/admin/settings/settings.mako',
322 self._get_template_context(c), self.request)
294 self._get_template_context(c), self.request)
323 html = formencode.htmlfill.render(
295 html = formencode.htmlfill.render(
324 data,
296 data,
325 defaults=errors.value,
297 defaults=errors.value,
326 errors=errors.error_dict or {},
298 errors=errors.error_dict or {},
327 prefix_error=False,
299 prefix_error=False,
328 encoding="UTF-8",
300 encoding="UTF-8",
329 force_defaults=False
301 force_defaults=False
330 )
302 )
331 return Response(html)
303 return Response(html)
332
304
333 settings = [
305 settings = [
334 ('title', 'rhodecode_title', 'unicode'),
306 ('title', 'rhodecode_title', 'unicode'),
335 ('realm', 'rhodecode_realm', 'unicode'),
307 ('realm', 'rhodecode_realm', 'unicode'),
336 ('pre_code', 'rhodecode_pre_code', 'unicode'),
308 ('pre_code', 'rhodecode_pre_code', 'unicode'),
337 ('post_code', 'rhodecode_post_code', 'unicode'),
309 ('post_code', 'rhodecode_post_code', 'unicode'),
338 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
310 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
339 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
311 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
340 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
312 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
341 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
313 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
342 ]
314 ]
343 try:
315 try:
344 for setting, form_key, type_ in settings:
316 for setting, form_key, type_ in settings:
345 sett = SettingsModel().create_or_update_setting(
317 sett = SettingsModel().create_or_update_setting(
346 setting, form_result[form_key], type_)
318 setting, form_result[form_key], type_)
347 Session().add(sett)
319 Session().add(sett)
348
320
349 Session().commit()
321 Session().commit()
350 SettingsModel().invalidate_settings_cache()
322 SettingsModel().invalidate_settings_cache()
351 h.flash(_('Updated application settings'), category='success')
323 h.flash(_('Updated application settings'), category='success')
352 except Exception:
324 except Exception:
353 log.exception("Exception while updating application settings")
325 log.exception("Exception while updating application settings")
354 h.flash(
326 h.flash(
355 _('Error occurred during updating application settings'),
327 _('Error occurred during updating application settings'),
356 category='error')
328 category='error')
357
329
358 raise HTTPFound(h.route_path('admin_settings_global'))
330 raise HTTPFound(h.route_path('admin_settings_global'))
359
331
360 @LoginRequired()
332 @LoginRequired()
361 @HasPermissionAllDecorator('hg.admin')
333 @HasPermissionAllDecorator('hg.admin')
362 @view_config(
363 route_name='admin_settings_visual', request_method='GET',
364 renderer='rhodecode:templates/admin/settings/settings.mako')
365 def settings_visual(self):
334 def settings_visual(self):
366 c = self.load_default_context()
335 c = self.load_default_context()
367 c.active = 'visual'
336 c.active = 'visual'
368
337
369 data = render('rhodecode:templates/admin/settings/settings.mako',
338 data = render('rhodecode:templates/admin/settings/settings.mako',
370 self._get_template_context(c), self.request)
339 self._get_template_context(c), self.request)
371 html = formencode.htmlfill.render(
340 html = formencode.htmlfill.render(
372 data,
341 data,
373 defaults=self._form_defaults(),
342 defaults=self._form_defaults(),
374 encoding="UTF-8",
343 encoding="UTF-8",
375 force_defaults=False
344 force_defaults=False
376 )
345 )
377 return Response(html)
346 return Response(html)
378
347
379 @LoginRequired()
348 @LoginRequired()
380 @HasPermissionAllDecorator('hg.admin')
349 @HasPermissionAllDecorator('hg.admin')
381 @CSRFRequired()
350 @CSRFRequired()
382 @view_config(
383 route_name='admin_settings_visual_update', request_method='POST',
384 renderer='rhodecode:templates/admin/settings/settings.mako')
385 def settings_visual_update(self):
351 def settings_visual_update(self):
386 _ = self.request.translate
352 _ = self.request.translate
387 c = self.load_default_context()
353 c = self.load_default_context()
388 c.active = 'visual'
354 c.active = 'visual'
389 application_form = ApplicationVisualisationForm(self.request.translate)()
355 application_form = ApplicationVisualisationForm(self.request.translate)()
390 try:
356 try:
391 form_result = application_form.to_python(dict(self.request.POST))
357 form_result = application_form.to_python(dict(self.request.POST))
392 except formencode.Invalid as errors:
358 except formencode.Invalid as errors:
393 h.flash(
359 h.flash(
394 _("Some form inputs contain invalid data."),
360 _("Some form inputs contain invalid data."),
395 category='error')
361 category='error')
396 data = render('rhodecode:templates/admin/settings/settings.mako',
362 data = render('rhodecode:templates/admin/settings/settings.mako',
397 self._get_template_context(c), self.request)
363 self._get_template_context(c), self.request)
398 html = formencode.htmlfill.render(
364 html = formencode.htmlfill.render(
399 data,
365 data,
400 defaults=errors.value,
366 defaults=errors.value,
401 errors=errors.error_dict or {},
367 errors=errors.error_dict or {},
402 prefix_error=False,
368 prefix_error=False,
403 encoding="UTF-8",
369 encoding="UTF-8",
404 force_defaults=False
370 force_defaults=False
405 )
371 )
406 return Response(html)
372 return Response(html)
407
373
408 try:
374 try:
409 settings = [
375 settings = [
410 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
376 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
411 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
377 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
412 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
378 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
413 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
379 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
414 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
380 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
415 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
381 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
416 ('show_version', 'rhodecode_show_version', 'bool'),
382 ('show_version', 'rhodecode_show_version', 'bool'),
417 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
383 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
418 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
384 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
419 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
385 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
420 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
386 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
421 ('clone_uri_ssh_tmpl', 'rhodecode_clone_uri_ssh_tmpl', 'unicode'),
387 ('clone_uri_ssh_tmpl', 'rhodecode_clone_uri_ssh_tmpl', 'unicode'),
422 ('support_url', 'rhodecode_support_url', 'unicode'),
388 ('support_url', 'rhodecode_support_url', 'unicode'),
423 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
389 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
424 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
390 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
425 ]
391 ]
426 for setting, form_key, type_ in settings:
392 for setting, form_key, type_ in settings:
427 sett = SettingsModel().create_or_update_setting(
393 sett = SettingsModel().create_or_update_setting(
428 setting, form_result[form_key], type_)
394 setting, form_result[form_key], type_)
429 Session().add(sett)
395 Session().add(sett)
430
396
431 Session().commit()
397 Session().commit()
432 SettingsModel().invalidate_settings_cache()
398 SettingsModel().invalidate_settings_cache()
433 h.flash(_('Updated visualisation settings'), category='success')
399 h.flash(_('Updated visualisation settings'), category='success')
434 except Exception:
400 except Exception:
435 log.exception("Exception updating visualization settings")
401 log.exception("Exception updating visualization settings")
436 h.flash(_('Error occurred during updating '
402 h.flash(_('Error occurred during updating '
437 'visualisation settings'),
403 'visualisation settings'),
438 category='error')
404 category='error')
439
405
440 raise HTTPFound(h.route_path('admin_settings_visual'))
406 raise HTTPFound(h.route_path('admin_settings_visual'))
441
407
442 @LoginRequired()
408 @LoginRequired()
443 @HasPermissionAllDecorator('hg.admin')
409 @HasPermissionAllDecorator('hg.admin')
444 @view_config(
445 route_name='admin_settings_issuetracker', request_method='GET',
446 renderer='rhodecode:templates/admin/settings/settings.mako')
447 def settings_issuetracker(self):
410 def settings_issuetracker(self):
448 c = self.load_default_context()
411 c = self.load_default_context()
449 c.active = 'issuetracker'
412 c.active = 'issuetracker'
450 defaults = c.rc_config
413 defaults = c.rc_config
451
414
452 entry_key = 'rhodecode_issuetracker_pat_'
415 entry_key = 'rhodecode_issuetracker_pat_'
453
416
454 c.issuetracker_entries = {}
417 c.issuetracker_entries = {}
455 for k, v in defaults.items():
418 for k, v in defaults.items():
456 if k.startswith(entry_key):
419 if k.startswith(entry_key):
457 uid = k[len(entry_key):]
420 uid = k[len(entry_key):]
458 c.issuetracker_entries[uid] = None
421 c.issuetracker_entries[uid] = None
459
422
460 for uid in c.issuetracker_entries:
423 for uid in c.issuetracker_entries:
461 c.issuetracker_entries[uid] = AttributeDict({
424 c.issuetracker_entries[uid] = AttributeDict({
462 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
425 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
463 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
426 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
464 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
427 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
465 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
428 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
466 })
429 })
467
430
468 return self._get_template_context(c)
431 return self._get_template_context(c)
469
432
470 @LoginRequired()
433 @LoginRequired()
471 @HasPermissionAllDecorator('hg.admin')
434 @HasPermissionAllDecorator('hg.admin')
472 @CSRFRequired()
435 @CSRFRequired()
473 @view_config(
474 route_name='admin_settings_issuetracker_test', request_method='POST',
475 renderer='string', xhr=True)
476 def settings_issuetracker_test(self):
436 def settings_issuetracker_test(self):
477 error_container = []
437 error_container = []
478
438
479 urlified_commit = h.urlify_commit_message(
439 urlified_commit = h.urlify_commit_message(
480 self.request.POST.get('test_text', ''),
440 self.request.POST.get('test_text', ''),
481 'repo_group/test_repo1', error_container=error_container)
441 'repo_group/test_repo1', error_container=error_container)
482 if error_container:
442 if error_container:
483 def converter(inp):
443 def converter(inp):
484 return h.html_escape(unicode(inp))
444 return h.html_escape(unicode(inp))
485
445
486 return 'ERRORS: ' + '\n'.join(map(converter, error_container))
446 return 'ERRORS: ' + '\n'.join(map(converter, error_container))
487
447
488 return urlified_commit
448 return urlified_commit
489
449
490 @LoginRequired()
450 @LoginRequired()
491 @HasPermissionAllDecorator('hg.admin')
451 @HasPermissionAllDecorator('hg.admin')
492 @CSRFRequired()
452 @CSRFRequired()
493 @view_config(
494 route_name='admin_settings_issuetracker_update', request_method='POST',
495 renderer='rhodecode:templates/admin/settings/settings.mako')
496 def settings_issuetracker_update(self):
453 def settings_issuetracker_update(self):
497 _ = self.request.translate
454 _ = self.request.translate
498 self.load_default_context()
455 self.load_default_context()
499 settings_model = IssueTrackerSettingsModel()
456 settings_model = IssueTrackerSettingsModel()
500
457
501 try:
458 try:
502 form = IssueTrackerPatternsForm(self.request.translate)()
459 form = IssueTrackerPatternsForm(self.request.translate)()
503 data = form.to_python(self.request.POST)
460 data = form.to_python(self.request.POST)
504 except formencode.Invalid as errors:
461 except formencode.Invalid as errors:
505 log.exception('Failed to add new pattern')
462 log.exception('Failed to add new pattern')
506 error = errors
463 error = errors
507 h.flash(_('Invalid issue tracker pattern: {}'.format(error)),
464 h.flash(_('Invalid issue tracker pattern: {}'.format(error)),
508 category='error')
465 category='error')
509 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
466 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
510
467
511 if data:
468 if data:
512 for uid in data.get('delete_patterns', []):
469 for uid in data.get('delete_patterns', []):
513 settings_model.delete_entries(uid)
470 settings_model.delete_entries(uid)
514
471
515 for pattern in data.get('patterns', []):
472 for pattern in data.get('patterns', []):
516 for setting, value, type_ in pattern:
473 for setting, value, type_ in pattern:
517 sett = settings_model.create_or_update_setting(
474 sett = settings_model.create_or_update_setting(
518 setting, value, type_)
475 setting, value, type_)
519 Session().add(sett)
476 Session().add(sett)
520
477
521 Session().commit()
478 Session().commit()
522
479
523 SettingsModel().invalidate_settings_cache()
480 SettingsModel().invalidate_settings_cache()
524 h.flash(_('Updated issue tracker entries'), category='success')
481 h.flash(_('Updated issue tracker entries'), category='success')
525 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
482 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
526
483
527 @LoginRequired()
484 @LoginRequired()
528 @HasPermissionAllDecorator('hg.admin')
485 @HasPermissionAllDecorator('hg.admin')
529 @CSRFRequired()
486 @CSRFRequired()
530 @view_config(
531 route_name='admin_settings_issuetracker_delete', request_method='POST',
532 renderer='json_ext', xhr=True)
533 def settings_issuetracker_delete(self):
487 def settings_issuetracker_delete(self):
534 _ = self.request.translate
488 _ = self.request.translate
535 self.load_default_context()
489 self.load_default_context()
536 uid = self.request.POST.get('uid')
490 uid = self.request.POST.get('uid')
537 try:
491 try:
538 IssueTrackerSettingsModel().delete_entries(uid)
492 IssueTrackerSettingsModel().delete_entries(uid)
539 except Exception:
493 except Exception:
540 log.exception('Failed to delete issue tracker setting %s', uid)
494 log.exception('Failed to delete issue tracker setting %s', uid)
541 raise HTTPNotFound()
495 raise HTTPNotFound()
542
496
543 SettingsModel().invalidate_settings_cache()
497 SettingsModel().invalidate_settings_cache()
544 h.flash(_('Removed issue tracker entry.'), category='success')
498 h.flash(_('Removed issue tracker entry.'), category='success')
545
499
546 return {'deleted': uid}
500 return {'deleted': uid}
547
501
548 @LoginRequired()
502 @LoginRequired()
549 @HasPermissionAllDecorator('hg.admin')
503 @HasPermissionAllDecorator('hg.admin')
550 @view_config(
551 route_name='admin_settings_email', request_method='GET',
552 renderer='rhodecode:templates/admin/settings/settings.mako')
553 def settings_email(self):
504 def settings_email(self):
554 c = self.load_default_context()
505 c = self.load_default_context()
555 c.active = 'email'
506 c.active = 'email'
556 c.rhodecode_ini = rhodecode.CONFIG
507 c.rhodecode_ini = rhodecode.CONFIG
557
508
558 data = render('rhodecode:templates/admin/settings/settings.mako',
509 data = render('rhodecode:templates/admin/settings/settings.mako',
559 self._get_template_context(c), self.request)
510 self._get_template_context(c), self.request)
560 html = formencode.htmlfill.render(
511 html = formencode.htmlfill.render(
561 data,
512 data,
562 defaults=self._form_defaults(),
513 defaults=self._form_defaults(),
563 encoding="UTF-8",
514 encoding="UTF-8",
564 force_defaults=False
515 force_defaults=False
565 )
516 )
566 return Response(html)
517 return Response(html)
567
518
568 @LoginRequired()
519 @LoginRequired()
569 @HasPermissionAllDecorator('hg.admin')
520 @HasPermissionAllDecorator('hg.admin')
570 @CSRFRequired()
521 @CSRFRequired()
571 @view_config(
572 route_name='admin_settings_email_update', request_method='POST',
573 renderer='rhodecode:templates/admin/settings/settings.mako')
574 def settings_email_update(self):
522 def settings_email_update(self):
575 _ = self.request.translate
523 _ = self.request.translate
576 c = self.load_default_context()
524 c = self.load_default_context()
577 c.active = 'email'
525 c.active = 'email'
578
526
579 test_email = self.request.POST.get('test_email')
527 test_email = self.request.POST.get('test_email')
580
528
581 if not test_email:
529 if not test_email:
582 h.flash(_('Please enter email address'), category='error')
530 h.flash(_('Please enter email address'), category='error')
583 raise HTTPFound(h.route_path('admin_settings_email'))
531 raise HTTPFound(h.route_path('admin_settings_email'))
584
532
585 email_kwargs = {
533 email_kwargs = {
586 'date': datetime.datetime.now(),
534 'date': datetime.datetime.now(),
587 'user': self._rhodecode_db_user
535 'user': self._rhodecode_db_user
588 }
536 }
589
537
590 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
538 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
591 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
539 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
592
540
593 recipients = [test_email] if test_email else None
541 recipients = [test_email] if test_email else None
594
542
595 run_task(tasks.send_email, recipients, subject,
543 run_task(tasks.send_email, recipients, subject,
596 email_body_plaintext, email_body)
544 email_body_plaintext, email_body)
597
545
598 h.flash(_('Send email task created'), category='success')
546 h.flash(_('Send email task created'), category='success')
599 raise HTTPFound(h.route_path('admin_settings_email'))
547 raise HTTPFound(h.route_path('admin_settings_email'))
600
548
601 @LoginRequired()
549 @LoginRequired()
602 @HasPermissionAllDecorator('hg.admin')
550 @HasPermissionAllDecorator('hg.admin')
603 @view_config(
604 route_name='admin_settings_hooks', request_method='GET',
605 renderer='rhodecode:templates/admin/settings/settings.mako')
606 def settings_hooks(self):
551 def settings_hooks(self):
607 c = self.load_default_context()
552 c = self.load_default_context()
608 c.active = 'hooks'
553 c.active = 'hooks'
609
554
610 model = SettingsModel()
555 model = SettingsModel()
611 c.hooks = model.get_builtin_hooks()
556 c.hooks = model.get_builtin_hooks()
612 c.custom_hooks = model.get_custom_hooks()
557 c.custom_hooks = model.get_custom_hooks()
613
558
614 data = render('rhodecode:templates/admin/settings/settings.mako',
559 data = render('rhodecode:templates/admin/settings/settings.mako',
615 self._get_template_context(c), self.request)
560 self._get_template_context(c), self.request)
616 html = formencode.htmlfill.render(
561 html = formencode.htmlfill.render(
617 data,
562 data,
618 defaults=self._form_defaults(),
563 defaults=self._form_defaults(),
619 encoding="UTF-8",
564 encoding="UTF-8",
620 force_defaults=False
565 force_defaults=False
621 )
566 )
622 return Response(html)
567 return Response(html)
623
568
624 @LoginRequired()
569 @LoginRequired()
625 @HasPermissionAllDecorator('hg.admin')
570 @HasPermissionAllDecorator('hg.admin')
626 @CSRFRequired()
571 @CSRFRequired()
627 @view_config(
628 route_name='admin_settings_hooks_update', request_method='POST',
629 renderer='rhodecode:templates/admin/settings/settings.mako')
630 @view_config(
631 route_name='admin_settings_hooks_delete', request_method='POST',
632 renderer='rhodecode:templates/admin/settings/settings.mako')
633 def settings_hooks_update(self):
572 def settings_hooks_update(self):
634 _ = self.request.translate
573 _ = self.request.translate
635 c = self.load_default_context()
574 c = self.load_default_context()
636 c.active = 'hooks'
575 c.active = 'hooks'
637 if c.visual.allow_custom_hooks_settings:
576 if c.visual.allow_custom_hooks_settings:
638 ui_key = self.request.POST.get('new_hook_ui_key')
577 ui_key = self.request.POST.get('new_hook_ui_key')
639 ui_value = self.request.POST.get('new_hook_ui_value')
578 ui_value = self.request.POST.get('new_hook_ui_value')
640
579
641 hook_id = self.request.POST.get('hook_id')
580 hook_id = self.request.POST.get('hook_id')
642 new_hook = False
581 new_hook = False
643
582
644 model = SettingsModel()
583 model = SettingsModel()
645 try:
584 try:
646 if ui_value and ui_key:
585 if ui_value and ui_key:
647 model.create_or_update_hook(ui_key, ui_value)
586 model.create_or_update_hook(ui_key, ui_value)
648 h.flash(_('Added new hook'), category='success')
587 h.flash(_('Added new hook'), category='success')
649 new_hook = True
588 new_hook = True
650 elif hook_id:
589 elif hook_id:
651 RhodeCodeUi.delete(hook_id)
590 RhodeCodeUi.delete(hook_id)
652 Session().commit()
591 Session().commit()
653
592
654 # check for edits
593 # check for edits
655 update = False
594 update = False
656 _d = self.request.POST.dict_of_lists()
595 _d = self.request.POST.dict_of_lists()
657 for k, v in zip(_d.get('hook_ui_key', []),
596 for k, v in zip(_d.get('hook_ui_key', []),
658 _d.get('hook_ui_value_new', [])):
597 _d.get('hook_ui_value_new', [])):
659 model.create_or_update_hook(k, v)
598 model.create_or_update_hook(k, v)
660 update = True
599 update = True
661
600
662 if update and not new_hook:
601 if update and not new_hook:
663 h.flash(_('Updated hooks'), category='success')
602 h.flash(_('Updated hooks'), category='success')
664 Session().commit()
603 Session().commit()
665 except Exception:
604 except Exception:
666 log.exception("Exception during hook creation")
605 log.exception("Exception during hook creation")
667 h.flash(_('Error occurred during hook creation'),
606 h.flash(_('Error occurred during hook creation'),
668 category='error')
607 category='error')
669
608
670 raise HTTPFound(h.route_path('admin_settings_hooks'))
609 raise HTTPFound(h.route_path('admin_settings_hooks'))
671
610
672 @LoginRequired()
611 @LoginRequired()
673 @HasPermissionAllDecorator('hg.admin')
612 @HasPermissionAllDecorator('hg.admin')
674 @view_config(
675 route_name='admin_settings_search', request_method='GET',
676 renderer='rhodecode:templates/admin/settings/settings.mako')
677 def settings_search(self):
613 def settings_search(self):
678 c = self.load_default_context()
614 c = self.load_default_context()
679 c.active = 'search'
615 c.active = 'search'
680
616
681 c.searcher = searcher_from_config(self.request.registry.settings)
617 c.searcher = searcher_from_config(self.request.registry.settings)
682 c.statistics = c.searcher.statistics(self.request.translate)
618 c.statistics = c.searcher.statistics(self.request.translate)
683
619
684 return self._get_template_context(c)
620 return self._get_template_context(c)
685
621
686 @LoginRequired()
622 @LoginRequired()
687 @HasPermissionAllDecorator('hg.admin')
623 @HasPermissionAllDecorator('hg.admin')
688 @view_config(
689 route_name='admin_settings_automation', request_method='GET',
690 renderer='rhodecode:templates/admin/settings/settings.mako')
691 def settings_automation(self):
624 def settings_automation(self):
692 c = self.load_default_context()
625 c = self.load_default_context()
693 c.active = 'automation'
626 c.active = 'automation'
694
627
695 return self._get_template_context(c)
628 return self._get_template_context(c)
696
629
697 @LoginRequired()
630 @LoginRequired()
698 @HasPermissionAllDecorator('hg.admin')
631 @HasPermissionAllDecorator('hg.admin')
699 @view_config(
700 route_name='admin_settings_labs', request_method='GET',
701 renderer='rhodecode:templates/admin/settings/settings.mako')
702 def settings_labs(self):
632 def settings_labs(self):
703 c = self.load_default_context()
633 c = self.load_default_context()
704 if not c.labs_active:
634 if not c.labs_active:
705 raise HTTPFound(h.route_path('admin_settings'))
635 raise HTTPFound(h.route_path('admin_settings'))
706
636
707 c.active = 'labs'
637 c.active = 'labs'
708 c.lab_settings = _LAB_SETTINGS
638 c.lab_settings = _LAB_SETTINGS
709
639
710 data = render('rhodecode:templates/admin/settings/settings.mako',
640 data = render('rhodecode:templates/admin/settings/settings.mako',
711 self._get_template_context(c), self.request)
641 self._get_template_context(c), self.request)
712 html = formencode.htmlfill.render(
642 html = formencode.htmlfill.render(
713 data,
643 data,
714 defaults=self._form_defaults(),
644 defaults=self._form_defaults(),
715 encoding="UTF-8",
645 encoding="UTF-8",
716 force_defaults=False
646 force_defaults=False
717 )
647 )
718 return Response(html)
648 return Response(html)
719
649
720 @LoginRequired()
650 @LoginRequired()
721 @HasPermissionAllDecorator('hg.admin')
651 @HasPermissionAllDecorator('hg.admin')
722 @CSRFRequired()
652 @CSRFRequired()
723 @view_config(
724 route_name='admin_settings_labs_update', request_method='POST',
725 renderer='rhodecode:templates/admin/settings/settings.mako')
726 def settings_labs_update(self):
653 def settings_labs_update(self):
727 _ = self.request.translate
654 _ = self.request.translate
728 c = self.load_default_context()
655 c = self.load_default_context()
729 c.active = 'labs'
656 c.active = 'labs'
730
657
731 application_form = LabsSettingsForm(self.request.translate)()
658 application_form = LabsSettingsForm(self.request.translate)()
732 try:
659 try:
733 form_result = application_form.to_python(dict(self.request.POST))
660 form_result = application_form.to_python(dict(self.request.POST))
734 except formencode.Invalid as errors:
661 except formencode.Invalid as errors:
735 h.flash(
662 h.flash(
736 _("Some form inputs contain invalid data."),
663 _("Some form inputs contain invalid data."),
737 category='error')
664 category='error')
738 data = render('rhodecode:templates/admin/settings/settings.mako',
665 data = render('rhodecode:templates/admin/settings/settings.mako',
739 self._get_template_context(c), self.request)
666 self._get_template_context(c), self.request)
740 html = formencode.htmlfill.render(
667 html = formencode.htmlfill.render(
741 data,
668 data,
742 defaults=errors.value,
669 defaults=errors.value,
743 errors=errors.error_dict or {},
670 errors=errors.error_dict or {},
744 prefix_error=False,
671 prefix_error=False,
745 encoding="UTF-8",
672 encoding="UTF-8",
746 force_defaults=False
673 force_defaults=False
747 )
674 )
748 return Response(html)
675 return Response(html)
749
676
750 try:
677 try:
751 session = Session()
678 session = Session()
752 for setting in _LAB_SETTINGS:
679 for setting in _LAB_SETTINGS:
753 setting_name = setting.key[len('rhodecode_'):]
680 setting_name = setting.key[len('rhodecode_'):]
754 sett = SettingsModel().create_or_update_setting(
681 sett = SettingsModel().create_or_update_setting(
755 setting_name, form_result[setting.key], setting.type)
682 setting_name, form_result[setting.key], setting.type)
756 session.add(sett)
683 session.add(sett)
757
684
758 except Exception:
685 except Exception:
759 log.exception('Exception while updating lab settings')
686 log.exception('Exception while updating lab settings')
760 h.flash(_('Error occurred during updating labs settings'),
687 h.flash(_('Error occurred during updating labs settings'),
761 category='error')
688 category='error')
762 else:
689 else:
763 Session().commit()
690 Session().commit()
764 SettingsModel().invalidate_settings_cache()
691 SettingsModel().invalidate_settings_cache()
765 h.flash(_('Updated Labs settings'), category='success')
692 h.flash(_('Updated Labs settings'), category='success')
766 raise HTTPFound(h.route_path('admin_settings_labs'))
693 raise HTTPFound(h.route_path('admin_settings_labs'))
767
694
768 data = render('rhodecode:templates/admin/settings/settings.mako',
695 data = render('rhodecode:templates/admin/settings/settings.mako',
769 self._get_template_context(c), self.request)
696 self._get_template_context(c), self.request)
770 html = formencode.htmlfill.render(
697 html = formencode.htmlfill.render(
771 data,
698 data,
772 defaults=self._form_defaults(),
699 defaults=self._form_defaults(),
773 encoding="UTF-8",
700 encoding="UTF-8",
774 force_defaults=False
701 force_defaults=False
775 )
702 )
776 return Response(html)
703 return Response(html)
777
704
778
705
779 # :param key: name of the setting including the 'rhodecode_' prefix
706 # :param key: name of the setting including the 'rhodecode_' prefix
780 # :param type: the RhodeCodeSetting type to use.
707 # :param type: the RhodeCodeSetting type to use.
781 # :param group: the i18ned group in which we should dispaly this setting
708 # :param group: the i18ned group in which we should dispaly this setting
782 # :param label: the i18ned label we should display for this setting
709 # :param label: the i18ned label we should display for this setting
783 # :param help: the i18ned help we should dispaly for this setting
710 # :param help: the i18ned help we should dispaly for this setting
784 LabSetting = collections.namedtuple(
711 LabSetting = collections.namedtuple(
785 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
712 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
786
713
787
714
788 # This list has to be kept in sync with the form
715 # This list has to be kept in sync with the form
789 # rhodecode.model.forms.LabsSettingsForm.
716 # rhodecode.model.forms.LabsSettingsForm.
790 _LAB_SETTINGS = [
717 _LAB_SETTINGS = [
791
718
792 ]
719 ]
@@ -1,59 +1,56 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from pyramid.view import view_config
23
24
24
25 from rhodecode.apps._base import BaseAppView
25 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps.svn_support.utils import generate_mod_dav_svn_config
26 from rhodecode.apps.svn_support.utils import generate_mod_dav_svn_config
27 from rhodecode.lib.auth import (
27 from rhodecode.lib.auth import (
28 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
28 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
29
29
30 log = logging.getLogger(__name__)
30 log = logging.getLogger(__name__)
31
31
32
32
33 class SvnConfigAdminSettingsView(BaseAppView):
33 class AdminSvnConfigView(BaseAppView):
34
34
35 @LoginRequired()
35 @LoginRequired()
36 @HasPermissionAllDecorator('hg.admin')
36 @HasPermissionAllDecorator('hg.admin')
37 @CSRFRequired()
37 @CSRFRequired()
38 @view_config(
39 route_name='admin_settings_vcs_svn_generate_cfg',
40 request_method='POST', renderer='json')
41 def vcs_svn_generate_config(self):
38 def vcs_svn_generate_config(self):
42 _ = self.request.translate
39 _ = self.request.translate
43 try:
40 try:
44 file_path = generate_mod_dav_svn_config(self.request.registry)
41 file_path = generate_mod_dav_svn_config(self.request.registry)
45 msg = {
42 msg = {
46 'message': _('Apache configuration for Subversion generated at `{}`.').format(file_path),
43 'message': _('Apache configuration for Subversion generated at `{}`.').format(file_path),
47 'level': 'success',
44 'level': 'success',
48 }
45 }
49 except Exception:
46 except Exception:
50 log.exception(
47 log.exception(
51 'Exception while generating the Apache '
48 'Exception while generating the Apache '
52 'configuration for Subversion.')
49 'configuration for Subversion.')
53 msg = {
50 msg = {
54 'message': _('Failed to generate the Apache configuration for Subversion.'),
51 'message': _('Failed to generate the Apache configuration for Subversion.'),
55 'level': 'error',
52 'level': 'error',
56 }
53 }
57
54
58 data = {'message': msg}
55 data = {'message': msg}
59 return data
56 return data
@@ -1,206 +1,200 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import urllib2
22 import urllib2
23
23
24 from pyramid.view import view_config
24
25
25
26 import rhodecode
26 import rhodecode
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.apps._base.navigation import navigation_list
28 from rhodecode.apps._base.navigation import navigation_list
29 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
30 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
31 from rhodecode.lib.utils2 import str2bool
31 from rhodecode.lib.utils2 import str2bool
32 from rhodecode.lib import system_info
32 from rhodecode.lib import system_info
33 from rhodecode.model.update import UpdateModel
33 from rhodecode.model.update import UpdateModel
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37
37
38 class AdminSystemInfoSettingsView(BaseAppView):
38 class AdminSystemInfoSettingsView(BaseAppView):
39 def load_default_context(self):
39 def load_default_context(self):
40 c = self._get_local_tmpl_context()
40 c = self._get_local_tmpl_context()
41 return c
41 return c
42
42
43 @LoginRequired()
43 @LoginRequired()
44 @HasPermissionAllDecorator('hg.admin')
44 @HasPermissionAllDecorator('hg.admin')
45 @view_config(
46 route_name='admin_settings_system', request_method='GET',
47 renderer='rhodecode:templates/admin/settings/settings.mako')
48 def settings_system_info(self):
45 def settings_system_info(self):
49 _ = self.request.translate
46 _ = self.request.translate
50 c = self.load_default_context()
47 c = self.load_default_context()
51
48
52 c.active = 'system'
49 c.active = 'system'
53 c.navlist = navigation_list(self.request)
50 c.navlist = navigation_list(self.request)
54
51
55 # TODO(marcink), figure out how to allow only selected users to do this
52 # TODO(marcink), figure out how to allow only selected users to do this
56 c.allowed_to_snapshot = self._rhodecode_user.admin
53 c.allowed_to_snapshot = self._rhodecode_user.admin
57
54
58 snapshot = str2bool(self.request.params.get('snapshot'))
55 snapshot = str2bool(self.request.params.get('snapshot'))
59
56
60 c.rhodecode_update_url = UpdateModel().get_update_url()
57 c.rhodecode_update_url = UpdateModel().get_update_url()
61 server_info = system_info.get_system_info(self.request.environ)
58 server_info = system_info.get_system_info(self.request.environ)
62
59
63 for key, val in server_info.items():
60 for key, val in server_info.items():
64 setattr(c, key, val)
61 setattr(c, key, val)
65
62
66 def val(name, subkey='human_value'):
63 def val(name, subkey='human_value'):
67 return server_info[name][subkey]
64 return server_info[name][subkey]
68
65
69 def state(name):
66 def state(name):
70 return server_info[name]['state']
67 return server_info[name]['state']
71
68
72 def val2(name):
69 def val2(name):
73 val = server_info[name]['human_value']
70 val = server_info[name]['human_value']
74 state = server_info[name]['state']
71 state = server_info[name]['state']
75 return val, state
72 return val, state
76
73
77 update_info_msg = _('Note: please make sure this server can '
74 update_info_msg = _('Note: please make sure this server can '
78 'access `${url}` for the update link to work',
75 'access `${url}` for the update link to work',
79 mapping=dict(url=c.rhodecode_update_url))
76 mapping=dict(url=c.rhodecode_update_url))
80 version = UpdateModel().get_stored_version()
77 version = UpdateModel().get_stored_version()
81 is_outdated = UpdateModel().is_outdated(
78 is_outdated = UpdateModel().is_outdated(
82 rhodecode.__version__, version)
79 rhodecode.__version__, version)
83 update_state = {
80 update_state = {
84 'type': 'warning',
81 'type': 'warning',
85 'message': 'New version available: {}'.format(version)
82 'message': 'New version available: {}'.format(version)
86 } \
83 } \
87 if is_outdated else {}
84 if is_outdated else {}
88 c.data_items = [
85 c.data_items = [
89 # update info
86 # update info
90 (_('Update info'), h.literal(
87 (_('Update info'), h.literal(
91 '<span class="link" id="check_for_update" >%s.</span>' % (
88 '<span class="link" id="check_for_update" >%s.</span>' % (
92 _('Check for updates')) +
89 _('Check for updates')) +
93 '<br/> <span >%s.</span>' % (update_info_msg)
90 '<br/> <span >%s.</span>' % (update_info_msg)
94 ), ''),
91 ), ''),
95
92
96 # RhodeCode specific
93 # RhodeCode specific
97 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
94 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
98 (_('Latest version'), version, update_state),
95 (_('Latest version'), version, update_state),
99 (_('RhodeCode Base URL'), val('rhodecode_config')['config'].get('app.base_url'), state('rhodecode_config')),
96 (_('RhodeCode Base URL'), val('rhodecode_config')['config'].get('app.base_url'), state('rhodecode_config')),
100 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
97 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
101 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
98 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
102 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
99 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
103 (_('RhodeCode Certificate'), val('rhodecode_config')['cert_path'], state('rhodecode_config')),
100 (_('RhodeCode Certificate'), val('rhodecode_config')['cert_path'], state('rhodecode_config')),
104 (_('Workers'), val('rhodecode_config')['config']['server:main'].get('workers', '?'), state('rhodecode_config')),
101 (_('Workers'), val('rhodecode_config')['config']['server:main'].get('workers', '?'), state('rhodecode_config')),
105 (_('Worker Type'), val('rhodecode_config')['config']['server:main'].get('worker_class', 'sync'), state('rhodecode_config')),
102 (_('Worker Type'), val('rhodecode_config')['config']['server:main'].get('worker_class', 'sync'), state('rhodecode_config')),
106 ('', '', ''), # spacer
103 ('', '', ''), # spacer
107
104
108 # Database
105 # Database
109 (_('Database'), val('database')['url'], state('database')),
106 (_('Database'), val('database')['url'], state('database')),
110 (_('Database version'), val('database')['version'], state('database')),
107 (_('Database version'), val('database')['version'], state('database')),
111 ('', '', ''), # spacer
108 ('', '', ''), # spacer
112
109
113 # Platform/Python
110 # Platform/Python
114 (_('Platform'), val('platform')['name'], state('platform')),
111 (_('Platform'), val('platform')['name'], state('platform')),
115 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
112 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
116 (_('Lang'), val('locale'), state('locale')),
113 (_('Lang'), val('locale'), state('locale')),
117 (_('Python version'), val('python')['version'], state('python')),
114 (_('Python version'), val('python')['version'], state('python')),
118 (_('Python path'), val('python')['executable'], state('python')),
115 (_('Python path'), val('python')['executable'], state('python')),
119 ('', '', ''), # spacer
116 ('', '', ''), # spacer
120
117
121 # Systems stats
118 # Systems stats
122 (_('CPU'), val('cpu')['text'], state('cpu')),
119 (_('CPU'), val('cpu')['text'], state('cpu')),
123 (_('Load'), val('load')['text'], state('load')),
120 (_('Load'), val('load')['text'], state('load')),
124 (_('Memory'), val('memory')['text'], state('memory')),
121 (_('Memory'), val('memory')['text'], state('memory')),
125 (_('Uptime'), val('uptime')['text'], state('uptime')),
122 (_('Uptime'), val('uptime')['text'], state('uptime')),
126 ('', '', ''), # spacer
123 ('', '', ''), # spacer
127
124
128 # ulimit
125 # ulimit
129 (_('Ulimit'), val('ulimit')['text'], state('ulimit')),
126 (_('Ulimit'), val('ulimit')['text'], state('ulimit')),
130
127
131 # Repo storage
128 # Repo storage
132 (_('Storage location'), val('storage')['path'], state('storage')),
129 (_('Storage location'), val('storage')['path'], state('storage')),
133 (_('Storage info'), val('storage')['text'], state('storage')),
130 (_('Storage info'), val('storage')['text'], state('storage')),
134 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
131 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
135
132
136 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
133 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
137 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
134 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
138
135
139 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
136 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
140 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
137 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
141
138
142 (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')),
139 (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')),
143 (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')),
140 (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')),
144
141
145 (_('Search info'), val('search')['text'], state('search')),
142 (_('Search info'), val('search')['text'], state('search')),
146 (_('Search location'), val('search')['location'], state('search')),
143 (_('Search location'), val('search')['location'], state('search')),
147 ('', '', ''), # spacer
144 ('', '', ''), # spacer
148
145
149 # VCS specific
146 # VCS specific
150 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
147 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
151 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
148 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
152 (_('GIT'), val('git'), state('git')),
149 (_('GIT'), val('git'), state('git')),
153 (_('HG'), val('hg'), state('hg')),
150 (_('HG'), val('hg'), state('hg')),
154 (_('SVN'), val('svn'), state('svn')),
151 (_('SVN'), val('svn'), state('svn')),
155
152
156 ]
153 ]
157
154
158 c.vcsserver_data_items = [
155 c.vcsserver_data_items = [
159 (k, v) for k,v in (val('vcs_server_config') or {}).items()
156 (k, v) for k,v in (val('vcs_server_config') or {}).items()
160 ]
157 ]
161
158
162 if snapshot:
159 if snapshot:
163 if c.allowed_to_snapshot:
160 if c.allowed_to_snapshot:
164 c.data_items.pop(0) # remove server info
161 c.data_items.pop(0) # remove server info
165 self.request.override_renderer = 'admin/settings/settings_system_snapshot.mako'
162 self.request.override_renderer = 'admin/settings/settings_system_snapshot.mako'
166 else:
163 else:
167 h.flash('You are not allowed to do this', category='warning')
164 h.flash('You are not allowed to do this', category='warning')
168 return self._get_template_context(c)
165 return self._get_template_context(c)
169
166
170 @LoginRequired()
167 @LoginRequired()
171 @HasPermissionAllDecorator('hg.admin')
168 @HasPermissionAllDecorator('hg.admin')
172 @view_config(
173 route_name='admin_settings_system_update', request_method='GET',
174 renderer='rhodecode:templates/admin/settings/settings_system_update.mako')
175 def settings_system_info_check_update(self):
169 def settings_system_info_check_update(self):
176 _ = self.request.translate
170 _ = self.request.translate
177 c = self.load_default_context()
171 c = self.load_default_context()
178
172
179 update_url = UpdateModel().get_update_url()
173 update_url = UpdateModel().get_update_url()
180
174
181 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s)
175 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s)
182 try:
176 try:
183 data = UpdateModel().get_update_data(update_url)
177 data = UpdateModel().get_update_data(update_url)
184 except urllib2.URLError as e:
178 except urllib2.URLError as e:
185 log.exception("Exception contacting upgrade server")
179 log.exception("Exception contacting upgrade server")
186 self.request.override_renderer = 'string'
180 self.request.override_renderer = 'string'
187 return _err('Failed to contact upgrade server: %r' % e)
181 return _err('Failed to contact upgrade server: %r' % e)
188 except ValueError as e:
182 except ValueError as e:
189 log.exception("Bad data sent from update server")
183 log.exception("Bad data sent from update server")
190 self.request.override_renderer = 'string'
184 self.request.override_renderer = 'string'
191 return _err('Bad data sent from update server')
185 return _err('Bad data sent from update server')
192
186
193 latest = data['versions'][0]
187 latest = data['versions'][0]
194
188
195 c.update_url = update_url
189 c.update_url = update_url
196 c.latest_data = latest
190 c.latest_data = latest
197 c.latest_ver = latest['version']
191 c.latest_ver = latest['version']
198 c.cur_ver = rhodecode.__version__
192 c.cur_ver = rhodecode.__version__
199 c.should_upgrade = False
193 c.should_upgrade = False
200
194
201 is_oudated = UpdateModel().is_outdated(c.cur_ver, c.latest_ver)
195 is_oudated = UpdateModel().is_outdated(c.cur_ver, c.latest_ver)
202 if is_oudated:
196 if is_oudated:
203 c.should_upgrade = True
197 c.should_upgrade = True
204 c.important_notices = latest['general']
198 c.important_notices = latest['general']
205 UpdateModel().store_version(latest['version'])
199 UpdateModel().store_version(latest['version'])
206 return self._get_template_context(c)
200 return self._get_template_context(c)
@@ -1,268 +1,254 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
27
28 from pyramid.response import Response
28 from pyramid.response import Response
29 from pyramid.renderers import render
29 from pyramid.renderers import render
30
30
31 from rhodecode import events
31 from rhodecode import events
32 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
34 LoginRequired, NotAnonymous, CSRFRequired, HasPermissionAnyDecorator)
34 LoginRequired, NotAnonymous, CSRFRequired, HasPermissionAnyDecorator)
35 from rhodecode.lib import helpers as h, audit_logger
35 from rhodecode.lib import helpers as h, audit_logger
36 from rhodecode.lib.utils2 import safe_unicode
36 from rhodecode.lib.utils2 import safe_unicode
37
37
38 from rhodecode.model.forms import UserGroupForm
38 from rhodecode.model.forms import UserGroupForm
39 from rhodecode.model.permission import PermissionModel
39 from rhodecode.model.permission import PermissionModel
40 from rhodecode.model.scm import UserGroupList
40 from rhodecode.model.scm import UserGroupList
41 from rhodecode.model.db import (
41 from rhodecode.model.db import (
42 or_, count, User, UserGroup, UserGroupMember, in_filter_generator)
42 or_, count, User, UserGroup, UserGroupMember, in_filter_generator)
43 from rhodecode.model.meta import Session
43 from rhodecode.model.meta import Session
44 from rhodecode.model.user_group import UserGroupModel
44 from rhodecode.model.user_group import UserGroupModel
45 from rhodecode.model.db import true
45 from rhodecode.model.db import true
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class AdminUserGroupsView(BaseAppView, DataGridAppView):
50 class AdminUserGroupsView(BaseAppView, DataGridAppView):
51
51
52 def load_default_context(self):
52 def load_default_context(self):
53 c = self._get_local_tmpl_context()
53 c = self._get_local_tmpl_context()
54
55 PermissionModel().set_global_permission_choices(
54 PermissionModel().set_global_permission_choices(
56 c, gettext_translator=self.request.translate)
55 c, gettext_translator=self.request.translate)
57
58 return c
56 return c
59
57
60 # permission check in data loading of
58 # permission check in data loading of
61 # `user_groups_list_data` via UserGroupList
59 # `user_groups_list_data` via UserGroupList
62 @LoginRequired()
60 @LoginRequired()
63 @NotAnonymous()
61 @NotAnonymous()
64 @view_config(
65 route_name='user_groups', request_method='GET',
66 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
67 def user_groups_list(self):
62 def user_groups_list(self):
68 c = self.load_default_context()
63 c = self.load_default_context()
69 return self._get_template_context(c)
64 return self._get_template_context(c)
70
65
71 # permission check inside
66 # permission check inside
72 @LoginRequired()
67 @LoginRequired()
73 @NotAnonymous()
68 @NotAnonymous()
74 @view_config(
75 route_name='user_groups_data', request_method='GET',
76 renderer='json_ext', xhr=True)
77 def user_groups_list_data(self):
69 def user_groups_list_data(self):
78 self.load_default_context()
70 self.load_default_context()
79 column_map = {
71 column_map = {
80 'active': 'users_group_active',
72 'active': 'users_group_active',
81 'description': 'user_group_description',
73 'description': 'user_group_description',
82 'members': 'members_total',
74 'members': 'members_total',
83 'owner': 'user_username',
75 'owner': 'user_username',
84 'sync': 'group_data'
76 'sync': 'group_data'
85 }
77 }
86 draw, start, limit = self._extract_chunk(self.request)
78 draw, start, limit = self._extract_chunk(self.request)
87 search_q, order_by, order_dir = self._extract_ordering(
79 search_q, order_by, order_dir = self._extract_ordering(
88 self.request, column_map=column_map)
80 self.request, column_map=column_map)
89
81
90 _render = self.request.get_partial_renderer(
82 _render = self.request.get_partial_renderer(
91 'rhodecode:templates/data_table/_dt_elements.mako')
83 'rhodecode:templates/data_table/_dt_elements.mako')
92
84
93 def user_group_name(user_group_name):
85 def user_group_name(user_group_name):
94 return _render("user_group_name", user_group_name)
86 return _render("user_group_name", user_group_name)
95
87
96 def user_group_actions(user_group_id, user_group_name):
88 def user_group_actions(user_group_id, user_group_name):
97 return _render("user_group_actions", user_group_id, user_group_name)
89 return _render("user_group_actions", user_group_id, user_group_name)
98
90
99 def user_profile(username):
91 def user_profile(username):
100 return _render('user_profile', username)
92 return _render('user_profile', username)
101
93
102 _perms = ['usergroup.admin']
94 _perms = ['usergroup.admin']
103 allowed_ids = [-1] + self._rhodecode_user.user_group_acl_ids_from_stack(_perms)
95 allowed_ids = [-1] + self._rhodecode_user.user_group_acl_ids_from_stack(_perms)
104
96
105 user_groups_data_total_count = UserGroup.query()\
97 user_groups_data_total_count = UserGroup.query()\
106 .filter(or_(
98 .filter(or_(
107 # generate multiple IN to fix limitation problems
99 # generate multiple IN to fix limitation problems
108 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
100 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
109 ))\
101 ))\
110 .count()
102 .count()
111
103
112 user_groups_data_total_inactive_count = UserGroup.query()\
104 user_groups_data_total_inactive_count = UserGroup.query()\
113 .filter(or_(
105 .filter(or_(
114 # generate multiple IN to fix limitation problems
106 # generate multiple IN to fix limitation problems
115 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
107 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
116 ))\
108 ))\
117 .filter(UserGroup.users_group_active != true()).count()
109 .filter(UserGroup.users_group_active != true()).count()
118
110
119 member_count = count(UserGroupMember.user_id)
111 member_count = count(UserGroupMember.user_id)
120 base_q = Session.query(
112 base_q = Session.query(
121 UserGroup.users_group_name,
113 UserGroup.users_group_name,
122 UserGroup.user_group_description,
114 UserGroup.user_group_description,
123 UserGroup.users_group_active,
115 UserGroup.users_group_active,
124 UserGroup.users_group_id,
116 UserGroup.users_group_id,
125 UserGroup.group_data,
117 UserGroup.group_data,
126 User,
118 User,
127 member_count.label('member_count')
119 member_count.label('member_count')
128 ) \
120 ) \
129 .filter(or_(
121 .filter(or_(
130 # generate multiple IN to fix limitation problems
122 # generate multiple IN to fix limitation problems
131 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
123 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
132 )) \
124 )) \
133 .outerjoin(UserGroupMember, UserGroupMember.users_group_id == UserGroup.users_group_id) \
125 .outerjoin(UserGroupMember, UserGroupMember.users_group_id == UserGroup.users_group_id) \
134 .join(User, User.user_id == UserGroup.user_id) \
126 .join(User, User.user_id == UserGroup.user_id) \
135 .group_by(UserGroup, User)
127 .group_by(UserGroup, User)
136
128
137 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
129 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
138
130
139 if search_q:
131 if search_q:
140 like_expression = u'%{}%'.format(safe_unicode(search_q))
132 like_expression = u'%{}%'.format(safe_unicode(search_q))
141 base_q = base_q.filter(or_(
133 base_q = base_q.filter(or_(
142 UserGroup.users_group_name.ilike(like_expression),
134 UserGroup.users_group_name.ilike(like_expression),
143 ))
135 ))
144 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
136 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
145
137
146 user_groups_data_total_filtered_count = base_q.count()
138 user_groups_data_total_filtered_count = base_q.count()
147 user_groups_data_total_filtered_inactive_count = base_q_inactive.count()
139 user_groups_data_total_filtered_inactive_count = base_q_inactive.count()
148
140
149 sort_defined = False
141 sort_defined = False
150 if order_by == 'members_total':
142 if order_by == 'members_total':
151 sort_col = member_count
143 sort_col = member_count
152 sort_defined = True
144 sort_defined = True
153 elif order_by == 'user_username':
145 elif order_by == 'user_username':
154 sort_col = User.username
146 sort_col = User.username
155 else:
147 else:
156 sort_col = getattr(UserGroup, order_by, None)
148 sort_col = getattr(UserGroup, order_by, None)
157
149
158 if sort_defined or sort_col:
150 if sort_defined or sort_col:
159 if order_dir == 'asc':
151 if order_dir == 'asc':
160 sort_col = sort_col.asc()
152 sort_col = sort_col.asc()
161 else:
153 else:
162 sort_col = sort_col.desc()
154 sort_col = sort_col.desc()
163
155
164 base_q = base_q.order_by(sort_col)
156 base_q = base_q.order_by(sort_col)
165 base_q = base_q.offset(start).limit(limit)
157 base_q = base_q.offset(start).limit(limit)
166
158
167 # authenticated access to user groups
159 # authenticated access to user groups
168 auth_user_group_list = base_q.all()
160 auth_user_group_list = base_q.all()
169
161
170 user_groups_data = []
162 user_groups_data = []
171 for user_gr in auth_user_group_list:
163 for user_gr in auth_user_group_list:
172 row = {
164 row = {
173 "users_group_name": user_group_name(user_gr.users_group_name),
165 "users_group_name": user_group_name(user_gr.users_group_name),
174 "description": h.escape(user_gr.user_group_description),
166 "description": h.escape(user_gr.user_group_description),
175 "members": user_gr.member_count,
167 "members": user_gr.member_count,
176 # NOTE(marcink): because of advanced query we
168 # NOTE(marcink): because of advanced query we
177 # need to load it like that
169 # need to load it like that
178 "sync": UserGroup._load_sync(
170 "sync": UserGroup._load_sync(
179 UserGroup._load_group_data(user_gr.group_data)),
171 UserGroup._load_group_data(user_gr.group_data)),
180 "active": h.bool2icon(user_gr.users_group_active),
172 "active": h.bool2icon(user_gr.users_group_active),
181 "owner": user_profile(user_gr.User.username),
173 "owner": user_profile(user_gr.User.username),
182 "action": user_group_actions(
174 "action": user_group_actions(
183 user_gr.users_group_id, user_gr.users_group_name)
175 user_gr.users_group_id, user_gr.users_group_name)
184 }
176 }
185 user_groups_data.append(row)
177 user_groups_data.append(row)
186
178
187 data = ({
179 data = ({
188 'draw': draw,
180 'draw': draw,
189 'data': user_groups_data,
181 'data': user_groups_data,
190 'recordsTotal': user_groups_data_total_count,
182 'recordsTotal': user_groups_data_total_count,
191 'recordsTotalInactive': user_groups_data_total_inactive_count,
183 'recordsTotalInactive': user_groups_data_total_inactive_count,
192 'recordsFiltered': user_groups_data_total_filtered_count,
184 'recordsFiltered': user_groups_data_total_filtered_count,
193 'recordsFilteredInactive': user_groups_data_total_filtered_inactive_count,
185 'recordsFilteredInactive': user_groups_data_total_filtered_inactive_count,
194 })
186 })
195
187
196 return data
188 return data
197
189
198 @LoginRequired()
190 @LoginRequired()
199 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
191 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
200 @view_config(
201 route_name='user_groups_new', request_method='GET',
202 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
203 def user_groups_new(self):
192 def user_groups_new(self):
204 c = self.load_default_context()
193 c = self.load_default_context()
205 return self._get_template_context(c)
194 return self._get_template_context(c)
206
195
207 @LoginRequired()
196 @LoginRequired()
208 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
197 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
209 @CSRFRequired()
198 @CSRFRequired()
210 @view_config(
211 route_name='user_groups_create', request_method='POST',
212 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
213 def user_groups_create(self):
199 def user_groups_create(self):
214 _ = self.request.translate
200 _ = self.request.translate
215 c = self.load_default_context()
201 c = self.load_default_context()
216 users_group_form = UserGroupForm(self.request.translate)()
202 users_group_form = UserGroupForm(self.request.translate)()
217
203
218 user_group_name = self.request.POST.get('users_group_name')
204 user_group_name = self.request.POST.get('users_group_name')
219 try:
205 try:
220 form_result = users_group_form.to_python(dict(self.request.POST))
206 form_result = users_group_form.to_python(dict(self.request.POST))
221 user_group = UserGroupModel().create(
207 user_group = UserGroupModel().create(
222 name=form_result['users_group_name'],
208 name=form_result['users_group_name'],
223 description=form_result['user_group_description'],
209 description=form_result['user_group_description'],
224 owner=self._rhodecode_user.user_id,
210 owner=self._rhodecode_user.user_id,
225 active=form_result['users_group_active'])
211 active=form_result['users_group_active'])
226 Session().flush()
212 Session().flush()
227 creation_data = user_group.get_api_data()
213 creation_data = user_group.get_api_data()
228 user_group_name = form_result['users_group_name']
214 user_group_name = form_result['users_group_name']
229
215
230 audit_logger.store_web(
216 audit_logger.store_web(
231 'user_group.create', action_data={'data': creation_data},
217 'user_group.create', action_data={'data': creation_data},
232 user=self._rhodecode_user)
218 user=self._rhodecode_user)
233
219
234 user_group_link = h.link_to(
220 user_group_link = h.link_to(
235 h.escape(user_group_name),
221 h.escape(user_group_name),
236 h.route_path(
222 h.route_path(
237 'edit_user_group', user_group_id=user_group.users_group_id))
223 'edit_user_group', user_group_id=user_group.users_group_id))
238 h.flash(h.literal(_('Created user group %(user_group_link)s')
224 h.flash(h.literal(_('Created user group %(user_group_link)s')
239 % {'user_group_link': user_group_link}),
225 % {'user_group_link': user_group_link}),
240 category='success')
226 category='success')
241 Session().commit()
227 Session().commit()
242 user_group_id = user_group.users_group_id
228 user_group_id = user_group.users_group_id
243 except formencode.Invalid as errors:
229 except formencode.Invalid as errors:
244
230
245 data = render(
231 data = render(
246 'rhodecode:templates/admin/user_groups/user_group_add.mako',
232 'rhodecode:templates/admin/user_groups/user_group_add.mako',
247 self._get_template_context(c), self.request)
233 self._get_template_context(c), self.request)
248 html = formencode.htmlfill.render(
234 html = formencode.htmlfill.render(
249 data,
235 data,
250 defaults=errors.value,
236 defaults=errors.value,
251 errors=errors.error_dict or {},
237 errors=errors.error_dict or {},
252 prefix_error=False,
238 prefix_error=False,
253 encoding="UTF-8",
239 encoding="UTF-8",
254 force_defaults=False
240 force_defaults=False
255 )
241 )
256 return Response(html)
242 return Response(html)
257
243
258 except Exception:
244 except Exception:
259 log.exception("Exception creating user group")
245 log.exception("Exception creating user group")
260 h.flash(_('Error occurred during creation of user group %s') \
246 h.flash(_('Error occurred during creation of user group %s') \
261 % user_group_name, category='error')
247 % user_group_name, category='error')
262 raise HTTPFound(h.route_path('user_groups_new'))
248 raise HTTPFound(h.route_path('user_groups_new'))
263
249
264 affected_user_ids = [self._rhodecode_user.user_id]
250 affected_user_ids = [self._rhodecode_user.user_id]
265 PermissionModel().trigger_permission_flush(affected_user_ids)
251 PermissionModel().trigger_permission_flush(affected_user_ids)
266
252
267 raise HTTPFound(
253 raise HTTPFound(
268 h.route_path('edit_user_group', user_group_id=user_group_id))
254 h.route_path('edit_user_group', user_group_id=user_group_id))
@@ -1,1418 +1,1318 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
28 from pyramid.renderers import render
27 from pyramid.renderers import render
29 from pyramid.response import Response
28 from pyramid.response import Response
30
29
31 from rhodecode import events
30 from rhodecode import events
32 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
34 from rhodecode.authentication.base import get_authn_registry, RhodeCodeExternalAuthPlugin
33 from rhodecode.authentication.base import get_authn_registry, RhodeCodeExternalAuthPlugin
35 from rhodecode.authentication.plugins import auth_rhodecode
34 from rhodecode.authentication.plugins import auth_rhodecode
36 from rhodecode.events import trigger
35 from rhodecode.events import trigger
37 from rhodecode.model.db import true, UserNotice
36 from rhodecode.model.db import true, UserNotice
38
37
39 from rhodecode.lib import audit_logger, rc_cache, auth
38 from rhodecode.lib import audit_logger, rc_cache, auth
40 from rhodecode.lib.exceptions import (
39 from rhodecode.lib.exceptions import (
41 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
40 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
42 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
41 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
43 UserOwnsArtifactsException, DefaultUserException)
42 UserOwnsArtifactsException, DefaultUserException)
44 from rhodecode.lib.ext_json import json
43 from rhodecode.lib.ext_json import json
45 from rhodecode.lib.auth import (
44 from rhodecode.lib.auth import (
46 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
45 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
47 from rhodecode.lib import helpers as h
46 from rhodecode.lib import helpers as h
48 from rhodecode.lib.helpers import SqlPage
47 from rhodecode.lib.helpers import SqlPage
49 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
48 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
50 from rhodecode.model.auth_token import AuthTokenModel
49 from rhodecode.model.auth_token import AuthTokenModel
51 from rhodecode.model.forms import (
50 from rhodecode.model.forms import (
52 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
51 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
53 UserExtraEmailForm, UserExtraIpForm)
52 UserExtraEmailForm, UserExtraIpForm)
54 from rhodecode.model.permission import PermissionModel
53 from rhodecode.model.permission import PermissionModel
55 from rhodecode.model.repo_group import RepoGroupModel
54 from rhodecode.model.repo_group import RepoGroupModel
56 from rhodecode.model.ssh_key import SshKeyModel
55 from rhodecode.model.ssh_key import SshKeyModel
57 from rhodecode.model.user import UserModel
56 from rhodecode.model.user import UserModel
58 from rhodecode.model.user_group import UserGroupModel
57 from rhodecode.model.user_group import UserGroupModel
59 from rhodecode.model.db import (
58 from rhodecode.model.db import (
60 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
59 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
61 UserApiKeys, UserSshKeys, RepoGroup)
60 UserApiKeys, UserSshKeys, RepoGroup)
62 from rhodecode.model.meta import Session
61 from rhodecode.model.meta import Session
63
62
64 log = logging.getLogger(__name__)
63 log = logging.getLogger(__name__)
65
64
66
65
67 class AdminUsersView(BaseAppView, DataGridAppView):
66 class AdminUsersView(BaseAppView, DataGridAppView):
68
67
69 def load_default_context(self):
68 def load_default_context(self):
70 c = self._get_local_tmpl_context()
69 c = self._get_local_tmpl_context()
71 return c
70 return c
72
71
73 @LoginRequired()
72 @LoginRequired()
74 @HasPermissionAllDecorator('hg.admin')
73 @HasPermissionAllDecorator('hg.admin')
75 @view_config(
76 route_name='users', request_method='GET',
77 renderer='rhodecode:templates/admin/users/users.mako')
78 def users_list(self):
74 def users_list(self):
79 c = self.load_default_context()
75 c = self.load_default_context()
80 return self._get_template_context(c)
76 return self._get_template_context(c)
81
77
82 @LoginRequired()
78 @LoginRequired()
83 @HasPermissionAllDecorator('hg.admin')
79 @HasPermissionAllDecorator('hg.admin')
84 @view_config(
85 # renderer defined below
86 route_name='users_data', request_method='GET',
87 renderer='json_ext', xhr=True)
88 def users_list_data(self):
80 def users_list_data(self):
89 self.load_default_context()
81 self.load_default_context()
90 column_map = {
82 column_map = {
91 'first_name': 'name',
83 'first_name': 'name',
92 'last_name': 'lastname',
84 'last_name': 'lastname',
93 }
85 }
94 draw, start, limit = self._extract_chunk(self.request)
86 draw, start, limit = self._extract_chunk(self.request)
95 search_q, order_by, order_dir = self._extract_ordering(
87 search_q, order_by, order_dir = self._extract_ordering(
96 self.request, column_map=column_map)
88 self.request, column_map=column_map)
97 _render = self.request.get_partial_renderer(
89 _render = self.request.get_partial_renderer(
98 'rhodecode:templates/data_table/_dt_elements.mako')
90 'rhodecode:templates/data_table/_dt_elements.mako')
99
91
100 def user_actions(user_id, username):
92 def user_actions(user_id, username):
101 return _render("user_actions", user_id, username)
93 return _render("user_actions", user_id, username)
102
94
103 users_data_total_count = User.query()\
95 users_data_total_count = User.query()\
104 .filter(User.username != User.DEFAULT_USER) \
96 .filter(User.username != User.DEFAULT_USER) \
105 .count()
97 .count()
106
98
107 users_data_total_inactive_count = User.query()\
99 users_data_total_inactive_count = User.query()\
108 .filter(User.username != User.DEFAULT_USER) \
100 .filter(User.username != User.DEFAULT_USER) \
109 .filter(User.active != true())\
101 .filter(User.active != true())\
110 .count()
102 .count()
111
103
112 # json generate
104 # json generate
113 base_q = User.query().filter(User.username != User.DEFAULT_USER)
105 base_q = User.query().filter(User.username != User.DEFAULT_USER)
114 base_inactive_q = base_q.filter(User.active != true())
106 base_inactive_q = base_q.filter(User.active != true())
115
107
116 if search_q:
108 if search_q:
117 like_expression = u'%{}%'.format(safe_unicode(search_q))
109 like_expression = u'%{}%'.format(safe_unicode(search_q))
118 base_q = base_q.filter(or_(
110 base_q = base_q.filter(or_(
119 User.username.ilike(like_expression),
111 User.username.ilike(like_expression),
120 User._email.ilike(like_expression),
112 User._email.ilike(like_expression),
121 User.name.ilike(like_expression),
113 User.name.ilike(like_expression),
122 User.lastname.ilike(like_expression),
114 User.lastname.ilike(like_expression),
123 ))
115 ))
124 base_inactive_q = base_q.filter(User.active != true())
116 base_inactive_q = base_q.filter(User.active != true())
125
117
126 users_data_total_filtered_count = base_q.count()
118 users_data_total_filtered_count = base_q.count()
127 users_data_total_filtered_inactive_count = base_inactive_q.count()
119 users_data_total_filtered_inactive_count = base_inactive_q.count()
128
120
129 sort_col = getattr(User, order_by, None)
121 sort_col = getattr(User, order_by, None)
130 if sort_col:
122 if sort_col:
131 if order_dir == 'asc':
123 if order_dir == 'asc':
132 # handle null values properly to order by NULL last
124 # handle null values properly to order by NULL last
133 if order_by in ['last_activity']:
125 if order_by in ['last_activity']:
134 sort_col = coalesce(sort_col, datetime.date.max)
126 sort_col = coalesce(sort_col, datetime.date.max)
135 sort_col = sort_col.asc()
127 sort_col = sort_col.asc()
136 else:
128 else:
137 # handle null values properly to order by NULL last
129 # handle null values properly to order by NULL last
138 if order_by in ['last_activity']:
130 if order_by in ['last_activity']:
139 sort_col = coalesce(sort_col, datetime.date.min)
131 sort_col = coalesce(sort_col, datetime.date.min)
140 sort_col = sort_col.desc()
132 sort_col = sort_col.desc()
141
133
142 base_q = base_q.order_by(sort_col)
134 base_q = base_q.order_by(sort_col)
143 base_q = base_q.offset(start).limit(limit)
135 base_q = base_q.offset(start).limit(limit)
144
136
145 users_list = base_q.all()
137 users_list = base_q.all()
146
138
147 users_data = []
139 users_data = []
148 for user in users_list:
140 for user in users_list:
149 users_data.append({
141 users_data.append({
150 "username": h.gravatar_with_user(self.request, user.username),
142 "username": h.gravatar_with_user(self.request, user.username),
151 "email": user.email,
143 "email": user.email,
152 "first_name": user.first_name,
144 "first_name": user.first_name,
153 "last_name": user.last_name,
145 "last_name": user.last_name,
154 "last_login": h.format_date(user.last_login),
146 "last_login": h.format_date(user.last_login),
155 "last_activity": h.format_date(user.last_activity),
147 "last_activity": h.format_date(user.last_activity),
156 "active": h.bool2icon(user.active),
148 "active": h.bool2icon(user.active),
157 "active_raw": user.active,
149 "active_raw": user.active,
158 "admin": h.bool2icon(user.admin),
150 "admin": h.bool2icon(user.admin),
159 "extern_type": user.extern_type,
151 "extern_type": user.extern_type,
160 "extern_name": user.extern_name,
152 "extern_name": user.extern_name,
161 "action": user_actions(user.user_id, user.username),
153 "action": user_actions(user.user_id, user.username),
162 })
154 })
163 data = ({
155 data = ({
164 'draw': draw,
156 'draw': draw,
165 'data': users_data,
157 'data': users_data,
166 'recordsTotal': users_data_total_count,
158 'recordsTotal': users_data_total_count,
167 'recordsFiltered': users_data_total_filtered_count,
159 'recordsFiltered': users_data_total_filtered_count,
168 'recordsTotalInactive': users_data_total_inactive_count,
160 'recordsTotalInactive': users_data_total_inactive_count,
169 'recordsFilteredInactive': users_data_total_filtered_inactive_count
161 'recordsFilteredInactive': users_data_total_filtered_inactive_count
170 })
162 })
171
163
172 return data
164 return data
173
165
174 def _set_personal_repo_group_template_vars(self, c_obj):
166 def _set_personal_repo_group_template_vars(self, c_obj):
175 DummyUser = AttributeDict({
167 DummyUser = AttributeDict({
176 'username': '${username}',
168 'username': '${username}',
177 'user_id': '${user_id}',
169 'user_id': '${user_id}',
178 })
170 })
179 c_obj.default_create_repo_group = RepoGroupModel() \
171 c_obj.default_create_repo_group = RepoGroupModel() \
180 .get_default_create_personal_repo_group()
172 .get_default_create_personal_repo_group()
181 c_obj.personal_repo_group_name = RepoGroupModel() \
173 c_obj.personal_repo_group_name = RepoGroupModel() \
182 .get_personal_group_name(DummyUser)
174 .get_personal_group_name(DummyUser)
183
175
184 @LoginRequired()
176 @LoginRequired()
185 @HasPermissionAllDecorator('hg.admin')
177 @HasPermissionAllDecorator('hg.admin')
186 @view_config(
187 route_name='users_new', request_method='GET',
188 renderer='rhodecode:templates/admin/users/user_add.mako')
189 def users_new(self):
178 def users_new(self):
190 _ = self.request.translate
179 _ = self.request.translate
191 c = self.load_default_context()
180 c = self.load_default_context()
192 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
181 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
193 self._set_personal_repo_group_template_vars(c)
182 self._set_personal_repo_group_template_vars(c)
194 return self._get_template_context(c)
183 return self._get_template_context(c)
195
184
196 @LoginRequired()
185 @LoginRequired()
197 @HasPermissionAllDecorator('hg.admin')
186 @HasPermissionAllDecorator('hg.admin')
198 @CSRFRequired()
187 @CSRFRequired()
199 @view_config(
200 route_name='users_create', request_method='POST',
201 renderer='rhodecode:templates/admin/users/user_add.mako')
202 def users_create(self):
188 def users_create(self):
203 _ = self.request.translate
189 _ = self.request.translate
204 c = self.load_default_context()
190 c = self.load_default_context()
205 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
191 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
206 user_model = UserModel()
192 user_model = UserModel()
207 user_form = UserForm(self.request.translate)()
193 user_form = UserForm(self.request.translate)()
208 try:
194 try:
209 form_result = user_form.to_python(dict(self.request.POST))
195 form_result = user_form.to_python(dict(self.request.POST))
210 user = user_model.create(form_result)
196 user = user_model.create(form_result)
211 Session().flush()
197 Session().flush()
212 creation_data = user.get_api_data()
198 creation_data = user.get_api_data()
213 username = form_result['username']
199 username = form_result['username']
214
200
215 audit_logger.store_web(
201 audit_logger.store_web(
216 'user.create', action_data={'data': creation_data},
202 'user.create', action_data={'data': creation_data},
217 user=c.rhodecode_user)
203 user=c.rhodecode_user)
218
204
219 user_link = h.link_to(
205 user_link = h.link_to(
220 h.escape(username),
206 h.escape(username),
221 h.route_path('user_edit', user_id=user.user_id))
207 h.route_path('user_edit', user_id=user.user_id))
222 h.flash(h.literal(_('Created user %(user_link)s')
208 h.flash(h.literal(_('Created user %(user_link)s')
223 % {'user_link': user_link}), category='success')
209 % {'user_link': user_link}), category='success')
224 Session().commit()
210 Session().commit()
225 except formencode.Invalid as errors:
211 except formencode.Invalid as errors:
226 self._set_personal_repo_group_template_vars(c)
212 self._set_personal_repo_group_template_vars(c)
227 data = render(
213 data = render(
228 'rhodecode:templates/admin/users/user_add.mako',
214 'rhodecode:templates/admin/users/user_add.mako',
229 self._get_template_context(c), self.request)
215 self._get_template_context(c), self.request)
230 html = formencode.htmlfill.render(
216 html = formencode.htmlfill.render(
231 data,
217 data,
232 defaults=errors.value,
218 defaults=errors.value,
233 errors=errors.error_dict or {},
219 errors=errors.error_dict or {},
234 prefix_error=False,
220 prefix_error=False,
235 encoding="UTF-8",
221 encoding="UTF-8",
236 force_defaults=False
222 force_defaults=False
237 )
223 )
238 return Response(html)
224 return Response(html)
239 except UserCreationError as e:
225 except UserCreationError as e:
240 h.flash(e, 'error')
226 h.flash(e, 'error')
241 except Exception:
227 except Exception:
242 log.exception("Exception creation of user")
228 log.exception("Exception creation of user")
243 h.flash(_('Error occurred during creation of user %s')
229 h.flash(_('Error occurred during creation of user %s')
244 % self.request.POST.get('username'), category='error')
230 % self.request.POST.get('username'), category='error')
245 raise HTTPFound(h.route_path('users'))
231 raise HTTPFound(h.route_path('users'))
246
232
247
233
248 class UsersView(UserAppView):
234 class UsersView(UserAppView):
249 ALLOW_SCOPED_TOKENS = False
235 ALLOW_SCOPED_TOKENS = False
250 """
236 """
251 This view has alternative version inside EE, if modified please take a look
237 This view has alternative version inside EE, if modified please take a look
252 in there as well.
238 in there as well.
253 """
239 """
254
240
255 def get_auth_plugins(self):
241 def get_auth_plugins(self):
256 valid_plugins = []
242 valid_plugins = []
257 authn_registry = get_authn_registry(self.request.registry)
243 authn_registry = get_authn_registry(self.request.registry)
258 for plugin in authn_registry.get_plugins_for_authentication():
244 for plugin in authn_registry.get_plugins_for_authentication():
259 if isinstance(plugin, RhodeCodeExternalAuthPlugin):
245 if isinstance(plugin, RhodeCodeExternalAuthPlugin):
260 valid_plugins.append(plugin)
246 valid_plugins.append(plugin)
261 elif plugin.name == 'rhodecode':
247 elif plugin.name == 'rhodecode':
262 valid_plugins.append(plugin)
248 valid_plugins.append(plugin)
263
249
264 # extend our choices if user has set a bound plugin which isn't enabled at the
250 # extend our choices if user has set a bound plugin which isn't enabled at the
265 # moment
251 # moment
266 extern_type = self.db_user.extern_type
252 extern_type = self.db_user.extern_type
267 if extern_type not in [x.uid for x in valid_plugins]:
253 if extern_type not in [x.uid for x in valid_plugins]:
268 try:
254 try:
269 plugin = authn_registry.get_plugin_by_uid(extern_type)
255 plugin = authn_registry.get_plugin_by_uid(extern_type)
270 if plugin:
256 if plugin:
271 valid_plugins.append(plugin)
257 valid_plugins.append(plugin)
272
258
273 except Exception:
259 except Exception:
274 log.exception(
260 log.exception(
275 'Could not extend user plugins with `{}`'.format(extern_type))
261 'Could not extend user plugins with `{}`'.format(extern_type))
276 return valid_plugins
262 return valid_plugins
277
263
278 def load_default_context(self):
264 def load_default_context(self):
279 req = self.request
265 req = self.request
280
266
281 c = self._get_local_tmpl_context()
267 c = self._get_local_tmpl_context()
282 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
268 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
283 c.allowed_languages = [
269 c.allowed_languages = [
284 ('en', 'English (en)'),
270 ('en', 'English (en)'),
285 ('de', 'German (de)'),
271 ('de', 'German (de)'),
286 ('fr', 'French (fr)'),
272 ('fr', 'French (fr)'),
287 ('it', 'Italian (it)'),
273 ('it', 'Italian (it)'),
288 ('ja', 'Japanese (ja)'),
274 ('ja', 'Japanese (ja)'),
289 ('pl', 'Polish (pl)'),
275 ('pl', 'Polish (pl)'),
290 ('pt', 'Portuguese (pt)'),
276 ('pt', 'Portuguese (pt)'),
291 ('ru', 'Russian (ru)'),
277 ('ru', 'Russian (ru)'),
292 ('zh', 'Chinese (zh)'),
278 ('zh', 'Chinese (zh)'),
293 ]
279 ]
294
280
295 c.allowed_extern_types = [
281 c.allowed_extern_types = [
296 (x.uid, x.get_display_name()) for x in self.get_auth_plugins()
282 (x.uid, x.get_display_name()) for x in self.get_auth_plugins()
297 ]
283 ]
298 perms = req.registry.settings.get('available_permissions')
284 perms = req.registry.settings.get('available_permissions')
299 if not perms:
285 if not perms:
300 # inject info about available permissions
286 # inject info about available permissions
301 auth.set_available_permissions(req.registry.settings)
287 auth.set_available_permissions(req.registry.settings)
302
288
303 c.available_permissions = req.registry.settings['available_permissions']
289 c.available_permissions = req.registry.settings['available_permissions']
304 PermissionModel().set_global_permission_choices(
290 PermissionModel().set_global_permission_choices(
305 c, gettext_translator=req.translate)
291 c, gettext_translator=req.translate)
306
292
307 return c
293 return c
308
294
309 @LoginRequired()
295 @LoginRequired()
310 @HasPermissionAllDecorator('hg.admin')
296 @HasPermissionAllDecorator('hg.admin')
311 @CSRFRequired()
297 @CSRFRequired()
312 @view_config(
313 route_name='user_update', request_method='POST',
314 renderer='rhodecode:templates/admin/users/user_edit.mako')
315 def user_update(self):
298 def user_update(self):
316 _ = self.request.translate
299 _ = self.request.translate
317 c = self.load_default_context()
300 c = self.load_default_context()
318
301
319 user_id = self.db_user_id
302 user_id = self.db_user_id
320 c.user = self.db_user
303 c.user = self.db_user
321
304
322 c.active = 'profile'
305 c.active = 'profile'
323 c.extern_type = c.user.extern_type
306 c.extern_type = c.user.extern_type
324 c.extern_name = c.user.extern_name
307 c.extern_name = c.user.extern_name
325 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
308 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
326 available_languages = [x[0] for x in c.allowed_languages]
309 available_languages = [x[0] for x in c.allowed_languages]
327 _form = UserForm(self.request.translate, edit=True,
310 _form = UserForm(self.request.translate, edit=True,
328 available_languages=available_languages,
311 available_languages=available_languages,
329 old_data={'user_id': user_id,
312 old_data={'user_id': user_id,
330 'email': c.user.email})()
313 'email': c.user.email})()
331 form_result = {}
314 form_result = {}
332 old_values = c.user.get_api_data()
315 old_values = c.user.get_api_data()
333 try:
316 try:
334 form_result = _form.to_python(dict(self.request.POST))
317 form_result = _form.to_python(dict(self.request.POST))
335 skip_attrs = ['extern_name']
318 skip_attrs = ['extern_name']
336 # TODO: plugin should define if username can be updated
319 # TODO: plugin should define if username can be updated
337 if c.extern_type != "rhodecode":
320 if c.extern_type != "rhodecode":
338 # forbid updating username for external accounts
321 # forbid updating username for external accounts
339 skip_attrs.append('username')
322 skip_attrs.append('username')
340
323
341 UserModel().update_user(
324 UserModel().update_user(
342 user_id, skip_attrs=skip_attrs, **form_result)
325 user_id, skip_attrs=skip_attrs, **form_result)
343
326
344 audit_logger.store_web(
327 audit_logger.store_web(
345 'user.edit', action_data={'old_data': old_values},
328 'user.edit', action_data={'old_data': old_values},
346 user=c.rhodecode_user)
329 user=c.rhodecode_user)
347
330
348 Session().commit()
331 Session().commit()
349 h.flash(_('User updated successfully'), category='success')
332 h.flash(_('User updated successfully'), category='success')
350 except formencode.Invalid as errors:
333 except formencode.Invalid as errors:
351 data = render(
334 data = render(
352 'rhodecode:templates/admin/users/user_edit.mako',
335 'rhodecode:templates/admin/users/user_edit.mako',
353 self._get_template_context(c), self.request)
336 self._get_template_context(c), self.request)
354 html = formencode.htmlfill.render(
337 html = formencode.htmlfill.render(
355 data,
338 data,
356 defaults=errors.value,
339 defaults=errors.value,
357 errors=errors.error_dict or {},
340 errors=errors.error_dict or {},
358 prefix_error=False,
341 prefix_error=False,
359 encoding="UTF-8",
342 encoding="UTF-8",
360 force_defaults=False
343 force_defaults=False
361 )
344 )
362 return Response(html)
345 return Response(html)
363 except UserCreationError as e:
346 except UserCreationError as e:
364 h.flash(e, 'error')
347 h.flash(e, 'error')
365 except Exception:
348 except Exception:
366 log.exception("Exception updating user")
349 log.exception("Exception updating user")
367 h.flash(_('Error occurred during update of user %s')
350 h.flash(_('Error occurred during update of user %s')
368 % form_result.get('username'), category='error')
351 % form_result.get('username'), category='error')
369 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
352 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
370
353
371 @LoginRequired()
354 @LoginRequired()
372 @HasPermissionAllDecorator('hg.admin')
355 @HasPermissionAllDecorator('hg.admin')
373 @CSRFRequired()
356 @CSRFRequired()
374 @view_config(
375 route_name='user_delete', request_method='POST',
376 renderer='rhodecode:templates/admin/users/user_edit.mako')
377 def user_delete(self):
357 def user_delete(self):
378 _ = self.request.translate
358 _ = self.request.translate
379 c = self.load_default_context()
359 c = self.load_default_context()
380 c.user = self.db_user
360 c.user = self.db_user
381
361
382 _repos = c.user.repositories
362 _repos = c.user.repositories
383 _repo_groups = c.user.repository_groups
363 _repo_groups = c.user.repository_groups
384 _user_groups = c.user.user_groups
364 _user_groups = c.user.user_groups
385 _pull_requests = c.user.user_pull_requests
365 _pull_requests = c.user.user_pull_requests
386 _artifacts = c.user.artifacts
366 _artifacts = c.user.artifacts
387
367
388 handle_repos = None
368 handle_repos = None
389 handle_repo_groups = None
369 handle_repo_groups = None
390 handle_user_groups = None
370 handle_user_groups = None
391 handle_pull_requests = None
371 handle_pull_requests = None
392 handle_artifacts = None
372 handle_artifacts = None
393
373
394 # calls for flash of handle based on handle case detach or delete
374 # calls for flash of handle based on handle case detach or delete
395 def set_handle_flash_repos():
375 def set_handle_flash_repos():
396 handle = handle_repos
376 handle = handle_repos
397 if handle == 'detach':
377 if handle == 'detach':
398 h.flash(_('Detached %s repositories') % len(_repos),
378 h.flash(_('Detached %s repositories') % len(_repos),
399 category='success')
379 category='success')
400 elif handle == 'delete':
380 elif handle == 'delete':
401 h.flash(_('Deleted %s repositories') % len(_repos),
381 h.flash(_('Deleted %s repositories') % len(_repos),
402 category='success')
382 category='success')
403
383
404 def set_handle_flash_repo_groups():
384 def set_handle_flash_repo_groups():
405 handle = handle_repo_groups
385 handle = handle_repo_groups
406 if handle == 'detach':
386 if handle == 'detach':
407 h.flash(_('Detached %s repository groups') % len(_repo_groups),
387 h.flash(_('Detached %s repository groups') % len(_repo_groups),
408 category='success')
388 category='success')
409 elif handle == 'delete':
389 elif handle == 'delete':
410 h.flash(_('Deleted %s repository groups') % len(_repo_groups),
390 h.flash(_('Deleted %s repository groups') % len(_repo_groups),
411 category='success')
391 category='success')
412
392
413 def set_handle_flash_user_groups():
393 def set_handle_flash_user_groups():
414 handle = handle_user_groups
394 handle = handle_user_groups
415 if handle == 'detach':
395 if handle == 'detach':
416 h.flash(_('Detached %s user groups') % len(_user_groups),
396 h.flash(_('Detached %s user groups') % len(_user_groups),
417 category='success')
397 category='success')
418 elif handle == 'delete':
398 elif handle == 'delete':
419 h.flash(_('Deleted %s user groups') % len(_user_groups),
399 h.flash(_('Deleted %s user groups') % len(_user_groups),
420 category='success')
400 category='success')
421
401
422 def set_handle_flash_pull_requests():
402 def set_handle_flash_pull_requests():
423 handle = handle_pull_requests
403 handle = handle_pull_requests
424 if handle == 'detach':
404 if handle == 'detach':
425 h.flash(_('Detached %s pull requests') % len(_pull_requests),
405 h.flash(_('Detached %s pull requests') % len(_pull_requests),
426 category='success')
406 category='success')
427 elif handle == 'delete':
407 elif handle == 'delete':
428 h.flash(_('Deleted %s pull requests') % len(_pull_requests),
408 h.flash(_('Deleted %s pull requests') % len(_pull_requests),
429 category='success')
409 category='success')
430
410
431 def set_handle_flash_artifacts():
411 def set_handle_flash_artifacts():
432 handle = handle_artifacts
412 handle = handle_artifacts
433 if handle == 'detach':
413 if handle == 'detach':
434 h.flash(_('Detached %s artifacts') % len(_artifacts),
414 h.flash(_('Detached %s artifacts') % len(_artifacts),
435 category='success')
415 category='success')
436 elif handle == 'delete':
416 elif handle == 'delete':
437 h.flash(_('Deleted %s artifacts') % len(_artifacts),
417 h.flash(_('Deleted %s artifacts') % len(_artifacts),
438 category='success')
418 category='success')
439
419
440 handle_user = User.get_first_super_admin()
420 handle_user = User.get_first_super_admin()
441 handle_user_id = safe_int(self.request.POST.get('detach_user_id'))
421 handle_user_id = safe_int(self.request.POST.get('detach_user_id'))
442 if handle_user_id:
422 if handle_user_id:
443 # NOTE(marcink): we get new owner for objects...
423 # NOTE(marcink): we get new owner for objects...
444 handle_user = User.get_or_404(handle_user_id)
424 handle_user = User.get_or_404(handle_user_id)
445
425
446 if _repos and self.request.POST.get('user_repos'):
426 if _repos and self.request.POST.get('user_repos'):
447 handle_repos = self.request.POST['user_repos']
427 handle_repos = self.request.POST['user_repos']
448
428
449 if _repo_groups and self.request.POST.get('user_repo_groups'):
429 if _repo_groups and self.request.POST.get('user_repo_groups'):
450 handle_repo_groups = self.request.POST['user_repo_groups']
430 handle_repo_groups = self.request.POST['user_repo_groups']
451
431
452 if _user_groups and self.request.POST.get('user_user_groups'):
432 if _user_groups and self.request.POST.get('user_user_groups'):
453 handle_user_groups = self.request.POST['user_user_groups']
433 handle_user_groups = self.request.POST['user_user_groups']
454
434
455 if _pull_requests and self.request.POST.get('user_pull_requests'):
435 if _pull_requests and self.request.POST.get('user_pull_requests'):
456 handle_pull_requests = self.request.POST['user_pull_requests']
436 handle_pull_requests = self.request.POST['user_pull_requests']
457
437
458 if _artifacts and self.request.POST.get('user_artifacts'):
438 if _artifacts and self.request.POST.get('user_artifacts'):
459 handle_artifacts = self.request.POST['user_artifacts']
439 handle_artifacts = self.request.POST['user_artifacts']
460
440
461 old_values = c.user.get_api_data()
441 old_values = c.user.get_api_data()
462
442
463 try:
443 try:
464
444
465 UserModel().delete(
445 UserModel().delete(
466 c.user,
446 c.user,
467 handle_repos=handle_repos,
447 handle_repos=handle_repos,
468 handle_repo_groups=handle_repo_groups,
448 handle_repo_groups=handle_repo_groups,
469 handle_user_groups=handle_user_groups,
449 handle_user_groups=handle_user_groups,
470 handle_pull_requests=handle_pull_requests,
450 handle_pull_requests=handle_pull_requests,
471 handle_artifacts=handle_artifacts,
451 handle_artifacts=handle_artifacts,
472 handle_new_owner=handle_user
452 handle_new_owner=handle_user
473 )
453 )
474
454
475 audit_logger.store_web(
455 audit_logger.store_web(
476 'user.delete', action_data={'old_data': old_values},
456 'user.delete', action_data={'old_data': old_values},
477 user=c.rhodecode_user)
457 user=c.rhodecode_user)
478
458
479 Session().commit()
459 Session().commit()
480 set_handle_flash_repos()
460 set_handle_flash_repos()
481 set_handle_flash_repo_groups()
461 set_handle_flash_repo_groups()
482 set_handle_flash_user_groups()
462 set_handle_flash_user_groups()
483 set_handle_flash_pull_requests()
463 set_handle_flash_pull_requests()
484 set_handle_flash_artifacts()
464 set_handle_flash_artifacts()
485 username = h.escape(old_values['username'])
465 username = h.escape(old_values['username'])
486 h.flash(_('Successfully deleted user `{}`').format(username), category='success')
466 h.flash(_('Successfully deleted user `{}`').format(username), category='success')
487 except (UserOwnsReposException, UserOwnsRepoGroupsException,
467 except (UserOwnsReposException, UserOwnsRepoGroupsException,
488 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
468 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
489 UserOwnsArtifactsException, DefaultUserException) as e:
469 UserOwnsArtifactsException, DefaultUserException) as e:
490 h.flash(e, category='warning')
470 h.flash(e, category='warning')
491 except Exception:
471 except Exception:
492 log.exception("Exception during deletion of user")
472 log.exception("Exception during deletion of user")
493 h.flash(_('An error occurred during deletion of user'),
473 h.flash(_('An error occurred during deletion of user'),
494 category='error')
474 category='error')
495 raise HTTPFound(h.route_path('users'))
475 raise HTTPFound(h.route_path('users'))
496
476
497 @LoginRequired()
477 @LoginRequired()
498 @HasPermissionAllDecorator('hg.admin')
478 @HasPermissionAllDecorator('hg.admin')
499 @view_config(
500 route_name='user_edit', request_method='GET',
501 renderer='rhodecode:templates/admin/users/user_edit.mako')
502 def user_edit(self):
479 def user_edit(self):
503 _ = self.request.translate
480 _ = self.request.translate
504 c = self.load_default_context()
481 c = self.load_default_context()
505 c.user = self.db_user
482 c.user = self.db_user
506
483
507 c.active = 'profile'
484 c.active = 'profile'
508 c.extern_type = c.user.extern_type
485 c.extern_type = c.user.extern_type
509 c.extern_name = c.user.extern_name
486 c.extern_name = c.user.extern_name
510 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
487 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
511
488
512 defaults = c.user.get_dict()
489 defaults = c.user.get_dict()
513 defaults.update({'language': c.user.user_data.get('language')})
490 defaults.update({'language': c.user.user_data.get('language')})
514
491
515 data = render(
492 data = render(
516 'rhodecode:templates/admin/users/user_edit.mako',
493 'rhodecode:templates/admin/users/user_edit.mako',
517 self._get_template_context(c), self.request)
494 self._get_template_context(c), self.request)
518 html = formencode.htmlfill.render(
495 html = formencode.htmlfill.render(
519 data,
496 data,
520 defaults=defaults,
497 defaults=defaults,
521 encoding="UTF-8",
498 encoding="UTF-8",
522 force_defaults=False
499 force_defaults=False
523 )
500 )
524 return Response(html)
501 return Response(html)
525
502
526 @LoginRequired()
503 @LoginRequired()
527 @HasPermissionAllDecorator('hg.admin')
504 @HasPermissionAllDecorator('hg.admin')
528 @view_config(
529 route_name='user_edit_advanced', request_method='GET',
530 renderer='rhodecode:templates/admin/users/user_edit.mako')
531 def user_edit_advanced(self):
505 def user_edit_advanced(self):
532 _ = self.request.translate
506 _ = self.request.translate
533 c = self.load_default_context()
507 c = self.load_default_context()
534
508
535 user_id = self.db_user_id
509 user_id = self.db_user_id
536 c.user = self.db_user
510 c.user = self.db_user
537
511
538 c.detach_user = User.get_first_super_admin()
512 c.detach_user = User.get_first_super_admin()
539 detach_user_id = safe_int(self.request.GET.get('detach_user_id'))
513 detach_user_id = safe_int(self.request.GET.get('detach_user_id'))
540 if detach_user_id:
514 if detach_user_id:
541 c.detach_user = User.get_or_404(detach_user_id)
515 c.detach_user = User.get_or_404(detach_user_id)
542
516
543 c.active = 'advanced'
517 c.active = 'advanced'
544 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
518 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
545 c.personal_repo_group_name = RepoGroupModel()\
519 c.personal_repo_group_name = RepoGroupModel()\
546 .get_personal_group_name(c.user)
520 .get_personal_group_name(c.user)
547
521
548 c.user_to_review_rules = sorted(
522 c.user_to_review_rules = sorted(
549 (x.user for x in c.user.user_review_rules),
523 (x.user for x in c.user.user_review_rules),
550 key=lambda u: u.username.lower())
524 key=lambda u: u.username.lower())
551
525
552 defaults = c.user.get_dict()
526 defaults = c.user.get_dict()
553
527
554 # Interim workaround if the user participated on any pull requests as a
528 # Interim workaround if the user participated on any pull requests as a
555 # reviewer.
529 # reviewer.
556 has_review = len(c.user.reviewer_pull_requests)
530 has_review = len(c.user.reviewer_pull_requests)
557 c.can_delete_user = not has_review
531 c.can_delete_user = not has_review
558 c.can_delete_user_message = ''
532 c.can_delete_user_message = ''
559 inactive_link = h.link_to(
533 inactive_link = h.link_to(
560 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
534 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
561 if has_review == 1:
535 if has_review == 1:
562 c.can_delete_user_message = h.literal(_(
536 c.can_delete_user_message = h.literal(_(
563 'The user participates as reviewer in {} pull request and '
537 'The user participates as reviewer in {} pull request and '
564 'cannot be deleted. \nYou can set the user to '
538 'cannot be deleted. \nYou can set the user to '
565 '"{}" instead of deleting it.').format(
539 '"{}" instead of deleting it.').format(
566 has_review, inactive_link))
540 has_review, inactive_link))
567 elif has_review:
541 elif has_review:
568 c.can_delete_user_message = h.literal(_(
542 c.can_delete_user_message = h.literal(_(
569 'The user participates as reviewer in {} pull requests and '
543 'The user participates as reviewer in {} pull requests and '
570 'cannot be deleted. \nYou can set the user to '
544 'cannot be deleted. \nYou can set the user to '
571 '"{}" instead of deleting it.').format(
545 '"{}" instead of deleting it.').format(
572 has_review, inactive_link))
546 has_review, inactive_link))
573
547
574 data = render(
548 data = render(
575 'rhodecode:templates/admin/users/user_edit.mako',
549 'rhodecode:templates/admin/users/user_edit.mako',
576 self._get_template_context(c), self.request)
550 self._get_template_context(c), self.request)
577 html = formencode.htmlfill.render(
551 html = formencode.htmlfill.render(
578 data,
552 data,
579 defaults=defaults,
553 defaults=defaults,
580 encoding="UTF-8",
554 encoding="UTF-8",
581 force_defaults=False
555 force_defaults=False
582 )
556 )
583 return Response(html)
557 return Response(html)
584
558
585 @LoginRequired()
559 @LoginRequired()
586 @HasPermissionAllDecorator('hg.admin')
560 @HasPermissionAllDecorator('hg.admin')
587 @view_config(
588 route_name='user_edit_global_perms', request_method='GET',
589 renderer='rhodecode:templates/admin/users/user_edit.mako')
590 def user_edit_global_perms(self):
561 def user_edit_global_perms(self):
591 _ = self.request.translate
562 _ = self.request.translate
592 c = self.load_default_context()
563 c = self.load_default_context()
593 c.user = self.db_user
564 c.user = self.db_user
594
565
595 c.active = 'global_perms'
566 c.active = 'global_perms'
596
567
597 c.default_user = User.get_default_user()
568 c.default_user = User.get_default_user()
598 defaults = c.user.get_dict()
569 defaults = c.user.get_dict()
599 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
570 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
600 defaults.update(c.default_user.get_default_perms())
571 defaults.update(c.default_user.get_default_perms())
601 defaults.update(c.user.get_default_perms())
572 defaults.update(c.user.get_default_perms())
602
573
603 data = render(
574 data = render(
604 'rhodecode:templates/admin/users/user_edit.mako',
575 'rhodecode:templates/admin/users/user_edit.mako',
605 self._get_template_context(c), self.request)
576 self._get_template_context(c), self.request)
606 html = formencode.htmlfill.render(
577 html = formencode.htmlfill.render(
607 data,
578 data,
608 defaults=defaults,
579 defaults=defaults,
609 encoding="UTF-8",
580 encoding="UTF-8",
610 force_defaults=False
581 force_defaults=False
611 )
582 )
612 return Response(html)
583 return Response(html)
613
584
614 @LoginRequired()
585 @LoginRequired()
615 @HasPermissionAllDecorator('hg.admin')
586 @HasPermissionAllDecorator('hg.admin')
616 @CSRFRequired()
587 @CSRFRequired()
617 @view_config(
618 route_name='user_edit_global_perms_update', request_method='POST',
619 renderer='rhodecode:templates/admin/users/user_edit.mako')
620 def user_edit_global_perms_update(self):
588 def user_edit_global_perms_update(self):
621 _ = self.request.translate
589 _ = self.request.translate
622 c = self.load_default_context()
590 c = self.load_default_context()
623
591
624 user_id = self.db_user_id
592 user_id = self.db_user_id
625 c.user = self.db_user
593 c.user = self.db_user
626
594
627 c.active = 'global_perms'
595 c.active = 'global_perms'
628 try:
596 try:
629 # first stage that verifies the checkbox
597 # first stage that verifies the checkbox
630 _form = UserIndividualPermissionsForm(self.request.translate)
598 _form = UserIndividualPermissionsForm(self.request.translate)
631 form_result = _form.to_python(dict(self.request.POST))
599 form_result = _form.to_python(dict(self.request.POST))
632 inherit_perms = form_result['inherit_default_permissions']
600 inherit_perms = form_result['inherit_default_permissions']
633 c.user.inherit_default_permissions = inherit_perms
601 c.user.inherit_default_permissions = inherit_perms
634 Session().add(c.user)
602 Session().add(c.user)
635
603
636 if not inherit_perms:
604 if not inherit_perms:
637 # only update the individual ones if we un check the flag
605 # only update the individual ones if we un check the flag
638 _form = UserPermissionsForm(
606 _form = UserPermissionsForm(
639 self.request.translate,
607 self.request.translate,
640 [x[0] for x in c.repo_create_choices],
608 [x[0] for x in c.repo_create_choices],
641 [x[0] for x in c.repo_create_on_write_choices],
609 [x[0] for x in c.repo_create_on_write_choices],
642 [x[0] for x in c.repo_group_create_choices],
610 [x[0] for x in c.repo_group_create_choices],
643 [x[0] for x in c.user_group_create_choices],
611 [x[0] for x in c.user_group_create_choices],
644 [x[0] for x in c.fork_choices],
612 [x[0] for x in c.fork_choices],
645 [x[0] for x in c.inherit_default_permission_choices])()
613 [x[0] for x in c.inherit_default_permission_choices])()
646
614
647 form_result = _form.to_python(dict(self.request.POST))
615 form_result = _form.to_python(dict(self.request.POST))
648 form_result.update({'perm_user_id': c.user.user_id})
616 form_result.update({'perm_user_id': c.user.user_id})
649
617
650 PermissionModel().update_user_permissions(form_result)
618 PermissionModel().update_user_permissions(form_result)
651
619
652 # TODO(marcink): implement global permissions
620 # TODO(marcink): implement global permissions
653 # audit_log.store_web('user.edit.permissions')
621 # audit_log.store_web('user.edit.permissions')
654
622
655 Session().commit()
623 Session().commit()
656
624
657 h.flash(_('User global permissions updated successfully'),
625 h.flash(_('User global permissions updated successfully'),
658 category='success')
626 category='success')
659
627
660 except formencode.Invalid as errors:
628 except formencode.Invalid as errors:
661 data = render(
629 data = render(
662 'rhodecode:templates/admin/users/user_edit.mako',
630 'rhodecode:templates/admin/users/user_edit.mako',
663 self._get_template_context(c), self.request)
631 self._get_template_context(c), self.request)
664 html = formencode.htmlfill.render(
632 html = formencode.htmlfill.render(
665 data,
633 data,
666 defaults=errors.value,
634 defaults=errors.value,
667 errors=errors.error_dict or {},
635 errors=errors.error_dict or {},
668 prefix_error=False,
636 prefix_error=False,
669 encoding="UTF-8",
637 encoding="UTF-8",
670 force_defaults=False
638 force_defaults=False
671 )
639 )
672 return Response(html)
640 return Response(html)
673 except Exception:
641 except Exception:
674 log.exception("Exception during permissions saving")
642 log.exception("Exception during permissions saving")
675 h.flash(_('An error occurred during permissions saving'),
643 h.flash(_('An error occurred during permissions saving'),
676 category='error')
644 category='error')
677
645
678 affected_user_ids = [user_id]
646 affected_user_ids = [user_id]
679 PermissionModel().trigger_permission_flush(affected_user_ids)
647 PermissionModel().trigger_permission_flush(affected_user_ids)
680 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
648 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
681
649
682 @LoginRequired()
650 @LoginRequired()
683 @HasPermissionAllDecorator('hg.admin')
651 @HasPermissionAllDecorator('hg.admin')
684 @CSRFRequired()
652 @CSRFRequired()
685 @view_config(
686 route_name='user_enable_force_password_reset', request_method='POST',
687 renderer='rhodecode:templates/admin/users/user_edit.mako')
688 def user_enable_force_password_reset(self):
653 def user_enable_force_password_reset(self):
689 _ = self.request.translate
654 _ = self.request.translate
690 c = self.load_default_context()
655 c = self.load_default_context()
691
656
692 user_id = self.db_user_id
657 user_id = self.db_user_id
693 c.user = self.db_user
658 c.user = self.db_user
694
659
695 try:
660 try:
696 c.user.update_userdata(force_password_change=True)
661 c.user.update_userdata(force_password_change=True)
697
662
698 msg = _('Force password change enabled for user')
663 msg = _('Force password change enabled for user')
699 audit_logger.store_web('user.edit.password_reset.enabled',
664 audit_logger.store_web('user.edit.password_reset.enabled',
700 user=c.rhodecode_user)
665 user=c.rhodecode_user)
701
666
702 Session().commit()
667 Session().commit()
703 h.flash(msg, category='success')
668 h.flash(msg, category='success')
704 except Exception:
669 except Exception:
705 log.exception("Exception during password reset for user")
670 log.exception("Exception during password reset for user")
706 h.flash(_('An error occurred during password reset for user'),
671 h.flash(_('An error occurred during password reset for user'),
707 category='error')
672 category='error')
708
673
709 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
674 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
710
675
711 @LoginRequired()
676 @LoginRequired()
712 @HasPermissionAllDecorator('hg.admin')
677 @HasPermissionAllDecorator('hg.admin')
713 @CSRFRequired()
678 @CSRFRequired()
714 @view_config(
715 route_name='user_disable_force_password_reset', request_method='POST',
716 renderer='rhodecode:templates/admin/users/user_edit.mako')
717 def user_disable_force_password_reset(self):
679 def user_disable_force_password_reset(self):
718 _ = self.request.translate
680 _ = self.request.translate
719 c = self.load_default_context()
681 c = self.load_default_context()
720
682
721 user_id = self.db_user_id
683 user_id = self.db_user_id
722 c.user = self.db_user
684 c.user = self.db_user
723
685
724 try:
686 try:
725 c.user.update_userdata(force_password_change=False)
687 c.user.update_userdata(force_password_change=False)
726
688
727 msg = _('Force password change disabled for user')
689 msg = _('Force password change disabled for user')
728 audit_logger.store_web(
690 audit_logger.store_web(
729 'user.edit.password_reset.disabled',
691 'user.edit.password_reset.disabled',
730 user=c.rhodecode_user)
692 user=c.rhodecode_user)
731
693
732 Session().commit()
694 Session().commit()
733 h.flash(msg, category='success')
695 h.flash(msg, category='success')
734 except Exception:
696 except Exception:
735 log.exception("Exception during password reset for user")
697 log.exception("Exception during password reset for user")
736 h.flash(_('An error occurred during password reset for user'),
698 h.flash(_('An error occurred during password reset for user'),
737 category='error')
699 category='error')
738
700
739 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
701 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
740
702
741 @LoginRequired()
703 @LoginRequired()
742 @HasPermissionAllDecorator('hg.admin')
704 @HasPermissionAllDecorator('hg.admin')
743 @CSRFRequired()
705 @CSRFRequired()
744 @view_config(
745 route_name='user_notice_dismiss', request_method='POST',
746 renderer='json_ext', xhr=True)
747 def user_notice_dismiss(self):
706 def user_notice_dismiss(self):
748 _ = self.request.translate
707 _ = self.request.translate
749 c = self.load_default_context()
708 c = self.load_default_context()
750
709
751 user_id = self.db_user_id
710 user_id = self.db_user_id
752 c.user = self.db_user
711 c.user = self.db_user
753 user_notice_id = safe_int(self.request.POST.get('notice_id'))
712 user_notice_id = safe_int(self.request.POST.get('notice_id'))
754 notice = UserNotice().query()\
713 notice = UserNotice().query()\
755 .filter(UserNotice.user_id == user_id)\
714 .filter(UserNotice.user_id == user_id)\
756 .filter(UserNotice.user_notice_id == user_notice_id)\
715 .filter(UserNotice.user_notice_id == user_notice_id)\
757 .scalar()
716 .scalar()
758 read = False
717 read = False
759 if notice:
718 if notice:
760 notice.notice_read = True
719 notice.notice_read = True
761 Session().add(notice)
720 Session().add(notice)
762 Session().commit()
721 Session().commit()
763 read = True
722 read = True
764
723
765 return {'notice': user_notice_id, 'read': read}
724 return {'notice': user_notice_id, 'read': read}
766
725
767 @LoginRequired()
726 @LoginRequired()
768 @HasPermissionAllDecorator('hg.admin')
727 @HasPermissionAllDecorator('hg.admin')
769 @CSRFRequired()
728 @CSRFRequired()
770 @view_config(
771 route_name='user_create_personal_repo_group', request_method='POST',
772 renderer='rhodecode:templates/admin/users/user_edit.mako')
773 def user_create_personal_repo_group(self):
729 def user_create_personal_repo_group(self):
774 """
730 """
775 Create personal repository group for this user
731 Create personal repository group for this user
776 """
732 """
777 from rhodecode.model.repo_group import RepoGroupModel
733 from rhodecode.model.repo_group import RepoGroupModel
778
734
779 _ = self.request.translate
735 _ = self.request.translate
780 c = self.load_default_context()
736 c = self.load_default_context()
781
737
782 user_id = self.db_user_id
738 user_id = self.db_user_id
783 c.user = self.db_user
739 c.user = self.db_user
784
740
785 personal_repo_group = RepoGroup.get_user_personal_repo_group(
741 personal_repo_group = RepoGroup.get_user_personal_repo_group(
786 c.user.user_id)
742 c.user.user_id)
787 if personal_repo_group:
743 if personal_repo_group:
788 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
744 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
789
745
790 personal_repo_group_name = RepoGroupModel().get_personal_group_name(c.user)
746 personal_repo_group_name = RepoGroupModel().get_personal_group_name(c.user)
791 named_personal_group = RepoGroup.get_by_group_name(
747 named_personal_group = RepoGroup.get_by_group_name(
792 personal_repo_group_name)
748 personal_repo_group_name)
793 try:
749 try:
794
750
795 if named_personal_group and named_personal_group.user_id == c.user.user_id:
751 if named_personal_group and named_personal_group.user_id == c.user.user_id:
796 # migrate the same named group, and mark it as personal
752 # migrate the same named group, and mark it as personal
797 named_personal_group.personal = True
753 named_personal_group.personal = True
798 Session().add(named_personal_group)
754 Session().add(named_personal_group)
799 Session().commit()
755 Session().commit()
800 msg = _('Linked repository group `%s` as personal' % (
756 msg = _('Linked repository group `%s` as personal' % (
801 personal_repo_group_name,))
757 personal_repo_group_name,))
802 h.flash(msg, category='success')
758 h.flash(msg, category='success')
803 elif not named_personal_group:
759 elif not named_personal_group:
804 RepoGroupModel().create_personal_repo_group(c.user)
760 RepoGroupModel().create_personal_repo_group(c.user)
805
761
806 msg = _('Created repository group `%s`' % (
762 msg = _('Created repository group `%s`' % (
807 personal_repo_group_name,))
763 personal_repo_group_name,))
808 h.flash(msg, category='success')
764 h.flash(msg, category='success')
809 else:
765 else:
810 msg = _('Repository group `%s` is already taken' % (
766 msg = _('Repository group `%s` is already taken' % (
811 personal_repo_group_name,))
767 personal_repo_group_name,))
812 h.flash(msg, category='warning')
768 h.flash(msg, category='warning')
813 except Exception:
769 except Exception:
814 log.exception("Exception during repository group creation")
770 log.exception("Exception during repository group creation")
815 msg = _(
771 msg = _(
816 'An error occurred during repository group creation for user')
772 'An error occurred during repository group creation for user')
817 h.flash(msg, category='error')
773 h.flash(msg, category='error')
818 Session().rollback()
774 Session().rollback()
819
775
820 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
776 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
821
777
822 @LoginRequired()
778 @LoginRequired()
823 @HasPermissionAllDecorator('hg.admin')
779 @HasPermissionAllDecorator('hg.admin')
824 @view_config(
825 route_name='edit_user_auth_tokens', request_method='GET',
826 renderer='rhodecode:templates/admin/users/user_edit.mako')
827 def auth_tokens(self):
780 def auth_tokens(self):
828 _ = self.request.translate
781 _ = self.request.translate
829 c = self.load_default_context()
782 c = self.load_default_context()
830 c.user = self.db_user
783 c.user = self.db_user
831
784
832 c.active = 'auth_tokens'
785 c.active = 'auth_tokens'
833
786
834 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
787 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
835 c.role_values = [
788 c.role_values = [
836 (x, AuthTokenModel.cls._get_role_name(x))
789 (x, AuthTokenModel.cls._get_role_name(x))
837 for x in AuthTokenModel.cls.ROLES]
790 for x in AuthTokenModel.cls.ROLES]
838 c.role_options = [(c.role_values, _("Role"))]
791 c.role_options = [(c.role_values, _("Role"))]
839 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
792 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
840 c.user.user_id, show_expired=True)
793 c.user.user_id, show_expired=True)
841 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
794 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
842 return self._get_template_context(c)
795 return self._get_template_context(c)
843
796
844 @LoginRequired()
797 @LoginRequired()
845 @HasPermissionAllDecorator('hg.admin')
798 @HasPermissionAllDecorator('hg.admin')
846 @view_config(
847 route_name='edit_user_auth_tokens_view', request_method='POST',
848 renderer='json_ext', xhr=True)
849 def auth_tokens_view(self):
799 def auth_tokens_view(self):
850 _ = self.request.translate
800 _ = self.request.translate
851 c = self.load_default_context()
801 c = self.load_default_context()
852 c.user = self.db_user
802 c.user = self.db_user
853
803
854 auth_token_id = self.request.POST.get('auth_token_id')
804 auth_token_id = self.request.POST.get('auth_token_id')
855
805
856 if auth_token_id:
806 if auth_token_id:
857 token = UserApiKeys.get_or_404(auth_token_id)
807 token = UserApiKeys.get_or_404(auth_token_id)
858
808
859 return {
809 return {
860 'auth_token': token.api_key
810 'auth_token': token.api_key
861 }
811 }
862
812
863 def maybe_attach_token_scope(self, token):
813 def maybe_attach_token_scope(self, token):
864 # implemented in EE edition
814 # implemented in EE edition
865 pass
815 pass
866
816
867 @LoginRequired()
817 @LoginRequired()
868 @HasPermissionAllDecorator('hg.admin')
818 @HasPermissionAllDecorator('hg.admin')
869 @CSRFRequired()
819 @CSRFRequired()
870 @view_config(
871 route_name='edit_user_auth_tokens_add', request_method='POST')
872 def auth_tokens_add(self):
820 def auth_tokens_add(self):
873 _ = self.request.translate
821 _ = self.request.translate
874 c = self.load_default_context()
822 c = self.load_default_context()
875
823
876 user_id = self.db_user_id
824 user_id = self.db_user_id
877 c.user = self.db_user
825 c.user = self.db_user
878
826
879 user_data = c.user.get_api_data()
827 user_data = c.user.get_api_data()
880 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
828 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
881 description = self.request.POST.get('description')
829 description = self.request.POST.get('description')
882 role = self.request.POST.get('role')
830 role = self.request.POST.get('role')
883
831
884 token = UserModel().add_auth_token(
832 token = UserModel().add_auth_token(
885 user=c.user.user_id,
833 user=c.user.user_id,
886 lifetime_minutes=lifetime, role=role, description=description,
834 lifetime_minutes=lifetime, role=role, description=description,
887 scope_callback=self.maybe_attach_token_scope)
835 scope_callback=self.maybe_attach_token_scope)
888 token_data = token.get_api_data()
836 token_data = token.get_api_data()
889
837
890 audit_logger.store_web(
838 audit_logger.store_web(
891 'user.edit.token.add', action_data={
839 'user.edit.token.add', action_data={
892 'data': {'token': token_data, 'user': user_data}},
840 'data': {'token': token_data, 'user': user_data}},
893 user=self._rhodecode_user, )
841 user=self._rhodecode_user, )
894 Session().commit()
842 Session().commit()
895
843
896 h.flash(_("Auth token successfully created"), category='success')
844 h.flash(_("Auth token successfully created"), category='success')
897 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
845 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
898
846
899 @LoginRequired()
847 @LoginRequired()
900 @HasPermissionAllDecorator('hg.admin')
848 @HasPermissionAllDecorator('hg.admin')
901 @CSRFRequired()
849 @CSRFRequired()
902 @view_config(
903 route_name='edit_user_auth_tokens_delete', request_method='POST')
904 def auth_tokens_delete(self):
850 def auth_tokens_delete(self):
905 _ = self.request.translate
851 _ = self.request.translate
906 c = self.load_default_context()
852 c = self.load_default_context()
907
853
908 user_id = self.db_user_id
854 user_id = self.db_user_id
909 c.user = self.db_user
855 c.user = self.db_user
910
856
911 user_data = c.user.get_api_data()
857 user_data = c.user.get_api_data()
912
858
913 del_auth_token = self.request.POST.get('del_auth_token')
859 del_auth_token = self.request.POST.get('del_auth_token')
914
860
915 if del_auth_token:
861 if del_auth_token:
916 token = UserApiKeys.get_or_404(del_auth_token)
862 token = UserApiKeys.get_or_404(del_auth_token)
917 token_data = token.get_api_data()
863 token_data = token.get_api_data()
918
864
919 AuthTokenModel().delete(del_auth_token, c.user.user_id)
865 AuthTokenModel().delete(del_auth_token, c.user.user_id)
920 audit_logger.store_web(
866 audit_logger.store_web(
921 'user.edit.token.delete', action_data={
867 'user.edit.token.delete', action_data={
922 'data': {'token': token_data, 'user': user_data}},
868 'data': {'token': token_data, 'user': user_data}},
923 user=self._rhodecode_user,)
869 user=self._rhodecode_user,)
924 Session().commit()
870 Session().commit()
925 h.flash(_("Auth token successfully deleted"), category='success')
871 h.flash(_("Auth token successfully deleted"), category='success')
926
872
927 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
873 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
928
874
929 @LoginRequired()
875 @LoginRequired()
930 @HasPermissionAllDecorator('hg.admin')
876 @HasPermissionAllDecorator('hg.admin')
931 @view_config(
932 route_name='edit_user_ssh_keys', request_method='GET',
933 renderer='rhodecode:templates/admin/users/user_edit.mako')
934 def ssh_keys(self):
877 def ssh_keys(self):
935 _ = self.request.translate
878 _ = self.request.translate
936 c = self.load_default_context()
879 c = self.load_default_context()
937 c.user = self.db_user
880 c.user = self.db_user
938
881
939 c.active = 'ssh_keys'
882 c.active = 'ssh_keys'
940 c.default_key = self.request.GET.get('default_key')
883 c.default_key = self.request.GET.get('default_key')
941 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
884 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
942 return self._get_template_context(c)
885 return self._get_template_context(c)
943
886
944 @LoginRequired()
887 @LoginRequired()
945 @HasPermissionAllDecorator('hg.admin')
888 @HasPermissionAllDecorator('hg.admin')
946 @view_config(
947 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
948 renderer='rhodecode:templates/admin/users/user_edit.mako')
949 def ssh_keys_generate_keypair(self):
889 def ssh_keys_generate_keypair(self):
950 _ = self.request.translate
890 _ = self.request.translate
951 c = self.load_default_context()
891 c = self.load_default_context()
952
892
953 c.user = self.db_user
893 c.user = self.db_user
954
894
955 c.active = 'ssh_keys_generate'
895 c.active = 'ssh_keys_generate'
956 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
896 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
957 private_format = self.request.GET.get('private_format') \
897 private_format = self.request.GET.get('private_format') \
958 or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT
898 or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT
959 c.private, c.public = SshKeyModel().generate_keypair(
899 c.private, c.public = SshKeyModel().generate_keypair(
960 comment=comment, private_format=private_format)
900 comment=comment, private_format=private_format)
961
901
962 return self._get_template_context(c)
902 return self._get_template_context(c)
963
903
964 @LoginRequired()
904 @LoginRequired()
965 @HasPermissionAllDecorator('hg.admin')
905 @HasPermissionAllDecorator('hg.admin')
966 @CSRFRequired()
906 @CSRFRequired()
967 @view_config(
968 route_name='edit_user_ssh_keys_add', request_method='POST')
969 def ssh_keys_add(self):
907 def ssh_keys_add(self):
970 _ = self.request.translate
908 _ = self.request.translate
971 c = self.load_default_context()
909 c = self.load_default_context()
972
910
973 user_id = self.db_user_id
911 user_id = self.db_user_id
974 c.user = self.db_user
912 c.user = self.db_user
975
913
976 user_data = c.user.get_api_data()
914 user_data = c.user.get_api_data()
977 key_data = self.request.POST.get('key_data')
915 key_data = self.request.POST.get('key_data')
978 description = self.request.POST.get('description')
916 description = self.request.POST.get('description')
979
917
980 fingerprint = 'unknown'
918 fingerprint = 'unknown'
981 try:
919 try:
982 if not key_data:
920 if not key_data:
983 raise ValueError('Please add a valid public key')
921 raise ValueError('Please add a valid public key')
984
922
985 key = SshKeyModel().parse_key(key_data.strip())
923 key = SshKeyModel().parse_key(key_data.strip())
986 fingerprint = key.hash_md5()
924 fingerprint = key.hash_md5()
987
925
988 ssh_key = SshKeyModel().create(
926 ssh_key = SshKeyModel().create(
989 c.user.user_id, fingerprint, key.keydata, description)
927 c.user.user_id, fingerprint, key.keydata, description)
990 ssh_key_data = ssh_key.get_api_data()
928 ssh_key_data = ssh_key.get_api_data()
991
929
992 audit_logger.store_web(
930 audit_logger.store_web(
993 'user.edit.ssh_key.add', action_data={
931 'user.edit.ssh_key.add', action_data={
994 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
932 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
995 user=self._rhodecode_user, )
933 user=self._rhodecode_user, )
996 Session().commit()
934 Session().commit()
997
935
998 # Trigger an event on change of keys.
936 # Trigger an event on change of keys.
999 trigger(SshKeyFileChangeEvent(), self.request.registry)
937 trigger(SshKeyFileChangeEvent(), self.request.registry)
1000
938
1001 h.flash(_("Ssh Key successfully created"), category='success')
939 h.flash(_("Ssh Key successfully created"), category='success')
1002
940
1003 except IntegrityError:
941 except IntegrityError:
1004 log.exception("Exception during ssh key saving")
942 log.exception("Exception during ssh key saving")
1005 err = 'Such key with fingerprint `{}` already exists, ' \
943 err = 'Such key with fingerprint `{}` already exists, ' \
1006 'please use a different one'.format(fingerprint)
944 'please use a different one'.format(fingerprint)
1007 h.flash(_('An error occurred during ssh key saving: {}').format(err),
945 h.flash(_('An error occurred during ssh key saving: {}').format(err),
1008 category='error')
946 category='error')
1009 except Exception as e:
947 except Exception as e:
1010 log.exception("Exception during ssh key saving")
948 log.exception("Exception during ssh key saving")
1011 h.flash(_('An error occurred during ssh key saving: {}').format(e),
949 h.flash(_('An error occurred during ssh key saving: {}').format(e),
1012 category='error')
950 category='error')
1013
951
1014 return HTTPFound(
952 return HTTPFound(
1015 h.route_path('edit_user_ssh_keys', user_id=user_id))
953 h.route_path('edit_user_ssh_keys', user_id=user_id))
1016
954
1017 @LoginRequired()
955 @LoginRequired()
1018 @HasPermissionAllDecorator('hg.admin')
956 @HasPermissionAllDecorator('hg.admin')
1019 @CSRFRequired()
957 @CSRFRequired()
1020 @view_config(
1021 route_name='edit_user_ssh_keys_delete', request_method='POST')
1022 def ssh_keys_delete(self):
958 def ssh_keys_delete(self):
1023 _ = self.request.translate
959 _ = self.request.translate
1024 c = self.load_default_context()
960 c = self.load_default_context()
1025
961
1026 user_id = self.db_user_id
962 user_id = self.db_user_id
1027 c.user = self.db_user
963 c.user = self.db_user
1028
964
1029 user_data = c.user.get_api_data()
965 user_data = c.user.get_api_data()
1030
966
1031 del_ssh_key = self.request.POST.get('del_ssh_key')
967 del_ssh_key = self.request.POST.get('del_ssh_key')
1032
968
1033 if del_ssh_key:
969 if del_ssh_key:
1034 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
970 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
1035 ssh_key_data = ssh_key.get_api_data()
971 ssh_key_data = ssh_key.get_api_data()
1036
972
1037 SshKeyModel().delete(del_ssh_key, c.user.user_id)
973 SshKeyModel().delete(del_ssh_key, c.user.user_id)
1038 audit_logger.store_web(
974 audit_logger.store_web(
1039 'user.edit.ssh_key.delete', action_data={
975 'user.edit.ssh_key.delete', action_data={
1040 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
976 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
1041 user=self._rhodecode_user,)
977 user=self._rhodecode_user,)
1042 Session().commit()
978 Session().commit()
1043 # Trigger an event on change of keys.
979 # Trigger an event on change of keys.
1044 trigger(SshKeyFileChangeEvent(), self.request.registry)
980 trigger(SshKeyFileChangeEvent(), self.request.registry)
1045 h.flash(_("Ssh key successfully deleted"), category='success')
981 h.flash(_("Ssh key successfully deleted"), category='success')
1046
982
1047 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
983 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
1048
984
1049 @LoginRequired()
985 @LoginRequired()
1050 @HasPermissionAllDecorator('hg.admin')
986 @HasPermissionAllDecorator('hg.admin')
1051 @view_config(
1052 route_name='edit_user_emails', request_method='GET',
1053 renderer='rhodecode:templates/admin/users/user_edit.mako')
1054 def emails(self):
987 def emails(self):
1055 _ = self.request.translate
988 _ = self.request.translate
1056 c = self.load_default_context()
989 c = self.load_default_context()
1057 c.user = self.db_user
990 c.user = self.db_user
1058
991
1059 c.active = 'emails'
992 c.active = 'emails'
1060 c.user_email_map = UserEmailMap.query() \
993 c.user_email_map = UserEmailMap.query() \
1061 .filter(UserEmailMap.user == c.user).all()
994 .filter(UserEmailMap.user == c.user).all()
1062
995
1063 return self._get_template_context(c)
996 return self._get_template_context(c)
1064
997
1065 @LoginRequired()
998 @LoginRequired()
1066 @HasPermissionAllDecorator('hg.admin')
999 @HasPermissionAllDecorator('hg.admin')
1067 @CSRFRequired()
1000 @CSRFRequired()
1068 @view_config(
1069 route_name='edit_user_emails_add', request_method='POST')
1070 def emails_add(self):
1001 def emails_add(self):
1071 _ = self.request.translate
1002 _ = self.request.translate
1072 c = self.load_default_context()
1003 c = self.load_default_context()
1073
1004
1074 user_id = self.db_user_id
1005 user_id = self.db_user_id
1075 c.user = self.db_user
1006 c.user = self.db_user
1076
1007
1077 email = self.request.POST.get('new_email')
1008 email = self.request.POST.get('new_email')
1078 user_data = c.user.get_api_data()
1009 user_data = c.user.get_api_data()
1079 try:
1010 try:
1080
1011
1081 form = UserExtraEmailForm(self.request.translate)()
1012 form = UserExtraEmailForm(self.request.translate)()
1082 data = form.to_python({'email': email})
1013 data = form.to_python({'email': email})
1083 email = data['email']
1014 email = data['email']
1084
1015
1085 UserModel().add_extra_email(c.user.user_id, email)
1016 UserModel().add_extra_email(c.user.user_id, email)
1086 audit_logger.store_web(
1017 audit_logger.store_web(
1087 'user.edit.email.add',
1018 'user.edit.email.add',
1088 action_data={'email': email, 'user': user_data},
1019 action_data={'email': email, 'user': user_data},
1089 user=self._rhodecode_user)
1020 user=self._rhodecode_user)
1090 Session().commit()
1021 Session().commit()
1091 h.flash(_("Added new email address `%s` for user account") % email,
1022 h.flash(_("Added new email address `%s` for user account") % email,
1092 category='success')
1023 category='success')
1093 except formencode.Invalid as error:
1024 except formencode.Invalid as error:
1094 h.flash(h.escape(error.error_dict['email']), category='error')
1025 h.flash(h.escape(error.error_dict['email']), category='error')
1095 except IntegrityError:
1026 except IntegrityError:
1096 log.warning("Email %s already exists", email)
1027 log.warning("Email %s already exists", email)
1097 h.flash(_('Email `{}` is already registered for another user.').format(email),
1028 h.flash(_('Email `{}` is already registered for another user.').format(email),
1098 category='error')
1029 category='error')
1099 except Exception:
1030 except Exception:
1100 log.exception("Exception during email saving")
1031 log.exception("Exception during email saving")
1101 h.flash(_('An error occurred during email saving'),
1032 h.flash(_('An error occurred during email saving'),
1102 category='error')
1033 category='error')
1103 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1034 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1104
1035
1105 @LoginRequired()
1036 @LoginRequired()
1106 @HasPermissionAllDecorator('hg.admin')
1037 @HasPermissionAllDecorator('hg.admin')
1107 @CSRFRequired()
1038 @CSRFRequired()
1108 @view_config(
1109 route_name='edit_user_emails_delete', request_method='POST')
1110 def emails_delete(self):
1039 def emails_delete(self):
1111 _ = self.request.translate
1040 _ = self.request.translate
1112 c = self.load_default_context()
1041 c = self.load_default_context()
1113
1042
1114 user_id = self.db_user_id
1043 user_id = self.db_user_id
1115 c.user = self.db_user
1044 c.user = self.db_user
1116
1045
1117 email_id = self.request.POST.get('del_email_id')
1046 email_id = self.request.POST.get('del_email_id')
1118 user_model = UserModel()
1047 user_model = UserModel()
1119
1048
1120 email = UserEmailMap.query().get(email_id).email
1049 email = UserEmailMap.query().get(email_id).email
1121 user_data = c.user.get_api_data()
1050 user_data = c.user.get_api_data()
1122 user_model.delete_extra_email(c.user.user_id, email_id)
1051 user_model.delete_extra_email(c.user.user_id, email_id)
1123 audit_logger.store_web(
1052 audit_logger.store_web(
1124 'user.edit.email.delete',
1053 'user.edit.email.delete',
1125 action_data={'email': email, 'user': user_data},
1054 action_data={'email': email, 'user': user_data},
1126 user=self._rhodecode_user)
1055 user=self._rhodecode_user)
1127 Session().commit()
1056 Session().commit()
1128 h.flash(_("Removed email address from user account"),
1057 h.flash(_("Removed email address from user account"),
1129 category='success')
1058 category='success')
1130 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1059 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1131
1060
1132 @LoginRequired()
1061 @LoginRequired()
1133 @HasPermissionAllDecorator('hg.admin')
1062 @HasPermissionAllDecorator('hg.admin')
1134 @view_config(
1135 route_name='edit_user_ips', request_method='GET',
1136 renderer='rhodecode:templates/admin/users/user_edit.mako')
1137 def ips(self):
1063 def ips(self):
1138 _ = self.request.translate
1064 _ = self.request.translate
1139 c = self.load_default_context()
1065 c = self.load_default_context()
1140 c.user = self.db_user
1066 c.user = self.db_user
1141
1067
1142 c.active = 'ips'
1068 c.active = 'ips'
1143 c.user_ip_map = UserIpMap.query() \
1069 c.user_ip_map = UserIpMap.query() \
1144 .filter(UserIpMap.user == c.user).all()
1070 .filter(UserIpMap.user == c.user).all()
1145
1071
1146 c.inherit_default_ips = c.user.inherit_default_permissions
1072 c.inherit_default_ips = c.user.inherit_default_permissions
1147 c.default_user_ip_map = UserIpMap.query() \
1073 c.default_user_ip_map = UserIpMap.query() \
1148 .filter(UserIpMap.user == User.get_default_user()).all()
1074 .filter(UserIpMap.user == User.get_default_user()).all()
1149
1075
1150 return self._get_template_context(c)
1076 return self._get_template_context(c)
1151
1077
1152 @LoginRequired()
1078 @LoginRequired()
1153 @HasPermissionAllDecorator('hg.admin')
1079 @HasPermissionAllDecorator('hg.admin')
1154 @CSRFRequired()
1080 @CSRFRequired()
1155 @view_config(
1156 route_name='edit_user_ips_add', request_method='POST')
1157 # NOTE(marcink): this view is allowed for default users, as we can
1081 # NOTE(marcink): this view is allowed for default users, as we can
1158 # edit their IP white list
1082 # edit their IP white list
1159 def ips_add(self):
1083 def ips_add(self):
1160 _ = self.request.translate
1084 _ = self.request.translate
1161 c = self.load_default_context()
1085 c = self.load_default_context()
1162
1086
1163 user_id = self.db_user_id
1087 user_id = self.db_user_id
1164 c.user = self.db_user
1088 c.user = self.db_user
1165
1089
1166 user_model = UserModel()
1090 user_model = UserModel()
1167 desc = self.request.POST.get('description')
1091 desc = self.request.POST.get('description')
1168 try:
1092 try:
1169 ip_list = user_model.parse_ip_range(
1093 ip_list = user_model.parse_ip_range(
1170 self.request.POST.get('new_ip'))
1094 self.request.POST.get('new_ip'))
1171 except Exception as e:
1095 except Exception as e:
1172 ip_list = []
1096 ip_list = []
1173 log.exception("Exception during ip saving")
1097 log.exception("Exception during ip saving")
1174 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1098 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1175 category='error')
1099 category='error')
1176 added = []
1100 added = []
1177 user_data = c.user.get_api_data()
1101 user_data = c.user.get_api_data()
1178 for ip in ip_list:
1102 for ip in ip_list:
1179 try:
1103 try:
1180 form = UserExtraIpForm(self.request.translate)()
1104 form = UserExtraIpForm(self.request.translate)()
1181 data = form.to_python({'ip': ip})
1105 data = form.to_python({'ip': ip})
1182 ip = data['ip']
1106 ip = data['ip']
1183
1107
1184 user_model.add_extra_ip(c.user.user_id, ip, desc)
1108 user_model.add_extra_ip(c.user.user_id, ip, desc)
1185 audit_logger.store_web(
1109 audit_logger.store_web(
1186 'user.edit.ip.add',
1110 'user.edit.ip.add',
1187 action_data={'ip': ip, 'user': user_data},
1111 action_data={'ip': ip, 'user': user_data},
1188 user=self._rhodecode_user)
1112 user=self._rhodecode_user)
1189 Session().commit()
1113 Session().commit()
1190 added.append(ip)
1114 added.append(ip)
1191 except formencode.Invalid as error:
1115 except formencode.Invalid as error:
1192 msg = error.error_dict['ip']
1116 msg = error.error_dict['ip']
1193 h.flash(msg, category='error')
1117 h.flash(msg, category='error')
1194 except Exception:
1118 except Exception:
1195 log.exception("Exception during ip saving")
1119 log.exception("Exception during ip saving")
1196 h.flash(_('An error occurred during ip saving'),
1120 h.flash(_('An error occurred during ip saving'),
1197 category='error')
1121 category='error')
1198 if added:
1122 if added:
1199 h.flash(
1123 h.flash(
1200 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1124 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1201 category='success')
1125 category='success')
1202 if 'default_user' in self.request.POST:
1126 if 'default_user' in self.request.POST:
1203 # case for editing global IP list we do it for 'DEFAULT' user
1127 # case for editing global IP list we do it for 'DEFAULT' user
1204 raise HTTPFound(h.route_path('admin_permissions_ips'))
1128 raise HTTPFound(h.route_path('admin_permissions_ips'))
1205 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1129 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1206
1130
1207 @LoginRequired()
1131 @LoginRequired()
1208 @HasPermissionAllDecorator('hg.admin')
1132 @HasPermissionAllDecorator('hg.admin')
1209 @CSRFRequired()
1133 @CSRFRequired()
1210 @view_config(
1211 route_name='edit_user_ips_delete', request_method='POST')
1212 # NOTE(marcink): this view is allowed for default users, as we can
1134 # NOTE(marcink): this view is allowed for default users, as we can
1213 # edit their IP white list
1135 # edit their IP white list
1214 def ips_delete(self):
1136 def ips_delete(self):
1215 _ = self.request.translate
1137 _ = self.request.translate
1216 c = self.load_default_context()
1138 c = self.load_default_context()
1217
1139
1218 user_id = self.db_user_id
1140 user_id = self.db_user_id
1219 c.user = self.db_user
1141 c.user = self.db_user
1220
1142
1221 ip_id = self.request.POST.get('del_ip_id')
1143 ip_id = self.request.POST.get('del_ip_id')
1222 user_model = UserModel()
1144 user_model = UserModel()
1223 user_data = c.user.get_api_data()
1145 user_data = c.user.get_api_data()
1224 ip = UserIpMap.query().get(ip_id).ip_addr
1146 ip = UserIpMap.query().get(ip_id).ip_addr
1225 user_model.delete_extra_ip(c.user.user_id, ip_id)
1147 user_model.delete_extra_ip(c.user.user_id, ip_id)
1226 audit_logger.store_web(
1148 audit_logger.store_web(
1227 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1149 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1228 user=self._rhodecode_user)
1150 user=self._rhodecode_user)
1229 Session().commit()
1151 Session().commit()
1230 h.flash(_("Removed ip address from user whitelist"), category='success')
1152 h.flash(_("Removed ip address from user whitelist"), category='success')
1231
1153
1232 if 'default_user' in self.request.POST:
1154 if 'default_user' in self.request.POST:
1233 # case for editing global IP list we do it for 'DEFAULT' user
1155 # case for editing global IP list we do it for 'DEFAULT' user
1234 raise HTTPFound(h.route_path('admin_permissions_ips'))
1156 raise HTTPFound(h.route_path('admin_permissions_ips'))
1235 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1157 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1236
1158
1237 @LoginRequired()
1159 @LoginRequired()
1238 @HasPermissionAllDecorator('hg.admin')
1160 @HasPermissionAllDecorator('hg.admin')
1239 @view_config(
1240 route_name='edit_user_groups_management', request_method='GET',
1241 renderer='rhodecode:templates/admin/users/user_edit.mako')
1242 def groups_management(self):
1161 def groups_management(self):
1243 c = self.load_default_context()
1162 c = self.load_default_context()
1244 c.user = self.db_user
1163 c.user = self.db_user
1245 c.data = c.user.group_member
1164 c.data = c.user.group_member
1246
1165
1247 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1166 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1248 for group in c.user.group_member]
1167 for group in c.user.group_member]
1249 c.groups = json.dumps(groups)
1168 c.groups = json.dumps(groups)
1250 c.active = 'groups'
1169 c.active = 'groups'
1251
1170
1252 return self._get_template_context(c)
1171 return self._get_template_context(c)
1253
1172
1254 @LoginRequired()
1173 @LoginRequired()
1255 @HasPermissionAllDecorator('hg.admin')
1174 @HasPermissionAllDecorator('hg.admin')
1256 @CSRFRequired()
1175 @CSRFRequired()
1257 @view_config(
1258 route_name='edit_user_groups_management_updates', request_method='POST')
1259 def groups_management_updates(self):
1176 def groups_management_updates(self):
1260 _ = self.request.translate
1177 _ = self.request.translate
1261 c = self.load_default_context()
1178 c = self.load_default_context()
1262
1179
1263 user_id = self.db_user_id
1180 user_id = self.db_user_id
1264 c.user = self.db_user
1181 c.user = self.db_user
1265
1182
1266 user_groups = set(self.request.POST.getall('users_group_id'))
1183 user_groups = set(self.request.POST.getall('users_group_id'))
1267 user_groups_objects = []
1184 user_groups_objects = []
1268
1185
1269 for ugid in user_groups:
1186 for ugid in user_groups:
1270 user_groups_objects.append(
1187 user_groups_objects.append(
1271 UserGroupModel().get_group(safe_int(ugid)))
1188 UserGroupModel().get_group(safe_int(ugid)))
1272 user_group_model = UserGroupModel()
1189 user_group_model = UserGroupModel()
1273 added_to_groups, removed_from_groups = \
1190 added_to_groups, removed_from_groups = \
1274 user_group_model.change_groups(c.user, user_groups_objects)
1191 user_group_model.change_groups(c.user, user_groups_objects)
1275
1192
1276 user_data = c.user.get_api_data()
1193 user_data = c.user.get_api_data()
1277 for user_group_id in added_to_groups:
1194 for user_group_id in added_to_groups:
1278 user_group = UserGroup.get(user_group_id)
1195 user_group = UserGroup.get(user_group_id)
1279 old_values = user_group.get_api_data()
1196 old_values = user_group.get_api_data()
1280 audit_logger.store_web(
1197 audit_logger.store_web(
1281 'user_group.edit.member.add',
1198 'user_group.edit.member.add',
1282 action_data={'user': user_data, 'old_data': old_values},
1199 action_data={'user': user_data, 'old_data': old_values},
1283 user=self._rhodecode_user)
1200 user=self._rhodecode_user)
1284
1201
1285 for user_group_id in removed_from_groups:
1202 for user_group_id in removed_from_groups:
1286 user_group = UserGroup.get(user_group_id)
1203 user_group = UserGroup.get(user_group_id)
1287 old_values = user_group.get_api_data()
1204 old_values = user_group.get_api_data()
1288 audit_logger.store_web(
1205 audit_logger.store_web(
1289 'user_group.edit.member.delete',
1206 'user_group.edit.member.delete',
1290 action_data={'user': user_data, 'old_data': old_values},
1207 action_data={'user': user_data, 'old_data': old_values},
1291 user=self._rhodecode_user)
1208 user=self._rhodecode_user)
1292
1209
1293 Session().commit()
1210 Session().commit()
1294 c.active = 'user_groups_management'
1211 c.active = 'user_groups_management'
1295 h.flash(_("Groups successfully changed"), category='success')
1212 h.flash(_("Groups successfully changed"), category='success')
1296
1213
1297 return HTTPFound(h.route_path(
1214 return HTTPFound(h.route_path(
1298 'edit_user_groups_management', user_id=user_id))
1215 'edit_user_groups_management', user_id=user_id))
1299
1216
1300 @LoginRequired()
1217 @LoginRequired()
1301 @HasPermissionAllDecorator('hg.admin')
1218 @HasPermissionAllDecorator('hg.admin')
1302 @view_config(
1303 route_name='edit_user_audit_logs', request_method='GET',
1304 renderer='rhodecode:templates/admin/users/user_edit.mako')
1305 def user_audit_logs(self):
1219 def user_audit_logs(self):
1306 _ = self.request.translate
1220 _ = self.request.translate
1307 c = self.load_default_context()
1221 c = self.load_default_context()
1308 c.user = self.db_user
1222 c.user = self.db_user
1309
1223
1310 c.active = 'audit'
1224 c.active = 'audit'
1311
1225
1312 p = safe_int(self.request.GET.get('page', 1), 1)
1226 p = safe_int(self.request.GET.get('page', 1), 1)
1313
1227
1314 filter_term = self.request.GET.get('filter')
1228 filter_term = self.request.GET.get('filter')
1315 user_log = UserModel().get_user_log(c.user, filter_term)
1229 user_log = UserModel().get_user_log(c.user, filter_term)
1316
1230
1317 def url_generator(page_num):
1231 def url_generator(page_num):
1318 query_params = {
1232 query_params = {
1319 'page': page_num
1233 'page': page_num
1320 }
1234 }
1321 if filter_term:
1235 if filter_term:
1322 query_params['filter'] = filter_term
1236 query_params['filter'] = filter_term
1323 return self.request.current_route_path(_query=query_params)
1237 return self.request.current_route_path(_query=query_params)
1324
1238
1325 c.audit_logs = SqlPage(
1239 c.audit_logs = SqlPage(
1326 user_log, page=p, items_per_page=10, url_maker=url_generator)
1240 user_log, page=p, items_per_page=10, url_maker=url_generator)
1327 c.filter_term = filter_term
1241 c.filter_term = filter_term
1328 return self._get_template_context(c)
1242 return self._get_template_context(c)
1329
1243
1330 @LoginRequired()
1244 @LoginRequired()
1331 @HasPermissionAllDecorator('hg.admin')
1245 @HasPermissionAllDecorator('hg.admin')
1332 @view_config(
1333 route_name='edit_user_audit_logs_download', request_method='GET',
1334 renderer='string')
1335 def user_audit_logs_download(self):
1246 def user_audit_logs_download(self):
1336 _ = self.request.translate
1247 _ = self.request.translate
1337 c = self.load_default_context()
1248 c = self.load_default_context()
1338 c.user = self.db_user
1249 c.user = self.db_user
1339
1250
1340 user_log = UserModel().get_user_log(c.user, filter_term=None)
1251 user_log = UserModel().get_user_log(c.user, filter_term=None)
1341
1252
1342 audit_log_data = {}
1253 audit_log_data = {}
1343 for entry in user_log:
1254 for entry in user_log:
1344 audit_log_data[entry.user_log_id] = entry.get_dict()
1255 audit_log_data[entry.user_log_id] = entry.get_dict()
1345
1256
1346 response = Response(json.dumps(audit_log_data, indent=4))
1257 response = Response(json.dumps(audit_log_data, indent=4))
1347 response.content_disposition = str(
1258 response.content_disposition = str(
1348 'attachment; filename=%s' % 'user_{}_audit_logs.json'.format(c.user.user_id))
1259 'attachment; filename=%s' % 'user_{}_audit_logs.json'.format(c.user.user_id))
1349 response.content_type = 'application/json'
1260 response.content_type = 'application/json'
1350
1261
1351 return response
1262 return response
1352
1263
1353 @LoginRequired()
1264 @LoginRequired()
1354 @HasPermissionAllDecorator('hg.admin')
1265 @HasPermissionAllDecorator('hg.admin')
1355 @view_config(
1356 route_name='edit_user_perms_summary', request_method='GET',
1357 renderer='rhodecode:templates/admin/users/user_edit.mako')
1358 def user_perms_summary(self):
1266 def user_perms_summary(self):
1359 _ = self.request.translate
1267 _ = self.request.translate
1360 c = self.load_default_context()
1268 c = self.load_default_context()
1361 c.user = self.db_user
1269 c.user = self.db_user
1362
1270
1363 c.active = 'perms_summary'
1271 c.active = 'perms_summary'
1364 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1272 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1365
1273
1366 return self._get_template_context(c)
1274 return self._get_template_context(c)
1367
1275
1368 @LoginRequired()
1276 @LoginRequired()
1369 @HasPermissionAllDecorator('hg.admin')
1277 @HasPermissionAllDecorator('hg.admin')
1370 @view_config(
1371 route_name='edit_user_perms_summary_json', request_method='GET',
1372 renderer='json_ext')
1373 def user_perms_summary_json(self):
1278 def user_perms_summary_json(self):
1374 self.load_default_context()
1279 self.load_default_context()
1375 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1280 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1376
1281
1377 return perm_user.permissions
1282 return perm_user.permissions
1378
1283
1379 @LoginRequired()
1284 @LoginRequired()
1380 @HasPermissionAllDecorator('hg.admin')
1285 @HasPermissionAllDecorator('hg.admin')
1381 @view_config(
1382 route_name='edit_user_caches', request_method='GET',
1383 renderer='rhodecode:templates/admin/users/user_edit.mako')
1384 def user_caches(self):
1286 def user_caches(self):
1385 _ = self.request.translate
1287 _ = self.request.translate
1386 c = self.load_default_context()
1288 c = self.load_default_context()
1387 c.user = self.db_user
1289 c.user = self.db_user
1388
1290
1389 c.active = 'caches'
1291 c.active = 'caches'
1390 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1292 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1391
1293
1392 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1294 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1393 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1295 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1394 c.backend = c.region.backend
1296 c.backend = c.region.backend
1395 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1297 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1396
1298
1397 return self._get_template_context(c)
1299 return self._get_template_context(c)
1398
1300
1399 @LoginRequired()
1301 @LoginRequired()
1400 @HasPermissionAllDecorator('hg.admin')
1302 @HasPermissionAllDecorator('hg.admin')
1401 @CSRFRequired()
1303 @CSRFRequired()
1402 @view_config(
1403 route_name='edit_user_caches_update', request_method='POST')
1404 def user_caches_update(self):
1304 def user_caches_update(self):
1405 _ = self.request.translate
1305 _ = self.request.translate
1406 c = self.load_default_context()
1306 c = self.load_default_context()
1407 c.user = self.db_user
1307 c.user = self.db_user
1408
1308
1409 c.active = 'caches'
1309 c.active = 'caches'
1410 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1310 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1411
1311
1412 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1312 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1413 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1313 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1414
1314
1415 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1315 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1416
1316
1417 return HTTPFound(h.route_path(
1317 return HTTPFound(h.route_path(
1418 'edit_user_caches', user_id=c.user.user_id))
1318 'edit_user_caches', user_id=c.user.user_id))
@@ -1,96 +1,106 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22
22
23 from pyramid.events import ApplicationCreated
23 from pyramid.events import ApplicationCreated
24 from pyramid.settings import asbool
24 from pyramid.settings import asbool
25
25
26 from rhodecode.apps._base import ADMIN_PREFIX
26 from rhodecode.apps._base import ADMIN_PREFIX
27 from rhodecode.lib.ext_json import json
27 from rhodecode.lib.ext_json import json
28
28
29
29
30 def url_gen(request):
30 def url_gen(request):
31 registry = request.registry
31 registry = request.registry
32 longpoll_url = registry.settings.get('channelstream.longpoll_url', '')
32 longpoll_url = registry.settings.get('channelstream.longpoll_url', '')
33 ws_url = registry.settings.get('channelstream.ws_url', '')
33 ws_url = registry.settings.get('channelstream.ws_url', '')
34 proxy_url = request.route_url('channelstream_proxy')
34 proxy_url = request.route_url('channelstream_proxy')
35 urls = {
35 urls = {
36 'connect': request.route_path('channelstream_connect'),
36 'connect': request.route_path('channelstream_connect'),
37 'subscribe': request.route_path('channelstream_subscribe'),
37 'subscribe': request.route_path('channelstream_subscribe'),
38 'longpoll': longpoll_url or proxy_url,
38 'longpoll': longpoll_url or proxy_url,
39 'ws': ws_url or proxy_url.replace('http', 'ws')
39 'ws': ws_url or proxy_url.replace('http', 'ws')
40 }
40 }
41 return json.dumps(urls)
41 return json.dumps(urls)
42
42
43
43
44 PLUGIN_DEFINITION = {
44 PLUGIN_DEFINITION = {
45 'name': 'channelstream',
45 'name': 'channelstream',
46 'config': {
46 'config': {
47 'javascript': [],
47 'javascript': [],
48 'css': [],
48 'css': [],
49 'template_hooks': {
49 'template_hooks': {
50 'plugin_init_template': 'rhodecode:templates/channelstream/plugin_init.mako'
50 'plugin_init_template': 'rhodecode:templates/channelstream/plugin_init.mako'
51 },
51 },
52 'url_gen': url_gen,
52 'url_gen': url_gen,
53 'static': None,
53 'static': None,
54 'enabled': False,
54 'enabled': False,
55 'server': '',
55 'server': '',
56 'secret': ''
56 'secret': ''
57 }
57 }
58 }
58 }
59
59
60
60
61 def maybe_create_history_store(event):
61 def maybe_create_history_store(event):
62 # create plugin history location
62 # create plugin history location
63 settings = event.app.registry.settings
63 settings = event.app.registry.settings
64 history_dir = settings.get('channelstream.history.location', '')
64 history_dir = settings.get('channelstream.history.location', '')
65 if history_dir and not os.path.exists(history_dir):
65 if history_dir and not os.path.exists(history_dir):
66 os.makedirs(history_dir, 0o750)
66 os.makedirs(history_dir, 0o750)
67
67
68
68
69 def includeme(config):
69 def includeme(config):
70 from rhodecode.apps.channelstream.views import ChannelstreamView
71
70 settings = config.registry.settings
72 settings = config.registry.settings
71 PLUGIN_DEFINITION['config']['enabled'] = asbool(
73 PLUGIN_DEFINITION['config']['enabled'] = asbool(
72 settings.get('channelstream.enabled'))
74 settings.get('channelstream.enabled'))
73 PLUGIN_DEFINITION['config']['server'] = settings.get(
75 PLUGIN_DEFINITION['config']['server'] = settings.get(
74 'channelstream.server', '')
76 'channelstream.server', '')
75 PLUGIN_DEFINITION['config']['secret'] = settings.get(
77 PLUGIN_DEFINITION['config']['secret'] = settings.get(
76 'channelstream.secret', '')
78 'channelstream.secret', '')
77 PLUGIN_DEFINITION['config']['history.location'] = settings.get(
79 PLUGIN_DEFINITION['config']['history.location'] = settings.get(
78 'channelstream.history.location', '')
80 'channelstream.history.location', '')
79 config.register_rhodecode_plugin(
81 config.register_rhodecode_plugin(
80 PLUGIN_DEFINITION['name'],
82 PLUGIN_DEFINITION['name'],
81 PLUGIN_DEFINITION['config']
83 PLUGIN_DEFINITION['config']
82 )
84 )
83 config.add_subscriber(maybe_create_history_store, ApplicationCreated)
85 config.add_subscriber(maybe_create_history_store, ApplicationCreated)
84
86
85 config.add_route(
87 config.add_route(
86 name='channelstream_connect',
88 name='channelstream_connect',
87 pattern=ADMIN_PREFIX + '/channelstream/connect')
89 pattern=ADMIN_PREFIX + '/channelstream/connect')
90 config.add_view(
91 ChannelstreamView,
92 attr='channelstream_connect',
93 route_name='channelstream_connect', renderer='json_ext')
94
88 config.add_route(
95 config.add_route(
89 name='channelstream_subscribe',
96 name='channelstream_subscribe',
90 pattern=ADMIN_PREFIX + '/channelstream/subscribe')
97 pattern=ADMIN_PREFIX + '/channelstream/subscribe')
98 config.add_view(
99 ChannelstreamView,
100 attr='channelstream_subscribe',
101 route_name='channelstream_subscribe', renderer='json_ext')
102
91 config.add_route(
103 config.add_route(
92 name='channelstream_proxy',
104 name='channelstream_proxy',
93 pattern=settings.get('channelstream.proxy_path') or '/_channelstream')
105 pattern=settings.get('channelstream.proxy_path') or '/_channelstream')
94
106
95 # Scan module for configuration decorators.
96 config.scan('.views', ignore='.tests')
@@ -1,187 +1,185 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import uuid
22 import uuid
23
23
24 from pyramid.view import view_config
24
25 from pyramid.httpexceptions import HTTPBadRequest, HTTPForbidden, HTTPBadGateway
25 from pyramid.httpexceptions import HTTPBadRequest, HTTPForbidden, HTTPBadGateway
26
26
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib.channelstream import (
28 from rhodecode.lib.channelstream import (
29 channelstream_request, get_channelstream_server_url,
29 channelstream_request, get_channelstream_server_url,
30 ChannelstreamConnectionException,
30 ChannelstreamConnectionException,
31 ChannelstreamPermissionException,
31 ChannelstreamPermissionException,
32 check_channel_permissions,
32 check_channel_permissions,
33 get_connection_validators,
33 get_connection_validators,
34 get_user_data,
34 get_user_data,
35 parse_channels_info,
35 parse_channels_info,
36 update_history_from_logs,
36 update_history_from_logs,
37 USER_STATE_PUBLIC_KEYS)
37 USER_STATE_PUBLIC_KEYS)
38
38
39 from rhodecode.lib.auth import NotAnonymous
39 from rhodecode.lib.auth import NotAnonymous
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43
43
44 class ChannelstreamView(BaseAppView):
44 class ChannelstreamView(BaseAppView):
45
45
46 def load_default_context(self):
46 def load_default_context(self):
47 c = self._get_local_tmpl_context()
47 c = self._get_local_tmpl_context()
48 self.channelstream_config = \
48 self.channelstream_config = \
49 self.request.registry.rhodecode_plugins['channelstream']
49 self.request.registry.rhodecode_plugins['channelstream']
50 if not self.channelstream_config.get('enabled'):
50 if not self.channelstream_config.get('enabled'):
51 log.error('Channelstream plugin is disabled')
51 log.error('Channelstream plugin is disabled')
52 raise HTTPBadRequest()
52 raise HTTPBadRequest()
53
53
54 return c
54 return c
55
55
56 @NotAnonymous()
56 @NotAnonymous()
57 @view_config(route_name='channelstream_connect', renderer='json_ext')
57 def channelstream_connect(self):
58 def connect(self):
59 """ handle authorization of users trying to connect """
58 """ handle authorization of users trying to connect """
60
59
61 self.load_default_context()
60 self.load_default_context()
62 try:
61 try:
63 json_body = self.request.json_body
62 json_body = self.request.json_body
64 except Exception:
63 except Exception:
65 log.exception('Failed to decode json from request')
64 log.exception('Failed to decode json from request')
66 raise HTTPBadRequest()
65 raise HTTPBadRequest()
67
66
68 try:
67 try:
69 channels = check_channel_permissions(
68 channels = check_channel_permissions(
70 json_body.get('channels'),
69 json_body.get('channels'),
71 get_connection_validators(self.request.registry))
70 get_connection_validators(self.request.registry))
72 except ChannelstreamPermissionException:
71 except ChannelstreamPermissionException:
73 log.error('Incorrect permissions for requested channels')
72 log.error('Incorrect permissions for requested channels')
74 raise HTTPForbidden()
73 raise HTTPForbidden()
75
74
76 user = self._rhodecode_user
75 user = self._rhodecode_user
77 if user.user_id:
76 if user.user_id:
78 user_data = get_user_data(user.user_id)
77 user_data = get_user_data(user.user_id)
79 else:
78 else:
80 user_data = {
79 user_data = {
81 'id': None,
80 'id': None,
82 'username': None,
81 'username': None,
83 'first_name': None,
82 'first_name': None,
84 'last_name': None,
83 'last_name': None,
85 'icon_link': None,
84 'icon_link': None,
86 'display_name': None,
85 'display_name': None,
87 'display_link': None,
86 'display_link': None,
88 }
87 }
89
88
90 #user_data['permissions'] = self._rhodecode_user.permissions_safe
89 #user_data['permissions'] = self._rhodecode_user.permissions_safe
91
90
92 payload = {
91 payload = {
93 'username': user.username,
92 'username': user.username,
94 'user_state': user_data,
93 'user_state': user_data,
95 'conn_id': str(uuid.uuid4()),
94 'conn_id': str(uuid.uuid4()),
96 'channels': channels,
95 'channels': channels,
97 'channel_configs': {},
96 'channel_configs': {},
98 'state_public_keys': USER_STATE_PUBLIC_KEYS,
97 'state_public_keys': USER_STATE_PUBLIC_KEYS,
99 'info': {
98 'info': {
100 'exclude_channels': ['broadcast']
99 'exclude_channels': ['broadcast']
101 }
100 }
102 }
101 }
103 filtered_channels = [channel for channel in channels
102 filtered_channels = [channel for channel in channels
104 if channel != 'broadcast']
103 if channel != 'broadcast']
105 for channel in filtered_channels:
104 for channel in filtered_channels:
106 payload['channel_configs'][channel] = {
105 payload['channel_configs'][channel] = {
107 'notify_presence': True,
106 'notify_presence': True,
108 'history_size': 100,
107 'history_size': 100,
109 'store_history': True,
108 'store_history': True,
110 'broadcast_presence_with_user_lists': True
109 'broadcast_presence_with_user_lists': True
111 }
110 }
112 # connect user to server
111 # connect user to server
113 channelstream_url = get_channelstream_server_url(
112 channelstream_url = get_channelstream_server_url(
114 self.channelstream_config, '/connect')
113 self.channelstream_config, '/connect')
115 try:
114 try:
116 connect_result = channelstream_request(
115 connect_result = channelstream_request(
117 self.channelstream_config, payload, '/connect')
116 self.channelstream_config, payload, '/connect')
118 except ChannelstreamConnectionException:
117 except ChannelstreamConnectionException:
119 log.exception(
118 log.exception(
120 'Channelstream service at {} is down'.format(channelstream_url))
119 'Channelstream service at {} is down'.format(channelstream_url))
121 return HTTPBadGateway()
120 return HTTPBadGateway()
122
121
123 channel_info = connect_result.get('channels_info')
122 channel_info = connect_result.get('channels_info')
124 if not channel_info:
123 if not channel_info:
125 raise HTTPBadRequest()
124 raise HTTPBadRequest()
126
125
127 connect_result['channels'] = channels
126 connect_result['channels'] = channels
128 connect_result['channels_info'] = parse_channels_info(
127 connect_result['channels_info'] = parse_channels_info(
129 channel_info, include_channel_info=filtered_channels)
128 channel_info, include_channel_info=filtered_channels)
130 update_history_from_logs(self.channelstream_config,
129 update_history_from_logs(self.channelstream_config,
131 filtered_channels, connect_result)
130 filtered_channels, connect_result)
132 return connect_result
131 return connect_result
133
132
134 @NotAnonymous()
133 @NotAnonymous()
135 @view_config(route_name='channelstream_subscribe', renderer='json_ext')
134 def channelstream_subscribe(self):
136 def subscribe(self):
137 """ can be used to subscribe specific connection to other channels """
135 """ can be used to subscribe specific connection to other channels """
138 self.load_default_context()
136 self.load_default_context()
139 try:
137 try:
140 json_body = self.request.json_body
138 json_body = self.request.json_body
141 except Exception:
139 except Exception:
142 log.exception('Failed to decode json from request')
140 log.exception('Failed to decode json from request')
143 raise HTTPBadRequest()
141 raise HTTPBadRequest()
144 try:
142 try:
145 channels = check_channel_permissions(
143 channels = check_channel_permissions(
146 json_body.get('channels'),
144 json_body.get('channels'),
147 get_connection_validators(self.request.registry))
145 get_connection_validators(self.request.registry))
148 except ChannelstreamPermissionException:
146 except ChannelstreamPermissionException:
149 log.error('Incorrect permissions for requested channels')
147 log.error('Incorrect permissions for requested channels')
150 raise HTTPForbidden()
148 raise HTTPForbidden()
151 payload = {'conn_id': json_body.get('conn_id', ''),
149 payload = {'conn_id': json_body.get('conn_id', ''),
152 'channels': channels,
150 'channels': channels,
153 'channel_configs': {},
151 'channel_configs': {},
154 'info': {
152 'info': {
155 'exclude_channels': ['broadcast']}
153 'exclude_channels': ['broadcast']}
156 }
154 }
157 filtered_channels = [chan for chan in channels if chan != 'broadcast']
155 filtered_channels = [chan for chan in channels if chan != 'broadcast']
158 for channel in filtered_channels:
156 for channel in filtered_channels:
159 payload['channel_configs'][channel] = {
157 payload['channel_configs'][channel] = {
160 'notify_presence': True,
158 'notify_presence': True,
161 'history_size': 100,
159 'history_size': 100,
162 'store_history': True,
160 'store_history': True,
163 'broadcast_presence_with_user_lists': True
161 'broadcast_presence_with_user_lists': True
164 }
162 }
165
163
166 channelstream_url = get_channelstream_server_url(
164 channelstream_url = get_channelstream_server_url(
167 self.channelstream_config, '/subscribe')
165 self.channelstream_config, '/subscribe')
168 try:
166 try:
169 connect_result = channelstream_request(
167 connect_result = channelstream_request(
170 self.channelstream_config, payload, '/subscribe')
168 self.channelstream_config, payload, '/subscribe')
171 except ChannelstreamConnectionException:
169 except ChannelstreamConnectionException:
172 log.exception(
170 log.exception(
173 'Channelstream service at {} is down'.format(channelstream_url))
171 'Channelstream service at {} is down'.format(channelstream_url))
174 return HTTPBadGateway()
172 return HTTPBadGateway()
175
173
176 channel_info = connect_result.get('channels_info')
174 channel_info = connect_result.get('channels_info')
177 if not channel_info:
175 if not channel_info:
178 raise HTTPBadRequest()
176 raise HTTPBadRequest()
179
177
180 # include_channel_info will limit history only to new channel
178 # include_channel_info will limit history only to new channel
181 # to not overwrite histories on other channels in client
179 # to not overwrite histories on other channels in client
182 connect_result['channels_info'] = parse_channels_info(
180 connect_result['channels_info'] = parse_channels_info(
183 channel_info,
181 channel_info,
184 include_channel_info=filtered_channels)
182 include_channel_info=filtered_channels)
185 update_history_from_logs(
183 update_history_from_logs(
186 self.channelstream_config, filtered_channels, connect_result)
184 self.channelstream_config, filtered_channels, connect_result)
187 return connect_result
185 return connect_result
@@ -1,59 +1,81 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 from rhodecode.apps._base import ADMIN_PREFIX
20 from rhodecode.apps._base import ADMIN_PREFIX
21 from rhodecode.lib.utils2 import str2bool
21 from rhodecode.lib.utils2 import str2bool
22
22
23
23
24 class DebugStylePredicate(object):
24 class DebugStylePredicate(object):
25 def __init__(self, val, config):
25 def __init__(self, val, config):
26 self.val = val
26 self.val = val
27
27
28 def text(self):
28 def text(self):
29 return 'debug style route = %s' % self.val
29 return 'debug style route = %s' % self.val
30
30
31 phash = text
31 phash = text
32
32
33 def __call__(self, info, request):
33 def __call__(self, info, request):
34 return str2bool(request.registry.settings.get('debug_style'))
34 return str2bool(request.registry.settings.get('debug_style'))
35
35
36
36
37 def includeme(config):
37 def includeme(config):
38 from rhodecode.apps.debug_style.views import DebugStyleView
39
38 config.add_route_predicate(
40 config.add_route_predicate(
39 'debug_style', DebugStylePredicate)
41 'debug_style', DebugStylePredicate)
40
42
41 config.add_route(
43 config.add_route(
42 name='debug_style_home',
44 name='debug_style_home',
43 pattern=ADMIN_PREFIX + '/debug_style',
45 pattern=ADMIN_PREFIX + '/debug_style',
44 debug_style=True)
46 debug_style=True)
47 config.add_view(
48 DebugStyleView,
49 attr='index',
50 route_name='debug_style_home', request_method='GET',
51 renderer=None)
52
45 config.add_route(
53 config.add_route(
46 name='debug_style_email',
54 name='debug_style_email',
47 pattern=ADMIN_PREFIX + '/debug_style/email/{email_id}',
55 pattern=ADMIN_PREFIX + '/debug_style/email/{email_id}',
48 debug_style=True)
56 debug_style=True)
57 config.add_view(
58 DebugStyleView,
59 attr='render_email',
60 route_name='debug_style_email', request_method='GET',
61 renderer=None)
62
49 config.add_route(
63 config.add_route(
50 name='debug_style_email_plain_rendered',
64 name='debug_style_email_plain_rendered',
51 pattern=ADMIN_PREFIX + '/debug_style/email-rendered/{email_id}',
65 pattern=ADMIN_PREFIX + '/debug_style/email-rendered/{email_id}',
52 debug_style=True)
66 debug_style=True)
67 config.add_view(
68 DebugStyleView,
69 attr='render_email',
70 route_name='debug_style_email_plain_rendered', request_method='GET',
71 renderer=None)
72
53 config.add_route(
73 config.add_route(
54 name='debug_style_template',
74 name='debug_style_template',
55 pattern=ADMIN_PREFIX + '/debug_style/t/{t_path}',
75 pattern=ADMIN_PREFIX + '/debug_style/t/{t_path}',
56 debug_style=True)
76 debug_style=True)
57
77 config.add_view(
58 # Scan module for configuration decorators.
78 DebugStyleView,
59 config.scan('.views', ignore='.tests')
79 attr='template',
80 route_name='debug_style_template', request_method='GET',
81 renderer=None)
@@ -1,486 +1,471 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import logging
22 import logging
23 import datetime
23 import datetime
24
24
25 from pyramid.view import view_config
26 from pyramid.renderers import render_to_response
25 from pyramid.renderers import render_to_response
27 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib.celerylib import run_task, tasks
27 from rhodecode.lib.celerylib import run_task, tasks
29 from rhodecode.lib.utils2 import AttributeDict
28 from rhodecode.lib.utils2 import AttributeDict
30 from rhodecode.model.db import User
29 from rhodecode.model.db import User
31 from rhodecode.model.notification import EmailNotificationModel
30 from rhodecode.model.notification import EmailNotificationModel
32
31
33 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
34
33
35
34
36 class DebugStyleView(BaseAppView):
35 class DebugStyleView(BaseAppView):
37
36
38 def load_default_context(self):
37 def load_default_context(self):
39 c = self._get_local_tmpl_context()
38 c = self._get_local_tmpl_context()
40
41 return c
39 return c
42
40
43 @view_config(
44 route_name='debug_style_home', request_method='GET',
45 renderer=None)
46 def index(self):
41 def index(self):
47 c = self.load_default_context()
42 c = self.load_default_context()
48 c.active = 'index'
43 c.active = 'index'
49
44
50 return render_to_response(
45 return render_to_response(
51 'debug_style/index.html', self._get_template_context(c),
46 'debug_style/index.html', self._get_template_context(c),
52 request=self.request)
47 request=self.request)
53
48
54 @view_config(
55 route_name='debug_style_email', request_method='GET',
56 renderer=None)
57 @view_config(
58 route_name='debug_style_email_plain_rendered', request_method='GET',
59 renderer=None)
60 def render_email(self):
49 def render_email(self):
61 c = self.load_default_context()
50 c = self.load_default_context()
62 email_id = self.request.matchdict['email_id']
51 email_id = self.request.matchdict['email_id']
63 c.active = 'emails'
52 c.active = 'emails'
64
53
65 pr = AttributeDict(
54 pr = AttributeDict(
66 pull_request_id=123,
55 pull_request_id=123,
67 title='digital_ocean: fix redis, elastic search start on boot, '
56 title='digital_ocean: fix redis, elastic search start on boot, '
68 'fix fd limits on supervisor, set postgres 11 version',
57 'fix fd limits on supervisor, set postgres 11 version',
69 description='''
58 description='''
70 Check if we should use full-topic or mini-topic.
59 Check if we should use full-topic or mini-topic.
71
60
72 - full topic produces some problems with merge states etc
61 - full topic produces some problems with merge states etc
73 - server-mini-topic needs probably tweeks.
62 - server-mini-topic needs probably tweeks.
74 ''',
63 ''',
75 repo_name='foobar',
64 repo_name='foobar',
76 source_ref_parts=AttributeDict(type='branch', name='fix-ticket-2000'),
65 source_ref_parts=AttributeDict(type='branch', name='fix-ticket-2000'),
77 target_ref_parts=AttributeDict(type='branch', name='master'),
66 target_ref_parts=AttributeDict(type='branch', name='master'),
78 )
67 )
79
68
80 target_repo = AttributeDict(repo_name='repo_group/target_repo')
69 target_repo = AttributeDict(repo_name='repo_group/target_repo')
81 source_repo = AttributeDict(repo_name='repo_group/source_repo')
70 source_repo = AttributeDict(repo_name='repo_group/source_repo')
82 user = User.get_by_username(self.request.GET.get('user')) or self._rhodecode_db_user
71 user = User.get_by_username(self.request.GET.get('user')) or self._rhodecode_db_user
83 # file/commit changes for PR update
72 # file/commit changes for PR update
84 commit_changes = AttributeDict({
73 commit_changes = AttributeDict({
85 'added': ['aaaaaaabbbbb', 'cccccccddddddd'],
74 'added': ['aaaaaaabbbbb', 'cccccccddddddd'],
86 'removed': ['eeeeeeeeeee'],
75 'removed': ['eeeeeeeeeee'],
87 })
76 })
88
77
89 file_changes = AttributeDict({
78 file_changes = AttributeDict({
90 'added': ['a/file1.md', 'file2.py'],
79 'added': ['a/file1.md', 'file2.py'],
91 'modified': ['b/modified_file.rst'],
80 'modified': ['b/modified_file.rst'],
92 'removed': ['.idea'],
81 'removed': ['.idea'],
93 })
82 })
94
83
95 exc_traceback = {
84 exc_traceback = {
96 'exc_utc_date': '2020-03-26T12:54:50.683281',
85 'exc_utc_date': '2020-03-26T12:54:50.683281',
97 'exc_id': 139638856342656,
86 'exc_id': 139638856342656,
98 'exc_timestamp': '1585227290.683288',
87 'exc_timestamp': '1585227290.683288',
99 'version': 'v1',
88 'version': 'v1',
100 'exc_message': 'Traceback (most recent call last):\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/tweens.py", line 41, in excview_tween\n response = handler(request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/router.py", line 148, in handle_request\n registry, request, context, context_iface, view_name\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/view.py", line 667, in _call_view\n response = view_callable(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/config/views.py", line 188, in attr_view\n return view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/config/views.py", line 214, in predicate_wrapper\n return view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/viewderivers.py", line 401, in viewresult_to_response\n result = view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/viewderivers.py", line 132, in _class_view\n response = getattr(inst, attr)()\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/apps/debug_style/views.py", line 355, in render_email\n template_type, **email_kwargs.get(email_id, {}))\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/model/notification.py", line 402, in render_email\n body = email_template.render(None, **_kwargs)\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/lib/partial_renderer.py", line 95, in render\n return self._render_with_exc(tmpl, args, kwargs)\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/lib/partial_renderer.py", line 79, in _render_with_exc\n return render_func.render(*args, **kwargs)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/template.py", line 476, in render\n return runtime._render(self, self.callable_, args, data)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 883, in _render\n **_kwargs_for_callable(callable_, data)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 920, in _render_context\n _exec_template(inherit, lclcontext, args=args, kwargs=kwargs)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 947, in _exec_template\n callable_(context, *args, **kwargs)\n File "rhodecode_templates_email_templates_base_mako", line 63, in render_body\n File "rhodecode_templates_email_templates_exception_tracker_mako", line 43, in render_body\nAttributeError: \'str\' object has no attribute \'get\'\n',
89 'exc_message': 'Traceback (most recent call last):\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/tweens.py", line 41, in excview_tween\n response = handler(request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/router.py", line 148, in handle_request\n registry, request, context, context_iface, view_name\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/view.py", line 667, in _call_view\n response = view_callable(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/config/views.py", line 188, in attr_view\n return view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/config/views.py", line 214, in predicate_wrapper\n return view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/viewderivers.py", line 401, in viewresult_to_response\n result = view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/viewderivers.py", line 132, in _class_view\n response = getattr(inst, attr)()\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/apps/debug_style/views.py", line 355, in render_email\n template_type, **email_kwargs.get(email_id, {}))\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/model/notification.py", line 402, in render_email\n body = email_template.render(None, **_kwargs)\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/lib/partial_renderer.py", line 95, in render\n return self._render_with_exc(tmpl, args, kwargs)\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/lib/partial_renderer.py", line 79, in _render_with_exc\n return render_func.render(*args, **kwargs)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/template.py", line 476, in render\n return runtime._render(self, self.callable_, args, data)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 883, in _render\n **_kwargs_for_callable(callable_, data)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 920, in _render_context\n _exec_template(inherit, lclcontext, args=args, kwargs=kwargs)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 947, in _exec_template\n callable_(context, *args, **kwargs)\n File "rhodecode_templates_email_templates_base_mako", line 63, in render_body\n File "rhodecode_templates_email_templates_exception_tracker_mako", line 43, in render_body\nAttributeError: \'str\' object has no attribute \'get\'\n',
101 'exc_type': 'AttributeError'
90 'exc_type': 'AttributeError'
102 }
91 }
103
92
104 email_kwargs = {
93 email_kwargs = {
105 'test': {},
94 'test': {},
106
95
107 'message': {
96 'message': {
108 'body': 'message body !'
97 'body': 'message body !'
109 },
98 },
110
99
111 'email_test': {
100 'email_test': {
112 'user': user,
101 'user': user,
113 'date': datetime.datetime.now(),
102 'date': datetime.datetime.now(),
114 },
103 },
115
104
116 'exception': {
105 'exception': {
117 'email_prefix': '[RHODECODE ERROR]',
106 'email_prefix': '[RHODECODE ERROR]',
118 'exc_id': exc_traceback['exc_id'],
107 'exc_id': exc_traceback['exc_id'],
119 'exc_url': 'http://server-url/{}'.format(exc_traceback['exc_id']),
108 'exc_url': 'http://server-url/{}'.format(exc_traceback['exc_id']),
120 'exc_type_name': 'NameError',
109 'exc_type_name': 'NameError',
121 'exc_traceback': exc_traceback,
110 'exc_traceback': exc_traceback,
122 },
111 },
123
112
124 'password_reset': {
113 'password_reset': {
125 'password_reset_url': 'http://example.com/reset-rhodecode-password/token',
114 'password_reset_url': 'http://example.com/reset-rhodecode-password/token',
126
115
127 'user': user,
116 'user': user,
128 'date': datetime.datetime.now(),
117 'date': datetime.datetime.now(),
129 'email': 'test@rhodecode.com',
118 'email': 'test@rhodecode.com',
130 'first_admin_email': User.get_first_super_admin().email
119 'first_admin_email': User.get_first_super_admin().email
131 },
120 },
132
121
133 'password_reset_confirmation': {
122 'password_reset_confirmation': {
134 'new_password': 'new-password-example',
123 'new_password': 'new-password-example',
135 'user': user,
124 'user': user,
136 'date': datetime.datetime.now(),
125 'date': datetime.datetime.now(),
137 'email': 'test@rhodecode.com',
126 'email': 'test@rhodecode.com',
138 'first_admin_email': User.get_first_super_admin().email
127 'first_admin_email': User.get_first_super_admin().email
139 },
128 },
140
129
141 'registration': {
130 'registration': {
142 'user': user,
131 'user': user,
143 'date': datetime.datetime.now(),
132 'date': datetime.datetime.now(),
144 },
133 },
145
134
146 'pull_request_comment': {
135 'pull_request_comment': {
147 'user': user,
136 'user': user,
148
137
149 'status_change': None,
138 'status_change': None,
150 'status_change_type': None,
139 'status_change_type': None,
151
140
152 'pull_request': pr,
141 'pull_request': pr,
153 'pull_request_commits': [],
142 'pull_request_commits': [],
154
143
155 'pull_request_target_repo': target_repo,
144 'pull_request_target_repo': target_repo,
156 'pull_request_target_repo_url': 'http://target-repo/url',
145 'pull_request_target_repo_url': 'http://target-repo/url',
157
146
158 'pull_request_source_repo': source_repo,
147 'pull_request_source_repo': source_repo,
159 'pull_request_source_repo_url': 'http://source-repo/url',
148 'pull_request_source_repo_url': 'http://source-repo/url',
160
149
161 'pull_request_url': 'http://localhost/pr1',
150 'pull_request_url': 'http://localhost/pr1',
162 'pr_comment_url': 'http://comment-url',
151 'pr_comment_url': 'http://comment-url',
163 'pr_comment_reply_url': 'http://comment-url#reply',
152 'pr_comment_reply_url': 'http://comment-url#reply',
164
153
165 'comment_file': None,
154 'comment_file': None,
166 'comment_line': None,
155 'comment_line': None,
167 'comment_type': 'note',
156 'comment_type': 'note',
168 'comment_body': 'This is my comment body. *I like !*',
157 'comment_body': 'This is my comment body. *I like !*',
169 'comment_id': 2048,
158 'comment_id': 2048,
170 'renderer_type': 'markdown',
159 'renderer_type': 'markdown',
171 'mention': True,
160 'mention': True,
172
161
173 },
162 },
174
163
175 'pull_request_comment+status': {
164 'pull_request_comment+status': {
176 'user': user,
165 'user': user,
177
166
178 'status_change': 'approved',
167 'status_change': 'approved',
179 'status_change_type': 'approved',
168 'status_change_type': 'approved',
180
169
181 'pull_request': pr,
170 'pull_request': pr,
182 'pull_request_commits': [],
171 'pull_request_commits': [],
183
172
184 'pull_request_target_repo': target_repo,
173 'pull_request_target_repo': target_repo,
185 'pull_request_target_repo_url': 'http://target-repo/url',
174 'pull_request_target_repo_url': 'http://target-repo/url',
186
175
187 'pull_request_source_repo': source_repo,
176 'pull_request_source_repo': source_repo,
188 'pull_request_source_repo_url': 'http://source-repo/url',
177 'pull_request_source_repo_url': 'http://source-repo/url',
189
178
190 'pull_request_url': 'http://localhost/pr1',
179 'pull_request_url': 'http://localhost/pr1',
191 'pr_comment_url': 'http://comment-url',
180 'pr_comment_url': 'http://comment-url',
192 'pr_comment_reply_url': 'http://comment-url#reply',
181 'pr_comment_reply_url': 'http://comment-url#reply',
193
182
194 'comment_type': 'todo',
183 'comment_type': 'todo',
195 'comment_file': None,
184 'comment_file': None,
196 'comment_line': None,
185 'comment_line': None,
197 'comment_body': '''
186 'comment_body': '''
198 I think something like this would be better
187 I think something like this would be better
199
188
200 ```py
189 ```py
201 // markdown renderer
190 // markdown renderer
202
191
203 def db():
192 def db():
204 global connection
193 global connection
205 return connection
194 return connection
206
195
207 ```
196 ```
208
197
209 ''',
198 ''',
210 'comment_id': 2048,
199 'comment_id': 2048,
211 'renderer_type': 'markdown',
200 'renderer_type': 'markdown',
212 'mention': True,
201 'mention': True,
213
202
214 },
203 },
215
204
216 'pull_request_comment+file': {
205 'pull_request_comment+file': {
217 'user': user,
206 'user': user,
218
207
219 'status_change': None,
208 'status_change': None,
220 'status_change_type': None,
209 'status_change_type': None,
221
210
222 'pull_request': pr,
211 'pull_request': pr,
223 'pull_request_commits': [],
212 'pull_request_commits': [],
224
213
225 'pull_request_target_repo': target_repo,
214 'pull_request_target_repo': target_repo,
226 'pull_request_target_repo_url': 'http://target-repo/url',
215 'pull_request_target_repo_url': 'http://target-repo/url',
227
216
228 'pull_request_source_repo': source_repo,
217 'pull_request_source_repo': source_repo,
229 'pull_request_source_repo_url': 'http://source-repo/url',
218 'pull_request_source_repo_url': 'http://source-repo/url',
230
219
231 'pull_request_url': 'http://localhost/pr1',
220 'pull_request_url': 'http://localhost/pr1',
232
221
233 'pr_comment_url': 'http://comment-url',
222 'pr_comment_url': 'http://comment-url',
234 'pr_comment_reply_url': 'http://comment-url#reply',
223 'pr_comment_reply_url': 'http://comment-url#reply',
235
224
236 'comment_file': 'rhodecode/model/get_flow_commits',
225 'comment_file': 'rhodecode/model/get_flow_commits',
237 'comment_line': 'o1210',
226 'comment_line': 'o1210',
238 'comment_type': 'todo',
227 'comment_type': 'todo',
239 'comment_body': '''
228 'comment_body': '''
240 I like this !
229 I like this !
241
230
242 But please check this code
231 But please check this code
243
232
244 .. code-block:: javascript
233 .. code-block:: javascript
245
234
246 // THIS IS RST CODE
235 // THIS IS RST CODE
247
236
248 this.createResolutionComment = function(commentId) {
237 this.createResolutionComment = function(commentId) {
249 // hide the trigger text
238 // hide the trigger text
250 $('#resolve-comment-{0}'.format(commentId)).hide();
239 $('#resolve-comment-{0}'.format(commentId)).hide();
251
240
252 var comment = $('#comment-'+commentId);
241 var comment = $('#comment-'+commentId);
253 var commentData = comment.data();
242 var commentData = comment.data();
254 if (commentData.commentInline) {
243 if (commentData.commentInline) {
255 this.createComment(comment, f_path, line_no, commentId)
244 this.createComment(comment, f_path, line_no, commentId)
256 } else {
245 } else {
257 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
246 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
258 }
247 }
259
248
260 return false;
249 return false;
261 };
250 };
262
251
263 This should work better !
252 This should work better !
264 ''',
253 ''',
265 'comment_id': 2048,
254 'comment_id': 2048,
266 'renderer_type': 'rst',
255 'renderer_type': 'rst',
267 'mention': True,
256 'mention': True,
268
257
269 },
258 },
270
259
271 'pull_request_update': {
260 'pull_request_update': {
272 'updating_user': user,
261 'updating_user': user,
273
262
274 'status_change': None,
263 'status_change': None,
275 'status_change_type': None,
264 'status_change_type': None,
276
265
277 'pull_request': pr,
266 'pull_request': pr,
278 'pull_request_commits': [],
267 'pull_request_commits': [],
279
268
280 'pull_request_target_repo': target_repo,
269 'pull_request_target_repo': target_repo,
281 'pull_request_target_repo_url': 'http://target-repo/url',
270 'pull_request_target_repo_url': 'http://target-repo/url',
282
271
283 'pull_request_source_repo': source_repo,
272 'pull_request_source_repo': source_repo,
284 'pull_request_source_repo_url': 'http://source-repo/url',
273 'pull_request_source_repo_url': 'http://source-repo/url',
285
274
286 'pull_request_url': 'http://localhost/pr1',
275 'pull_request_url': 'http://localhost/pr1',
287
276
288 # update comment links
277 # update comment links
289 'pr_comment_url': 'http://comment-url',
278 'pr_comment_url': 'http://comment-url',
290 'pr_comment_reply_url': 'http://comment-url#reply',
279 'pr_comment_reply_url': 'http://comment-url#reply',
291 'ancestor_commit_id': 'f39bd443',
280 'ancestor_commit_id': 'f39bd443',
292 'added_commits': commit_changes.added,
281 'added_commits': commit_changes.added,
293 'removed_commits': commit_changes.removed,
282 'removed_commits': commit_changes.removed,
294 'changed_files': (file_changes.added + file_changes.modified + file_changes.removed),
283 'changed_files': (file_changes.added + file_changes.modified + file_changes.removed),
295 'added_files': file_changes.added,
284 'added_files': file_changes.added,
296 'modified_files': file_changes.modified,
285 'modified_files': file_changes.modified,
297 'removed_files': file_changes.removed,
286 'removed_files': file_changes.removed,
298 },
287 },
299
288
300 'cs_comment': {
289 'cs_comment': {
301 'user': user,
290 'user': user,
302 'commit': AttributeDict(idx=123, raw_id='a'*40, message='Commit message'),
291 'commit': AttributeDict(idx=123, raw_id='a'*40, message='Commit message'),
303 'status_change': None,
292 'status_change': None,
304 'status_change_type': None,
293 'status_change_type': None,
305
294
306 'commit_target_repo_url': 'http://foo.example.com/#comment1',
295 'commit_target_repo_url': 'http://foo.example.com/#comment1',
307 'repo_name': 'test-repo',
296 'repo_name': 'test-repo',
308 'comment_type': 'note',
297 'comment_type': 'note',
309 'comment_file': None,
298 'comment_file': None,
310 'comment_line': None,
299 'comment_line': None,
311 'commit_comment_url': 'http://comment-url',
300 'commit_comment_url': 'http://comment-url',
312 'commit_comment_reply_url': 'http://comment-url#reply',
301 'commit_comment_reply_url': 'http://comment-url#reply',
313 'comment_body': 'This is my comment body. *I like !*',
302 'comment_body': 'This is my comment body. *I like !*',
314 'comment_id': 2048,
303 'comment_id': 2048,
315 'renderer_type': 'markdown',
304 'renderer_type': 'markdown',
316 'mention': True,
305 'mention': True,
317 },
306 },
318
307
319 'cs_comment+status': {
308 'cs_comment+status': {
320 'user': user,
309 'user': user,
321 'commit': AttributeDict(idx=123, raw_id='a' * 40, message='Commit message'),
310 'commit': AttributeDict(idx=123, raw_id='a' * 40, message='Commit message'),
322 'status_change': 'approved',
311 'status_change': 'approved',
323 'status_change_type': 'approved',
312 'status_change_type': 'approved',
324
313
325 'commit_target_repo_url': 'http://foo.example.com/#comment1',
314 'commit_target_repo_url': 'http://foo.example.com/#comment1',
326 'repo_name': 'test-repo',
315 'repo_name': 'test-repo',
327 'comment_type': 'note',
316 'comment_type': 'note',
328 'comment_file': None,
317 'comment_file': None,
329 'comment_line': None,
318 'comment_line': None,
330 'commit_comment_url': 'http://comment-url',
319 'commit_comment_url': 'http://comment-url',
331 'commit_comment_reply_url': 'http://comment-url#reply',
320 'commit_comment_reply_url': 'http://comment-url#reply',
332 'comment_body': '''
321 'comment_body': '''
333 Hello **world**
322 Hello **world**
334
323
335 This is a multiline comment :)
324 This is a multiline comment :)
336
325
337 - list
326 - list
338 - list2
327 - list2
339 ''',
328 ''',
340 'comment_id': 2048,
329 'comment_id': 2048,
341 'renderer_type': 'markdown',
330 'renderer_type': 'markdown',
342 'mention': True,
331 'mention': True,
343 },
332 },
344
333
345 'cs_comment+file': {
334 'cs_comment+file': {
346 'user': user,
335 'user': user,
347 'commit': AttributeDict(idx=123, raw_id='a' * 40, message='Commit message'),
336 'commit': AttributeDict(idx=123, raw_id='a' * 40, message='Commit message'),
348 'status_change': None,
337 'status_change': None,
349 'status_change_type': None,
338 'status_change_type': None,
350
339
351 'commit_target_repo_url': 'http://foo.example.com/#comment1',
340 'commit_target_repo_url': 'http://foo.example.com/#comment1',
352 'repo_name': 'test-repo',
341 'repo_name': 'test-repo',
353
342
354 'comment_type': 'note',
343 'comment_type': 'note',
355 'comment_file': 'test-file.py',
344 'comment_file': 'test-file.py',
356 'comment_line': 'n100',
345 'comment_line': 'n100',
357
346
358 'commit_comment_url': 'http://comment-url',
347 'commit_comment_url': 'http://comment-url',
359 'commit_comment_reply_url': 'http://comment-url#reply',
348 'commit_comment_reply_url': 'http://comment-url#reply',
360 'comment_body': 'This is my comment body. *I like !*',
349 'comment_body': 'This is my comment body. *I like !*',
361 'comment_id': 2048,
350 'comment_id': 2048,
362 'renderer_type': 'markdown',
351 'renderer_type': 'markdown',
363 'mention': True,
352 'mention': True,
364 },
353 },
365
354
366 'pull_request': {
355 'pull_request': {
367 'user': user,
356 'user': user,
368 'pull_request': pr,
357 'pull_request': pr,
369 'pull_request_commits': [
358 'pull_request_commits': [
370 ('472d1df03bf7206e278fcedc6ac92b46b01c4e21', '''\
359 ('472d1df03bf7206e278fcedc6ac92b46b01c4e21', '''\
371 my-account: moved email closer to profile as it's similar data just moved outside.
360 my-account: moved email closer to profile as it's similar data just moved outside.
372 '''),
361 '''),
373 ('cbfa3061b6de2696c7161ed15ba5c6a0045f90a7', '''\
362 ('cbfa3061b6de2696c7161ed15ba5c6a0045f90a7', '''\
374 users: description edit fixes
363 users: description edit fixes
375
364
376 - tests
365 - tests
377 - added metatags info
366 - added metatags info
378 '''),
367 '''),
379 ],
368 ],
380
369
381 'pull_request_target_repo': target_repo,
370 'pull_request_target_repo': target_repo,
382 'pull_request_target_repo_url': 'http://target-repo/url',
371 'pull_request_target_repo_url': 'http://target-repo/url',
383
372
384 'pull_request_source_repo': source_repo,
373 'pull_request_source_repo': source_repo,
385 'pull_request_source_repo_url': 'http://source-repo/url',
374 'pull_request_source_repo_url': 'http://source-repo/url',
386
375
387 'pull_request_url': 'http://code.rhodecode.com/_pull-request/123',
376 'pull_request_url': 'http://code.rhodecode.com/_pull-request/123',
388 'user_role': 'reviewer',
377 'user_role': 'reviewer',
389 },
378 },
390
379
391 'pull_request+reviewer_role': {
380 'pull_request+reviewer_role': {
392 'user': user,
381 'user': user,
393 'pull_request': pr,
382 'pull_request': pr,
394 'pull_request_commits': [
383 'pull_request_commits': [
395 ('472d1df03bf7206e278fcedc6ac92b46b01c4e21', '''\
384 ('472d1df03bf7206e278fcedc6ac92b46b01c4e21', '''\
396 my-account: moved email closer to profile as it's similar data just moved outside.
385 my-account: moved email closer to profile as it's similar data just moved outside.
397 '''),
386 '''),
398 ('cbfa3061b6de2696c7161ed15ba5c6a0045f90a7', '''\
387 ('cbfa3061b6de2696c7161ed15ba5c6a0045f90a7', '''\
399 users: description edit fixes
388 users: description edit fixes
400
389
401 - tests
390 - tests
402 - added metatags info
391 - added metatags info
403 '''),
392 '''),
404 ],
393 ],
405
394
406 'pull_request_target_repo': target_repo,
395 'pull_request_target_repo': target_repo,
407 'pull_request_target_repo_url': 'http://target-repo/url',
396 'pull_request_target_repo_url': 'http://target-repo/url',
408
397
409 'pull_request_source_repo': source_repo,
398 'pull_request_source_repo': source_repo,
410 'pull_request_source_repo_url': 'http://source-repo/url',
399 'pull_request_source_repo_url': 'http://source-repo/url',
411
400
412 'pull_request_url': 'http://code.rhodecode.com/_pull-request/123',
401 'pull_request_url': 'http://code.rhodecode.com/_pull-request/123',
413 'user_role': 'reviewer',
402 'user_role': 'reviewer',
414 },
403 },
415
404
416 'pull_request+observer_role': {
405 'pull_request+observer_role': {
417 'user': user,
406 'user': user,
418 'pull_request': pr,
407 'pull_request': pr,
419 'pull_request_commits': [
408 'pull_request_commits': [
420 ('472d1df03bf7206e278fcedc6ac92b46b01c4e21', '''\
409 ('472d1df03bf7206e278fcedc6ac92b46b01c4e21', '''\
421 my-account: moved email closer to profile as it's similar data just moved outside.
410 my-account: moved email closer to profile as it's similar data just moved outside.
422 '''),
411 '''),
423 ('cbfa3061b6de2696c7161ed15ba5c6a0045f90a7', '''\
412 ('cbfa3061b6de2696c7161ed15ba5c6a0045f90a7', '''\
424 users: description edit fixes
413 users: description edit fixes
425
414
426 - tests
415 - tests
427 - added metatags info
416 - added metatags info
428 '''),
417 '''),
429 ],
418 ],
430
419
431 'pull_request_target_repo': target_repo,
420 'pull_request_target_repo': target_repo,
432 'pull_request_target_repo_url': 'http://target-repo/url',
421 'pull_request_target_repo_url': 'http://target-repo/url',
433
422
434 'pull_request_source_repo': source_repo,
423 'pull_request_source_repo': source_repo,
435 'pull_request_source_repo_url': 'http://source-repo/url',
424 'pull_request_source_repo_url': 'http://source-repo/url',
436
425
437 'pull_request_url': 'http://code.rhodecode.com/_pull-request/123',
426 'pull_request_url': 'http://code.rhodecode.com/_pull-request/123',
438 'user_role': 'observer'
427 'user_role': 'observer'
439 }
428 }
440 }
429 }
441
430
442 template_type = email_id.split('+')[0]
431 template_type = email_id.split('+')[0]
443 (c.subject, c.email_body, c.email_body_plaintext) = EmailNotificationModel().render_email(
432 (c.subject, c.email_body, c.email_body_plaintext) = EmailNotificationModel().render_email(
444 template_type, **email_kwargs.get(email_id, {}))
433 template_type, **email_kwargs.get(email_id, {}))
445
434
446 test_email = self.request.GET.get('email')
435 test_email = self.request.GET.get('email')
447 if test_email:
436 if test_email:
448 recipients = [test_email]
437 recipients = [test_email]
449 run_task(tasks.send_email, recipients, c.subject,
438 run_task(tasks.send_email, recipients, c.subject,
450 c.email_body_plaintext, c.email_body)
439 c.email_body_plaintext, c.email_body)
451
440
452 if self.request.matched_route.name == 'debug_style_email_plain_rendered':
441 if self.request.matched_route.name == 'debug_style_email_plain_rendered':
453 template = 'debug_style/email_plain_rendered.mako'
442 template = 'debug_style/email_plain_rendered.mako'
454 else:
443 else:
455 template = 'debug_style/email.mako'
444 template = 'debug_style/email.mako'
456 return render_to_response(
445 return render_to_response(
457 template, self._get_template_context(c),
446 template, self._get_template_context(c),
458 request=self.request)
447 request=self.request)
459
448
460 @view_config(
461 route_name='debug_style_template', request_method='GET',
462 renderer=None)
463 def template(self):
449 def template(self):
464 t_path = self.request.matchdict['t_path']
450 t_path = self.request.matchdict['t_path']
465 c = self.load_default_context()
451 c = self.load_default_context()
466 c.active = os.path.splitext(t_path)[0]
452 c.active = os.path.splitext(t_path)[0]
467 c.came_from = ''
453 c.came_from = ''
468 # NOTE(marcink): extend the email types with variations based on data sets
454 # NOTE(marcink): extend the email types with variations based on data sets
469 c.email_types = {
455 c.email_types = {
470 'cs_comment+file': {},
456 'cs_comment+file': {},
471 'cs_comment+status': {},
457 'cs_comment+status': {},
472
458
473 'pull_request_comment+file': {},
459 'pull_request_comment+file': {},
474 'pull_request_comment+status': {},
460 'pull_request_comment+status': {},
475
461
476 'pull_request_update': {},
462 'pull_request_update': {},
477
463
478 'pull_request+reviewer_role': {},
464 'pull_request+reviewer_role': {},
479 'pull_request+observer_role': {},
465 'pull_request+observer_role': {},
480 }
466 }
481 c.email_types.update(EmailNotificationModel.email_types)
467 c.email_types.update(EmailNotificationModel.email_types)
482
468
483 return render_to_response(
469 return render_to_response(
484 'debug_style/' + t_path, self._get_template_context(c),
470 'debug_style/' + t_path, self._get_template_context(c),
485 request=self.request)
471 request=self.request)
486
@@ -1,52 +1,65 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import os
20 import os
21 from rhodecode.apps.file_store import config_keys
21 from rhodecode.apps.file_store import config_keys
22 from rhodecode.config.middleware import _bool_setting, _string_setting
22 from rhodecode.config.middleware import _bool_setting, _string_setting
23
23
24
24
25 def _sanitize_settings_and_apply_defaults(settings):
25 def _sanitize_settings_and_apply_defaults(settings):
26 """
26 """
27 Set defaults, convert to python types and validate settings.
27 Set defaults, convert to python types and validate settings.
28 """
28 """
29 _bool_setting(settings, config_keys.enabled, 'true')
29 _bool_setting(settings, config_keys.enabled, 'true')
30
30
31 _string_setting(settings, config_keys.backend, 'local')
31 _string_setting(settings, config_keys.backend, 'local')
32
32
33 default_store = os.path.join(os.path.dirname(settings['__file__']), 'upload_store')
33 default_store = os.path.join(os.path.dirname(settings['__file__']), 'upload_store')
34 _string_setting(settings, config_keys.store_path, default_store)
34 _string_setting(settings, config_keys.store_path, default_store)
35
35
36
36
37 def includeme(config):
37 def includeme(config):
38 from rhodecode.apps.file_store.views import FileStoreView
39
38 settings = config.registry.settings
40 settings = config.registry.settings
39 _sanitize_settings_and_apply_defaults(settings)
41 _sanitize_settings_and_apply_defaults(settings)
40
42
41 config.add_route(
43 config.add_route(
42 name='upload_file',
44 name='upload_file',
43 pattern='/_file_store/upload')
45 pattern='/_file_store/upload')
46 config.add_view(
47 FileStoreView,
48 attr='upload_file',
49 route_name='upload_file', request_method='POST', renderer='json_ext')
50
44 config.add_route(
51 config.add_route(
45 name='download_file',
52 name='download_file',
46 pattern='/_file_store/download/{fid:.*}')
53 pattern='/_file_store/download/{fid:.*}')
54 config.add_view(
55 FileStoreView,
56 attr='download_file',
57 route_name='download_file')
58
47 config.add_route(
59 config.add_route(
48 name='download_file_by_token',
60 name='download_file_by_token',
49 pattern='/_file_store/token-download/{_auth_token}/{fid:.*}')
61 pattern='/_file_store/token-download/{_auth_token}/{fid:.*}')
50
62 config.add_view(
51 # Scan module for configuration decorators.
63 FileStoreView,
52 config.scan('.views', ignore='.tests')
64 attr='download_file_by_token',
65 route_name='download_file_by_token')
@@ -1,261 +1,269 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import time
22 import time
23 import errno
23 import errno
24 import shutil
25 import hashlib
24 import hashlib
26
25
27 from rhodecode.lib.ext_json import json
26 from rhodecode.lib.ext_json import json
28 from rhodecode.apps.file_store import utils
27 from rhodecode.apps.file_store import utils
29 from rhodecode.apps.file_store.extensions import resolve_extensions
28 from rhodecode.apps.file_store.extensions import resolve_extensions
30 from rhodecode.apps.file_store.exceptions import (
29 from rhodecode.apps.file_store.exceptions import (
31 FileNotAllowedException, FileOverSizeException)
30 FileNotAllowedException, FileOverSizeException)
32
31
33 METADATA_VER = 'v1'
32 METADATA_VER = 'v1'
34
33
35
34
36 def safe_make_dirs(dir_path):
35 def safe_make_dirs(dir_path):
37 if not os.path.exists(dir_path):
36 if not os.path.exists(dir_path):
38 try:
37 try:
39 os.makedirs(dir_path)
38 os.makedirs(dir_path)
40 except OSError as e:
39 except OSError as e:
41 if e.errno != errno.EEXIST:
40 if e.errno != errno.EEXIST:
42 raise
41 raise
43 return
42 return
44
43
45
44
46 class LocalFileStorage(object):
45 class LocalFileStorage(object):
47
46
48 @classmethod
47 @classmethod
49 def apply_counter(cls, counter, filename):
48 def apply_counter(cls, counter, filename):
50 name_counted = '%d-%s' % (counter, filename)
49 name_counted = '%d-%s' % (counter, filename)
51 return name_counted
50 return name_counted
52
51
53 @classmethod
52 @classmethod
54 def resolve_name(cls, name, directory):
53 def resolve_name(cls, name, directory):
55 """
54 """
56 Resolves a unique name and the correct path. If a filename
55 Resolves a unique name and the correct path. If a filename
57 for that path already exists then a numeric prefix with values > 0 will be
56 for that path already exists then a numeric prefix with values > 0 will be
58 added, for example test.jpg -> 1-test.jpg etc. initially file would have 0 prefix.
57 added, for example test.jpg -> 1-test.jpg etc. initially file would have 0 prefix.
59
58
60 :param name: base name of file
59 :param name: base name of file
61 :param directory: absolute directory path
60 :param directory: absolute directory path
62 """
61 """
63
62
64 counter = 0
63 counter = 0
65 while True:
64 while True:
66 name_counted = cls.apply_counter(counter, name)
65 name_counted = cls.apply_counter(counter, name)
67
66
68 # sub_store prefix to optimize disk usage, e.g some_path/ab/final_file
67 # sub_store prefix to optimize disk usage, e.g some_path/ab/final_file
69 sub_store = cls._sub_store_from_filename(name_counted)
68 sub_store = cls._sub_store_from_filename(name_counted)
70 sub_store_path = os.path.join(directory, sub_store)
69 sub_store_path = os.path.join(directory, sub_store)
71 safe_make_dirs(sub_store_path)
70 safe_make_dirs(sub_store_path)
72
71
73 path = os.path.join(sub_store_path, name_counted)
72 path = os.path.join(sub_store_path, name_counted)
74 if not os.path.exists(path):
73 if not os.path.exists(path):
75 return name_counted, path
74 return name_counted, path
76 counter += 1
75 counter += 1
77
76
78 @classmethod
77 @classmethod
79 def _sub_store_from_filename(cls, filename):
78 def _sub_store_from_filename(cls, filename):
80 return filename[:2]
79 return filename[:2]
81
80
82 @classmethod
81 @classmethod
83 def calculate_path_hash(cls, file_path):
82 def calculate_path_hash(cls, file_path):
84 """
83 """
85 Efficient calculation of file_path sha256 sum
84 Efficient calculation of file_path sha256 sum
86
85
87 :param file_path:
86 :param file_path:
88 :return: sha256sum
87 :return: sha256sum
89 """
88 """
90 digest = hashlib.sha256()
89 digest = hashlib.sha256()
91 with open(file_path, 'rb') as f:
90 with open(file_path, 'rb') as f:
92 for chunk in iter(lambda: f.read(1024 * 100), b""):
91 for chunk in iter(lambda: f.read(1024 * 100), b""):
93 digest.update(chunk)
92 digest.update(chunk)
94
93
95 return digest.hexdigest()
94 return digest.hexdigest()
96
95
97 def __init__(self, base_path, extension_groups=None):
96 def __init__(self, base_path, extension_groups=None):
98
97
99 """
98 """
100 Local file storage
99 Local file storage
101
100
102 :param base_path: the absolute base path where uploads are stored
101 :param base_path: the absolute base path where uploads are stored
103 :param extension_groups: extensions string
102 :param extension_groups: extensions string
104 """
103 """
105
104
106 extension_groups = extension_groups or ['any']
105 extension_groups = extension_groups or ['any']
107 self.base_path = base_path
106 self.base_path = base_path
108 self.extensions = resolve_extensions([], groups=extension_groups)
107 self.extensions = resolve_extensions([], groups=extension_groups)
109
108
110 def __repr__(self):
109 def __repr__(self):
111 return '{}@{}'.format(self.__class__, self.base_path)
110 return '{}@{}'.format(self.__class__, self.base_path)
112
111
113 def store_path(self, filename):
112 def store_path(self, filename):
114 """
113 """
115 Returns absolute file path of the filename, joined to the
114 Returns absolute file path of the filename, joined to the
116 base_path.
115 base_path.
117
116
118 :param filename: base name of file
117 :param filename: base name of file
119 """
118 """
120 prefix_dir = ''
119 prefix_dir = ''
121 if '/' in filename:
120 if '/' in filename:
122 prefix_dir, filename = filename.split('/')
121 prefix_dir, filename = filename.split('/')
123 sub_store = self._sub_store_from_filename(filename)
122 sub_store = self._sub_store_from_filename(filename)
124 else:
123 else:
125 sub_store = self._sub_store_from_filename(filename)
124 sub_store = self._sub_store_from_filename(filename)
126 return os.path.join(self.base_path, prefix_dir, sub_store, filename)
125 return os.path.join(self.base_path, prefix_dir, sub_store, filename)
127
126
128 def delete(self, filename):
127 def delete(self, filename):
129 """
128 """
130 Deletes the filename. Filename is resolved with the
129 Deletes the filename. Filename is resolved with the
131 absolute path based on base_path. If file does not exist,
130 absolute path based on base_path. If file does not exist,
132 returns **False**, otherwise **True**
131 returns **False**, otherwise **True**
133
132
134 :param filename: base name of file
133 :param filename: base name of file
135 """
134 """
136 if self.exists(filename):
135 if self.exists(filename):
137 os.remove(self.store_path(filename))
136 os.remove(self.store_path(filename))
138 return True
137 return True
139 return False
138 return False
140
139
141 def exists(self, filename):
140 def exists(self, filename):
142 """
141 """
143 Checks if file exists. Resolves filename's absolute
142 Checks if file exists. Resolves filename's absolute
144 path based on base_path.
143 path based on base_path.
145
144
146 :param filename: file_uid name of file, e.g 0-f62b2b2d-9708-4079-a071-ec3f958448d4.svg
145 :param filename: file_uid name of file, e.g 0-f62b2b2d-9708-4079-a071-ec3f958448d4.svg
147 """
146 """
148 return os.path.exists(self.store_path(filename))
147 return os.path.exists(self.store_path(filename))
149
148
150 def filename_allowed(self, filename, extensions=None):
149 def filename_allowed(self, filename, extensions=None):
151 """Checks if a filename has an allowed extension
150 """Checks if a filename has an allowed extension
152
151
153 :param filename: base name of file
152 :param filename: base name of file
154 :param extensions: iterable of extensions (or self.extensions)
153 :param extensions: iterable of extensions (or self.extensions)
155 """
154 """
156 _, ext = os.path.splitext(filename)
155 _, ext = os.path.splitext(filename)
157 return self.extension_allowed(ext, extensions)
156 return self.extension_allowed(ext, extensions)
158
157
159 def extension_allowed(self, ext, extensions=None):
158 def extension_allowed(self, ext, extensions=None):
160 """
159 """
161 Checks if an extension is permitted. Both e.g. ".jpg" and
160 Checks if an extension is permitted. Both e.g. ".jpg" and
162 "jpg" can be passed in. Extension lookup is case-insensitive.
161 "jpg" can be passed in. Extension lookup is case-insensitive.
163
162
164 :param ext: extension to check
163 :param ext: extension to check
165 :param extensions: iterable of extensions to validate against (or self.extensions)
164 :param extensions: iterable of extensions to validate against (or self.extensions)
166 """
165 """
167 def normalize_ext(_ext):
166 def normalize_ext(_ext):
168 if _ext.startswith('.'):
167 if _ext.startswith('.'):
169 _ext = _ext[1:]
168 _ext = _ext[1:]
170 return _ext.lower()
169 return _ext.lower()
171
170
172 extensions = extensions or self.extensions
171 extensions = extensions or self.extensions
173 if not extensions:
172 if not extensions:
174 return True
173 return True
175
174
176 ext = normalize_ext(ext)
175 ext = normalize_ext(ext)
177
176
178 return ext in [normalize_ext(x) for x in extensions]
177 return ext in [normalize_ext(x) for x in extensions]
179
178
180 def save_file(self, file_obj, filename, directory=None, extensions=None,
179 def save_file(self, file_obj, filename, directory=None, extensions=None,
181 extra_metadata=None, max_filesize=None, randomized_name=True, **kwargs):
180 extra_metadata=None, max_filesize=None, randomized_name=True, **kwargs):
182 """
181 """
183 Saves a file object to the uploads location.
182 Saves a file object to the uploads location.
184 Returns the resolved filename, i.e. the directory +
183 Returns the resolved filename, i.e. the directory +
185 the (randomized/incremented) base name.
184 the (randomized/incremented) base name.
186
185
187 :param file_obj: **cgi.FieldStorage** object (or similar)
186 :param file_obj: **cgi.FieldStorage** object (or similar)
188 :param filename: original filename
187 :param filename: original filename
189 :param directory: relative path of sub-directory
188 :param directory: relative path of sub-directory
190 :param extensions: iterable of allowed extensions, if not default
189 :param extensions: iterable of allowed extensions, if not default
191 :param max_filesize: maximum size of file that should be allowed
190 :param max_filesize: maximum size of file that should be allowed
192 :param randomized_name: generate random generated UID or fixed based on the filename
191 :param randomized_name: generate random generated UID or fixed based on the filename
193 :param extra_metadata: extra JSON metadata to store next to the file with .meta suffix
192 :param extra_metadata: extra JSON metadata to store next to the file with .meta suffix
194
193
195 """
194 """
196
195
197 extensions = extensions or self.extensions
196 extensions = extensions or self.extensions
198
197
199 if not self.filename_allowed(filename, extensions):
198 if not self.filename_allowed(filename, extensions):
200 raise FileNotAllowedException()
199 raise FileNotAllowedException()
201
200
202 if directory:
201 if directory:
203 dest_directory = os.path.join(self.base_path, directory)
202 dest_directory = os.path.join(self.base_path, directory)
204 else:
203 else:
205 dest_directory = self.base_path
204 dest_directory = self.base_path
206
205
207 safe_make_dirs(dest_directory)
206 safe_make_dirs(dest_directory)
208
207
209 uid_filename = utils.uid_filename(filename, randomized=randomized_name)
208 uid_filename = utils.uid_filename(filename, randomized=randomized_name)
210
209
211 # resolve also produces special sub-dir for file optimized store
210 # resolve also produces special sub-dir for file optimized store
212 filename, path = self.resolve_name(uid_filename, dest_directory)
211 filename, path = self.resolve_name(uid_filename, dest_directory)
213 stored_file_dir = os.path.dirname(path)
212 stored_file_dir = os.path.dirname(path)
214
213
215 file_obj.seek(0)
214 no_body_seek = kwargs.pop('no_body_seek', False)
215 if no_body_seek:
216 pass
217 else:
218 file_obj.seek(0)
216
219
217 with open(path, "wb") as dest:
220 with open(path, "wb") as dest:
218 shutil.copyfileobj(file_obj, dest)
221 length = 256 * 1024
222 while 1:
223 buf = file_obj.read(length)
224 if not buf:
225 break
226 dest.write(buf)
219
227
220 metadata = {}
228 metadata = {}
221 if extra_metadata:
229 if extra_metadata:
222 metadata = extra_metadata
230 metadata = extra_metadata
223
231
224 size = os.stat(path).st_size
232 size = os.stat(path).st_size
225
233
226 if max_filesize and size > max_filesize:
234 if max_filesize and size > max_filesize:
227 # free up the copied file, and raise exc
235 # free up the copied file, and raise exc
228 os.remove(path)
236 os.remove(path)
229 raise FileOverSizeException()
237 raise FileOverSizeException()
230
238
231 file_hash = self.calculate_path_hash(path)
239 file_hash = self.calculate_path_hash(path)
232
240
233 metadata.update({
241 metadata.update({
234 "filename": filename,
242 "filename": filename,
235 "size": size,
243 "size": size,
236 "time": time.time(),
244 "time": time.time(),
237 "sha256": file_hash,
245 "sha256": file_hash,
238 "meta_ver": METADATA_VER
246 "meta_ver": METADATA_VER
239 })
247 })
240
248
241 filename_meta = filename + '.meta'
249 filename_meta = filename + '.meta'
242 with open(os.path.join(stored_file_dir, filename_meta), "wb") as dest_meta:
250 with open(os.path.join(stored_file_dir, filename_meta), "wb") as dest_meta:
243 dest_meta.write(json.dumps(metadata))
251 dest_meta.write(json.dumps(metadata))
244
252
245 if directory:
253 if directory:
246 filename = os.path.join(directory, filename)
254 filename = os.path.join(directory, filename)
247
255
248 return filename, metadata
256 return filename, metadata
249
257
250 def get_metadata(self, filename):
258 def get_metadata(self, filename):
251 """
259 """
252 Reads JSON stored metadata for a file
260 Reads JSON stored metadata for a file
253
261
254 :param filename:
262 :param filename:
255 :return:
263 :return:
256 """
264 """
257 filename = self.store_path(filename)
265 filename = self.store_path(filename)
258 filename_meta = filename + '.meta'
266 filename_meta = filename + '.meta'
259
267
260 with open(filename_meta, "rb") as source_meta:
268 with open(filename_meta, "rb") as source_meta:
261 return json.loads(source_meta.read())
269 return json.loads(source_meta.read())
@@ -1,195 +1,202 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import logging
20 import logging
21
21
22 from pyramid.view import view_config
22
23 from pyramid.response import FileResponse
23 from pyramid.response import FileResponse
24 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
24 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps.file_store import utils
27 from rhodecode.apps.file_store import utils
28 from rhodecode.apps.file_store.exceptions import (
28 from rhodecode.apps.file_store.exceptions import (
29 FileNotAllowedException, FileOverSizeException)
29 FileNotAllowedException, FileOverSizeException)
30
30
31 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
32 from rhodecode.lib import audit_logger
32 from rhodecode.lib import audit_logger
33 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
34 CSRFRequired, NotAnonymous, HasRepoPermissionAny, HasRepoGroupPermissionAny,
34 CSRFRequired, NotAnonymous, HasRepoPermissionAny, HasRepoGroupPermissionAny,
35 LoginRequired)
35 LoginRequired)
36 from rhodecode.lib.vcs.conf.mtypes import get_mimetypes_db
36 from rhodecode.lib.vcs.conf.mtypes import get_mimetypes_db
37 from rhodecode.model.db import Session, FileStore, UserApiKeys
37 from rhodecode.model.db import Session, FileStore, UserApiKeys
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41
41
42 class FileStoreView(BaseAppView):
42 class FileStoreView(BaseAppView):
43 upload_key = 'store_file'
43 upload_key = 'store_file'
44
44
45 def load_default_context(self):
45 def load_default_context(self):
46 c = self._get_local_tmpl_context()
46 c = self._get_local_tmpl_context()
47 self.storage = utils.get_file_storage(self.request.registry.settings)
47 self.storage = utils.get_file_storage(self.request.registry.settings)
48 return c
48 return c
49
49
50 def _guess_type(self, file_name):
50 def _guess_type(self, file_name):
51 """
51 """
52 Our own type guesser for mimetypes using the rich DB
52 Our own type guesser for mimetypes using the rich DB
53 """
53 """
54 if not hasattr(self, 'db'):
54 if not hasattr(self, 'db'):
55 self.db = get_mimetypes_db()
55 self.db = get_mimetypes_db()
56 _content_type, _encoding = self.db.guess_type(file_name, strict=False)
56 _content_type, _encoding = self.db.guess_type(file_name, strict=False)
57 return _content_type, _encoding
57 return _content_type, _encoding
58
58
59 def _serve_file(self, file_uid):
59 def _serve_file(self, file_uid):
60
61 if not self.storage.exists(file_uid):
60 if not self.storage.exists(file_uid):
62 store_path = self.storage.store_path(file_uid)
61 store_path = self.storage.store_path(file_uid)
63 log.debug('File with FID:%s not found in the store under `%s`',
62 log.debug('File with FID:%s not found in the store under `%s`',
64 file_uid, store_path)
63 file_uid, store_path)
65 raise HTTPNotFound()
64 raise HTTPNotFound()
66
65
67 db_obj = FileStore.get_by_store_uid(file_uid, safe=True)
66 db_obj = FileStore.get_by_store_uid(file_uid, safe=True)
68 if not db_obj:
67 if not db_obj:
69 raise HTTPNotFound()
68 raise HTTPNotFound()
70
69
71 # private upload for user
70 # private upload for user
72 if db_obj.check_acl and db_obj.scope_user_id:
71 if db_obj.check_acl and db_obj.scope_user_id:
73 log.debug('Artifact: checking scope access for bound artifact user: `%s`',
72 log.debug('Artifact: checking scope access for bound artifact user: `%s`',
74 db_obj.scope_user_id)
73 db_obj.scope_user_id)
75 user = db_obj.user
74 user = db_obj.user
76 if self._rhodecode_db_user.user_id != user.user_id:
75 if self._rhodecode_db_user.user_id != user.user_id:
77 log.warning('Access to file store object forbidden')
76 log.warning('Access to file store object forbidden')
78 raise HTTPNotFound()
77 raise HTTPNotFound()
79
78
80 # scoped to repository permissions
79 # scoped to repository permissions
81 if db_obj.check_acl and db_obj.scope_repo_id:
80 if db_obj.check_acl and db_obj.scope_repo_id:
82 log.debug('Artifact: checking scope access for bound artifact repo: `%s`',
81 log.debug('Artifact: checking scope access for bound artifact repo: `%s`',
83 db_obj.scope_repo_id)
82 db_obj.scope_repo_id)
84 repo = db_obj.repo
83 repo = db_obj.repo
85 perm_set = ['repository.read', 'repository.write', 'repository.admin']
84 perm_set = ['repository.read', 'repository.write', 'repository.admin']
86 has_perm = HasRepoPermissionAny(*perm_set)(repo.repo_name, 'FileStore check')
85 has_perm = HasRepoPermissionAny(*perm_set)(repo.repo_name, 'FileStore check')
87 if not has_perm:
86 if not has_perm:
88 log.warning('Access to file store object `%s` forbidden', file_uid)
87 log.warning('Access to file store object `%s` forbidden', file_uid)
89 raise HTTPNotFound()
88 raise HTTPNotFound()
90
89
91 # scoped to repository group permissions
90 # scoped to repository group permissions
92 if db_obj.check_acl and db_obj.scope_repo_group_id:
91 if db_obj.check_acl and db_obj.scope_repo_group_id:
93 log.debug('Artifact: checking scope access for bound artifact repo group: `%s`',
92 log.debug('Artifact: checking scope access for bound artifact repo group: `%s`',
94 db_obj.scope_repo_group_id)
93 db_obj.scope_repo_group_id)
95 repo_group = db_obj.repo_group
94 repo_group = db_obj.repo_group
96 perm_set = ['group.read', 'group.write', 'group.admin']
95 perm_set = ['group.read', 'group.write', 'group.admin']
97 has_perm = HasRepoGroupPermissionAny(*perm_set)(repo_group.group_name, 'FileStore check')
96 has_perm = HasRepoGroupPermissionAny(*perm_set)(repo_group.group_name, 'FileStore check')
98 if not has_perm:
97 if not has_perm:
99 log.warning('Access to file store object `%s` forbidden', file_uid)
98 log.warning('Access to file store object `%s` forbidden', file_uid)
100 raise HTTPNotFound()
99 raise HTTPNotFound()
101
100
102 FileStore.bump_access_counter(file_uid)
101 FileStore.bump_access_counter(file_uid)
103
102
104 file_path = self.storage.store_path(file_uid)
103 file_path = self.storage.store_path(file_uid)
105 content_type = 'application/octet-stream'
104 content_type = 'application/octet-stream'
106 content_encoding = None
105 content_encoding = None
107
106
108 _content_type, _encoding = self._guess_type(file_path)
107 _content_type, _encoding = self._guess_type(file_path)
109 if _content_type:
108 if _content_type:
110 content_type = _content_type
109 content_type = _content_type
111
110
112 # For file store we don't submit any session data, this logic tells the
111 # For file store we don't submit any session data, this logic tells the
113 # Session lib to skip it
112 # Session lib to skip it
114 setattr(self.request, '_file_response', True)
113 setattr(self.request, '_file_response', True)
115 return FileResponse(file_path, request=self.request,
114 response = FileResponse(
116 content_type=content_type, content_encoding=content_encoding)
115 file_path, request=self.request,
116 content_type=content_type, content_encoding=content_encoding)
117
118 file_name = db_obj.file_display_name
119
120 response.headers["Content-Disposition"] = (
121 'attachment; filename="{}"'.format(str(file_name))
122 )
123 response.headers["X-RC-Artifact-Id"] = str(db_obj.file_store_id)
124 response.headers["X-RC-Artifact-Desc"] = str(db_obj.file_description)
125 response.headers["X-RC-Artifact-Sha256"] = str(db_obj.file_hash)
126 return response
117
127
118 @LoginRequired()
128 @LoginRequired()
119 @NotAnonymous()
129 @NotAnonymous()
120 @CSRFRequired()
130 @CSRFRequired()
121 @view_config(route_name='upload_file', request_method='POST', renderer='json_ext')
122 def upload_file(self):
131 def upload_file(self):
123 self.load_default_context()
132 self.load_default_context()
124 file_obj = self.request.POST.get(self.upload_key)
133 file_obj = self.request.POST.get(self.upload_key)
125
134
126 if file_obj is None:
135 if file_obj is None:
127 return {'store_fid': None,
136 return {'store_fid': None,
128 'access_path': None,
137 'access_path': None,
129 'error': '{} data field is missing'.format(self.upload_key)}
138 'error': '{} data field is missing'.format(self.upload_key)}
130
139
131 if not hasattr(file_obj, 'filename'):
140 if not hasattr(file_obj, 'filename'):
132 return {'store_fid': None,
141 return {'store_fid': None,
133 'access_path': None,
142 'access_path': None,
134 'error': 'filename cannot be read from the data field'}
143 'error': 'filename cannot be read from the data field'}
135
144
136 filename = file_obj.filename
145 filename = file_obj.filename
137
146
138 metadata = {
147 metadata = {
139 'user_uploaded': {'username': self._rhodecode_user.username,
148 'user_uploaded': {'username': self._rhodecode_user.username,
140 'user_id': self._rhodecode_user.user_id,
149 'user_id': self._rhodecode_user.user_id,
141 'ip': self._rhodecode_user.ip_addr}}
150 'ip': self._rhodecode_user.ip_addr}}
142 try:
151 try:
143 store_uid, metadata = self.storage.save_file(
152 store_uid, metadata = self.storage.save_file(
144 file_obj.file, filename, extra_metadata=metadata)
153 file_obj.file, filename, extra_metadata=metadata)
145 except FileNotAllowedException:
154 except FileNotAllowedException:
146 return {'store_fid': None,
155 return {'store_fid': None,
147 'access_path': None,
156 'access_path': None,
148 'error': 'File {} is not allowed.'.format(filename)}
157 'error': 'File {} is not allowed.'.format(filename)}
149
158
150 except FileOverSizeException:
159 except FileOverSizeException:
151 return {'store_fid': None,
160 return {'store_fid': None,
152 'access_path': None,
161 'access_path': None,
153 'error': 'File {} is exceeding allowed limit.'.format(filename)}
162 'error': 'File {} is exceeding allowed limit.'.format(filename)}
154
163
155 try:
164 try:
156 entry = FileStore.create(
165 entry = FileStore.create(
157 file_uid=store_uid, filename=metadata["filename"],
166 file_uid=store_uid, filename=metadata["filename"],
158 file_hash=metadata["sha256"], file_size=metadata["size"],
167 file_hash=metadata["sha256"], file_size=metadata["size"],
159 file_description=u'upload attachment',
168 file_description=u'upload attachment',
160 check_acl=False, user_id=self._rhodecode_user.user_id
169 check_acl=False, user_id=self._rhodecode_user.user_id
161 )
170 )
162 Session().add(entry)
171 Session().add(entry)
163 Session().commit()
172 Session().commit()
164 log.debug('Stored upload in DB as %s', entry)
173 log.debug('Stored upload in DB as %s', entry)
165 except Exception:
174 except Exception:
166 log.exception('Failed to store file %s', filename)
175 log.exception('Failed to store file %s', filename)
167 return {'store_fid': None,
176 return {'store_fid': None,
168 'access_path': None,
177 'access_path': None,
169 'error': 'File {} failed to store in DB.'.format(filename)}
178 'error': 'File {} failed to store in DB.'.format(filename)}
170
179
171 return {'store_fid': store_uid,
180 return {'store_fid': store_uid,
172 'access_path': h.route_path('download_file', fid=store_uid)}
181 'access_path': h.route_path('download_file', fid=store_uid)}
173
182
174 # ACL is checked by scopes, if no scope the file is accessible to all
183 # ACL is checked by scopes, if no scope the file is accessible to all
175 @view_config(route_name='download_file')
176 def download_file(self):
184 def download_file(self):
177 self.load_default_context()
185 self.load_default_context()
178 file_uid = self.request.matchdict['fid']
186 file_uid = self.request.matchdict['fid']
179 log.debug('Requesting FID:%s from store %s', file_uid, self.storage)
187 log.debug('Requesting FID:%s from store %s', file_uid, self.storage)
180 return self._serve_file(file_uid)
188 return self._serve_file(file_uid)
181
189
182 # in addition to @LoginRequired ACL is checked by scopes
190 # in addition to @LoginRequired ACL is checked by scopes
183 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_ARTIFACT_DOWNLOAD])
191 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_ARTIFACT_DOWNLOAD])
184 @NotAnonymous()
192 @NotAnonymous()
185 @view_config(route_name='download_file_by_token')
186 def download_file_by_token(self):
193 def download_file_by_token(self):
187 """
194 """
188 Special view that allows to access the download file by special URL that
195 Special view that allows to access the download file by special URL that
189 is stored inside the URL.
196 is stored inside the URL.
190
197
191 http://example.com/_file_store/token-download/TOKEN/FILE_UID
198 http://example.com/_file_store/token-download/TOKEN/FILE_UID
192 """
199 """
193 self.load_default_context()
200 self.load_default_context()
194 file_uid = self.request.matchdict['fid']
201 file_uid = self.request.matchdict['fid']
195 return self._serve_file(file_uid)
202 return self._serve_file(file_uid)
@@ -1,62 +1,120 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 from rhodecode.apps._base import ADMIN_PREFIX
20 from rhodecode.apps._base import ADMIN_PREFIX
21
21
22
22
23 def admin_routes(config):
23 def admin_routes(config):
24 from rhodecode.apps.gist.views import GistView
25
24 config.add_route(
26 config.add_route(
25 name='gists_show', pattern='/gists')
27 name='gists_show', pattern='/gists')
28 config.add_view(
29 GistView,
30 attr='gist_show_all',
31 route_name='gists_show', request_method='GET',
32 renderer='rhodecode:templates/admin/gists/gist_index.mako')
33
26 config.add_route(
34 config.add_route(
27 name='gists_new', pattern='/gists/new')
35 name='gists_new', pattern='/gists/new')
36 config.add_view(
37 GistView,
38 attr='gist_new',
39 route_name='gists_new', request_method='GET',
40 renderer='rhodecode:templates/admin/gists/gist_new.mako')
41
28 config.add_route(
42 config.add_route(
29 name='gists_create', pattern='/gists/create')
43 name='gists_create', pattern='/gists/create')
44 config.add_view(
45 GistView,
46 attr='gist_create',
47 route_name='gists_create', request_method='POST',
48 renderer='rhodecode:templates/admin/gists/gist_new.mako')
30
49
31 config.add_route(
50 config.add_route(
32 name='gist_show', pattern='/gists/{gist_id}')
51 name='gist_show', pattern='/gists/{gist_id}')
52 config.add_view(
53 GistView,
54 attr='gist_show',
55 route_name='gist_show', request_method='GET',
56 renderer='rhodecode:templates/admin/gists/gist_show.mako')
57
58 config.add_route(
59 name='gist_show_rev',
60 pattern='/gists/{gist_id}/rev/{revision}')
61
62 config.add_view(
63 GistView,
64 attr='gist_show',
65 route_name='gist_show_rev', request_method='GET',
66 renderer='rhodecode:templates/admin/gists/gist_show.mako')
67
68 config.add_route(
69 name='gist_show_formatted',
70 pattern='/gists/{gist_id}/rev/{revision}/{format}')
71 config.add_view(
72 GistView,
73 attr='gist_show',
74 route_name='gist_show_formatted', request_method='GET',
75 renderer=None)
76
77 config.add_route(
78 name='gist_show_formatted_path',
79 pattern='/gists/{gist_id}/rev/{revision}/{format}/{f_path:.*}')
80 config.add_view(
81 GistView,
82 attr='gist_show',
83 route_name='gist_show_formatted_path', request_method='GET',
84 renderer=None)
33
85
34 config.add_route(
86 config.add_route(
35 name='gist_delete', pattern='/gists/{gist_id}/delete')
87 name='gist_delete', pattern='/gists/{gist_id}/delete')
88 config.add_view(
89 GistView,
90 attr='gist_delete',
91 route_name='gist_delete', request_method='POST')
36
92
37 config.add_route(
93 config.add_route(
38 name='gist_edit', pattern='/gists/{gist_id}/edit')
94 name='gist_edit', pattern='/gists/{gist_id}/edit')
95 config.add_view(
96 GistView,
97 attr='gist_edit',
98 route_name='gist_edit', request_method='GET',
99 renderer='rhodecode:templates/admin/gists/gist_edit.mako')
100
101 config.add_route(
102 name='gist_update', pattern='/gists/{gist_id}/update')
103 config.add_view(
104 GistView,
105 attr='gist_update',
106 route_name='gist_update', request_method='POST',
107 renderer='rhodecode:templates/admin/gists/gist_edit.mako')
39
108
40 config.add_route(
109 config.add_route(
41 name='gist_edit_check_revision',
110 name='gist_edit_check_revision',
42 pattern='/gists/{gist_id}/edit/check_revision')
111 pattern='/gists/{gist_id}/edit/check_revision')
43
112 config.add_view(
44 config.add_route(
113 GistView,
45 name='gist_update', pattern='/gists/{gist_id}/update')
114 attr='gist_edit_check_revision',
46
115 route_name='gist_edit_check_revision', request_method='GET',
47 config.add_route(
116 renderer='json_ext')
48 name='gist_show_rev',
49 pattern='/gists/{gist_id}/{revision}')
50 config.add_route(
51 name='gist_show_formatted',
52 pattern='/gists/{gist_id}/{revision}/{format}')
53
54 config.add_route(
55 name='gist_show_formatted_path',
56 pattern='/gists/{gist_id}/{revision}/{format}/{f_path:.*}')
57
117
58
118
59 def includeme(config):
119 def includeme(config):
60 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
120 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
61 # Scan module for configuration decorators.
62 config.scan('.views', ignore='.tests')
@@ -1,391 +1,391 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import mock
21 import mock
22 import pytest
22 import pytest
23
23
24 from rhodecode.lib import helpers as h
24 from rhodecode.lib import helpers as h
25 from rhodecode.model.db import User, Gist
25 from rhodecode.model.db import User, Gist
26 from rhodecode.model.gist import GistModel
26 from rhodecode.model.gist import GistModel
27 from rhodecode.model.meta import Session
27 from rhodecode.model.meta import Session
28 from rhodecode.tests import (
28 from rhodecode.tests import (
29 TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
29 TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
30 TestController, assert_session_flash)
30 TestController, assert_session_flash)
31
31
32
32
33 def route_path(name, params=None, **kwargs):
33 def route_path(name, params=None, **kwargs):
34 import urllib
34 import urllib
35 from rhodecode.apps._base import ADMIN_PREFIX
35 from rhodecode.apps._base import ADMIN_PREFIX
36
36
37 base_url = {
37 base_url = {
38 'gists_show': ADMIN_PREFIX + '/gists',
38 'gists_show': ADMIN_PREFIX + '/gists',
39 'gists_new': ADMIN_PREFIX + '/gists/new',
39 'gists_new': ADMIN_PREFIX + '/gists/new',
40 'gists_create': ADMIN_PREFIX + '/gists/create',
40 'gists_create': ADMIN_PREFIX + '/gists/create',
41 'gist_show': ADMIN_PREFIX + '/gists/{gist_id}',
41 'gist_show': ADMIN_PREFIX + '/gists/{gist_id}',
42 'gist_delete': ADMIN_PREFIX + '/gists/{gist_id}/delete',
42 'gist_delete': ADMIN_PREFIX + '/gists/{gist_id}/delete',
43 'gist_edit': ADMIN_PREFIX + '/gists/{gist_id}/edit',
43 'gist_edit': ADMIN_PREFIX + '/gists/{gist_id}/edit',
44 'gist_edit_check_revision': ADMIN_PREFIX + '/gists/{gist_id}/edit/check_revision',
44 'gist_edit_check_revision': ADMIN_PREFIX + '/gists/{gist_id}/edit/check_revision',
45 'gist_update': ADMIN_PREFIX + '/gists/{gist_id}/update',
45 'gist_update': ADMIN_PREFIX + '/gists/{gist_id}/update',
46 'gist_show_rev': ADMIN_PREFIX + '/gists/{gist_id}/{revision}',
46 'gist_show_rev': ADMIN_PREFIX + '/gists/{gist_id}/rev/{revision}',
47 'gist_show_formatted': ADMIN_PREFIX + '/gists/{gist_id}/{revision}/{format}',
47 'gist_show_formatted': ADMIN_PREFIX + '/gists/{gist_id}/rev/{revision}/{format}',
48 'gist_show_formatted_path': ADMIN_PREFIX + '/gists/{gist_id}/{revision}/{format}/{f_path}',
48 'gist_show_formatted_path': ADMIN_PREFIX + '/gists/{gist_id}/rev/{revision}/{format}/{f_path}',
49
49
50 }[name].format(**kwargs)
50 }[name].format(**kwargs)
51
51
52 if params:
52 if params:
53 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
53 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
54 return base_url
54 return base_url
55
55
56
56
57 class GistUtility(object):
57 class GistUtility(object):
58
58
59 def __init__(self):
59 def __init__(self):
60 self._gist_ids = []
60 self._gist_ids = []
61
61
62 def __call__(
62 def __call__(
63 self, f_name, content='some gist', lifetime=-1,
63 self, f_name, content='some gist', lifetime=-1,
64 description='gist-desc', gist_type='public',
64 description='gist-desc', gist_type='public',
65 acl_level=Gist.GIST_PUBLIC, owner=TEST_USER_ADMIN_LOGIN):
65 acl_level=Gist.GIST_PUBLIC, owner=TEST_USER_ADMIN_LOGIN):
66 gist_mapping = {
66 gist_mapping = {
67 f_name: {'content': content}
67 f_name: {'content': content}
68 }
68 }
69 user = User.get_by_username(owner)
69 user = User.get_by_username(owner)
70 gist = GistModel().create(
70 gist = GistModel().create(
71 description, owner=user, gist_mapping=gist_mapping,
71 description, owner=user, gist_mapping=gist_mapping,
72 gist_type=gist_type, lifetime=lifetime, gist_acl_level=acl_level)
72 gist_type=gist_type, lifetime=lifetime, gist_acl_level=acl_level)
73 Session().commit()
73 Session().commit()
74 self._gist_ids.append(gist.gist_id)
74 self._gist_ids.append(gist.gist_id)
75 return gist
75 return gist
76
76
77 def cleanup(self):
77 def cleanup(self):
78 for gist_id in self._gist_ids:
78 for gist_id in self._gist_ids:
79 gist = Gist.get(gist_id)
79 gist = Gist.get(gist_id)
80 if gist:
80 if gist:
81 Session().delete(gist)
81 Session().delete(gist)
82
82
83 Session().commit()
83 Session().commit()
84
84
85
85
86 @pytest.fixture()
86 @pytest.fixture()
87 def create_gist(request):
87 def create_gist(request):
88 gist_utility = GistUtility()
88 gist_utility = GistUtility()
89 request.addfinalizer(gist_utility.cleanup)
89 request.addfinalizer(gist_utility.cleanup)
90 return gist_utility
90 return gist_utility
91
91
92
92
93 class TestGistsController(TestController):
93 class TestGistsController(TestController):
94
94
95 def test_index_empty(self, create_gist):
95 def test_index_empty(self, create_gist):
96 self.log_user()
96 self.log_user()
97 response = self.app.get(route_path('gists_show'))
97 response = self.app.get(route_path('gists_show'))
98 response.mustcontain('data: [],')
98 response.mustcontain('data: [],')
99
99
100 def test_index(self, create_gist):
100 def test_index(self, create_gist):
101 self.log_user()
101 self.log_user()
102 g1 = create_gist('gist1')
102 g1 = create_gist('gist1')
103 g2 = create_gist('gist2', lifetime=1400)
103 g2 = create_gist('gist2', lifetime=1400)
104 g3 = create_gist('gist3', description='gist3-desc')
104 g3 = create_gist('gist3', description='gist3-desc')
105 g4 = create_gist('gist4', gist_type='private').gist_access_id
105 g4 = create_gist('gist4', gist_type='private').gist_access_id
106 response = self.app.get(route_path('gists_show'))
106 response = self.app.get(route_path('gists_show'))
107
107
108 response.mustcontain(g1.gist_access_id)
108 response.mustcontain(g1.gist_access_id)
109 response.mustcontain(g2.gist_access_id)
109 response.mustcontain(g2.gist_access_id)
110 response.mustcontain(g3.gist_access_id)
110 response.mustcontain(g3.gist_access_id)
111 response.mustcontain('gist3-desc')
111 response.mustcontain('gist3-desc')
112 response.mustcontain(no=[g4])
112 response.mustcontain(no=[g4])
113
113
114 # Expiration information should be visible
114 # Expiration information should be visible
115 expires_tag = '%s' % h.age_component(
115 expires_tag = '%s' % h.age_component(
116 h.time_to_utcdatetime(g2.gist_expires))
116 h.time_to_utcdatetime(g2.gist_expires))
117 response.mustcontain(expires_tag.replace('"', '\\"'))
117 response.mustcontain(expires_tag.replace('"', '\\"'))
118
118
119 def test_index_private_gists(self, create_gist):
119 def test_index_private_gists(self, create_gist):
120 self.log_user()
120 self.log_user()
121 gist = create_gist('gist5', gist_type='private')
121 gist = create_gist('gist5', gist_type='private')
122 response = self.app.get(route_path('gists_show', params=dict(private=1)))
122 response = self.app.get(route_path('gists_show', params=dict(private=1)))
123
123
124 # and privates
124 # and privates
125 response.mustcontain(gist.gist_access_id)
125 response.mustcontain(gist.gist_access_id)
126
126
127 def test_index_show_all(self, create_gist):
127 def test_index_show_all(self, create_gist):
128 self.log_user()
128 self.log_user()
129 create_gist('gist1')
129 create_gist('gist1')
130 create_gist('gist2', lifetime=1400)
130 create_gist('gist2', lifetime=1400)
131 create_gist('gist3', description='gist3-desc')
131 create_gist('gist3', description='gist3-desc')
132 create_gist('gist4', gist_type='private')
132 create_gist('gist4', gist_type='private')
133
133
134 response = self.app.get(route_path('gists_show', params=dict(all=1)))
134 response = self.app.get(route_path('gists_show', params=dict(all=1)))
135
135
136 assert len(GistModel.get_all()) == 4
136 assert len(GistModel.get_all()) == 4
137 # and privates
137 # and privates
138 for gist in GistModel.get_all():
138 for gist in GistModel.get_all():
139 response.mustcontain(gist.gist_access_id)
139 response.mustcontain(gist.gist_access_id)
140
140
141 def test_index_show_all_hidden_from_regular(self, create_gist):
141 def test_index_show_all_hidden_from_regular(self, create_gist):
142 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
142 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
143 create_gist('gist2', gist_type='private')
143 create_gist('gist2', gist_type='private')
144 create_gist('gist3', gist_type='private')
144 create_gist('gist3', gist_type='private')
145 create_gist('gist4', gist_type='private')
145 create_gist('gist4', gist_type='private')
146
146
147 response = self.app.get(route_path('gists_show', params=dict(all=1)))
147 response = self.app.get(route_path('gists_show', params=dict(all=1)))
148
148
149 assert len(GistModel.get_all()) == 3
149 assert len(GistModel.get_all()) == 3
150 # since we don't have access to private in this view, we
150 # since we don't have access to private in this view, we
151 # should see nothing
151 # should see nothing
152 for gist in GistModel.get_all():
152 for gist in GistModel.get_all():
153 response.mustcontain(no=[gist.gist_access_id])
153 response.mustcontain(no=[gist.gist_access_id])
154
154
155 def test_create(self):
155 def test_create(self):
156 self.log_user()
156 self.log_user()
157 response = self.app.post(
157 response = self.app.post(
158 route_path('gists_create'),
158 route_path('gists_create'),
159 params={'lifetime': -1,
159 params={'lifetime': -1,
160 'content': 'gist test',
160 'content': 'gist test',
161 'filename': 'foo',
161 'filename': 'foo',
162 'gist_type': 'public',
162 'gist_type': 'public',
163 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC,
163 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC,
164 'csrf_token': self.csrf_token},
164 'csrf_token': self.csrf_token},
165 status=302)
165 status=302)
166 response = response.follow()
166 response = response.follow()
167 response.mustcontain('added file: foo')
167 response.mustcontain('added file: foo')
168 response.mustcontain('gist test')
168 response.mustcontain('gist test')
169
169
170 def test_create_with_path_with_dirs(self):
170 def test_create_with_path_with_dirs(self):
171 self.log_user()
171 self.log_user()
172 response = self.app.post(
172 response = self.app.post(
173 route_path('gists_create'),
173 route_path('gists_create'),
174 params={'lifetime': -1,
174 params={'lifetime': -1,
175 'content': 'gist test',
175 'content': 'gist test',
176 'filename': '/home/foo',
176 'filename': '/home/foo',
177 'gist_type': 'public',
177 'gist_type': 'public',
178 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC,
178 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC,
179 'csrf_token': self.csrf_token},
179 'csrf_token': self.csrf_token},
180 status=200)
180 status=200)
181 response.mustcontain('Filename /home/foo cannot be inside a directory')
181 response.mustcontain('Filename /home/foo cannot be inside a directory')
182
182
183 def test_access_expired_gist(self, create_gist):
183 def test_access_expired_gist(self, create_gist):
184 self.log_user()
184 self.log_user()
185 gist = create_gist('never-see-me')
185 gist = create_gist('never-see-me')
186 gist.gist_expires = 0 # 1970
186 gist.gist_expires = 0 # 1970
187 Session().add(gist)
187 Session().add(gist)
188 Session().commit()
188 Session().commit()
189
189
190 self.app.get(route_path('gist_show', gist_id=gist.gist_access_id),
190 self.app.get(route_path('gist_show', gist_id=gist.gist_access_id),
191 status=404)
191 status=404)
192
192
193 def test_create_private(self):
193 def test_create_private(self):
194 self.log_user()
194 self.log_user()
195 response = self.app.post(
195 response = self.app.post(
196 route_path('gists_create'),
196 route_path('gists_create'),
197 params={'lifetime': -1,
197 params={'lifetime': -1,
198 'content': 'private gist test',
198 'content': 'private gist test',
199 'filename': 'private-foo',
199 'filename': 'private-foo',
200 'gist_type': 'private',
200 'gist_type': 'private',
201 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC,
201 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC,
202 'csrf_token': self.csrf_token},
202 'csrf_token': self.csrf_token},
203 status=302)
203 status=302)
204 response = response.follow()
204 response = response.follow()
205 response.mustcontain('added file: private-foo<')
205 response.mustcontain('added file: private-foo<')
206 response.mustcontain('private gist test')
206 response.mustcontain('private gist test')
207 response.mustcontain('Private Gist')
207 response.mustcontain('Private Gist')
208 # Make sure private gists are not indexed by robots
208 # Make sure private gists are not indexed by robots
209 response.mustcontain(
209 response.mustcontain(
210 '<meta name="robots" content="noindex, nofollow">')
210 '<meta name="robots" content="noindex, nofollow">')
211
211
212 def test_create_private_acl_private(self):
212 def test_create_private_acl_private(self):
213 self.log_user()
213 self.log_user()
214 response = self.app.post(
214 response = self.app.post(
215 route_path('gists_create'),
215 route_path('gists_create'),
216 params={'lifetime': -1,
216 params={'lifetime': -1,
217 'content': 'private gist test',
217 'content': 'private gist test',
218 'filename': 'private-foo',
218 'filename': 'private-foo',
219 'gist_type': 'private',
219 'gist_type': 'private',
220 'gist_acl_level': Gist.ACL_LEVEL_PRIVATE,
220 'gist_acl_level': Gist.ACL_LEVEL_PRIVATE,
221 'csrf_token': self.csrf_token},
221 'csrf_token': self.csrf_token},
222 status=302)
222 status=302)
223 response = response.follow()
223 response = response.follow()
224 response.mustcontain('added file: private-foo<')
224 response.mustcontain('added file: private-foo<')
225 response.mustcontain('private gist test')
225 response.mustcontain('private gist test')
226 response.mustcontain('Private Gist')
226 response.mustcontain('Private Gist')
227 # Make sure private gists are not indexed by robots
227 # Make sure private gists are not indexed by robots
228 response.mustcontain(
228 response.mustcontain(
229 '<meta name="robots" content="noindex, nofollow">')
229 '<meta name="robots" content="noindex, nofollow">')
230
230
231 def test_create_with_description(self):
231 def test_create_with_description(self):
232 self.log_user()
232 self.log_user()
233 response = self.app.post(
233 response = self.app.post(
234 route_path('gists_create'),
234 route_path('gists_create'),
235 params={'lifetime': -1,
235 params={'lifetime': -1,
236 'content': 'gist test',
236 'content': 'gist test',
237 'filename': 'foo-desc',
237 'filename': 'foo-desc',
238 'description': 'gist-desc',
238 'description': 'gist-desc',
239 'gist_type': 'public',
239 'gist_type': 'public',
240 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC,
240 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC,
241 'csrf_token': self.csrf_token},
241 'csrf_token': self.csrf_token},
242 status=302)
242 status=302)
243 response = response.follow()
243 response = response.follow()
244 response.mustcontain('added file: foo-desc')
244 response.mustcontain('added file: foo-desc')
245 response.mustcontain('gist test')
245 response.mustcontain('gist test')
246 response.mustcontain('gist-desc')
246 response.mustcontain('gist-desc')
247
247
248 def test_create_public_with_anonymous_access(self):
248 def test_create_public_with_anonymous_access(self):
249 self.log_user()
249 self.log_user()
250 params = {
250 params = {
251 'lifetime': -1,
251 'lifetime': -1,
252 'content': 'gist test',
252 'content': 'gist test',
253 'filename': 'foo-desc',
253 'filename': 'foo-desc',
254 'description': 'gist-desc',
254 'description': 'gist-desc',
255 'gist_type': 'public',
255 'gist_type': 'public',
256 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC,
256 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC,
257 'csrf_token': self.csrf_token
257 'csrf_token': self.csrf_token
258 }
258 }
259 response = self.app.post(
259 response = self.app.post(
260 route_path('gists_create'), params=params, status=302)
260 route_path('gists_create'), params=params, status=302)
261 self.logout_user()
261 self.logout_user()
262 response = response.follow()
262 response = response.follow()
263 response.mustcontain('added file: foo-desc')
263 response.mustcontain('added file: foo-desc')
264 response.mustcontain('gist test')
264 response.mustcontain('gist test')
265 response.mustcontain('gist-desc')
265 response.mustcontain('gist-desc')
266
266
267 def test_new(self):
267 def test_new(self):
268 self.log_user()
268 self.log_user()
269 self.app.get(route_path('gists_new'))
269 self.app.get(route_path('gists_new'))
270
270
271 def test_delete(self, create_gist):
271 def test_delete(self, create_gist):
272 self.log_user()
272 self.log_user()
273 gist = create_gist('delete-me')
273 gist = create_gist('delete-me')
274 response = self.app.post(
274 response = self.app.post(
275 route_path('gist_delete', gist_id=gist.gist_id),
275 route_path('gist_delete', gist_id=gist.gist_id),
276 params={'csrf_token': self.csrf_token})
276 params={'csrf_token': self.csrf_token})
277 assert_session_flash(response, 'Deleted gist %s' % gist.gist_id)
277 assert_session_flash(response, 'Deleted gist %s' % gist.gist_id)
278
278
279 def test_delete_normal_user_his_gist(self, create_gist):
279 def test_delete_normal_user_his_gist(self, create_gist):
280 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
280 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
281 gist = create_gist('delete-me', owner=TEST_USER_REGULAR_LOGIN)
281 gist = create_gist('delete-me', owner=TEST_USER_REGULAR_LOGIN)
282
282
283 response = self.app.post(
283 response = self.app.post(
284 route_path('gist_delete', gist_id=gist.gist_id),
284 route_path('gist_delete', gist_id=gist.gist_id),
285 params={'csrf_token': self.csrf_token})
285 params={'csrf_token': self.csrf_token})
286 assert_session_flash(response, 'Deleted gist %s' % gist.gist_id)
286 assert_session_flash(response, 'Deleted gist %s' % gist.gist_id)
287
287
288 def test_delete_normal_user_not_his_own_gist(self, create_gist):
288 def test_delete_normal_user_not_his_own_gist(self, create_gist):
289 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
289 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
290 gist = create_gist('delete-me-2')
290 gist = create_gist('delete-me-2')
291
291
292 self.app.post(
292 self.app.post(
293 route_path('gist_delete', gist_id=gist.gist_id),
293 route_path('gist_delete', gist_id=gist.gist_id),
294 params={'csrf_token': self.csrf_token}, status=404)
294 params={'csrf_token': self.csrf_token}, status=404)
295
295
296 def test_show(self, create_gist):
296 def test_show(self, create_gist):
297 gist = create_gist('gist-show-me')
297 gist = create_gist('gist-show-me')
298 response = self.app.get(route_path('gist_show', gist_id=gist.gist_access_id))
298 response = self.app.get(route_path('gist_show', gist_id=gist.gist_access_id))
299
299
300 response.mustcontain('added file: gist-show-me<')
300 response.mustcontain('added file: gist-show-me<')
301
301
302 assert_response = response.assert_response()
302 assert_response = response.assert_response()
303 assert_response.element_equals_to(
303 assert_response.element_equals_to(
304 'div.rc-user span.user',
304 'div.rc-user span.user',
305 '<a href="/_profiles/test_admin">test_admin</a>')
305 '<a href="/_profiles/test_admin">test_admin</a>')
306
306
307 response.mustcontain('gist-desc')
307 response.mustcontain('gist-desc')
308
308
309 def test_show_without_hg(self, create_gist):
309 def test_show_without_hg(self, create_gist):
310 with mock.patch(
310 with mock.patch(
311 'rhodecode.lib.vcs.settings.ALIASES', ['git']):
311 'rhodecode.lib.vcs.settings.ALIASES', ['git']):
312 gist = create_gist('gist-show-me-again')
312 gist = create_gist('gist-show-me-again')
313 self.app.get(
313 self.app.get(
314 route_path('gist_show', gist_id=gist.gist_access_id), status=200)
314 route_path('gist_show', gist_id=gist.gist_access_id), status=200)
315
315
316 def test_show_acl_private(self, create_gist):
316 def test_show_acl_private(self, create_gist):
317 gist = create_gist('gist-show-me-only-when-im-logged-in',
317 gist = create_gist('gist-show-me-only-when-im-logged-in',
318 acl_level=Gist.ACL_LEVEL_PRIVATE)
318 acl_level=Gist.ACL_LEVEL_PRIVATE)
319 self.app.get(
319 self.app.get(
320 route_path('gist_show', gist_id=gist.gist_access_id), status=404)
320 route_path('gist_show', gist_id=gist.gist_access_id), status=404)
321
321
322 # now we log-in we should see thi gist
322 # now we log-in we should see thi gist
323 self.log_user()
323 self.log_user()
324 response = self.app.get(
324 response = self.app.get(
325 route_path('gist_show', gist_id=gist.gist_access_id))
325 route_path('gist_show', gist_id=gist.gist_access_id))
326 response.mustcontain('added file: gist-show-me-only-when-im-logged-in')
326 response.mustcontain('added file: gist-show-me-only-when-im-logged-in')
327
327
328 assert_response = response.assert_response()
328 assert_response = response.assert_response()
329 assert_response.element_equals_to(
329 assert_response.element_equals_to(
330 'div.rc-user span.user',
330 'div.rc-user span.user',
331 '<a href="/_profiles/test_admin">test_admin</a>')
331 '<a href="/_profiles/test_admin">test_admin</a>')
332 response.mustcontain('gist-desc')
332 response.mustcontain('gist-desc')
333
333
334 def test_show_as_raw(self, create_gist):
334 def test_show_as_raw(self, create_gist):
335 gist = create_gist('gist-show-me', content='GIST CONTENT')
335 gist = create_gist('gist-show-me', content='GIST CONTENT')
336 response = self.app.get(
336 response = self.app.get(
337 route_path('gist_show_formatted',
337 route_path('gist_show_formatted',
338 gist_id=gist.gist_access_id, revision='tip',
338 gist_id=gist.gist_access_id, revision='tip',
339 format='raw'))
339 format='raw'))
340 assert response.body == 'GIST CONTENT'
340 assert response.body == 'GIST CONTENT'
341
341
342 def test_show_as_raw_individual_file(self, create_gist):
342 def test_show_as_raw_individual_file(self, create_gist):
343 gist = create_gist('gist-show-me-raw', content='GIST BODY')
343 gist = create_gist('gist-show-me-raw', content='GIST BODY')
344 response = self.app.get(
344 response = self.app.get(
345 route_path('gist_show_formatted_path',
345 route_path('gist_show_formatted_path',
346 gist_id=gist.gist_access_id, format='raw',
346 gist_id=gist.gist_access_id, format='raw',
347 revision='tip', f_path='gist-show-me-raw'))
347 revision='tip', f_path='gist-show-me-raw'))
348 assert response.body == 'GIST BODY'
348 assert response.body == 'GIST BODY'
349
349
350 def test_edit_page(self, create_gist):
350 def test_edit_page(self, create_gist):
351 self.log_user()
351 self.log_user()
352 gist = create_gist('gist-for-edit', content='GIST EDIT BODY')
352 gist = create_gist('gist-for-edit', content='GIST EDIT BODY')
353 response = self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id))
353 response = self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id))
354 response.mustcontain('GIST EDIT BODY')
354 response.mustcontain('GIST EDIT BODY')
355
355
356 def test_edit_page_non_logged_user(self, create_gist):
356 def test_edit_page_non_logged_user(self, create_gist):
357 gist = create_gist('gist-for-edit', content='GIST EDIT BODY')
357 gist = create_gist('gist-for-edit', content='GIST EDIT BODY')
358 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id),
358 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id),
359 status=302)
359 status=302)
360
360
361 def test_edit_normal_user_his_gist(self, create_gist):
361 def test_edit_normal_user_his_gist(self, create_gist):
362 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
362 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
363 gist = create_gist('gist-for-edit', owner=TEST_USER_REGULAR_LOGIN)
363 gist = create_gist('gist-for-edit', owner=TEST_USER_REGULAR_LOGIN)
364 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id,
364 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id,
365 status=200))
365 status=200))
366
366
367 def test_edit_normal_user_not_his_own_gist(self, create_gist):
367 def test_edit_normal_user_not_his_own_gist(self, create_gist):
368 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
368 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
369 gist = create_gist('delete-me')
369 gist = create_gist('delete-me')
370 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id),
370 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id),
371 status=404)
371 status=404)
372
372
373 def test_user_first_name_is_escaped(self, user_util, create_gist):
373 def test_user_first_name_is_escaped(self, user_util, create_gist):
374 xss_atack_string = '"><script>alert(\'First Name\')</script>'
374 xss_atack_string = '"><script>alert(\'First Name\')</script>'
375 xss_escaped_string = h.html_escape(h.escape(xss_atack_string))
375 xss_escaped_string = h.html_escape(h.escape(xss_atack_string))
376 password = 'test'
376 password = 'test'
377 user = user_util.create_user(
377 user = user_util.create_user(
378 firstname=xss_atack_string, password=password)
378 firstname=xss_atack_string, password=password)
379 create_gist('gist', gist_type='public', owner=user.username)
379 create_gist('gist', gist_type='public', owner=user.username)
380 response = self.app.get(route_path('gists_show'))
380 response = self.app.get(route_path('gists_show'))
381 response.mustcontain(xss_escaped_string)
381 response.mustcontain(xss_escaped_string)
382
382
383 def test_user_last_name_is_escaped(self, user_util, create_gist):
383 def test_user_last_name_is_escaped(self, user_util, create_gist):
384 xss_atack_string = '"><script>alert(\'Last Name\')</script>'
384 xss_atack_string = '"><script>alert(\'Last Name\')</script>'
385 xss_escaped_string = h.html_escape(h.escape(xss_atack_string))
385 xss_escaped_string = h.html_escape(h.escape(xss_atack_string))
386 password = 'test'
386 password = 'test'
387 user = user_util.create_user(
387 user = user_util.create_user(
388 lastname=xss_atack_string, password=password)
388 lastname=xss_atack_string, password=password)
389 create_gist('gist', gist_type='public', owner=user.username)
389 create_gist('gist', gist_type='public', owner=user.username)
390 response = self.app.get(route_path('gists_show'))
390 response = self.app.get(route_path('gists_show'))
391 response.mustcontain(xss_escaped_string)
391 response.mustcontain(xss_escaped_string)
@@ -1,419 +1,386 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2020 RhodeCode GmbH
3 # Copyright (C) 2013-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import time
21 import time
22 import logging
22 import logging
23
23
24 import formencode
24 import formencode
25 import formencode.htmlfill
25 import formencode.htmlfill
26 import peppercorn
26 import peppercorn
27
27
28 from pyramid.httpexceptions import HTTPNotFound, HTTPFound, HTTPBadRequest
28 from pyramid.httpexceptions import HTTPNotFound, HTTPFound, HTTPBadRequest
29 from pyramid.view import view_config
30 from pyramid.renderers import render
29 from pyramid.renderers import render
31 from pyramid.response import Response
30 from pyramid.response import Response
32
31
33 from rhodecode.apps._base import BaseAppView
32 from rhodecode.apps._base import BaseAppView
34 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
35 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
34 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
36 from rhodecode.lib.utils2 import time_to_datetime
35 from rhodecode.lib.utils2 import time_to_datetime
37 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.ext_json import json
38 from rhodecode.lib.vcs.exceptions import VCSError, NodeNotChangedError
37 from rhodecode.lib.vcs.exceptions import VCSError, NodeNotChangedError
39 from rhodecode.model.gist import GistModel
38 from rhodecode.model.gist import GistModel
40 from rhodecode.model.meta import Session
39 from rhodecode.model.meta import Session
41 from rhodecode.model.db import Gist, User, or_
40 from rhodecode.model.db import Gist, User, or_
42 from rhodecode.model import validation_schema
41 from rhodecode.model import validation_schema
43 from rhodecode.model.validation_schema.schemas import gist_schema
42 from rhodecode.model.validation_schema.schemas import gist_schema
44
43
45
44
46 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
47
46
48
47
49 class GistView(BaseAppView):
48 class GistView(BaseAppView):
50
49
51 def load_default_context(self):
50 def load_default_context(self):
52 _ = self.request.translate
51 _ = self.request.translate
53 c = self._get_local_tmpl_context()
52 c = self._get_local_tmpl_context()
54 c.user = c.auth_user.get_instance()
53 c.user = c.auth_user.get_instance()
55
54
56 c.lifetime_values = [
55 c.lifetime_values = [
57 (-1, _('forever')),
56 (-1, _('forever')),
58 (5, _('5 minutes')),
57 (5, _('5 minutes')),
59 (60, _('1 hour')),
58 (60, _('1 hour')),
60 (60 * 24, _('1 day')),
59 (60 * 24, _('1 day')),
61 (60 * 24 * 30, _('1 month')),
60 (60 * 24 * 30, _('1 month')),
62 ]
61 ]
63
62
64 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
63 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
65 c.acl_options = [
64 c.acl_options = [
66 (Gist.ACL_LEVEL_PRIVATE, _("Requires registered account")),
65 (Gist.ACL_LEVEL_PRIVATE, _("Requires registered account")),
67 (Gist.ACL_LEVEL_PUBLIC, _("Can be accessed by anonymous users"))
66 (Gist.ACL_LEVEL_PUBLIC, _("Can be accessed by anonymous users"))
68 ]
67 ]
69
68
70 return c
69 return c
71
70
72 @LoginRequired()
71 @LoginRequired()
73 @view_config(
74 route_name='gists_show', request_method='GET',
75 renderer='rhodecode:templates/admin/gists/gist_index.mako')
76 def gist_show_all(self):
72 def gist_show_all(self):
77 c = self.load_default_context()
73 c = self.load_default_context()
78
74
79 not_default_user = self._rhodecode_user.username != User.DEFAULT_USER
75 not_default_user = self._rhodecode_user.username != User.DEFAULT_USER
80 c.show_private = self.request.GET.get('private') and not_default_user
76 c.show_private = self.request.GET.get('private') and not_default_user
81 c.show_public = self.request.GET.get('public') and not_default_user
77 c.show_public = self.request.GET.get('public') and not_default_user
82 c.show_all = self.request.GET.get('all') and self._rhodecode_user.admin
78 c.show_all = self.request.GET.get('all') and self._rhodecode_user.admin
83
79
84 gists = _gists = Gist().query()\
80 gists = _gists = Gist().query()\
85 .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\
81 .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\
86 .order_by(Gist.created_on.desc())
82 .order_by(Gist.created_on.desc())
87
83
88 c.active = 'public'
84 c.active = 'public'
89 # MY private
85 # MY private
90 if c.show_private and not c.show_public:
86 if c.show_private and not c.show_public:
91 gists = _gists.filter(Gist.gist_type == Gist.GIST_PRIVATE)\
87 gists = _gists.filter(Gist.gist_type == Gist.GIST_PRIVATE)\
92 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
88 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
93 c.active = 'my_private'
89 c.active = 'my_private'
94 # MY public
90 # MY public
95 elif c.show_public and not c.show_private:
91 elif c.show_public and not c.show_private:
96 gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)\
92 gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)\
97 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
93 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
98 c.active = 'my_public'
94 c.active = 'my_public'
99 # MY public+private
95 # MY public+private
100 elif c.show_private and c.show_public:
96 elif c.show_private and c.show_public:
101 gists = _gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC,
97 gists = _gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC,
102 Gist.gist_type == Gist.GIST_PRIVATE))\
98 Gist.gist_type == Gist.GIST_PRIVATE))\
103 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
99 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
104 c.active = 'my_all'
100 c.active = 'my_all'
105 # Show all by super-admin
101 # Show all by super-admin
106 elif c.show_all:
102 elif c.show_all:
107 c.active = 'all'
103 c.active = 'all'
108 gists = _gists
104 gists = _gists
109
105
110 # default show ALL public gists
106 # default show ALL public gists
111 if not c.show_public and not c.show_private and not c.show_all:
107 if not c.show_public and not c.show_private and not c.show_all:
112 gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)
108 gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)
113 c.active = 'public'
109 c.active = 'public'
114
110
115 _render = self.request.get_partial_renderer(
111 _render = self.request.get_partial_renderer(
116 'rhodecode:templates/data_table/_dt_elements.mako')
112 'rhodecode:templates/data_table/_dt_elements.mako')
117
113
118 data = []
114 data = []
119
115
120 for gist in gists:
116 for gist in gists:
121 data.append({
117 data.append({
122 'created_on': _render('gist_created', gist.created_on),
118 'created_on': _render('gist_created', gist.created_on),
123 'created_on_raw': gist.created_on,
119 'created_on_raw': gist.created_on,
124 'type': _render('gist_type', gist.gist_type),
120 'type': _render('gist_type', gist.gist_type),
125 'access_id': _render('gist_access_id', gist.gist_access_id, gist.owner.full_contact),
121 'access_id': _render('gist_access_id', gist.gist_access_id, gist.owner.full_contact),
126 'author': _render('gist_author', gist.owner.full_contact, gist.created_on, gist.gist_expires),
122 'author': _render('gist_author', gist.owner.full_contact, gist.created_on, gist.gist_expires),
127 'author_raw': h.escape(gist.owner.full_contact),
123 'author_raw': h.escape(gist.owner.full_contact),
128 'expires': _render('gist_expires', gist.gist_expires),
124 'expires': _render('gist_expires', gist.gist_expires),
129 'description': _render('gist_description', gist.gist_description)
125 'description': _render('gist_description', gist.gist_description)
130 })
126 })
131 c.data = json.dumps(data)
127 c.data = json.dumps(data)
132
128
133 return self._get_template_context(c)
129 return self._get_template_context(c)
134
130
135 @LoginRequired()
131 @LoginRequired()
136 @NotAnonymous()
132 @NotAnonymous()
137 @view_config(
138 route_name='gists_new', request_method='GET',
139 renderer='rhodecode:templates/admin/gists/gist_new.mako')
140 def gist_new(self):
133 def gist_new(self):
141 c = self.load_default_context()
134 c = self.load_default_context()
142 return self._get_template_context(c)
135 return self._get_template_context(c)
143
136
144 @LoginRequired()
137 @LoginRequired()
145 @NotAnonymous()
138 @NotAnonymous()
146 @CSRFRequired()
139 @CSRFRequired()
147 @view_config(
148 route_name='gists_create', request_method='POST',
149 renderer='rhodecode:templates/admin/gists/gist_new.mako')
150 def gist_create(self):
140 def gist_create(self):
151 _ = self.request.translate
141 _ = self.request.translate
152 c = self.load_default_context()
142 c = self.load_default_context()
153
143
154 data = dict(self.request.POST)
144 data = dict(self.request.POST)
155 data['filename'] = data.get('filename') or Gist.DEFAULT_FILENAME
145 data['filename'] = data.get('filename') or Gist.DEFAULT_FILENAME
156
146
157 data['nodes'] = [{
147 data['nodes'] = [{
158 'filename': data['filename'],
148 'filename': data['filename'],
159 'content': data.get('content'),
149 'content': data.get('content'),
160 'mimetype': data.get('mimetype') # None is autodetect
150 'mimetype': data.get('mimetype') # None is autodetect
161 }]
151 }]
162 gist_type = {
152 gist_type = {
163 'public': Gist.GIST_PUBLIC,
153 'public': Gist.GIST_PUBLIC,
164 'private': Gist.GIST_PRIVATE
154 'private': Gist.GIST_PRIVATE
165 }.get(data.get('gist_type')) or Gist.GIST_PRIVATE
155 }.get(data.get('gist_type')) or Gist.GIST_PRIVATE
166
156
167 data['gist_type'] = gist_type
157 data['gist_type'] = gist_type
168
158
169 data['gist_acl_level'] = (
159 data['gist_acl_level'] = (
170 data.get('gist_acl_level') or Gist.ACL_LEVEL_PRIVATE)
160 data.get('gist_acl_level') or Gist.ACL_LEVEL_PRIVATE)
171
161
172 schema = gist_schema.GistSchema().bind(
162 schema = gist_schema.GistSchema().bind(
173 lifetime_options=[x[0] for x in c.lifetime_values])
163 lifetime_options=[x[0] for x in c.lifetime_values])
174
164
175 try:
165 try:
176
166
177 schema_data = schema.deserialize(data)
167 schema_data = schema.deserialize(data)
178 # convert to safer format with just KEYs so we sure no duplicates
168 # convert to safer format with just KEYs so we sure no duplicates
179 schema_data['nodes'] = gist_schema.sequence_to_nodes(
169 schema_data['nodes'] = gist_schema.sequence_to_nodes(
180 schema_data['nodes'])
170 schema_data['nodes'])
181
171
182 gist = GistModel().create(
172 gist = GistModel().create(
183 gist_id=schema_data['gistid'], # custom access id not real ID
173 gist_id=schema_data['gistid'], # custom access id not real ID
184 description=schema_data['description'],
174 description=schema_data['description'],
185 owner=self._rhodecode_user.user_id,
175 owner=self._rhodecode_user.user_id,
186 gist_mapping=schema_data['nodes'],
176 gist_mapping=schema_data['nodes'],
187 gist_type=schema_data['gist_type'],
177 gist_type=schema_data['gist_type'],
188 lifetime=schema_data['lifetime'],
178 lifetime=schema_data['lifetime'],
189 gist_acl_level=schema_data['gist_acl_level']
179 gist_acl_level=schema_data['gist_acl_level']
190 )
180 )
191 Session().commit()
181 Session().commit()
192 new_gist_id = gist.gist_access_id
182 new_gist_id = gist.gist_access_id
193 except validation_schema.Invalid as errors:
183 except validation_schema.Invalid as errors:
194 defaults = data
184 defaults = data
195 errors = errors.asdict()
185 errors = errors.asdict()
196
186
197 if 'nodes.0.content' in errors:
187 if 'nodes.0.content' in errors:
198 errors['content'] = errors['nodes.0.content']
188 errors['content'] = errors['nodes.0.content']
199 del errors['nodes.0.content']
189 del errors['nodes.0.content']
200 if 'nodes.0.filename' in errors:
190 if 'nodes.0.filename' in errors:
201 errors['filename'] = errors['nodes.0.filename']
191 errors['filename'] = errors['nodes.0.filename']
202 del errors['nodes.0.filename']
192 del errors['nodes.0.filename']
203
193
204 data = render('rhodecode:templates/admin/gists/gist_new.mako',
194 data = render('rhodecode:templates/admin/gists/gist_new.mako',
205 self._get_template_context(c), self.request)
195 self._get_template_context(c), self.request)
206 html = formencode.htmlfill.render(
196 html = formencode.htmlfill.render(
207 data,
197 data,
208 defaults=defaults,
198 defaults=defaults,
209 errors=errors,
199 errors=errors,
210 prefix_error=False,
200 prefix_error=False,
211 encoding="UTF-8",
201 encoding="UTF-8",
212 force_defaults=False
202 force_defaults=False
213 )
203 )
214 return Response(html)
204 return Response(html)
215
205
216 except Exception:
206 except Exception:
217 log.exception("Exception while trying to create a gist")
207 log.exception("Exception while trying to create a gist")
218 h.flash(_('Error occurred during gist creation'), category='error')
208 h.flash(_('Error occurred during gist creation'), category='error')
219 raise HTTPFound(h.route_url('gists_new'))
209 raise HTTPFound(h.route_url('gists_new'))
220 raise HTTPFound(h.route_url('gist_show', gist_id=new_gist_id))
210 raise HTTPFound(h.route_url('gist_show', gist_id=new_gist_id))
221
211
222 @LoginRequired()
212 @LoginRequired()
223 @NotAnonymous()
213 @NotAnonymous()
224 @CSRFRequired()
214 @CSRFRequired()
225 @view_config(
226 route_name='gist_delete', request_method='POST')
227 def gist_delete(self):
215 def gist_delete(self):
228 _ = self.request.translate
216 _ = self.request.translate
229 gist_id = self.request.matchdict['gist_id']
217 gist_id = self.request.matchdict['gist_id']
230
218
231 c = self.load_default_context()
219 c = self.load_default_context()
232 c.gist = Gist.get_or_404(gist_id)
220 c.gist = Gist.get_or_404(gist_id)
233
221
234 owner = c.gist.gist_owner == self._rhodecode_user.user_id
222 owner = c.gist.gist_owner == self._rhodecode_user.user_id
235 if not (h.HasPermissionAny('hg.admin')() or owner):
223 if not (h.HasPermissionAny('hg.admin')() or owner):
236 log.warning('Deletion of Gist was forbidden '
224 log.warning('Deletion of Gist was forbidden '
237 'by unauthorized user: `%s`', self._rhodecode_user)
225 'by unauthorized user: `%s`', self._rhodecode_user)
238 raise HTTPNotFound()
226 raise HTTPNotFound()
239
227
240 GistModel().delete(c.gist)
228 GistModel().delete(c.gist)
241 Session().commit()
229 Session().commit()
242 h.flash(_('Deleted gist %s') % c.gist.gist_access_id, category='success')
230 h.flash(_('Deleted gist %s') % c.gist.gist_access_id, category='success')
243
231
244 raise HTTPFound(h.route_url('gists_show'))
232 raise HTTPFound(h.route_url('gists_show'))
245
233
246 def _get_gist(self, gist_id):
234 def _get_gist(self, gist_id):
247
235
248 gist = Gist.get_or_404(gist_id)
236 gist = Gist.get_or_404(gist_id)
249
237
250 # Check if this gist is expired
238 # Check if this gist is expired
251 if gist.gist_expires != -1:
239 if gist.gist_expires != -1:
252 if time.time() > gist.gist_expires:
240 if time.time() > gist.gist_expires:
253 log.error(
241 log.error(
254 'Gist expired at %s', time_to_datetime(gist.gist_expires))
242 'Gist expired at %s', time_to_datetime(gist.gist_expires))
255 raise HTTPNotFound()
243 raise HTTPNotFound()
256
244
257 # check if this gist requires a login
245 # check if this gist requires a login
258 is_default_user = self._rhodecode_user.username == User.DEFAULT_USER
246 is_default_user = self._rhodecode_user.username == User.DEFAULT_USER
259 if gist.acl_level == Gist.ACL_LEVEL_PRIVATE and is_default_user:
247 if gist.acl_level == Gist.ACL_LEVEL_PRIVATE and is_default_user:
260 log.error("Anonymous user %s tried to access protected gist `%s`",
248 log.error("Anonymous user %s tried to access protected gist `%s`",
261 self._rhodecode_user, gist_id)
249 self._rhodecode_user, gist_id)
262 raise HTTPNotFound()
250 raise HTTPNotFound()
263 return gist
251 return gist
264
252
265 @LoginRequired()
253 @LoginRequired()
266 @view_config(
267 route_name='gist_show', request_method='GET',
268 renderer='rhodecode:templates/admin/gists/gist_show.mako')
269 @view_config(
270 route_name='gist_show_rev', request_method='GET',
271 renderer='rhodecode:templates/admin/gists/gist_show.mako')
272 @view_config(
273 route_name='gist_show_formatted', request_method='GET',
274 renderer=None)
275 @view_config(
276 route_name='gist_show_formatted_path', request_method='GET',
277 renderer=None)
278 def gist_show(self):
254 def gist_show(self):
279 gist_id = self.request.matchdict['gist_id']
255 gist_id = self.request.matchdict['gist_id']
280
256
281 # TODO(marcink): expose those via matching dict
257 # TODO(marcink): expose those via matching dict
282 revision = self.request.matchdict.get('revision', 'tip')
258 revision = self.request.matchdict.get('revision', 'tip')
283 f_path = self.request.matchdict.get('f_path', None)
259 f_path = self.request.matchdict.get('f_path', None)
284 return_format = self.request.matchdict.get('format')
260 return_format = self.request.matchdict.get('format')
285
261
286 c = self.load_default_context()
262 c = self.load_default_context()
287 c.gist = self._get_gist(gist_id)
263 c.gist = self._get_gist(gist_id)
288 c.render = not self.request.GET.get('no-render', False)
264 c.render = not self.request.GET.get('no-render', False)
289
265
290 try:
266 try:
291 c.file_last_commit, c.files = GistModel().get_gist_files(
267 c.file_last_commit, c.files = GistModel().get_gist_files(
292 gist_id, revision=revision)
268 gist_id, revision=revision)
293 except VCSError:
269 except VCSError:
294 log.exception("Exception in gist show")
270 log.exception("Exception in gist show")
295 raise HTTPNotFound()
271 raise HTTPNotFound()
296
272
297 if return_format == 'raw':
273 if return_format == 'raw':
298 content = '\n\n'.join([f.content for f in c.files
274 content = '\n\n'.join([f.content for f in c.files
299 if (f_path is None or f.path == f_path)])
275 if (f_path is None or f.path == f_path)])
300 response = Response(content)
276 response = Response(content)
301 response.content_type = 'text/plain'
277 response.content_type = 'text/plain'
302 return response
278 return response
303 elif return_format:
279 elif return_format:
304 raise HTTPBadRequest()
280 raise HTTPBadRequest()
305
281
306 return self._get_template_context(c)
282 return self._get_template_context(c)
307
283
308 @LoginRequired()
284 @LoginRequired()
309 @NotAnonymous()
285 @NotAnonymous()
310 @view_config(
311 route_name='gist_edit', request_method='GET',
312 renderer='rhodecode:templates/admin/gists/gist_edit.mako')
313 def gist_edit(self):
286 def gist_edit(self):
314 _ = self.request.translate
287 _ = self.request.translate
315 gist_id = self.request.matchdict['gist_id']
288 gist_id = self.request.matchdict['gist_id']
316 c = self.load_default_context()
289 c = self.load_default_context()
317 c.gist = self._get_gist(gist_id)
290 c.gist = self._get_gist(gist_id)
318
291
319 owner = c.gist.gist_owner == self._rhodecode_user.user_id
292 owner = c.gist.gist_owner == self._rhodecode_user.user_id
320 if not (h.HasPermissionAny('hg.admin')() or owner):
293 if not (h.HasPermissionAny('hg.admin')() or owner):
321 raise HTTPNotFound()
294 raise HTTPNotFound()
322
295
323 try:
296 try:
324 c.file_last_commit, c.files = GistModel().get_gist_files(gist_id)
297 c.file_last_commit, c.files = GistModel().get_gist_files(gist_id)
325 except VCSError:
298 except VCSError:
326 log.exception("Exception in gist edit")
299 log.exception("Exception in gist edit")
327 raise HTTPNotFound()
300 raise HTTPNotFound()
328
301
329 if c.gist.gist_expires == -1:
302 if c.gist.gist_expires == -1:
330 expiry = _('never')
303 expiry = _('never')
331 else:
304 else:
332 # this cannot use timeago, since it's used in select2 as a value
305 # this cannot use timeago, since it's used in select2 as a value
333 expiry = h.age(h.time_to_datetime(c.gist.gist_expires))
306 expiry = h.age(h.time_to_datetime(c.gist.gist_expires))
334
307
335 c.lifetime_values.append(
308 c.lifetime_values.append(
336 (0, _('%(expiry)s - current value') % {'expiry': _(expiry)})
309 (0, _('%(expiry)s - current value') % {'expiry': _(expiry)})
337 )
310 )
338
311
339 return self._get_template_context(c)
312 return self._get_template_context(c)
340
313
341 @LoginRequired()
314 @LoginRequired()
342 @NotAnonymous()
315 @NotAnonymous()
343 @CSRFRequired()
316 @CSRFRequired()
344 @view_config(
345 route_name='gist_update', request_method='POST',
346 renderer='rhodecode:templates/admin/gists/gist_edit.mako')
347 def gist_update(self):
317 def gist_update(self):
348 _ = self.request.translate
318 _ = self.request.translate
349 gist_id = self.request.matchdict['gist_id']
319 gist_id = self.request.matchdict['gist_id']
350 c = self.load_default_context()
320 c = self.load_default_context()
351 c.gist = self._get_gist(gist_id)
321 c.gist = self._get_gist(gist_id)
352
322
353 owner = c.gist.gist_owner == self._rhodecode_user.user_id
323 owner = c.gist.gist_owner == self._rhodecode_user.user_id
354 if not (h.HasPermissionAny('hg.admin')() or owner):
324 if not (h.HasPermissionAny('hg.admin')() or owner):
355 raise HTTPNotFound()
325 raise HTTPNotFound()
356
326
357 data = peppercorn.parse(self.request.POST.items())
327 data = peppercorn.parse(self.request.POST.items())
358
328
359 schema = gist_schema.GistSchema()
329 schema = gist_schema.GistSchema()
360 schema = schema.bind(
330 schema = schema.bind(
361 # '0' is special value to leave lifetime untouched
331 # '0' is special value to leave lifetime untouched
362 lifetime_options=[x[0] for x in c.lifetime_values] + [0],
332 lifetime_options=[x[0] for x in c.lifetime_values] + [0],
363 )
333 )
364
334
365 try:
335 try:
366 schema_data = schema.deserialize(data)
336 schema_data = schema.deserialize(data)
367 # convert to safer format with just KEYs so we sure no duplicates
337 # convert to safer format with just KEYs so we sure no duplicates
368 schema_data['nodes'] = gist_schema.sequence_to_nodes(
338 schema_data['nodes'] = gist_schema.sequence_to_nodes(
369 schema_data['nodes'])
339 schema_data['nodes'])
370
340
371 GistModel().update(
341 GistModel().update(
372 gist=c.gist,
342 gist=c.gist,
373 description=schema_data['description'],
343 description=schema_data['description'],
374 owner=c.gist.owner,
344 owner=c.gist.owner,
375 gist_mapping=schema_data['nodes'],
345 gist_mapping=schema_data['nodes'],
376 lifetime=schema_data['lifetime'],
346 lifetime=schema_data['lifetime'],
377 gist_acl_level=schema_data['gist_acl_level']
347 gist_acl_level=schema_data['gist_acl_level']
378 )
348 )
379
349
380 Session().commit()
350 Session().commit()
381 h.flash(_('Successfully updated gist content'), category='success')
351 h.flash(_('Successfully updated gist content'), category='success')
382 except NodeNotChangedError:
352 except NodeNotChangedError:
383 # raised if nothing was changed in repo itself. We anyway then
353 # raised if nothing was changed in repo itself. We anyway then
384 # store only DB stuff for gist
354 # store only DB stuff for gist
385 Session().commit()
355 Session().commit()
386 h.flash(_('Successfully updated gist data'), category='success')
356 h.flash(_('Successfully updated gist data'), category='success')
387 except validation_schema.Invalid as errors:
357 except validation_schema.Invalid as errors:
388 errors = h.escape(errors.asdict())
358 errors = h.escape(errors.asdict())
389 h.flash(_('Error occurred during update of gist {}: {}').format(
359 h.flash(_('Error occurred during update of gist {}: {}').format(
390 gist_id, errors), category='error')
360 gist_id, errors), category='error')
391 except Exception:
361 except Exception:
392 log.exception("Exception in gist edit")
362 log.exception("Exception in gist edit")
393 h.flash(_('Error occurred during update of gist %s') % gist_id,
363 h.flash(_('Error occurred during update of gist %s') % gist_id,
394 category='error')
364 category='error')
395
365
396 raise HTTPFound(h.route_url('gist_show', gist_id=gist_id))
366 raise HTTPFound(h.route_url('gist_show', gist_id=gist_id))
397
367
398 @LoginRequired()
368 @LoginRequired()
399 @NotAnonymous()
369 @NotAnonymous()
400 @view_config(
401 route_name='gist_edit_check_revision', request_method='GET',
402 renderer='json_ext')
403 def gist_edit_check_revision(self):
370 def gist_edit_check_revision(self):
404 _ = self.request.translate
371 _ = self.request.translate
405 gist_id = self.request.matchdict['gist_id']
372 gist_id = self.request.matchdict['gist_id']
406 c = self.load_default_context()
373 c = self.load_default_context()
407 c.gist = self._get_gist(gist_id)
374 c.gist = self._get_gist(gist_id)
408
375
409 last_rev = c.gist.scm_instance().get_commit()
376 last_rev = c.gist.scm_instance().get_commit()
410 success = True
377 success = True
411 revision = self.request.GET.get('revision')
378 revision = self.request.GET.get('revision')
412
379
413 if revision != last_rev.raw_id:
380 if revision != last_rev.raw_id:
414 log.error('Last revision %s is different then submitted %s',
381 log.error('Last revision %s is different then submitted %s',
415 revision, last_rev)
382 revision, last_rev)
416 # our gist has newer version than we
383 # our gist has newer version than we
417 success = False
384 success = False
418
385
419 return {'success': success}
386 return {'success': success}
@@ -1,93 +1,147 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 from rhodecode.config import routing_links
20 from rhodecode.config import routing_links
21
21
22
22
23 class VCSCallPredicate(object):
23 class VCSCallPredicate(object):
24 def __init__(self, val, config):
24 def __init__(self, val, config):
25 self.val = val
25 self.val = val
26
26
27 def text(self):
27 def text(self):
28 return 'vcs_call route = %s' % self.val
28 return 'vcs_call route = %s' % self.val
29
29
30 phash = text
30 phash = text
31
31
32 def __call__(self, info, request):
32 def __call__(self, info, request):
33 if hasattr(request, 'vcs_call'):
33 if hasattr(request, 'vcs_call'):
34 # skip vcs calls
34 # skip vcs calls
35 return False
35 return False
36
36
37 return True
37 return True
38
38
39
39
40 def includeme(config):
40 def includeme(config):
41 from rhodecode.apps.home.views import HomeView
42
43 config.add_route_predicate(
44 'skip_vcs_call', VCSCallPredicate)
41
45
42 config.add_route(
46 config.add_route(
43 name='home',
47 name='home',
44 pattern='/')
48 pattern='/')
49 config.add_view(
50 HomeView,
51 attr='main_page',
52 route_name='home', request_method='GET',
53 renderer='rhodecode:templates/index.mako')
45
54
46 config.add_route(
55 config.add_route(
47 name='main_page_repos_data',
56 name='main_page_repos_data',
48 pattern='/_home_repos')
57 pattern='/_home_repos')
58 config.add_view(
59 HomeView,
60 attr='main_page_repos_data',
61 route_name='main_page_repos_data',
62 request_method='GET', renderer='json_ext', xhr=True)
49
63
50 config.add_route(
64 config.add_route(
51 name='main_page_repo_groups_data',
65 name='main_page_repo_groups_data',
52 pattern='/_home_repo_groups')
66 pattern='/_home_repo_groups')
67 config.add_view(
68 HomeView,
69 attr='main_page_repo_groups_data',
70 route_name='main_page_repo_groups_data',
71 request_method='GET', renderer='json_ext', xhr=True)
53
72
54 config.add_route(
73 config.add_route(
55 name='user_autocomplete_data',
74 name='user_autocomplete_data',
56 pattern='/_users')
75 pattern='/_users')
76 config.add_view(
77 HomeView,
78 attr='user_autocomplete_data',
79 route_name='user_autocomplete_data', request_method='GET',
80 renderer='json_ext', xhr=True)
57
81
58 config.add_route(
82 config.add_route(
59 name='user_group_autocomplete_data',
83 name='user_group_autocomplete_data',
60 pattern='/_user_groups')
84 pattern='/_user_groups')
85 config.add_view(
86 HomeView,
87 attr='user_group_autocomplete_data',
88 route_name='user_group_autocomplete_data', request_method='GET',
89 renderer='json_ext', xhr=True)
61
90
62 config.add_route(
91 config.add_route(
63 name='repo_list_data',
92 name='repo_list_data',
64 pattern='/_repos')
93 pattern='/_repos')
94 config.add_view(
95 HomeView,
96 attr='repo_list_data',
97 route_name='repo_list_data', request_method='GET',
98 renderer='json_ext', xhr=True)
65
99
66 config.add_route(
100 config.add_route(
67 name='repo_group_list_data',
101 name='repo_group_list_data',
68 pattern='/_repo_groups')
102 pattern='/_repo_groups')
103 config.add_view(
104 HomeView,
105 attr='repo_group_list_data',
106 route_name='repo_group_list_data', request_method='GET',
107 renderer='json_ext', xhr=True)
69
108
70 config.add_route(
109 config.add_route(
71 name='goto_switcher_data',
110 name='goto_switcher_data',
72 pattern='/_goto_data')
111 pattern='/_goto_data')
112 config.add_view(
113 HomeView,
114 attr='goto_switcher_data',
115 route_name='goto_switcher_data', request_method='GET',
116 renderer='json_ext', xhr=True)
73
117
74 config.add_route(
118 config.add_route(
75 name='markup_preview',
119 name='markup_preview',
76 pattern='/_markup_preview')
120 pattern='/_markup_preview')
121 config.add_view(
122 HomeView,
123 attr='markup_preview',
124 route_name='markup_preview', request_method='POST',
125 renderer='string', xhr=True)
77
126
78 config.add_route(
127 config.add_route(
79 name='file_preview',
128 name='file_preview',
80 pattern='/_file_preview')
129 pattern='/_file_preview')
130 config.add_view(
131 HomeView,
132 attr='file_preview',
133 route_name='file_preview', request_method='POST',
134 renderer='string', xhr=True)
81
135
82 config.add_route(
136 config.add_route(
83 name='store_user_session_value',
137 name='store_user_session_value',
84 pattern='/_store_session_attr')
138 pattern='/_store_session_attr')
139 config.add_view(
140 HomeView,
141 attr='store_user_session_attr',
142 route_name='store_user_session_value', request_method='POST',
143 renderer='string', xhr=True)
85
144
86 # register our static links via redirection mechanism
145 # register our static links via redirection mechanism
87 routing_links.connect_redirection_links(config)
146 routing_links.connect_redirection_links(config)
88
147
89 # Scan module for configuration decorators.
90 config.scan('.views', ignore='.tests')
91
92 config.add_route_predicate(
93 'skip_vcs_call', VCSCallPredicate)
@@ -1,897 +1,856 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import re
21 import re
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from pyramid.httpexceptions import HTTPNotFound
25 from pyramid.httpexceptions import HTTPNotFound
26 from pyramid.view import view_config
27
26
28 from rhodecode.apps._base import BaseAppView, DataGridAppView
27 from rhodecode.apps._base import BaseAppView, DataGridAppView
29 from rhodecode.lib import helpers as h
28 from rhodecode.lib import helpers as h
30 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
31 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator, CSRFRequired,
30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator, CSRFRequired,
32 HasRepoGroupPermissionAny, AuthUser)
31 HasRepoGroupPermissionAny, AuthUser)
33 from rhodecode.lib.codeblocks import filenode_as_lines_tokens
32 from rhodecode.lib.codeblocks import filenode_as_lines_tokens
34 from rhodecode.lib.index import searcher_from_config
33 from rhodecode.lib.index import searcher_from_config
35 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int, safe_str
34 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int, safe_str
36 from rhodecode.lib.vcs.nodes import FileNode
35 from rhodecode.lib.vcs.nodes import FileNode
37 from rhodecode.model.db import (
36 from rhodecode.model.db import (
38 func, true, or_, case, cast, in_filter_generator, String, Session,
37 func, true, or_, case, cast, in_filter_generator, String, Session,
39 Repository, RepoGroup, User, UserGroup, PullRequest)
38 Repository, RepoGroup, User, UserGroup, PullRequest)
40 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.repo import RepoModel
41 from rhodecode.model.repo_group import RepoGroupModel
40 from rhodecode.model.repo_group import RepoGroupModel
42 from rhodecode.model.user import UserModel
41 from rhodecode.model.user import UserModel
43 from rhodecode.model.user_group import UserGroupModel
42 from rhodecode.model.user_group import UserGroupModel
44
43
45 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
46
45
47
46
48 class HomeView(BaseAppView, DataGridAppView):
47 class HomeView(BaseAppView, DataGridAppView):
49
48
50 def load_default_context(self):
49 def load_default_context(self):
51 c = self._get_local_tmpl_context()
50 c = self._get_local_tmpl_context()
52 c.user = c.auth_user.get_instance()
51 c.user = c.auth_user.get_instance()
53
54 return c
52 return c
55
53
56 @LoginRequired()
54 @LoginRequired()
57 @view_config(
58 route_name='user_autocomplete_data', request_method='GET',
59 renderer='json_ext', xhr=True)
60 def user_autocomplete_data(self):
55 def user_autocomplete_data(self):
61 self.load_default_context()
56 self.load_default_context()
62 query = self.request.GET.get('query')
57 query = self.request.GET.get('query')
63 active = str2bool(self.request.GET.get('active') or True)
58 active = str2bool(self.request.GET.get('active') or True)
64 include_groups = str2bool(self.request.GET.get('user_groups'))
59 include_groups = str2bool(self.request.GET.get('user_groups'))
65 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
60 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
66 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
61 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
67
62
68 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
63 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
69 query, active, include_groups)
64 query, active, include_groups)
70
65
71 _users = UserModel().get_users(
66 _users = UserModel().get_users(
72 name_contains=query, only_active=active)
67 name_contains=query, only_active=active)
73
68
74 def maybe_skip_default_user(usr):
69 def maybe_skip_default_user(usr):
75 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
70 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
76 return False
71 return False
77 return True
72 return True
78 _users = filter(maybe_skip_default_user, _users)
73 _users = filter(maybe_skip_default_user, _users)
79
74
80 if include_groups:
75 if include_groups:
81 # extend with user groups
76 # extend with user groups
82 _user_groups = UserGroupModel().get_user_groups(
77 _user_groups = UserGroupModel().get_user_groups(
83 name_contains=query, only_active=active,
78 name_contains=query, only_active=active,
84 expand_groups=expand_groups)
79 expand_groups=expand_groups)
85 _users = _users + _user_groups
80 _users = _users + _user_groups
86
81
87 return {'suggestions': _users}
82 return {'suggestions': _users}
88
83
89 @LoginRequired()
84 @LoginRequired()
90 @NotAnonymous()
85 @NotAnonymous()
91 @view_config(
92 route_name='user_group_autocomplete_data', request_method='GET',
93 renderer='json_ext', xhr=True)
94 def user_group_autocomplete_data(self):
86 def user_group_autocomplete_data(self):
95 self.load_default_context()
87 self.load_default_context()
96 query = self.request.GET.get('query')
88 query = self.request.GET.get('query')
97 active = str2bool(self.request.GET.get('active') or True)
89 active = str2bool(self.request.GET.get('active') or True)
98 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
90 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
99
91
100 log.debug('generating user group list, query:%s, active:%s',
92 log.debug('generating user group list, query:%s, active:%s',
101 query, active)
93 query, active)
102
94
103 _user_groups = UserGroupModel().get_user_groups(
95 _user_groups = UserGroupModel().get_user_groups(
104 name_contains=query, only_active=active,
96 name_contains=query, only_active=active,
105 expand_groups=expand_groups)
97 expand_groups=expand_groups)
106 _user_groups = _user_groups
98 _user_groups = _user_groups
107
99
108 return {'suggestions': _user_groups}
100 return {'suggestions': _user_groups}
109
101
110 def _get_repo_list(self, name_contains=None, repo_type=None, repo_group_name='', limit=20):
102 def _get_repo_list(self, name_contains=None, repo_type=None, repo_group_name='', limit=20):
111 org_query = name_contains
103 org_query = name_contains
112 allowed_ids = self._rhodecode_user.repo_acl_ids(
104 allowed_ids = self._rhodecode_user.repo_acl_ids(
113 ['repository.read', 'repository.write', 'repository.admin'],
105 ['repository.read', 'repository.write', 'repository.admin'],
114 cache=True, name_filter=name_contains) or [-1]
106 cache=True, name_filter=name_contains) or [-1]
115
107
116 query = Session().query(
108 query = Session().query(
117 Repository.repo_name,
109 Repository.repo_name,
118 Repository.repo_id,
110 Repository.repo_id,
119 Repository.repo_type,
111 Repository.repo_type,
120 Repository.private,
112 Repository.private,
121 )\
113 )\
122 .filter(Repository.archived.isnot(true()))\
114 .filter(Repository.archived.isnot(true()))\
123 .filter(or_(
115 .filter(or_(
124 # generate multiple IN to fix limitation problems
116 # generate multiple IN to fix limitation problems
125 *in_filter_generator(Repository.repo_id, allowed_ids)
117 *in_filter_generator(Repository.repo_id, allowed_ids)
126 ))
118 ))
127
119
128 query = query.order_by(case(
120 query = query.order_by(case(
129 [
121 [
130 (Repository.repo_name.startswith(repo_group_name), repo_group_name+'/'),
122 (Repository.repo_name.startswith(repo_group_name), repo_group_name+'/'),
131 ],
123 ],
132 ))
124 ))
133 query = query.order_by(func.length(Repository.repo_name))
125 query = query.order_by(func.length(Repository.repo_name))
134 query = query.order_by(Repository.repo_name)
126 query = query.order_by(Repository.repo_name)
135
127
136 if repo_type:
128 if repo_type:
137 query = query.filter(Repository.repo_type == repo_type)
129 query = query.filter(Repository.repo_type == repo_type)
138
130
139 if name_contains:
131 if name_contains:
140 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
132 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
141 query = query.filter(
133 query = query.filter(
142 Repository.repo_name.ilike(ilike_expression))
134 Repository.repo_name.ilike(ilike_expression))
143 query = query.limit(limit)
135 query = query.limit(limit)
144
136
145 acl_iter = query
137 acl_iter = query
146
138
147 return [
139 return [
148 {
140 {
149 'id': obj.repo_name,
141 'id': obj.repo_name,
150 'value': org_query,
142 'value': org_query,
151 'value_display': obj.repo_name,
143 'value_display': obj.repo_name,
152 'text': obj.repo_name,
144 'text': obj.repo_name,
153 'type': 'repo',
145 'type': 'repo',
154 'repo_id': obj.repo_id,
146 'repo_id': obj.repo_id,
155 'repo_type': obj.repo_type,
147 'repo_type': obj.repo_type,
156 'private': obj.private,
148 'private': obj.private,
157 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
149 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
158 }
150 }
159 for obj in acl_iter]
151 for obj in acl_iter]
160
152
161 def _get_repo_group_list(self, name_contains=None, repo_group_name='', limit=20):
153 def _get_repo_group_list(self, name_contains=None, repo_group_name='', limit=20):
162 org_query = name_contains
154 org_query = name_contains
163 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
155 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
164 ['group.read', 'group.write', 'group.admin'],
156 ['group.read', 'group.write', 'group.admin'],
165 cache=True, name_filter=name_contains) or [-1]
157 cache=True, name_filter=name_contains) or [-1]
166
158
167 query = Session().query(
159 query = Session().query(
168 RepoGroup.group_id,
160 RepoGroup.group_id,
169 RepoGroup.group_name,
161 RepoGroup.group_name,
170 )\
162 )\
171 .filter(or_(
163 .filter(or_(
172 # generate multiple IN to fix limitation problems
164 # generate multiple IN to fix limitation problems
173 *in_filter_generator(RepoGroup.group_id, allowed_ids)
165 *in_filter_generator(RepoGroup.group_id, allowed_ids)
174 ))
166 ))
175
167
176 query = query.order_by(case(
168 query = query.order_by(case(
177 [
169 [
178 (RepoGroup.group_name.startswith(repo_group_name), repo_group_name+'/'),
170 (RepoGroup.group_name.startswith(repo_group_name), repo_group_name+'/'),
179 ],
171 ],
180 ))
172 ))
181 query = query.order_by(func.length(RepoGroup.group_name))
173 query = query.order_by(func.length(RepoGroup.group_name))
182 query = query.order_by(RepoGroup.group_name)
174 query = query.order_by(RepoGroup.group_name)
183
175
184 if name_contains:
176 if name_contains:
185 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
177 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
186 query = query.filter(
178 query = query.filter(
187 RepoGroup.group_name.ilike(ilike_expression))
179 RepoGroup.group_name.ilike(ilike_expression))
188 query = query.limit(limit)
180 query = query.limit(limit)
189
181
190 acl_iter = query
182 acl_iter = query
191
183
192 return [
184 return [
193 {
185 {
194 'id': obj.group_name,
186 'id': obj.group_name,
195 'value': org_query,
187 'value': org_query,
196 'value_display': obj.group_name,
188 'value_display': obj.group_name,
197 'text': obj.group_name,
189 'text': obj.group_name,
198 'type': 'repo_group',
190 'type': 'repo_group',
199 'repo_group_id': obj.group_id,
191 'repo_group_id': obj.group_id,
200 'url': h.route_path(
192 'url': h.route_path(
201 'repo_group_home', repo_group_name=obj.group_name)
193 'repo_group_home', repo_group_name=obj.group_name)
202 }
194 }
203 for obj in acl_iter]
195 for obj in acl_iter]
204
196
205 def _get_user_list(self, name_contains=None, limit=20):
197 def _get_user_list(self, name_contains=None, limit=20):
206 org_query = name_contains
198 org_query = name_contains
207 if not name_contains:
199 if not name_contains:
208 return [], False
200 return [], False
209
201
210 # TODO(marcink): should all logged in users be allowed to search others?
202 # TODO(marcink): should all logged in users be allowed to search others?
211 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
203 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
212 if not allowed_user_search:
204 if not allowed_user_search:
213 return [], False
205 return [], False
214
206
215 name_contains = re.compile('(?:user:[ ]?)(.+)').findall(name_contains)
207 name_contains = re.compile('(?:user:[ ]?)(.+)').findall(name_contains)
216 if len(name_contains) != 1:
208 if len(name_contains) != 1:
217 return [], False
209 return [], False
218
210
219 name_contains = name_contains[0]
211 name_contains = name_contains[0]
220
212
221 query = User.query()\
213 query = User.query()\
222 .order_by(func.length(User.username))\
214 .order_by(func.length(User.username))\
223 .order_by(User.username) \
215 .order_by(User.username) \
224 .filter(User.username != User.DEFAULT_USER)
216 .filter(User.username != User.DEFAULT_USER)
225
217
226 if name_contains:
218 if name_contains:
227 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
219 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
228 query = query.filter(
220 query = query.filter(
229 User.username.ilike(ilike_expression))
221 User.username.ilike(ilike_expression))
230 query = query.limit(limit)
222 query = query.limit(limit)
231
223
232 acl_iter = query
224 acl_iter = query
233
225
234 return [
226 return [
235 {
227 {
236 'id': obj.user_id,
228 'id': obj.user_id,
237 'value': org_query,
229 'value': org_query,
238 'value_display': 'user: `{}`'.format(obj.username),
230 'value_display': 'user: `{}`'.format(obj.username),
239 'type': 'user',
231 'type': 'user',
240 'icon_link': h.gravatar_url(obj.email, 30),
232 'icon_link': h.gravatar_url(obj.email, 30),
241 'url': h.route_path(
233 'url': h.route_path(
242 'user_profile', username=obj.username)
234 'user_profile', username=obj.username)
243 }
235 }
244 for obj in acl_iter], True
236 for obj in acl_iter], True
245
237
246 def _get_user_groups_list(self, name_contains=None, limit=20):
238 def _get_user_groups_list(self, name_contains=None, limit=20):
247 org_query = name_contains
239 org_query = name_contains
248 if not name_contains:
240 if not name_contains:
249 return [], False
241 return [], False
250
242
251 # TODO(marcink): should all logged in users be allowed to search others?
243 # TODO(marcink): should all logged in users be allowed to search others?
252 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
244 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
253 if not allowed_user_search:
245 if not allowed_user_search:
254 return [], False
246 return [], False
255
247
256 name_contains = re.compile('(?:user_group:[ ]?)(.+)').findall(name_contains)
248 name_contains = re.compile('(?:user_group:[ ]?)(.+)').findall(name_contains)
257 if len(name_contains) != 1:
249 if len(name_contains) != 1:
258 return [], False
250 return [], False
259
251
260 name_contains = name_contains[0]
252 name_contains = name_contains[0]
261
253
262 query = UserGroup.query()\
254 query = UserGroup.query()\
263 .order_by(func.length(UserGroup.users_group_name))\
255 .order_by(func.length(UserGroup.users_group_name))\
264 .order_by(UserGroup.users_group_name)
256 .order_by(UserGroup.users_group_name)
265
257
266 if name_contains:
258 if name_contains:
267 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
259 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
268 query = query.filter(
260 query = query.filter(
269 UserGroup.users_group_name.ilike(ilike_expression))
261 UserGroup.users_group_name.ilike(ilike_expression))
270 query = query.limit(limit)
262 query = query.limit(limit)
271
263
272 acl_iter = query
264 acl_iter = query
273
265
274 return [
266 return [
275 {
267 {
276 'id': obj.users_group_id,
268 'id': obj.users_group_id,
277 'value': org_query,
269 'value': org_query,
278 'value_display': 'user_group: `{}`'.format(obj.users_group_name),
270 'value_display': 'user_group: `{}`'.format(obj.users_group_name),
279 'type': 'user_group',
271 'type': 'user_group',
280 'url': h.route_path(
272 'url': h.route_path(
281 'user_group_profile', user_group_name=obj.users_group_name)
273 'user_group_profile', user_group_name=obj.users_group_name)
282 }
274 }
283 for obj in acl_iter], True
275 for obj in acl_iter], True
284
276
285 def _get_pull_request_list(self, name_contains=None, limit=20):
277 def _get_pull_request_list(self, name_contains=None, limit=20):
286 org_query = name_contains
278 org_query = name_contains
287 if not name_contains:
279 if not name_contains:
288 return [], False
280 return [], False
289
281
290 # TODO(marcink): should all logged in users be allowed to search others?
282 # TODO(marcink): should all logged in users be allowed to search others?
291 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
283 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
292 if not allowed_user_search:
284 if not allowed_user_search:
293 return [], False
285 return [], False
294
286
295 name_contains = re.compile('(?:pr:[ ]?)(.+)').findall(name_contains)
287 name_contains = re.compile('(?:pr:[ ]?)(.+)').findall(name_contains)
296 if len(name_contains) != 1:
288 if len(name_contains) != 1:
297 return [], False
289 return [], False
298
290
299 name_contains = name_contains[0]
291 name_contains = name_contains[0]
300
292
301 allowed_ids = self._rhodecode_user.repo_acl_ids(
293 allowed_ids = self._rhodecode_user.repo_acl_ids(
302 ['repository.read', 'repository.write', 'repository.admin'],
294 ['repository.read', 'repository.write', 'repository.admin'],
303 cache=True) or [-1]
295 cache=True) or [-1]
304
296
305 query = Session().query(
297 query = Session().query(
306 PullRequest.pull_request_id,
298 PullRequest.pull_request_id,
307 PullRequest.title,
299 PullRequest.title,
308 )
300 )
309 query = query.join(Repository, Repository.repo_id == PullRequest.target_repo_id)
301 query = query.join(Repository, Repository.repo_id == PullRequest.target_repo_id)
310
302
311 query = query.filter(or_(
303 query = query.filter(or_(
312 # generate multiple IN to fix limitation problems
304 # generate multiple IN to fix limitation problems
313 *in_filter_generator(Repository.repo_id, allowed_ids)
305 *in_filter_generator(Repository.repo_id, allowed_ids)
314 ))
306 ))
315
307
316 query = query.order_by(PullRequest.pull_request_id)
308 query = query.order_by(PullRequest.pull_request_id)
317
309
318 if name_contains:
310 if name_contains:
319 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
311 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
320 query = query.filter(or_(
312 query = query.filter(or_(
321 cast(PullRequest.pull_request_id, String).ilike(ilike_expression),
313 cast(PullRequest.pull_request_id, String).ilike(ilike_expression),
322 PullRequest.title.ilike(ilike_expression),
314 PullRequest.title.ilike(ilike_expression),
323 PullRequest.description.ilike(ilike_expression),
315 PullRequest.description.ilike(ilike_expression),
324 ))
316 ))
325
317
326 query = query.limit(limit)
318 query = query.limit(limit)
327
319
328 acl_iter = query
320 acl_iter = query
329
321
330 return [
322 return [
331 {
323 {
332 'id': obj.pull_request_id,
324 'id': obj.pull_request_id,
333 'value': org_query,
325 'value': org_query,
334 'value_display': 'pull request: `!{} - {}`'.format(
326 'value_display': 'pull request: `!{} - {}`'.format(
335 obj.pull_request_id, safe_str(obj.title[:50])),
327 obj.pull_request_id, safe_str(obj.title[:50])),
336 'type': 'pull_request',
328 'type': 'pull_request',
337 'url': h.route_path('pull_requests_global', pull_request_id=obj.pull_request_id)
329 'url': h.route_path('pull_requests_global', pull_request_id=obj.pull_request_id)
338 }
330 }
339 for obj in acl_iter], True
331 for obj in acl_iter], True
340
332
341 def _get_hash_commit_list(self, auth_user, searcher, query, repo=None, repo_group=None):
333 def _get_hash_commit_list(self, auth_user, searcher, query, repo=None, repo_group=None):
342 repo_name = repo_group_name = None
334 repo_name = repo_group_name = None
343 if repo:
335 if repo:
344 repo_name = repo.repo_name
336 repo_name = repo.repo_name
345 if repo_group:
337 if repo_group:
346 repo_group_name = repo_group.group_name
338 repo_group_name = repo_group.group_name
347
339
348 org_query = query
340 org_query = query
349 if not query or len(query) < 3 or not searcher:
341 if not query or len(query) < 3 or not searcher:
350 return [], False
342 return [], False
351
343
352 commit_hashes = re.compile('(?:commit:[ ]?)([0-9a-f]{2,40})').findall(query)
344 commit_hashes = re.compile('(?:commit:[ ]?)([0-9a-f]{2,40})').findall(query)
353
345
354 if len(commit_hashes) != 1:
346 if len(commit_hashes) != 1:
355 return [], False
347 return [], False
356
348
357 commit_hash = commit_hashes[0]
349 commit_hash = commit_hashes[0]
358
350
359 result = searcher.search(
351 result = searcher.search(
360 'commit_id:{}*'.format(commit_hash), 'commit', auth_user,
352 'commit_id:{}*'.format(commit_hash), 'commit', auth_user,
361 repo_name, repo_group_name, raise_on_exc=False)
353 repo_name, repo_group_name, raise_on_exc=False)
362
354
363 commits = []
355 commits = []
364 for entry in result['results']:
356 for entry in result['results']:
365 repo_data = {
357 repo_data = {
366 'repository_id': entry.get('repository_id'),
358 'repository_id': entry.get('repository_id'),
367 'repository_type': entry.get('repo_type'),
359 'repository_type': entry.get('repo_type'),
368 'repository_name': entry.get('repository'),
360 'repository_name': entry.get('repository'),
369 }
361 }
370
362
371 commit_entry = {
363 commit_entry = {
372 'id': entry['commit_id'],
364 'id': entry['commit_id'],
373 'value': org_query,
365 'value': org_query,
374 'value_display': '`{}` commit: {}'.format(
366 'value_display': '`{}` commit: {}'.format(
375 entry['repository'], entry['commit_id']),
367 entry['repository'], entry['commit_id']),
376 'type': 'commit',
368 'type': 'commit',
377 'repo': entry['repository'],
369 'repo': entry['repository'],
378 'repo_data': repo_data,
370 'repo_data': repo_data,
379
371
380 'url': h.route_path(
372 'url': h.route_path(
381 'repo_commit',
373 'repo_commit',
382 repo_name=entry['repository'], commit_id=entry['commit_id'])
374 repo_name=entry['repository'], commit_id=entry['commit_id'])
383 }
375 }
384
376
385 commits.append(commit_entry)
377 commits.append(commit_entry)
386 return commits, True
378 return commits, True
387
379
388 def _get_path_list(self, auth_user, searcher, query, repo=None, repo_group=None):
380 def _get_path_list(self, auth_user, searcher, query, repo=None, repo_group=None):
389 repo_name = repo_group_name = None
381 repo_name = repo_group_name = None
390 if repo:
382 if repo:
391 repo_name = repo.repo_name
383 repo_name = repo.repo_name
392 if repo_group:
384 if repo_group:
393 repo_group_name = repo_group.group_name
385 repo_group_name = repo_group.group_name
394
386
395 org_query = query
387 org_query = query
396 if not query or len(query) < 3 or not searcher:
388 if not query or len(query) < 3 or not searcher:
397 return [], False
389 return [], False
398
390
399 paths_re = re.compile('(?:file:[ ]?)(.+)').findall(query)
391 paths_re = re.compile('(?:file:[ ]?)(.+)').findall(query)
400 if len(paths_re) != 1:
392 if len(paths_re) != 1:
401 return [], False
393 return [], False
402
394
403 file_path = paths_re[0]
395 file_path = paths_re[0]
404
396
405 search_path = searcher.escape_specials(file_path)
397 search_path = searcher.escape_specials(file_path)
406 result = searcher.search(
398 result = searcher.search(
407 'file.raw:*{}*'.format(search_path), 'path', auth_user,
399 'file.raw:*{}*'.format(search_path), 'path', auth_user,
408 repo_name, repo_group_name, raise_on_exc=False)
400 repo_name, repo_group_name, raise_on_exc=False)
409
401
410 files = []
402 files = []
411 for entry in result['results']:
403 for entry in result['results']:
412 repo_data = {
404 repo_data = {
413 'repository_id': entry.get('repository_id'),
405 'repository_id': entry.get('repository_id'),
414 'repository_type': entry.get('repo_type'),
406 'repository_type': entry.get('repo_type'),
415 'repository_name': entry.get('repository'),
407 'repository_name': entry.get('repository'),
416 }
408 }
417
409
418 file_entry = {
410 file_entry = {
419 'id': entry['commit_id'],
411 'id': entry['commit_id'],
420 'value': org_query,
412 'value': org_query,
421 'value_display': '`{}` file: {}'.format(
413 'value_display': '`{}` file: {}'.format(
422 entry['repository'], entry['file']),
414 entry['repository'], entry['file']),
423 'type': 'file',
415 'type': 'file',
424 'repo': entry['repository'],
416 'repo': entry['repository'],
425 'repo_data': repo_data,
417 'repo_data': repo_data,
426
418
427 'url': h.route_path(
419 'url': h.route_path(
428 'repo_files',
420 'repo_files',
429 repo_name=entry['repository'], commit_id=entry['commit_id'],
421 repo_name=entry['repository'], commit_id=entry['commit_id'],
430 f_path=entry['file'])
422 f_path=entry['file'])
431 }
423 }
432
424
433 files.append(file_entry)
425 files.append(file_entry)
434 return files, True
426 return files, True
435
427
436 @LoginRequired()
428 @LoginRequired()
437 @view_config(
438 route_name='repo_list_data', request_method='GET',
439 renderer='json_ext', xhr=True)
440 def repo_list_data(self):
429 def repo_list_data(self):
441 _ = self.request.translate
430 _ = self.request.translate
442 self.load_default_context()
431 self.load_default_context()
443
432
444 query = self.request.GET.get('query')
433 query = self.request.GET.get('query')
445 repo_type = self.request.GET.get('repo_type')
434 repo_type = self.request.GET.get('repo_type')
446 log.debug('generating repo list, query:%s, repo_type:%s',
435 log.debug('generating repo list, query:%s, repo_type:%s',
447 query, repo_type)
436 query, repo_type)
448
437
449 res = []
438 res = []
450 repos = self._get_repo_list(query, repo_type=repo_type)
439 repos = self._get_repo_list(query, repo_type=repo_type)
451 if repos:
440 if repos:
452 res.append({
441 res.append({
453 'text': _('Repositories'),
442 'text': _('Repositories'),
454 'children': repos
443 'children': repos
455 })
444 })
456
445
457 data = {
446 data = {
458 'more': False,
447 'more': False,
459 'results': res
448 'results': res
460 }
449 }
461 return data
450 return data
462
451
463 @LoginRequired()
452 @LoginRequired()
464 @view_config(
465 route_name='repo_group_list_data', request_method='GET',
466 renderer='json_ext', xhr=True)
467 def repo_group_list_data(self):
453 def repo_group_list_data(self):
468 _ = self.request.translate
454 _ = self.request.translate
469 self.load_default_context()
455 self.load_default_context()
470
456
471 query = self.request.GET.get('query')
457 query = self.request.GET.get('query')
472
458
473 log.debug('generating repo group list, query:%s',
459 log.debug('generating repo group list, query:%s',
474 query)
460 query)
475
461
476 res = []
462 res = []
477 repo_groups = self._get_repo_group_list(query)
463 repo_groups = self._get_repo_group_list(query)
478 if repo_groups:
464 if repo_groups:
479 res.append({
465 res.append({
480 'text': _('Repository Groups'),
466 'text': _('Repository Groups'),
481 'children': repo_groups
467 'children': repo_groups
482 })
468 })
483
469
484 data = {
470 data = {
485 'more': False,
471 'more': False,
486 'results': res
472 'results': res
487 }
473 }
488 return data
474 return data
489
475
490 def _get_default_search_queries(self, search_context, searcher, query):
476 def _get_default_search_queries(self, search_context, searcher, query):
491 if not searcher:
477 if not searcher:
492 return []
478 return []
493
479
494 is_es_6 = searcher.is_es_6
480 is_es_6 = searcher.is_es_6
495
481
496 queries = []
482 queries = []
497 repo_group_name, repo_name, repo_context = None, None, None
483 repo_group_name, repo_name, repo_context = None, None, None
498
484
499 # repo group context
485 # repo group context
500 if search_context.get('search_context[repo_group_name]'):
486 if search_context.get('search_context[repo_group_name]'):
501 repo_group_name = search_context.get('search_context[repo_group_name]')
487 repo_group_name = search_context.get('search_context[repo_group_name]')
502 if search_context.get('search_context[repo_name]'):
488 if search_context.get('search_context[repo_name]'):
503 repo_name = search_context.get('search_context[repo_name]')
489 repo_name = search_context.get('search_context[repo_name]')
504 repo_context = search_context.get('search_context[repo_view_type]')
490 repo_context = search_context.get('search_context[repo_view_type]')
505
491
506 if is_es_6 and repo_name:
492 if is_es_6 and repo_name:
507 # files
493 # files
508 def query_modifier():
494 def query_modifier():
509 qry = query
495 qry = query
510 return {'q': qry, 'type': 'content'}
496 return {'q': qry, 'type': 'content'}
511
497
512 label = u'File content search for `{}`'.format(h.escape(query))
498 label = u'File content search for `{}`'.format(h.escape(query))
513 file_qry = {
499 file_qry = {
514 'id': -10,
500 'id': -10,
515 'value': query,
501 'value': query,
516 'value_display': label,
502 'value_display': label,
517 'value_icon': '<i class="icon-code"></i>',
503 'value_icon': '<i class="icon-code"></i>',
518 'type': 'search',
504 'type': 'search',
519 'subtype': 'repo',
505 'subtype': 'repo',
520 'url': h.route_path('search_repo',
506 'url': h.route_path('search_repo',
521 repo_name=repo_name,
507 repo_name=repo_name,
522 _query=query_modifier())
508 _query=query_modifier())
523 }
509 }
524
510
525 # commits
511 # commits
526 def query_modifier():
512 def query_modifier():
527 qry = query
513 qry = query
528 return {'q': qry, 'type': 'commit'}
514 return {'q': qry, 'type': 'commit'}
529
515
530 label = u'Commit search for `{}`'.format(h.escape(query))
516 label = u'Commit search for `{}`'.format(h.escape(query))
531 commit_qry = {
517 commit_qry = {
532 'id': -20,
518 'id': -20,
533 'value': query,
519 'value': query,
534 'value_display': label,
520 'value_display': label,
535 'value_icon': '<i class="icon-history"></i>',
521 'value_icon': '<i class="icon-history"></i>',
536 'type': 'search',
522 'type': 'search',
537 'subtype': 'repo',
523 'subtype': 'repo',
538 'url': h.route_path('search_repo',
524 'url': h.route_path('search_repo',
539 repo_name=repo_name,
525 repo_name=repo_name,
540 _query=query_modifier())
526 _query=query_modifier())
541 }
527 }
542
528
543 if repo_context in ['commit', 'commits']:
529 if repo_context in ['commit', 'commits']:
544 queries.extend([commit_qry, file_qry])
530 queries.extend([commit_qry, file_qry])
545 elif repo_context in ['files', 'summary']:
531 elif repo_context in ['files', 'summary']:
546 queries.extend([file_qry, commit_qry])
532 queries.extend([file_qry, commit_qry])
547 else:
533 else:
548 queries.extend([commit_qry, file_qry])
534 queries.extend([commit_qry, file_qry])
549
535
550 elif is_es_6 and repo_group_name:
536 elif is_es_6 and repo_group_name:
551 # files
537 # files
552 def query_modifier():
538 def query_modifier():
553 qry = query
539 qry = query
554 return {'q': qry, 'type': 'content'}
540 return {'q': qry, 'type': 'content'}
555
541
556 label = u'File content search for `{}`'.format(query)
542 label = u'File content search for `{}`'.format(query)
557 file_qry = {
543 file_qry = {
558 'id': -30,
544 'id': -30,
559 'value': query,
545 'value': query,
560 'value_display': label,
546 'value_display': label,
561 'value_icon': '<i class="icon-code"></i>',
547 'value_icon': '<i class="icon-code"></i>',
562 'type': 'search',
548 'type': 'search',
563 'subtype': 'repo_group',
549 'subtype': 'repo_group',
564 'url': h.route_path('search_repo_group',
550 'url': h.route_path('search_repo_group',
565 repo_group_name=repo_group_name,
551 repo_group_name=repo_group_name,
566 _query=query_modifier())
552 _query=query_modifier())
567 }
553 }
568
554
569 # commits
555 # commits
570 def query_modifier():
556 def query_modifier():
571 qry = query
557 qry = query
572 return {'q': qry, 'type': 'commit'}
558 return {'q': qry, 'type': 'commit'}
573
559
574 label = u'Commit search for `{}`'.format(query)
560 label = u'Commit search for `{}`'.format(query)
575 commit_qry = {
561 commit_qry = {
576 'id': -40,
562 'id': -40,
577 'value': query,
563 'value': query,
578 'value_display': label,
564 'value_display': label,
579 'value_icon': '<i class="icon-history"></i>',
565 'value_icon': '<i class="icon-history"></i>',
580 'type': 'search',
566 'type': 'search',
581 'subtype': 'repo_group',
567 'subtype': 'repo_group',
582 'url': h.route_path('search_repo_group',
568 'url': h.route_path('search_repo_group',
583 repo_group_name=repo_group_name,
569 repo_group_name=repo_group_name,
584 _query=query_modifier())
570 _query=query_modifier())
585 }
571 }
586
572
587 if repo_context in ['commit', 'commits']:
573 if repo_context in ['commit', 'commits']:
588 queries.extend([commit_qry, file_qry])
574 queries.extend([commit_qry, file_qry])
589 elif repo_context in ['files', 'summary']:
575 elif repo_context in ['files', 'summary']:
590 queries.extend([file_qry, commit_qry])
576 queries.extend([file_qry, commit_qry])
591 else:
577 else:
592 queries.extend([commit_qry, file_qry])
578 queries.extend([commit_qry, file_qry])
593
579
594 # Global, not scoped
580 # Global, not scoped
595 if not queries:
581 if not queries:
596 queries.append(
582 queries.append(
597 {
583 {
598 'id': -1,
584 'id': -1,
599 'value': query,
585 'value': query,
600 'value_display': u'File content search for: `{}`'.format(query),
586 'value_display': u'File content search for: `{}`'.format(query),
601 'value_icon': '<i class="icon-code"></i>',
587 'value_icon': '<i class="icon-code"></i>',
602 'type': 'search',
588 'type': 'search',
603 'subtype': 'global',
589 'subtype': 'global',
604 'url': h.route_path('search',
590 'url': h.route_path('search',
605 _query={'q': query, 'type': 'content'})
591 _query={'q': query, 'type': 'content'})
606 })
592 })
607 queries.append(
593 queries.append(
608 {
594 {
609 'id': -2,
595 'id': -2,
610 'value': query,
596 'value': query,
611 'value_display': u'Commit search for: `{}`'.format(query),
597 'value_display': u'Commit search for: `{}`'.format(query),
612 'value_icon': '<i class="icon-history"></i>',
598 'value_icon': '<i class="icon-history"></i>',
613 'type': 'search',
599 'type': 'search',
614 'subtype': 'global',
600 'subtype': 'global',
615 'url': h.route_path('search',
601 'url': h.route_path('search',
616 _query={'q': query, 'type': 'commit'})
602 _query={'q': query, 'type': 'commit'})
617 })
603 })
618
604
619 return queries
605 return queries
620
606
621 @LoginRequired()
607 @LoginRequired()
622 @view_config(
623 route_name='goto_switcher_data', request_method='GET',
624 renderer='json_ext', xhr=True)
625 def goto_switcher_data(self):
608 def goto_switcher_data(self):
626 c = self.load_default_context()
609 c = self.load_default_context()
627
610
628 _ = self.request.translate
611 _ = self.request.translate
629
612
630 query = self.request.GET.get('query')
613 query = self.request.GET.get('query')
631 log.debug('generating main filter data, query %s', query)
614 log.debug('generating main filter data, query %s', query)
632
615
633 res = []
616 res = []
634 if not query:
617 if not query:
635 return {'suggestions': res}
618 return {'suggestions': res}
636
619
637 def no_match(name):
620 def no_match(name):
638 return {
621 return {
639 'id': -1,
622 'id': -1,
640 'value': "",
623 'value': "",
641 'value_display': name,
624 'value_display': name,
642 'type': 'text',
625 'type': 'text',
643 'url': ""
626 'url': ""
644 }
627 }
645 searcher = searcher_from_config(self.request.registry.settings)
628 searcher = searcher_from_config(self.request.registry.settings)
646 has_specialized_search = False
629 has_specialized_search = False
647
630
648 # set repo context
631 # set repo context
649 repo = None
632 repo = None
650 repo_id = safe_int(self.request.GET.get('search_context[repo_id]'))
633 repo_id = safe_int(self.request.GET.get('search_context[repo_id]'))
651 if repo_id:
634 if repo_id:
652 repo = Repository.get(repo_id)
635 repo = Repository.get(repo_id)
653
636
654 # set group context
637 # set group context
655 repo_group = None
638 repo_group = None
656 repo_group_id = safe_int(self.request.GET.get('search_context[repo_group_id]'))
639 repo_group_id = safe_int(self.request.GET.get('search_context[repo_group_id]'))
657 if repo_group_id:
640 if repo_group_id:
658 repo_group = RepoGroup.get(repo_group_id)
641 repo_group = RepoGroup.get(repo_group_id)
659 prefix_match = False
642 prefix_match = False
660
643
661 # user: type search
644 # user: type search
662 if not prefix_match:
645 if not prefix_match:
663 users, prefix_match = self._get_user_list(query)
646 users, prefix_match = self._get_user_list(query)
664 if users:
647 if users:
665 has_specialized_search = True
648 has_specialized_search = True
666 for serialized_user in users:
649 for serialized_user in users:
667 res.append(serialized_user)
650 res.append(serialized_user)
668 elif prefix_match:
651 elif prefix_match:
669 has_specialized_search = True
652 has_specialized_search = True
670 res.append(no_match('No matching users found'))
653 res.append(no_match('No matching users found'))
671
654
672 # user_group: type search
655 # user_group: type search
673 if not prefix_match:
656 if not prefix_match:
674 user_groups, prefix_match = self._get_user_groups_list(query)
657 user_groups, prefix_match = self._get_user_groups_list(query)
675 if user_groups:
658 if user_groups:
676 has_specialized_search = True
659 has_specialized_search = True
677 for serialized_user_group in user_groups:
660 for serialized_user_group in user_groups:
678 res.append(serialized_user_group)
661 res.append(serialized_user_group)
679 elif prefix_match:
662 elif prefix_match:
680 has_specialized_search = True
663 has_specialized_search = True
681 res.append(no_match('No matching user groups found'))
664 res.append(no_match('No matching user groups found'))
682
665
683 # pr: type search
666 # pr: type search
684 if not prefix_match:
667 if not prefix_match:
685 pull_requests, prefix_match = self._get_pull_request_list(query)
668 pull_requests, prefix_match = self._get_pull_request_list(query)
686 if pull_requests:
669 if pull_requests:
687 has_specialized_search = True
670 has_specialized_search = True
688 for serialized_pull_request in pull_requests:
671 for serialized_pull_request in pull_requests:
689 res.append(serialized_pull_request)
672 res.append(serialized_pull_request)
690 elif prefix_match:
673 elif prefix_match:
691 has_specialized_search = True
674 has_specialized_search = True
692 res.append(no_match('No matching pull requests found'))
675 res.append(no_match('No matching pull requests found'))
693
676
694 # FTS commit: type search
677 # FTS commit: type search
695 if not prefix_match:
678 if not prefix_match:
696 commits, prefix_match = self._get_hash_commit_list(
679 commits, prefix_match = self._get_hash_commit_list(
697 c.auth_user, searcher, query, repo, repo_group)
680 c.auth_user, searcher, query, repo, repo_group)
698 if commits:
681 if commits:
699 has_specialized_search = True
682 has_specialized_search = True
700 unique_repos = collections.OrderedDict()
683 unique_repos = collections.OrderedDict()
701 for commit in commits:
684 for commit in commits:
702 repo_name = commit['repo']
685 repo_name = commit['repo']
703 unique_repos.setdefault(repo_name, []).append(commit)
686 unique_repos.setdefault(repo_name, []).append(commit)
704
687
705 for _repo, commits in unique_repos.items():
688 for _repo, commits in unique_repos.items():
706 for commit in commits:
689 for commit in commits:
707 res.append(commit)
690 res.append(commit)
708 elif prefix_match:
691 elif prefix_match:
709 has_specialized_search = True
692 has_specialized_search = True
710 res.append(no_match('No matching commits found'))
693 res.append(no_match('No matching commits found'))
711
694
712 # FTS file: type search
695 # FTS file: type search
713 if not prefix_match:
696 if not prefix_match:
714 paths, prefix_match = self._get_path_list(
697 paths, prefix_match = self._get_path_list(
715 c.auth_user, searcher, query, repo, repo_group)
698 c.auth_user, searcher, query, repo, repo_group)
716 if paths:
699 if paths:
717 has_specialized_search = True
700 has_specialized_search = True
718 unique_repos = collections.OrderedDict()
701 unique_repos = collections.OrderedDict()
719 for path in paths:
702 for path in paths:
720 repo_name = path['repo']
703 repo_name = path['repo']
721 unique_repos.setdefault(repo_name, []).append(path)
704 unique_repos.setdefault(repo_name, []).append(path)
722
705
723 for repo, paths in unique_repos.items():
706 for repo, paths in unique_repos.items():
724 for path in paths:
707 for path in paths:
725 res.append(path)
708 res.append(path)
726 elif prefix_match:
709 elif prefix_match:
727 has_specialized_search = True
710 has_specialized_search = True
728 res.append(no_match('No matching files found'))
711 res.append(no_match('No matching files found'))
729
712
730 # main suggestions
713 # main suggestions
731 if not has_specialized_search:
714 if not has_specialized_search:
732 repo_group_name = ''
715 repo_group_name = ''
733 if repo_group:
716 if repo_group:
734 repo_group_name = repo_group.group_name
717 repo_group_name = repo_group.group_name
735
718
736 for _q in self._get_default_search_queries(self.request.GET, searcher, query):
719 for _q in self._get_default_search_queries(self.request.GET, searcher, query):
737 res.append(_q)
720 res.append(_q)
738
721
739 repo_groups = self._get_repo_group_list(query, repo_group_name=repo_group_name)
722 repo_groups = self._get_repo_group_list(query, repo_group_name=repo_group_name)
740 for serialized_repo_group in repo_groups:
723 for serialized_repo_group in repo_groups:
741 res.append(serialized_repo_group)
724 res.append(serialized_repo_group)
742
725
743 repos = self._get_repo_list(query, repo_group_name=repo_group_name)
726 repos = self._get_repo_list(query, repo_group_name=repo_group_name)
744 for serialized_repo in repos:
727 for serialized_repo in repos:
745 res.append(serialized_repo)
728 res.append(serialized_repo)
746
729
747 if not repos and not repo_groups:
730 if not repos and not repo_groups:
748 res.append(no_match('No matches found'))
731 res.append(no_match('No matches found'))
749
732
750 return {'suggestions': res}
733 return {'suggestions': res}
751
734
752 @LoginRequired()
735 @LoginRequired()
753 @view_config(
754 route_name='home', request_method='GET',
755 renderer='rhodecode:templates/index.mako')
756 def main_page(self):
736 def main_page(self):
757 c = self.load_default_context()
737 c = self.load_default_context()
758 c.repo_group = None
738 c.repo_group = None
759 return self._get_template_context(c)
739 return self._get_template_context(c)
760
740
761 def _main_page_repo_groups_data(self, repo_group_id):
741 def _main_page_repo_groups_data(self, repo_group_id):
762 column_map = {
742 column_map = {
763 'name': 'group_name_hash',
743 'name': 'group_name_hash',
764 'desc': 'group_description',
744 'desc': 'group_description',
765 'last_change': 'updated_on',
745 'last_change': 'updated_on',
766 'owner': 'user_username',
746 'owner': 'user_username',
767 }
747 }
768 draw, start, limit = self._extract_chunk(self.request)
748 draw, start, limit = self._extract_chunk(self.request)
769 search_q, order_by, order_dir = self._extract_ordering(
749 search_q, order_by, order_dir = self._extract_ordering(
770 self.request, column_map=column_map)
750 self.request, column_map=column_map)
771 return RepoGroupModel().get_repo_groups_data_table(
751 return RepoGroupModel().get_repo_groups_data_table(
772 draw, start, limit,
752 draw, start, limit,
773 search_q, order_by, order_dir,
753 search_q, order_by, order_dir,
774 self._rhodecode_user, repo_group_id)
754 self._rhodecode_user, repo_group_id)
775
755
776 def _main_page_repos_data(self, repo_group_id):
756 def _main_page_repos_data(self, repo_group_id):
777 column_map = {
757 column_map = {
778 'name': 'repo_name',
758 'name': 'repo_name',
779 'desc': 'description',
759 'desc': 'description',
780 'last_change': 'updated_on',
760 'last_change': 'updated_on',
781 'owner': 'user_username',
761 'owner': 'user_username',
782 }
762 }
783 draw, start, limit = self._extract_chunk(self.request)
763 draw, start, limit = self._extract_chunk(self.request)
784 search_q, order_by, order_dir = self._extract_ordering(
764 search_q, order_by, order_dir = self._extract_ordering(
785 self.request, column_map=column_map)
765 self.request, column_map=column_map)
786 return RepoModel().get_repos_data_table(
766 return RepoModel().get_repos_data_table(
787 draw, start, limit,
767 draw, start, limit,
788 search_q, order_by, order_dir,
768 search_q, order_by, order_dir,
789 self._rhodecode_user, repo_group_id)
769 self._rhodecode_user, repo_group_id)
790
770
791 @LoginRequired()
771 @LoginRequired()
792 @view_config(
793 route_name='main_page_repo_groups_data',
794 request_method='GET', renderer='json_ext', xhr=True)
795 def main_page_repo_groups_data(self):
772 def main_page_repo_groups_data(self):
796 self.load_default_context()
773 self.load_default_context()
797 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
774 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
798
775
799 if repo_group_id:
776 if repo_group_id:
800 group = RepoGroup.get_or_404(repo_group_id)
777 group = RepoGroup.get_or_404(repo_group_id)
801 _perms = AuthUser.repo_group_read_perms
778 _perms = AuthUser.repo_group_read_perms
802 if not HasRepoGroupPermissionAny(*_perms)(
779 if not HasRepoGroupPermissionAny(*_perms)(
803 group.group_name, 'user is allowed to list repo group children'):
780 group.group_name, 'user is allowed to list repo group children'):
804 raise HTTPNotFound()
781 raise HTTPNotFound()
805
782
806 return self._main_page_repo_groups_data(repo_group_id)
783 return self._main_page_repo_groups_data(repo_group_id)
807
784
808 @LoginRequired()
785 @LoginRequired()
809 @view_config(
810 route_name='main_page_repos_data',
811 request_method='GET', renderer='json_ext', xhr=True)
812 def main_page_repos_data(self):
786 def main_page_repos_data(self):
813 self.load_default_context()
787 self.load_default_context()
814 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
788 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
815
789
816 if repo_group_id:
790 if repo_group_id:
817 group = RepoGroup.get_or_404(repo_group_id)
791 group = RepoGroup.get_or_404(repo_group_id)
818 _perms = AuthUser.repo_group_read_perms
792 _perms = AuthUser.repo_group_read_perms
819 if not HasRepoGroupPermissionAny(*_perms)(
793 if not HasRepoGroupPermissionAny(*_perms)(
820 group.group_name, 'user is allowed to list repo group children'):
794 group.group_name, 'user is allowed to list repo group children'):
821 raise HTTPNotFound()
795 raise HTTPNotFound()
822
796
823 return self._main_page_repos_data(repo_group_id)
797 return self._main_page_repos_data(repo_group_id)
824
798
825 @LoginRequired()
799 @LoginRequired()
826 @HasRepoGroupPermissionAnyDecorator(*AuthUser.repo_group_read_perms)
800 @HasRepoGroupPermissionAnyDecorator(*AuthUser.repo_group_read_perms)
827 @view_config(
828 route_name='repo_group_home', request_method='GET',
829 renderer='rhodecode:templates/index_repo_group.mako')
830 @view_config(
831 route_name='repo_group_home_slash', request_method='GET',
832 renderer='rhodecode:templates/index_repo_group.mako')
833 def repo_group_main_page(self):
801 def repo_group_main_page(self):
834 c = self.load_default_context()
802 c = self.load_default_context()
835 c.repo_group = self.request.db_repo_group
803 c.repo_group = self.request.db_repo_group
836 return self._get_template_context(c)
804 return self._get_template_context(c)
837
805
838 @LoginRequired()
806 @LoginRequired()
839 @CSRFRequired()
807 @CSRFRequired()
840 @view_config(
841 route_name='markup_preview', request_method='POST',
842 renderer='string', xhr=True)
843 def markup_preview(self):
808 def markup_preview(self):
844 # Technically a CSRF token is not needed as no state changes with this
809 # Technically a CSRF token is not needed as no state changes with this
845 # call. However, as this is a POST is better to have it, so automated
810 # call. However, as this is a POST is better to have it, so automated
846 # tools don't flag it as potential CSRF.
811 # tools don't flag it as potential CSRF.
847 # Post is required because the payload could be bigger than the maximum
812 # Post is required because the payload could be bigger than the maximum
848 # allowed by GET.
813 # allowed by GET.
849
814
850 text = self.request.POST.get('text')
815 text = self.request.POST.get('text')
851 renderer = self.request.POST.get('renderer') or 'rst'
816 renderer = self.request.POST.get('renderer') or 'rst'
852 if text:
817 if text:
853 return h.render(text, renderer=renderer, mentions=True)
818 return h.render(text, renderer=renderer, mentions=True)
854 return ''
819 return ''
855
820
856 @LoginRequired()
821 @LoginRequired()
857 @CSRFRequired()
822 @CSRFRequired()
858 @view_config(
859 route_name='file_preview', request_method='POST',
860 renderer='string', xhr=True)
861 def file_preview(self):
823 def file_preview(self):
862 # Technically a CSRF token is not needed as no state changes with this
824 # Technically a CSRF token is not needed as no state changes with this
863 # call. However, as this is a POST is better to have it, so automated
825 # call. However, as this is a POST is better to have it, so automated
864 # tools don't flag it as potential CSRF.
826 # tools don't flag it as potential CSRF.
865 # Post is required because the payload could be bigger than the maximum
827 # Post is required because the payload could be bigger than the maximum
866 # allowed by GET.
828 # allowed by GET.
867
829
868 text = self.request.POST.get('text')
830 text = self.request.POST.get('text')
869 file_path = self.request.POST.get('file_path')
831 file_path = self.request.POST.get('file_path')
870
832
871 renderer = h.renderer_from_filename(file_path)
833 renderer = h.renderer_from_filename(file_path)
872
834
873 if renderer:
835 if renderer:
874 return h.render(text, renderer=renderer, mentions=True)
836 return h.render(text, renderer=renderer, mentions=True)
875 else:
837 else:
876 self.load_default_context()
838 self.load_default_context()
877 _render = self.request.get_partial_renderer(
839 _render = self.request.get_partial_renderer(
878 'rhodecode:templates/files/file_content.mako')
840 'rhodecode:templates/files/file_content.mako')
879
841
880 lines = filenode_as_lines_tokens(FileNode(file_path, text))
842 lines = filenode_as_lines_tokens(FileNode(file_path, text))
881
843
882 return _render('render_lines', lines)
844 return _render('render_lines', lines)
883
845
884 @LoginRequired()
846 @LoginRequired()
885 @CSRFRequired()
847 @CSRFRequired()
886 @view_config(
887 route_name='store_user_session_value', request_method='POST',
888 renderer='string', xhr=True)
889 def store_user_session_attr(self):
848 def store_user_session_attr(self):
890 key = self.request.POST.get('key')
849 key = self.request.POST.get('key')
891 val = self.request.POST.get('val')
850 val = self.request.POST.get('val')
892
851
893 existing_value = self.request.session.get(key)
852 existing_value = self.request.session.get(key)
894 if existing_value != val:
853 if existing_value != val:
895 self.request.session[key] = val
854 self.request.session[key] = val
896
855
897 return 'stored:{}:{}'.format(key, val)
856 return 'stored:{}:{}'.format(key, val)
@@ -1,45 +1,67 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2018-2020 RhodeCode GmbH
3 # Copyright (C) 2018-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 def includeme(config):
22 def includeme(config):
23
23 from rhodecode.apps.hovercards.views import HoverCardsView, HoverCardsRepoView
24 config.add_route(
24 config.add_route(
25 name='hovercard_user',
25 name='hovercard_user',
26 pattern='/_hovercard/user/{user_id}')
26 pattern='/_hovercard/user/{user_id}')
27 config.add_view(
28 HoverCardsView,
29 attr='hovercard_user',
30 route_name='hovercard_user', request_method='GET', xhr=True,
31 renderer='rhodecode:templates/hovercards/hovercard_user.mako')
27
32
28 config.add_route(
33 config.add_route(
29 name='hovercard_username',
34 name='hovercard_username',
30 pattern='/_hovercard/username/{username}')
35 pattern='/_hovercard/username/{username}')
36 config.add_view(
37 HoverCardsView,
38 attr='hovercard_username',
39 route_name='hovercard_username', request_method='GET', xhr=True,
40 renderer='rhodecode:templates/hovercards/hovercard_user.mako')
31
41
32 config.add_route(
42 config.add_route(
33 name='hovercard_user_group',
43 name='hovercard_user_group',
34 pattern='/_hovercard/user_group/{user_group_id}')
44 pattern='/_hovercard/user_group/{user_group_id}')
45 config.add_view(
46 HoverCardsView,
47 attr='hovercard_user_group',
48 route_name='hovercard_user_group', request_method='GET', xhr=True,
49 renderer='rhodecode:templates/hovercards/hovercard_user_group.mako')
35
50
36 config.add_route(
51 config.add_route(
37 name='hovercard_pull_request',
52 name='hovercard_pull_request',
38 pattern='/_hovercard/pull_request/{pull_request_id}')
53 pattern='/_hovercard/pull_request/{pull_request_id}')
54 config.add_view(
55 HoverCardsView,
56 attr='hovercard_pull_request',
57 route_name='hovercard_pull_request', request_method='GET', xhr=True,
58 renderer='rhodecode:templates/hovercards/hovercard_pull_request.mako')
39
59
40 config.add_route(
60 config.add_route(
41 name='hovercard_repo_commit',
61 name='hovercard_repo_commit',
42 pattern='/_hovercard/commit/{repo_name:.*?[^/]}/{commit_id}', repo_route=True)
62 pattern='/_hovercard/commit/{repo_name:.*?[^/]}/{commit_id}', repo_route=True)
43
63 config.add_view(
44 # Scan module for configuration decorators.
64 HoverCardsRepoView,
45 config.scan('.views', ignore='.tests')
65 attr='hovercard_repo_commit',
66 route_name='hovercard_repo_commit', request_method='GET', xhr=True,
67 renderer='rhodecode:templates/hovercards/hovercard_repo_commit.mako')
@@ -1,123 +1,108 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import re
21 import re
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from pyramid.httpexceptions import HTTPNotFound
25 from pyramid.httpexceptions import HTTPNotFound
26 from pyramid.view import view_config
26
27
27
28 from rhodecode.apps._base import BaseAppView, RepoAppView
28 from rhodecode.apps._base import BaseAppView, RepoAppView
29 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib.auth import (
30 from rhodecode.lib.auth import (
31 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator, CSRFRequired,
31 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator, CSRFRequired,
32 HasRepoPermissionAnyDecorator)
32 HasRepoPermissionAnyDecorator)
33 from rhodecode.lib.codeblocks import filenode_as_lines_tokens
33 from rhodecode.lib.codeblocks import filenode_as_lines_tokens
34 from rhodecode.lib.index import searcher_from_config
34 from rhodecode.lib.index import searcher_from_config
35 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
35 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
36 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.ext_json import json
37 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError, EmptyRepositoryError
37 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError, EmptyRepositoryError
38 from rhodecode.lib.vcs.nodes import FileNode
38 from rhodecode.lib.vcs.nodes import FileNode
39 from rhodecode.model.db import (
39 from rhodecode.model.db import (
40 func, true, or_, case, in_filter_generator, Repository, RepoGroup, User, UserGroup, PullRequest)
40 func, true, or_, case, in_filter_generator, Repository, RepoGroup, User, UserGroup, PullRequest)
41 from rhodecode.model.repo import RepoModel
41 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.repo_group import RepoGroupModel
42 from rhodecode.model.repo_group import RepoGroupModel
43 from rhodecode.model.scm import RepoGroupList, RepoList
43 from rhodecode.model.scm import RepoGroupList, RepoList
44 from rhodecode.model.user import UserModel
44 from rhodecode.model.user import UserModel
45 from rhodecode.model.user_group import UserGroupModel
45 from rhodecode.model.user_group import UserGroupModel
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class HoverCardsView(BaseAppView):
50 class HoverCardsView(BaseAppView):
51
51
52 def load_default_context(self):
52 def load_default_context(self):
53 c = self._get_local_tmpl_context()
53 c = self._get_local_tmpl_context()
54 return c
54 return c
55
55
56 @LoginRequired()
56 @LoginRequired()
57 @view_config(
58 route_name='hovercard_user', request_method='GET', xhr=True,
59 renderer='rhodecode:templates/hovercards/hovercard_user.mako')
60 def hovercard_user(self):
57 def hovercard_user(self):
61 c = self.load_default_context()
58 c = self.load_default_context()
62 user_id = self.request.matchdict['user_id']
59 user_id = self.request.matchdict['user_id']
63 c.user = User.get_or_404(user_id)
60 c.user = User.get_or_404(user_id)
64 return self._get_template_context(c)
61 return self._get_template_context(c)
65
62
66 @LoginRequired()
63 @LoginRequired()
67 @view_config(
68 route_name='hovercard_username', request_method='GET', xhr=True,
69 renderer='rhodecode:templates/hovercards/hovercard_user.mako')
70 def hovercard_username(self):
64 def hovercard_username(self):
71 c = self.load_default_context()
65 c = self.load_default_context()
72 username = self.request.matchdict['username']
66 username = self.request.matchdict['username']
73 c.user = User.get_by_username(username)
67 c.user = User.get_by_username(username)
74 if not c.user:
68 if not c.user:
75 raise HTTPNotFound()
69 raise HTTPNotFound()
76
70
77 return self._get_template_context(c)
71 return self._get_template_context(c)
78
72
79 @LoginRequired()
73 @LoginRequired()
80 @view_config(
81 route_name='hovercard_user_group', request_method='GET', xhr=True,
82 renderer='rhodecode:templates/hovercards/hovercard_user_group.mako')
83 def hovercard_user_group(self):
74 def hovercard_user_group(self):
84 c = self.load_default_context()
75 c = self.load_default_context()
85 user_group_id = self.request.matchdict['user_group_id']
76 user_group_id = self.request.matchdict['user_group_id']
86 c.user_group = UserGroup.get_or_404(user_group_id)
77 c.user_group = UserGroup.get_or_404(user_group_id)
87 return self._get_template_context(c)
78 return self._get_template_context(c)
88
79
89 @LoginRequired()
80 @LoginRequired()
90 @view_config(
91 route_name='hovercard_pull_request', request_method='GET', xhr=True,
92 renderer='rhodecode:templates/hovercards/hovercard_pull_request.mako')
93 def hovercard_pull_request(self):
81 def hovercard_pull_request(self):
94 c = self.load_default_context()
82 c = self.load_default_context()
95 c.pull_request = PullRequest.get_or_404(
83 c.pull_request = PullRequest.get_or_404(
96 self.request.matchdict['pull_request_id'])
84 self.request.matchdict['pull_request_id'])
97 perms = ['repository.read', 'repository.write', 'repository.admin']
85 perms = ['repository.read', 'repository.write', 'repository.admin']
98 c.can_view_pr = h.HasRepoPermissionAny(*perms)(
86 c.can_view_pr = h.HasRepoPermissionAny(*perms)(
99 c.pull_request.target_repo.repo_name)
87 c.pull_request.target_repo.repo_name)
100 return self._get_template_context(c)
88 return self._get_template_context(c)
101
89
102
90
103 class HoverCardsRepoView(RepoAppView):
91 class HoverCardsRepoView(RepoAppView):
104 def load_default_context(self):
92 def load_default_context(self):
105 c = self._get_local_tmpl_context()
93 c = self._get_local_tmpl_context()
106 return c
94 return c
107
95
108 @LoginRequired()
96 @LoginRequired()
109 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', 'repository.admin')
97 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', 'repository.admin')
110 @view_config(
111 route_name='hovercard_repo_commit', request_method='GET', xhr=True,
112 renderer='rhodecode:templates/hovercards/hovercard_repo_commit.mako')
113 def hovercard_repo_commit(self):
98 def hovercard_repo_commit(self):
114 c = self.load_default_context()
99 c = self.load_default_context()
115 commit_id = self.request.matchdict['commit_id']
100 commit_id = self.request.matchdict['commit_id']
116 pre_load = ['author', 'branch', 'date', 'message']
101 pre_load = ['author', 'branch', 'date', 'message']
117 try:
102 try:
118 c.commit = self.rhodecode_vcs_repo.get_commit(
103 c.commit = self.rhodecode_vcs_repo.get_commit(
119 commit_id=commit_id, pre_load=pre_load)
104 commit_id=commit_id, pre_load=pre_load)
120 except (CommitDoesNotExistError, EmptyRepositoryError):
105 except (CommitDoesNotExistError, EmptyRepositoryError):
121 raise HTTPNotFound()
106 raise HTTPNotFound()
122
107
123 return self._get_template_context(c)
108 return self._get_template_context(c)
@@ -1,53 +1,102 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 from rhodecode.apps._base import ADMIN_PREFIX
22 from rhodecode.apps._base import ADMIN_PREFIX
23
23
24
24
25 def admin_routes(config):
25 def admin_routes(config):
26 from rhodecode.apps.journal.views import JournalView
26
27
27 config.add_route(
28 config.add_route(
28 name='journal', pattern='/journal')
29 name='journal', pattern='/journal')
30 config.add_view(
31 JournalView,
32 attr='journal',
33 route_name='journal', request_method='GET',
34 renderer=None)
35
29 config.add_route(
36 config.add_route(
30 name='journal_rss', pattern='/journal/rss')
37 name='journal_rss', pattern='/journal/rss')
38 config.add_view(
39 JournalView,
40 attr='journal_rss',
41 route_name='journal_rss', request_method='GET',
42 renderer=None)
43
31 config.add_route(
44 config.add_route(
32 name='journal_atom', pattern='/journal/atom')
45 name='journal_atom', pattern='/journal/atom')
46 config.add_view(
47 JournalView,
48 attr='journal_atom',
49 route_name='journal_atom', request_method='GET',
50 renderer=None)
33
51
34 config.add_route(
52 config.add_route(
35 name='journal_public', pattern='/public_journal')
53 name='journal_public', pattern='/public_journal')
54 config.add_view(
55 JournalView,
56 attr='journal_public',
57 route_name='journal_public', request_method='GET',
58 renderer=None)
59
36 config.add_route(
60 config.add_route(
37 name='journal_public_atom', pattern='/public_journal/atom')
61 name='journal_public_atom', pattern='/public_journal/atom')
62 config.add_view(
63 JournalView,
64 attr='journal_public_atom',
65 route_name='journal_public_atom', request_method='GET',
66 renderer=None)
67
38 config.add_route(
68 config.add_route(
39 name='journal_public_atom_old', pattern='/public_journal_atom')
69 name='journal_public_atom_old', pattern='/public_journal_atom')
70 config.add_view(
71 JournalView,
72 attr='journal_public_atom',
73 route_name='journal_public_atom_old', request_method='GET',
74 renderer=None)
40
75
41 config.add_route(
76 config.add_route(
42 name='journal_public_rss', pattern='/public_journal/rss')
77 name='journal_public_rss', pattern='/public_journal/rss')
78 config.add_view(
79 JournalView,
80 attr='journal_public_rss',
81 route_name='journal_public_rss', request_method='GET',
82 renderer=None)
83
43 config.add_route(
84 config.add_route(
44 name='journal_public_rss_old', pattern='/public_journal_rss')
85 name='journal_public_rss_old', pattern='/public_journal_rss')
86 config.add_view(
87 JournalView,
88 attr='journal_public_rss',
89 route_name='journal_public_rss_old', request_method='GET',
90 renderer=None)
45
91
46 config.add_route(
92 config.add_route(
47 name='toggle_following', pattern='/toggle_following')
93 name='toggle_following', pattern='/toggle_following')
94 config.add_view(
95 JournalView,
96 attr='toggle_following',
97 route_name='toggle_following', request_method='POST',
98 renderer='json_ext')
48
99
49
100
50 def includeme(config):
101 def includeme(config):
51 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
102 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
52 # Scan module for configuration decorators.
53 config.scan('.views', ignore='.tests')
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now