mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-12-19 02:22:37 +00:00
Compare commits
1576 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92fc0ceb16 | ||
|
|
217ea03c18 | ||
|
|
923f0ed477 | ||
|
|
3c6cfaa6cf | ||
|
|
0bfe255966 | ||
|
|
af5d531131 | ||
|
|
10d58ef424 | ||
|
|
64064cb526 | ||
|
|
46941037dd | ||
|
|
8ad71b6dd9 | ||
|
|
220ccda04d | ||
|
|
6d30fe797c | ||
|
|
3318df1771 | ||
|
|
0ccd806eca | ||
|
|
7ebe1c2916 | ||
|
|
08904c2a9f | ||
|
|
19e34270d1 | ||
|
|
afa515d570 | ||
|
|
bcba408517 | ||
|
|
80d94074e7 | ||
|
|
9347405f08 | ||
|
|
da4ad11a69 | ||
|
|
b81e424e80 | ||
|
|
1f15937139 | ||
|
|
f4fa011714 | ||
|
|
c5a9a85818 | ||
|
|
e23bfa0f69 | ||
|
|
451690fe8b | ||
|
|
5bea782b9f | ||
|
|
f26f7ca1e7 | ||
|
|
fb9ac54843 | ||
|
|
927ca162d0 | ||
|
|
583d295fd1 | ||
|
|
b51af1f7d7 | ||
|
|
edcf9921fe | ||
|
|
eef172d0e2 | ||
|
|
5b407fe8bc | ||
|
|
1bb9048910 | ||
|
|
787cdbcadf | ||
|
|
b14ca14120 | ||
|
|
4b2fd0d0da | ||
|
|
3393f18399 | ||
|
|
7e1a379e47 | ||
|
|
213fdd461b | ||
|
|
148c7ffb43 | ||
|
|
75be45ce43 | ||
|
|
04eb670ada | ||
|
|
66f3706142 | ||
|
|
9ea98bf2b2 | ||
|
|
4695f80172 | ||
|
|
0452d53c3f | ||
|
|
ec30ef1f8b | ||
|
|
1c0ad08d80 | ||
|
|
e1ab453780 | ||
|
|
1a6597b572 | ||
|
|
865522953a | ||
|
|
4d4a107101 | ||
|
|
82f70cb0dc | ||
|
|
820186c6d0 | ||
|
|
4468e2d379 | ||
|
|
bd802e6a50 | ||
|
|
9362c272cb | ||
|
|
ee4534ac4b | ||
|
|
7ef09a4ca1 | ||
|
|
31daaed4cd | ||
|
|
71202e83f5 | ||
|
|
b1640e5592 | ||
|
|
076b7babcb | ||
|
|
8569910658 | ||
|
|
34c556d375 | ||
|
|
a43d6ad34d | ||
|
|
ca6825008b | ||
|
|
9c6f118dbd | ||
|
|
5730e60089 | ||
|
|
afc7f3bb9c | ||
|
|
c411b0a38e | ||
|
|
403b6fc563 | ||
|
|
55ae8bb5e6 | ||
|
|
dbcf785e42 | ||
|
|
e6cd126045 | ||
|
|
420f3c0c4c | ||
|
|
9b2c5cb305 | ||
|
|
907f0068db | ||
|
|
a16b3260ba | ||
|
|
1845821f6c | ||
|
|
27d906a877 | ||
|
|
431ba36a26 | ||
|
|
229c782157 | ||
|
|
5bacab7475 | ||
|
|
999286a089 | ||
|
|
68ccaf0cb3 | ||
|
|
8d58d58519 | ||
|
|
8efc0331de | ||
|
|
7c479c2479 | ||
|
|
96551856a2 | ||
|
|
b1f5cc7728 | ||
|
|
1a84661ca9 | ||
|
|
c87b9f203f | ||
|
|
9442acfb74 | ||
|
|
50bea55732 | ||
|
|
a4ece2b271 | ||
|
|
b460e4abaa | ||
|
|
087ba9ae95 | ||
|
|
19926e67e1 | ||
|
|
af15622319 | ||
|
|
9850633350 | ||
|
|
5f2345852d | ||
|
|
ad1c17aa7b | ||
|
|
3a79bfd5f6 | ||
|
|
5ee8519274 | ||
|
|
ff546774e9 | ||
|
|
1f4fc9b6f0 | ||
|
|
f8142e23cd | ||
|
|
196f1654ab | ||
|
|
3b8a24eeb7 | ||
|
|
7dde15cb04 | ||
|
|
3e5d949610 | ||
|
|
25c3691f6b | ||
|
|
f3bc6c0b22 | ||
|
|
caf0d85939 | ||
|
|
a463f632e8 | ||
|
|
a0e6d09770 | ||
|
|
54623a5b06 | ||
|
|
7afff5e392 | ||
|
|
9a5fee5a4c | ||
|
|
a840e611cd | ||
|
|
566419cac4 | ||
|
|
7b362bfc76 | ||
|
|
f528dd4888 | ||
|
|
3c95c6fe11 | ||
|
|
0b17d55f30 | ||
|
|
3861943518 | ||
|
|
71a72dd957 | ||
|
|
78ac1968dd | ||
|
|
93453cc8c3 | ||
|
|
11527b9033 | ||
|
|
2ff2266417 | ||
|
|
1ef1bea703 | ||
|
|
6804f23b51 | ||
|
|
ea7e1f19b2 | ||
|
|
3320e6105c | ||
|
|
072e74ce49 | ||
|
|
492b1c4311 | ||
|
|
0babada459 | ||
|
|
7b0993959e | ||
|
|
701582fe38 | ||
|
|
d289960ff2 | ||
|
|
6adf37a30d | ||
|
|
b371676813 | ||
|
|
2e0eab9289 | ||
|
|
0cac8d66b3 | ||
|
|
2053e9210e | ||
|
|
7c44f74068 | ||
|
|
3e0f5af848 | ||
|
|
245d28b03d | ||
|
|
75f4f6d0a2 | ||
|
|
f9e167cb0e | ||
|
|
f224e49de7 | ||
|
|
4ec4869896 | ||
|
|
63ff868553 | ||
|
|
234130091b | ||
|
|
216e0f28b9 | ||
|
|
f3483484d7 | ||
|
|
d704a35ead | ||
|
|
b97deec1de | ||
|
|
5b6488e1b2 | ||
|
|
be3fdac8a9 | ||
|
|
96afd82341 | ||
|
|
728e4b7edd | ||
|
|
73c3de97b8 | ||
|
|
e98626988b | ||
|
|
01a52812f0 | ||
|
|
041d99f0be | ||
|
|
75edc26a10 | ||
|
|
2f7b169405 | ||
|
|
76ef9b292b | ||
|
|
1540cbdcaa | ||
|
|
eefe0709f8 | ||
|
|
5ae6e81a1d | ||
|
|
8a62488cb9 | ||
|
|
e951b64b0a | ||
|
|
5d129fd0da | ||
|
|
b529127461 | ||
|
|
2e40b9607e | ||
|
|
d878089ebd | ||
|
|
9d32285446 | ||
|
|
4658a4c90f | ||
|
|
d06ea2944e | ||
|
|
d50ea83f40 | ||
|
|
154aad1e22 | ||
|
|
17163dd909 | ||
|
|
b789a8bb05 | ||
|
|
9341ce9f84 | ||
|
|
4d0f7d0254 | ||
|
|
195cbbbe42 | ||
|
|
6e5e340a25 | ||
|
|
eb74d13059 | ||
|
|
16f916c40a | ||
|
|
4dd6d4498b | ||
|
|
dd5bf546df | ||
|
|
d6debde566 | ||
|
|
efc66cc7ee | ||
|
|
b310731ba7 | ||
|
|
4bd2681bf0 | ||
|
|
38e09753f4 | ||
|
|
98e3adbb11 | ||
|
|
c700b101c1 | ||
|
|
7b3647e78a | ||
|
|
f4f042c407 | ||
|
|
0a8eeca629 | ||
|
|
fdd55511a6 | ||
|
|
0cff6ab29b | ||
|
|
cda677a30f | ||
|
|
5571651c02 | ||
|
|
2680396680 | ||
|
|
227f97c2f5 | ||
|
|
ad3231c8a3 | ||
|
|
e39d8dce3c | ||
|
|
0bdc425c55 | ||
|
|
0a7f63cc5e | ||
|
|
c6e0e9a79a | ||
|
|
4cebfc7f6a | ||
|
|
7fde392774 | ||
|
|
3ee051303a | ||
|
|
9fa31be4bf | ||
|
|
ae2e4049db | ||
|
|
48b71bb11b | ||
|
|
7e4edc3c63 | ||
|
|
11b3c57c92 | ||
|
|
e339ed1fb3 | ||
|
|
2d48c8028b | ||
|
|
185c53d311 | ||
|
|
2f030c02ec | ||
|
|
f4457ff1e2 | ||
|
|
c6ed6d8acb | ||
|
|
331cfe2aed | ||
|
|
e48dbabef2 | ||
|
|
181973f235 | ||
|
|
440a2ad241 | ||
|
|
1936a6d5ee | ||
|
|
4517a92b2b | ||
|
|
44a2a51f59 | ||
|
|
2d4498578a | ||
|
|
ac902501ec | ||
|
|
363b5d04d9 | ||
|
|
dec89ae5ee | ||
|
|
5d37269a6c | ||
|
|
9a39ccd37d | ||
|
|
5896ea9c63 | ||
|
|
8323de1c07 | ||
|
|
8c0bf0b71b | ||
|
|
5812c50a33 | ||
|
|
7b339df430 | ||
|
|
aec78dc3c7 | ||
|
|
6bb13a26f5 | ||
|
|
b92137afd9 | ||
|
|
962763dc7b | ||
|
|
f4eca83a49 | ||
|
|
02135ea04f | ||
|
|
79eb838250 | ||
|
|
82710294f4 | ||
|
|
2d18acf6f7 | ||
|
|
a860bed34f | ||
|
|
f8c323cf5c | ||
|
|
b6f5b335bd | ||
|
|
f451f8a979 | ||
|
|
5c4dfabc48 | ||
|
|
72ccaf7b1c | ||
|
|
e4b788a012 | ||
|
|
6eaba4e2fb | ||
|
|
0c0c0e6d6f | ||
|
|
20cf7c7c52 | ||
|
|
230d6137f3 | ||
|
|
aa9533eb5b | ||
|
|
23f9454e5d | ||
|
|
9eee79f7d4 | ||
|
|
c0089a98f4 | ||
|
|
87242c13a1 | ||
|
|
184432a2a6 | ||
|
|
555861c319 | ||
|
|
a505995f49 | ||
|
|
b477b649f5 | ||
|
|
6e18383531 | ||
|
|
7833ff6671 | ||
|
|
5d433456d4 | ||
|
|
fc5ec3f21c | ||
|
|
efb5d4135a | ||
|
|
5f2c9c3801 | ||
|
|
56f38e57bc | ||
|
|
586c04cba6 | ||
|
|
306605915c | ||
|
|
272701a8fd | ||
|
|
9febe488b5 | ||
|
|
c8c6ba1c19 | ||
|
|
2df7bd8510 | ||
|
|
c16a986c4b | ||
|
|
44db0e8a5d | ||
|
|
91c994924f | ||
|
|
61407331bc | ||
|
|
1b1a686b96 | ||
|
|
cc30a20b7c | ||
|
|
0379e5160c | ||
|
|
386ce629ac | ||
|
|
5677bf0995 | ||
|
|
c17dc26f8c | ||
|
|
b4cc2bbff9 | ||
|
|
e74289b223 | ||
|
|
98afa032a7 | ||
|
|
0d3fab216b | ||
|
|
081f4b1c0a | ||
|
|
e9fe5b3004 | ||
|
|
1de2923dd3 | ||
|
|
6890d549ed | ||
|
|
9426f58a6b | ||
|
|
6a2a0013a8 | ||
|
|
864a4c0485 | ||
|
|
8fa08d7ea3 | ||
|
|
3648a1458b | ||
|
|
5001f48982 | ||
|
|
93bf15adc9 | ||
|
|
83eb8f77d1 | ||
|
|
4eaaa2462b | ||
|
|
0910236f12 | ||
|
|
77149bf36c | ||
|
|
6337d0d0a1 | ||
|
|
093e3924a2 | ||
|
|
e143408e57 | ||
|
|
dfdf33bbb5 | ||
|
|
8fd5e6521f | ||
|
|
6a2b9cd9bf | ||
|
|
55fa5800b1 | ||
|
|
6cdba2e8d2 | ||
|
|
043aa08f23 | ||
|
|
63519ec076 | ||
|
|
48fa26a3bc | ||
|
|
d969563e43 | ||
|
|
287236a447 | ||
|
|
4fb4a9f622 | ||
|
|
4dcd4749c3 | ||
|
|
508d1c2c1f | ||
|
|
600ea42633 | ||
|
|
41cbd3e0f6 | ||
|
|
e593ccb01c | ||
|
|
d5e1ca7908 | ||
|
|
06fb502a1d | ||
|
|
3f6d7637c3 | ||
|
|
0d2b4d7ca3 | ||
|
|
42c5c02709 | ||
|
|
d1d73da322 | ||
|
|
88fcf8dbd7 | ||
|
|
396bc9b6ae | ||
|
|
27ffed1be9 | ||
|
|
af9d42695f | ||
|
|
422b4424fe | ||
|
|
dc172e1ef0 | ||
|
|
cd7946f3f0 | ||
|
|
4994a4a387 | ||
|
|
e26eba9919 | ||
|
|
44cd82f3e1 | ||
|
|
adf14b0f2e | ||
|
|
f5a7c4131d | ||
|
|
e7031d0ac1 | ||
|
|
ef27188f86 | ||
|
|
a7734812fc | ||
|
|
fb62fbde6c | ||
|
|
521f4ea86b | ||
|
|
e9827c8b25 | ||
|
|
29b099efc0 | ||
|
|
e9fc56c056 | ||
|
|
e9103ee608 | ||
|
|
f8dae2a3c9 | ||
|
|
52c6244b2b | ||
|
|
7977294c5c | ||
|
|
e1331084e5 | ||
|
|
5268c0faae | ||
|
|
b2d9b69874 | ||
|
|
336589af98 | ||
|
|
5087c0e06f | ||
|
|
43ecd39d0e | ||
|
|
209b23c5ce | ||
|
|
7e7d4401e6 | ||
|
|
b4c3471d2c | ||
|
|
f4a822062f | ||
|
|
4583beaec0 | ||
|
|
42a6feb35e | ||
|
|
4503df910d | ||
|
|
a09b0c6c06 | ||
|
|
55c7e06185 | ||
|
|
a95a0da6f7 | ||
|
|
0fd43f48f0 | ||
|
|
dffd05cd20 | ||
|
|
681046119d | ||
|
|
b19c49da41 | ||
|
|
cc31c04b5f | ||
|
|
89f62d8e6b | ||
|
|
41b73c3701 | ||
|
|
cff3a790ef | ||
|
|
8328edd69c | ||
|
|
9bf5d6dd45 | ||
|
|
ac9a0c6d26 | ||
|
|
064cb16d25 | ||
|
|
f89b1fd44b | ||
|
|
b25096925b | ||
|
|
e8ff576324 | ||
|
|
5ac1467564 | ||
|
|
1c54e5acd8 | ||
|
|
b8d0272e37 | ||
|
|
0b7a90b83c | ||
|
|
7cf000262d | ||
|
|
fa7e0d84f9 | ||
|
|
98f6d0146c | ||
|
|
d0fac3f838 | ||
|
|
a41988d2b5 | ||
|
|
b801d2f2e9 | ||
|
|
6bed9210dd | ||
|
|
eac4630272 | ||
|
|
11a0d72b01 | ||
|
|
7dcd04ca1a | ||
|
|
ccfb151fb1 | ||
|
|
ae98eee0c7 | ||
|
|
829a4406a2 | ||
|
|
91c42e8530 | ||
|
|
250acea751 | ||
|
|
00d9f71384 | ||
|
|
ffce909ee3 | ||
|
|
0e62ea787c | ||
|
|
cedb862420 | ||
|
|
a6054ff6a5 | ||
|
|
a67b445026 | ||
|
|
8d33990050 | ||
|
|
516e75cbf4 | ||
|
|
15ca775005 | ||
|
|
41ca43bf33 | ||
|
|
c6a604fd5f | ||
|
|
8e84efb296 | ||
|
|
caee286973 | ||
|
|
672b82c3d6 | ||
|
|
55554a025f | ||
|
|
c91ce2b99f | ||
|
|
02a901467b | ||
|
|
00b3c7c945 | ||
|
|
2f9598ba49 | ||
|
|
3138abb00c | ||
|
|
3afb8647bd | ||
|
|
0682f4fc90 | ||
|
|
44ec69fdbd | ||
|
|
04945809a5 | ||
|
|
e38b113d7e | ||
|
|
eb448dc3f2 | ||
|
|
954a97bba7 | ||
|
|
41e03c629f | ||
|
|
1fd2e782f8 | ||
|
|
1f6a8e8f02 | ||
|
|
eca0a9a7d7 | ||
|
|
775f8f2ffd | ||
|
|
1e8ef8c925 | ||
|
|
c354b55f61 | ||
|
|
76ac0215fe | ||
|
|
4f93a3ca92 | ||
|
|
bcae30814d | ||
|
|
98bb6c63f5 | ||
|
|
8b8b11ce1e | ||
|
|
5f61f2b555 | ||
|
|
62d2e01cdf | ||
|
|
5e89ee9202 | ||
|
|
5d313a827b | ||
|
|
edf6baa52d | ||
|
|
3c69860b24 | ||
|
|
aa2255a87e | ||
|
|
3cc9e0a66c | ||
|
|
ec1f6677ec | ||
|
|
ae98fb4332 | ||
|
|
29730757b8 | ||
|
|
1c2feedb27 | ||
|
|
b227d9cdc1 | ||
|
|
da6a0c286d | ||
|
|
13a042bc0f | ||
|
|
7f9644dbac | ||
|
|
ece9b16351 | ||
|
|
23b896b301 | ||
|
|
dd52baae12 | ||
|
|
eb1ca4c0f2 | ||
|
|
8e75e519fa | ||
|
|
389c6b5a84 | ||
|
|
6fc7a4cb21 | ||
|
|
68455156a3 | ||
|
|
2827a64095 | ||
|
|
c21505e92d | ||
|
|
7d855e5ad8 | ||
|
|
17956bf0db | ||
|
|
267a7fc9f7 | ||
|
|
9ef4762817 | ||
|
|
29457ad867 | ||
|
|
d5082d1379 | ||
|
|
c07536b26f | ||
|
|
678999b9f5 | ||
|
|
4295aec492 | ||
|
|
75a2b9eac2 | ||
|
|
2fc68ca6f1 | ||
|
|
56a156b717 | ||
|
|
7c59134635 | ||
|
|
f98f0e2c06 | ||
|
|
63746accf9 | ||
|
|
670e2d7352 | ||
|
|
db3b60faf9 | ||
|
|
f1b8c1965d | ||
|
|
aa2a77ee7e | ||
|
|
b107d15097 | ||
|
|
52b9c400eb | ||
|
|
851bd99eaf | ||
|
|
0c0eb843ab | ||
|
|
bd19f8afe8 | ||
|
|
bafffc95b8 | ||
|
|
14581ac775 | ||
|
|
6ddd48bfb1 | ||
|
|
ff1828bdd6 | ||
|
|
84e5177ce2 | ||
|
|
1c9be184fd | ||
|
|
57155c2469 | ||
|
|
f9578f4474 | ||
|
|
a37e14221e | ||
|
|
005272cdc0 | ||
|
|
f88cb3da20 | ||
|
|
9f42dfb26e | ||
|
|
f702fc7d93 | ||
|
|
05adb4e95f | ||
|
|
210a2d7fe6 | ||
|
|
c9dc1ea254 | ||
|
|
79b5618756 | ||
|
|
bf922459ff | ||
|
|
a27fb18a17 | ||
|
|
f62f750266 | ||
|
|
b072e98148 | ||
|
|
352bfeeb7a | ||
|
|
1d7bdd5f5f | ||
|
|
8702761303 | ||
|
|
ce3cc80037 | ||
|
|
9556d33d1c | ||
|
|
02650c9cdc | ||
|
|
75c6047236 | ||
|
|
e6c369cfd8 | ||
|
|
131496bfee | ||
|
|
c084412e53 | ||
|
|
ad81d6c28e | ||
|
|
08fb8f5d92 | ||
|
|
4df08f0521 | ||
|
|
c963937f00 | ||
|
|
b64ae358fb | ||
|
|
42c5783e43 | ||
|
|
a2350e7f1d | ||
|
|
b3114a1f3d | ||
|
|
b4cf540e51 | ||
|
|
deeb9cdfa6 | ||
|
|
7a6a1b9b59 | ||
|
|
15d1e021de | ||
|
|
f063832bc6 | ||
|
|
1d30c1900d | ||
|
|
78a227af3e | ||
|
|
e978308335 | ||
|
|
7193d7fc1b | ||
|
|
d6a95d3f1a | ||
|
|
829e1f4cac | ||
|
|
33dc7dec84 | ||
|
|
b90960e6e1 | ||
|
|
b365ba7982 | ||
|
|
9cfccf8a1b | ||
|
|
16f727c60d | ||
|
|
ac2ba63856 | ||
|
|
55c95c58f6 | ||
|
|
907703d911 | ||
|
|
e1919d0a62 | ||
|
|
4ac4b517f4 | ||
|
|
f296dce935 | ||
|
|
bc5a240121 | ||
|
|
b4498f2267 | ||
|
|
b2932803b0 | ||
|
|
cea336a8ce | ||
|
|
16864ca34e | ||
|
|
c5785e17aa | ||
|
|
f89c6124a6 | ||
|
|
47b1a13bea | ||
|
|
df52240227 | ||
|
|
4944ac8e75 | ||
|
|
a88db3d9db | ||
|
|
fd1b9d97c0 | ||
|
|
5e95f262b2 | ||
|
|
4493a82425 | ||
|
|
078751979e | ||
|
|
1fd98ae404 | ||
|
|
560de55c3c | ||
|
|
dd6e0f548c | ||
|
|
a07a90f1c9 | ||
|
|
64642ab48b | ||
|
|
12f51c9474 | ||
|
|
50106474d4 | ||
|
|
b3e2b30e71 | ||
|
|
7427f02e0b | ||
|
|
330c078597 | ||
|
|
eb05588776 | ||
|
|
98d3a36d98 | ||
|
|
fedf50f9dc | ||
|
|
1876b7dfba | ||
|
|
873d909879 | ||
|
|
15b8a2efb9 | ||
|
|
45444149dd | ||
|
|
a5485011c8 | ||
|
|
7c5592fa36 | ||
|
|
715b04e2b2 | ||
|
|
73086328e4 | ||
|
|
7f126ab652 | ||
|
|
5bcd122de9 | ||
|
|
a92aec2775 | ||
|
|
cc9554cdb4 | ||
|
|
100dd24ff5 | ||
|
|
3a812c2267 | ||
|
|
f47781a330 | ||
|
|
f96dd15f20 | ||
|
|
ffdc1eb198 | ||
|
|
91d1a62c98 | ||
|
|
793adbccab | ||
|
|
72ea036a29 | ||
|
|
9e322323f3 | ||
|
|
6951f4dd97 | ||
|
|
54887a3460 | ||
|
|
dafe71af42 | ||
|
|
82b9c2519b | ||
|
|
d563a11acf | ||
|
|
c5438fd65d | ||
|
|
66b8e59ae7 | ||
|
|
e424315d93 | ||
|
|
4c801bb828 | ||
|
|
1bba00412a | ||
|
|
bf40aa8df0 | ||
|
|
0c349f9350 | ||
|
|
4dc5d7d70e | ||
|
|
3fc30aa96c | ||
|
|
bb1349e962 | ||
|
|
c9ee8edeaf | ||
|
|
bfd8a9c66d | ||
|
|
030551d987 | ||
|
|
2f4096bf3d | ||
|
|
ebe129b3c2 | ||
|
|
61a8d6a5ca | ||
|
|
c6e7872ba8 | ||
|
|
096d4a4221 | ||
|
|
9b5cb8b0c5 | ||
|
|
bea5dadbac | ||
|
|
18f8864720 | ||
|
|
f71290d645 | ||
|
|
e69d283af9 | ||
|
|
31f5b2c06c | ||
|
|
611d5fc1fa | ||
|
|
8dba54e7c5 | ||
|
|
2ef487a92f | ||
|
|
aebf32d7be | ||
|
|
870e04feac | ||
|
|
4b2fbbfb84 | ||
|
|
0946645783 | ||
|
|
f3d2e5d1b3 | ||
|
|
6bfb026e47 | ||
|
|
aa428b0299 | ||
|
|
0a74602df3 | ||
|
|
05e7311b0b | ||
|
|
f40d51c84f | ||
|
|
46a2311e30 | ||
|
|
76e59f5bcd | ||
|
|
e731c01cc4 | ||
|
|
6067dbcf21 | ||
|
|
4057064b7f | ||
|
|
3f1e75c6f9 | ||
|
|
5434b65773 | ||
|
|
a01126c6c7 | ||
|
|
200ca65dc5 | ||
|
|
78a7bfbd30 | ||
|
|
fc58906bce | ||
|
|
e0d7a0e239 | ||
|
|
82077f4a0e | ||
|
|
c31b56ddd9 | ||
|
|
0a08ba3b9c | ||
|
|
2ea3ad4ca5 | ||
|
|
0d2fc27a97 | ||
|
|
f53cf8d544 | ||
|
|
596e5a6dd1 | ||
|
|
aeff0ab5f3 | ||
|
|
0b211d33b2 | ||
|
|
f4136decde | ||
|
|
74f22274b4 | ||
|
|
9e4874834f | ||
|
|
bd323d608e | ||
|
|
f9e41d71dc | ||
|
|
08775551c2 | ||
|
|
fb9e2ac9f6 | ||
|
|
edce831e46 | ||
|
|
12e1e559b5 | ||
|
|
fa02e3fb75 | ||
|
|
ed5a57042a | ||
|
|
6fbc4ce4fb | ||
|
|
eedaaddbf5 | ||
|
|
bae4387068 | ||
|
|
8de863525a | ||
|
|
6ce9815d51 | ||
|
|
9d201bbf98 | ||
|
|
96adaca3c3 | ||
|
|
cf65e9bfe3 | ||
|
|
b51ad05a4b | ||
|
|
ca178c534e | ||
|
|
d7948425df | ||
|
|
a0be7333fb | ||
|
|
fde8702916 | ||
|
|
bf0575b74d | ||
|
|
3dfb2d6af2 | ||
|
|
c25335cf95 | ||
|
|
ebbe49fab2 | ||
|
|
28ad362b8d | ||
|
|
b595ac9bc5 | ||
|
|
11f0024c33 | ||
|
|
dc3a9561c2 | ||
|
|
4f7ab69508 | ||
|
|
23b777b23b | ||
|
|
fe235823b4 | ||
|
|
2afeba8b5f | ||
|
|
87247da0ec | ||
|
|
d0ba67ed50 | ||
|
|
c163427db2 | ||
|
|
c7ecfd7943 | ||
|
|
db5ff0f922 | ||
|
|
c29381e657 | ||
|
|
ff62968bf6 | ||
|
|
4e1e60046a | ||
|
|
82c4705350 | ||
|
|
517c682201 | ||
|
|
bf92b61b78 | ||
|
|
f6d2ac9b7b | ||
|
|
98a8ef1526 | ||
|
|
4167987d67 | ||
|
|
ef41ba3117 | ||
|
|
458bee9a19 | ||
|
|
d22f60364a | ||
|
|
90321ab68c | ||
|
|
a5814c8100 | ||
|
|
87fd342b68 | ||
|
|
d544f7f936 | ||
|
|
fbe4f4c119 | ||
|
|
4d53b6f078 | ||
|
|
d2c65dd783 | ||
|
|
0c1463bc8b | ||
|
|
39b4145868 | ||
|
|
a57df0e05f | ||
|
|
36d98a237f | ||
|
|
cf719f492d | ||
|
|
69bcf3de2e | ||
|
|
d3cf2afb51 | ||
|
|
3c5ebeeb20 | ||
|
|
58f564b028 | ||
|
|
8fd65a372e | ||
|
|
f8f1aee997 | ||
|
|
9bb7c1adc1 | ||
|
|
d5cb0a2e59 | ||
|
|
7c4dee324f | ||
|
|
4c1821e42e | ||
|
|
f31ed0afb8 | ||
|
|
5e5f3ad383 | ||
|
|
f802fb645d | ||
|
|
93e8c9e534 | ||
|
|
0b405fdefa | ||
|
|
c995050ed7 | ||
|
|
0dbf2ab2e5 | ||
|
|
22cc8ffbdd | ||
|
|
6b8b0f4a0b | ||
|
|
b3502a9291 | ||
|
|
1c92516fd0 | ||
|
|
c3f02dbaab | ||
|
|
214c4d2f75 | ||
|
|
5a98b61c1e | ||
|
|
11ae5a4a4a | ||
|
|
5f3583279c | ||
|
|
950358f5ba | ||
|
|
84b3e29db6 | ||
|
|
a33d5cc53b | ||
|
|
3c7fa1b60b | ||
|
|
f4fc757379 | ||
|
|
7cf617c3fc | ||
|
|
87deb18791 | ||
|
|
82a6369568 | ||
|
|
3a10fd4160 | ||
|
|
a2f8f43321 | ||
|
|
6ebe8e16bd | ||
|
|
86522627d3 | ||
|
|
334e3bef0e | ||
|
|
0930716a54 | ||
|
|
f984acf1b2 | ||
|
|
87c7452d19 | ||
|
|
792fc3b0f0 | ||
|
|
673ebbec1b | ||
|
|
c2f78b1170 | ||
|
|
20f8c12576 | ||
|
|
1bc6492064 | ||
|
|
a7ae132b2b | ||
|
|
a978bf990d | ||
|
|
491f59026c | ||
|
|
a6531fedb5 | ||
|
|
e5250294b0 | ||
|
|
f3dc9b886b | ||
|
|
70664d435f | ||
|
|
ee97144a10 | ||
|
|
9870d3aa85 | ||
|
|
c3699889e2 | ||
|
|
237d51cff9 | ||
|
|
ccdb770972 | ||
|
|
8fcfcf7ad3 | ||
|
|
31280a3869 | ||
|
|
70a4f137b8 | ||
|
|
01ead7c8e1 | ||
|
|
ad3524f942 | ||
|
|
1a1f20920d | ||
|
|
b4d164954d | ||
|
|
52d0961519 | ||
|
|
e093475351 | ||
|
|
13822cbf6d | ||
|
|
b732f4e32c | ||
|
|
c0d1bf8ecc | ||
|
|
1f5cdc3fa4 | ||
|
|
21b17ca41a | ||
|
|
70713af95a | ||
|
|
2f2b0b28b3 | ||
|
|
7574966ccf | ||
|
|
3986bbf7ac | ||
|
|
4102dc6842 | ||
|
|
d1dc3342a2 | ||
|
|
dfedfc7e7a | ||
|
|
7ff6391801 | ||
|
|
5f96f8c229 | ||
|
|
7e7583e4fc | ||
|
|
74b4ff4993 | ||
|
|
ca50c0a9b3 | ||
|
|
cea6ea4826 | ||
|
|
3bd3564b2b | ||
|
|
9ee9be3339 | ||
|
|
ce530e417c | ||
|
|
c8cade622a | ||
|
|
f65ee12b16 | ||
|
|
230b3edf7b | ||
|
|
670e8afe76 | ||
|
|
e52178811c | ||
|
|
74ab311d3e | ||
|
|
719bea683a | ||
|
|
dc5598f8dc | ||
|
|
c6fef20390 | ||
|
|
87d5b6c473 | ||
|
|
6b377ec5ef | ||
|
|
aebb98189c | ||
|
|
c9ca81e80f | ||
|
|
90ec6a2962 | ||
|
|
7502a372b5 | ||
|
|
d366b711da | ||
|
|
16d2b0f1f3 | ||
|
|
87e485bfe2 | ||
|
|
2f48595cd8 | ||
|
|
fa1a167f8e | ||
|
|
8b7c5b1545 | ||
|
|
44f8b978f9 | ||
|
|
8081a864da | ||
|
|
674731f7d9 | ||
|
|
1a82b8586d | ||
|
|
892e0562b7 | ||
|
|
9e12f3cd17 | ||
|
|
4b7cd79682 | ||
|
|
ed5b2f8bf8 | ||
|
|
d85c893e3a | ||
|
|
2c46707c2b | ||
|
|
2ff295e3b2 | ||
|
|
c96d165d3f | ||
|
|
cff009e758 | ||
|
|
e3c68071b9 | ||
|
|
ffc8ddd92e | ||
|
|
587e3fdd74 | ||
|
|
e4ecb19e38 | ||
|
|
25fcbf37c3 | ||
|
|
408b27c561 | ||
|
|
e35ba52236 | ||
|
|
b75c525df7 | ||
|
|
77682611c1 | ||
|
|
865cc30d16 | ||
|
|
c75fd3c108 | ||
|
|
8387c8dbe4 | ||
|
|
4b322fe9f6 | ||
|
|
f87db2f4fc | ||
|
|
78aba7ae87 | ||
|
|
91f64eccda | ||
|
|
3c12c33911 | ||
|
|
118fc8c42f | ||
|
|
9f56ac8a21 | ||
|
|
a8c2a348aa | ||
|
|
d7bc8516ff | ||
|
|
26ef7b7be3 | ||
|
|
d3d72f85fd | ||
|
|
40ce9256fc | ||
|
|
a6d87f6de4 | ||
|
|
b2836b9f4f | ||
|
|
09565375b5 | ||
|
|
864bcb27ca | ||
|
|
47abac000e | ||
|
|
326f2160d5 | ||
|
|
0a13b4bb99 | ||
|
|
02087e00fa | ||
|
|
e805e9cede | ||
|
|
3f9e2f0af7 | ||
|
|
ffb4a26215 | ||
|
|
5055a9f352 | ||
|
|
28afd1f6f9 | ||
|
|
11f710b30a | ||
|
|
b8b4cc2621 | ||
|
|
0078089cdf | ||
|
|
715c9a1242 | ||
|
|
d5af966623 | ||
|
|
cbfcc75c82 | ||
|
|
08a19550b2 | ||
|
|
ec0cf38c3c | ||
|
|
62e21d9fc0 | ||
|
|
e7cafe86d1 | ||
|
|
df429d56d5 | ||
|
|
39dfbc734e | ||
|
|
9428c186b3 | ||
|
|
6db57424aa | ||
|
|
7dc8b128d6 | ||
|
|
0ed08c2f4c | ||
|
|
afdf0062eb | ||
|
|
fad842a8a0 | ||
|
|
02944d407c | ||
|
|
ddb34a0f59 | ||
|
|
1603d0f146 | ||
|
|
1cba714b00 | ||
|
|
ef122ec169 | ||
|
|
b5f94d84f7 | ||
|
|
471ed51360 | ||
|
|
86c5a0ba8b | ||
|
|
6f4d7dd6c6 | ||
|
|
76e2f750ce | ||
|
|
f15dc881b6 | ||
|
|
fa7150850f | ||
|
|
6fc666e60d | ||
|
|
89fa0658e1 | ||
|
|
847e37e641 | ||
|
|
b7021b5ecb | ||
|
|
5464c884db | ||
|
|
dea9151abd | ||
|
|
737f0bc512 | ||
|
|
36d8eebecf | ||
|
|
ecd3317428 | ||
|
|
fe5dfcf73f | ||
|
|
9bfee3fce2 | ||
|
|
c4bd35bb8a | ||
|
|
9925a270e1 | ||
|
|
83d7290448 | ||
|
|
124e042565 | ||
|
|
0db3e41bde | ||
|
|
3681bfc015 | ||
|
|
143ed8e4ed | ||
|
|
0625c8121e | ||
|
|
82b00008d9 | ||
|
|
1457281b73 | ||
|
|
4931737164 | ||
|
|
4797f99f60 | ||
|
|
5828897503 | ||
|
|
f4c080dd19 | ||
|
|
b3e20b8b13 | ||
|
|
354c3c563b | ||
|
|
a2376d3afd | ||
|
|
5f23c358da | ||
|
|
bdcf9ba153 | ||
|
|
3a8fad7c7d | ||
|
|
df9ef23034 | ||
|
|
ec0bdcce9e | ||
|
|
1ba6fa65e0 | ||
|
|
a8ac99f642 | ||
|
|
6ef0686978 | ||
|
|
8ddda8e5af | ||
|
|
444f494c0b | ||
|
|
3fdec62e25 | ||
|
|
80415afa9f | ||
|
|
9bcc325c1e | ||
|
|
c1d274028f | ||
|
|
041bab88c0 | ||
|
|
996da2d90e | ||
|
|
dbb441dfb6 | ||
|
|
4f7c668c45 | ||
|
|
9f2e49c9e9 | ||
|
|
c0afeba215 | ||
|
|
1eb2038ec4 | ||
|
|
c2ea867d95 | ||
|
|
1b7294f714 | ||
|
|
d1f37d4bbf | ||
|
|
6cb9ae69b2 | ||
|
|
de43980ee2 | ||
|
|
3977b73185 | ||
|
|
d5dcab15a8 | ||
|
|
f08d317bfd | ||
|
|
3fb79c7726 | ||
|
|
8cd8f41cb0 | ||
|
|
d8e5c64826 | ||
|
|
ede7365da8 | ||
|
|
05818671a7 | ||
|
|
57eed7fab6 | ||
|
|
fb94050732 | ||
|
|
02d3ae37aa | ||
|
|
1fe18e8073 | ||
|
|
de1a08a52f | ||
|
|
f47f8a66fe | ||
|
|
f6722c5a26 | ||
|
|
42a0e30891 | ||
|
|
01b08710d1 | ||
|
|
164f48e131 | ||
|
|
fe6f7bcfc1 | ||
|
|
48826a9ad9 | ||
|
|
7d4e9c5669 | ||
|
|
088f815a30 | ||
|
|
7d1e8d258c | ||
|
|
50f50a4f28 | ||
|
|
820dcaaae7 | ||
|
|
10de4f4ef8 | ||
|
|
214b4026ec | ||
|
|
a5e59e6787 | ||
|
|
907d93bcad | ||
|
|
c84df42ff4 | ||
|
|
8fb0d6315d | ||
|
|
72e746181f | ||
|
|
a10fa3c55b | ||
|
|
d0633adc38 | ||
|
|
d8ee6d6092 | ||
|
|
a315df29ca | ||
|
|
2e9711ae8a | ||
|
|
75f66a3809 | ||
|
|
fff9f31913 | ||
|
|
ef73469d5d | ||
|
|
05e4e7301c | ||
|
|
2d7d9d4de7 | ||
|
|
fdef282c57 | ||
|
|
39528a935b | ||
|
|
22b0b77b83 | ||
|
|
a188968e5d | ||
|
|
3c9814191d | ||
|
|
785b375f4a | ||
|
|
fbfbfcc274 | ||
|
|
42ff5a382a | ||
|
|
acc03f14dd | ||
|
|
0ee5d3b79f | ||
|
|
53a5c53857 | ||
|
|
e14bbb2698 | ||
|
|
6b56c43a3a | ||
|
|
d222376b1f | ||
|
|
c3a206b291 | ||
|
|
afffb50b94 | ||
|
|
45e06d215e | ||
|
|
2208320049 | ||
|
|
af2b6742cf | ||
|
|
4254775149 | ||
|
|
44d41e86c9 | ||
|
|
586d6e6abb | ||
|
|
9ab6c58676 | ||
|
|
3d9c0a212e | ||
|
|
f51c6efddc | ||
|
|
bb235f3e88 | ||
|
|
c706a410a2 | ||
|
|
d0c74a7704 | ||
|
|
32dacecd24 | ||
|
|
a7066a5c85 | ||
|
|
4fe74e9e25 | ||
|
|
637fc91413 | ||
|
|
b5aa69db61 | ||
|
|
89a98be6ce | ||
|
|
c6ec00e84d | ||
|
|
99ce688a70 | ||
|
|
3cdabaf883 | ||
|
|
be59bff3d0 | ||
|
|
28f0302e4d | ||
|
|
947f16e371 | ||
|
|
7e8f734301 | ||
|
|
69841a4198 | ||
|
|
4f7daa96a7 | ||
|
|
31c220605e | ||
|
|
2f439fd417 | ||
|
|
f4c835d47a | ||
|
|
c929c1a87e | ||
|
|
b7915ccdeb | ||
|
|
421e98696c | ||
|
|
00e1bf6cff | ||
|
|
9454854f6c | ||
|
|
74ae8636a8 | ||
|
|
d93305f6a4 | ||
|
|
332d75b62a | ||
|
|
5db704f793 | ||
|
|
37be53f334 | ||
|
|
5f59c729b7 | ||
|
|
a8af4e8804 | ||
|
|
8f5e91bc34 | ||
|
|
277bbc0798 | ||
|
|
d97654b851 | ||
|
|
7ec82cbc60 | ||
|
|
63c2343d2c | ||
|
|
d93fe11373 | ||
|
|
fedfbba30c | ||
|
|
49ccba85a2 | ||
|
|
9903adf71b | ||
|
|
48cfc4d4ce | ||
|
|
1f5cc1eef1 | ||
|
|
ce1bb66f23 | ||
|
|
6f9528d78d | ||
|
|
fc01b952b3 | ||
|
|
51f4c1cde0 | ||
|
|
8bb82d168b | ||
|
|
1ef16d55ef | ||
|
|
a242bdd3b1 | ||
|
|
97bc3d5e7c | ||
|
|
dbb27a7771 | ||
|
|
ea11aa4abb | ||
|
|
1af40b6ca0 | ||
|
|
1b220326bf | ||
|
|
8ed3bb8586 | ||
|
|
887cda3287 | ||
|
|
40de7d64c6 | ||
|
|
fbc948fd7c | ||
|
|
12b2f2f499 | ||
|
|
37c10c6cf5 | ||
|
|
3451c2b418 | ||
|
|
158e2a9112 | ||
|
|
1c7a09c9e3 | ||
|
|
28b7768601 | ||
|
|
d21bb5207b | ||
|
|
7a82c91bc8 | ||
|
|
bf2ed21020 | ||
|
|
560be610fe | ||
|
|
3d1d652cfb | ||
|
|
f70567f44b | ||
|
|
0b9308282f | ||
|
|
2259bc5ea2 | ||
|
|
eb93e634e0 | ||
|
|
e0802ef2c3 | ||
|
|
1993dba2e0 | ||
|
|
018bf80930 | ||
|
|
6eaa1bc3f9 | ||
|
|
d8d9174454 | ||
|
|
bd2f0168a1 | ||
|
|
a81601b0a1 | ||
|
|
3c166abd2d | ||
|
|
25d40388da | ||
|
|
74aa51578b | ||
|
|
48cde91dc1 | ||
|
|
9455a1c3c0 | ||
|
|
f19f28cf3e | ||
|
|
59426d4aa9 | ||
|
|
e9e601fe45 | ||
|
|
4d1da1d292 | ||
|
|
341b654078 | ||
|
|
875a069a18 | ||
|
|
106abc24f8 | ||
|
|
ab520ee345 | ||
|
|
e212d413dc | ||
|
|
6f2f025b04 | ||
|
|
a884d0d531 | ||
|
|
6cf11ab411 | ||
|
|
c8623c3b8c | ||
|
|
9d54baac09 | ||
|
|
382bb89e8e | ||
|
|
92aeecbc3e | ||
|
|
9219786f2d | ||
|
|
d2516cc328 | ||
|
|
ab9744d529 | ||
|
|
3ac3581622 | ||
|
|
13ca70b328 | ||
|
|
911abeddd0 | ||
|
|
4ba306f597 | ||
|
|
cc7ce04402 | ||
|
|
dfcf0995cd | ||
|
|
55b049c86a | ||
|
|
e5bdceed58 | ||
|
|
7bda48bd9f | ||
|
|
63a502ba62 | ||
|
|
e0d492f599 | ||
|
|
48e74ed0ea | ||
|
|
95f1a19a0a | ||
|
|
63f3fa98db | ||
|
|
fdfd2f97d2 | ||
|
|
4e3e166132 | ||
|
|
21ffa8b28a | ||
|
|
5f9f970abd | ||
|
|
ff85e2ef57 | ||
|
|
d40d231e9a | ||
|
|
6e86e3f118 | ||
|
|
cedf0e4532 | ||
|
|
05438baad9 | ||
|
|
e603730320 | ||
|
|
c0cff6d1fe | ||
|
|
65060a9416 | ||
|
|
e2f1754a8e | ||
|
|
cf10a8898f | ||
|
|
533f32ed48 | ||
|
|
8918da48f8 | ||
|
|
f3b5823cce | ||
|
|
6b43ad60b3 | ||
|
|
1a60f38c19 | ||
|
|
6330f2d42d | ||
|
|
033b44f9d2 | ||
|
|
bf5acf7ef1 | ||
|
|
522d19a7e7 | ||
|
|
4c34246750 | ||
|
|
52d528613e | ||
|
|
0809916b01 | ||
|
|
cc56c92562 | ||
|
|
447c3f3146 | ||
|
|
eba8e54261 | ||
|
|
a8c95093c4 | ||
|
|
0d72c4a7e0 | ||
|
|
963b0911cf | ||
|
|
e8f68eb6c1 | ||
|
|
59927ffcd9 | ||
|
|
aac5eed9fc | ||
|
|
a412864c80 | ||
|
|
3c825440f0 | ||
|
|
303cf41bb9 | ||
|
|
0512988979 | ||
|
|
423a487bd1 | ||
|
|
e415ef8354 | ||
|
|
ece8f082fb | ||
|
|
c52ea089da | ||
|
|
64e1e6191b | ||
|
|
58875d9a95 | ||
|
|
782bad916e | ||
|
|
1472f0437f | ||
|
|
1256944b96 | ||
|
|
218e425333 | ||
|
|
5562a04f79 | ||
|
|
75409519a1 | ||
|
|
62689b240b | ||
|
|
6f570bcbf1 | ||
|
|
b5962a098a | ||
|
|
371aff3251 | ||
|
|
bab4326aeb | ||
|
|
dc4ee2f0fa | ||
|
|
bf9a2714d3 | ||
|
|
29824fb7d2 | ||
|
|
5b1ee67820 | ||
|
|
3598bc79c3 | ||
|
|
17e1fe2acb | ||
|
|
858c7df86b | ||
|
|
2d07eeb16b | ||
|
|
1448d23ca6 | ||
|
|
2eb942a947 | ||
|
|
cdbdc853ea | ||
|
|
c3a54a8927 | ||
|
|
dfcbdb0c35 | ||
|
|
28ec1eb0ad | ||
|
|
5f6af8c07d | ||
|
|
e7d600ee50 | ||
|
|
b9f82fd0ac | ||
|
|
186c22decd | ||
|
|
9a20ec9c09 | ||
|
|
1b44172bc5 | ||
|
|
bbf5e28571 | ||
|
|
7bf1555c67 | ||
|
|
003601bbdd | ||
|
|
58182712a2 | ||
|
|
930eb1d2e1 | ||
|
|
98260b5b52 | ||
|
|
9a3065ad4c | ||
|
|
79554b47d3 | ||
|
|
31d2f2a799 | ||
|
|
1983533e76 | ||
|
|
e4880a247f | ||
|
|
e43da3d6e1 | ||
|
|
b064be3ec0 | ||
|
|
c33084421d | ||
|
|
7f7853dbc9 | ||
|
|
4d3856975b | ||
|
|
8905c27b86 | ||
|
|
034fee0f75 | ||
|
|
d41d58e30f | ||
|
|
ae690050e7 | ||
|
|
6273e6be9b | ||
|
|
85d13c03e0 | ||
|
|
a8e20ac1c1 | ||
|
|
582365967d | ||
|
|
768cfc7561 | ||
|
|
297820b65a | ||
|
|
abad929485 | ||
|
|
43412d7ef6 | ||
|
|
8f699fa366 | ||
|
|
8e9b3f134b | ||
|
|
48ba1993e0 | ||
|
|
1a0ff422fe | ||
|
|
5de6563ab2 | ||
|
|
6d96b5dbaf | ||
|
|
9ca4a8c941 | ||
|
|
320b17c8db | ||
|
|
e8ebc94191 | ||
|
|
f10a7a75ae | ||
|
|
e08d542c87 | ||
|
|
824ba433f6 | ||
|
|
327febaf59 | ||
|
|
0f8d4f5b32 | ||
|
|
63216addf6 | ||
|
|
9dd951dd0d | ||
|
|
ed8ae300ae | ||
|
|
2e6ba2ffb2 | ||
|
|
dd4ef4c383 | ||
|
|
ef717f888b | ||
|
|
b15e06ffec | ||
|
|
583fd410f5 | ||
|
|
3e73dbdb11 | ||
|
|
908905918e | ||
|
|
3da43bf01d | ||
|
|
bb1a941240 | ||
|
|
4fbb4c6082 | ||
|
|
d563216e3f | ||
|
|
9a5c00e148 | ||
|
|
7e16ce41e3 | ||
|
|
152c59365f | ||
|
|
895dfe475c | ||
|
|
29239e8b77 | ||
|
|
8bead0a33b | ||
|
|
844f9bf409 | ||
|
|
c17d95dae0 | ||
|
|
50443de888 | ||
|
|
04655b9042 | ||
|
|
8a87f316df | ||
|
|
2cd092af42 | ||
|
|
7a8e616f17 | ||
|
|
10616b8d9e | ||
|
|
d6165e5975 | ||
|
|
9cd75390bf | ||
|
|
8adaf629b4 | ||
|
|
c71f417ebf | ||
|
|
795807ddbe | ||
|
|
7715e62def | ||
|
|
41a5a69164 | ||
|
|
5286bf3ac1 | ||
|
|
ddafd7ba26 | ||
|
|
08a32028c1 | ||
|
|
9f9f22548f | ||
|
|
69b91df96b | ||
|
|
3e86c07411 | ||
|
|
eafef9fc7f | ||
|
|
44d33f70e4 | ||
|
|
0574b43971 | ||
|
|
03d9570895 | ||
|
|
c300f0b549 | ||
|
|
e5185ebd57 | ||
|
|
1ba9351957 | ||
|
|
505b801423 | ||
|
|
35a0b42bea | ||
|
|
4f289963d1 | ||
|
|
1631c32868 | ||
|
|
9721b805f3 | ||
|
|
d2b1b19404 | ||
|
|
49632241b6 | ||
|
|
b4f23f9731 | ||
|
|
1cdcc66dba | ||
|
|
0a1a260a22 | ||
|
|
0e377cd0e9 | ||
|
|
3f3cc6359a | ||
|
|
eb0a6f00b5 | ||
|
|
a87610a8d8 | ||
|
|
e3630a9961 | ||
|
|
6ba78e2cf3 | ||
|
|
63a06b5dd8 | ||
|
|
ec60697912 | ||
|
|
d6a8c04d45 | ||
|
|
d46f5858f8 | ||
|
|
a8491eafea | ||
|
|
0d046d8356 | ||
|
|
9c55450a9e | ||
|
|
00e986a64e | ||
|
|
dc4bf669b0 | ||
|
|
059a8de44a | ||
|
|
aa25b7745c | ||
|
|
1097b11115 | ||
|
|
9f67daeb1e | ||
|
|
2065692199 | ||
|
|
33e342f03f | ||
|
|
7a6027f35a | ||
|
|
e46a6f1d12 | ||
|
|
0f3996369b | ||
|
|
0847539e02 | ||
|
|
f576f2eda2 | ||
|
|
21ac3eaf8b | ||
|
|
b237cbb20f | ||
|
|
9b5b48dd1a | ||
|
|
b7eac837f7 | ||
|
|
217bb81722 | ||
|
|
b791073802 | ||
|
|
45cb39e971 | ||
|
|
2df1dd2bb1 | ||
|
|
4cd3dd3670 | ||
|
|
b18ca8c94f | ||
|
|
75fb37d247 | ||
|
|
f863ed0f4f | ||
|
|
3eaf4cd142 | ||
|
|
7859499c97 | ||
|
|
e8ceb58292 | ||
|
|
84610f2a2c | ||
|
|
d906df5b00 | ||
|
|
22f362aab3 | ||
|
|
4942900886 | ||
|
|
1e505d3d0f | ||
|
|
1eca517978 | ||
|
|
38acce7460 | ||
|
|
3855fecc69 | ||
|
|
466b922ea0 | ||
|
|
9e52579ca6 | ||
|
|
1ca1e519b6 | ||
|
|
54a9070c58 | ||
|
|
e108aae3c0 | ||
|
|
27c00410d3 | ||
|
|
74ae7d138e | ||
|
|
20ce5d11a6 | ||
|
|
cbe919c4b3 | ||
|
|
497cba6b13 | ||
|
|
e2849be72c | ||
|
|
a025930957 | ||
|
|
990c78e7cc | ||
|
|
0ef12906d3 | ||
|
|
61a37731ec | ||
|
|
d3217b6a67 | ||
|
|
04266cc20b | ||
|
|
4f36cf7dd1 | ||
|
|
490041587b | ||
|
|
3a3da94468 | ||
|
|
b7ad6cfe62 | ||
|
|
4463e7545d | ||
|
|
d0eafc8b8e | ||
|
|
8b98c20d68 | ||
|
|
caa5060ecd | ||
|
|
aabcf7f31c | ||
|
|
40d48cdfe4 | ||
|
|
8196537878 | ||
|
|
33a00f043b | ||
|
|
f235e20153 | ||
|
|
cf2455c084 | ||
|
|
fc1068a9dc | ||
|
|
35a0ca1875 | ||
|
|
56519354b6 | ||
|
|
78e4e13fb9 | ||
|
|
699b8d9980 | ||
|
|
ba9581801c | ||
|
|
0a5fdf4ea1 | ||
|
|
3849fa2b15 | ||
|
|
0952cbc7c6 | ||
|
|
bb06c39dd4 | ||
|
|
d60dc31443 | ||
|
|
76b3cd8edd | ||
|
|
638ba31694 | ||
|
|
c31b169cae | ||
|
|
fc167526ae | ||
|
|
55eff5eab9 | ||
|
|
f5a7f4e086 | ||
|
|
f7b0932cdd | ||
|
|
ba89ce8fb9 | ||
|
|
9d62deeabe | ||
|
|
459b41f327 | ||
|
|
3062e3f64a | ||
|
|
c1362ca4e2 | ||
|
|
9d24912ad9 | ||
|
|
db290609a8 | ||
|
|
4bc5eced6c | ||
|
|
b82a66c83d | ||
|
|
bf7079df9e | ||
|
|
f137c5740e | ||
|
|
ee47905966 | ||
|
|
f6cd193f9e | ||
|
|
a31775dd23 | ||
|
|
30ba1e5886 | ||
|
|
f97bfa7bf1 | ||
|
|
ace028fa7f | ||
|
|
69f6401e87 | ||
|
|
bd4d974df1 | ||
|
|
6e7446f530 | ||
|
|
afe9471aa2 | ||
|
|
4d56b84861 | ||
|
|
8fede58c64 | ||
|
|
370904212f | ||
|
|
ae03a5aeb7 | ||
|
|
24a38841dd | ||
|
|
bbc6156bd7 | ||
|
|
f387df41d7 | ||
|
|
ceb8b2f5b3 | ||
|
|
877781a6ca | ||
|
|
612d5efd1b | ||
|
|
7ea03801d0 | ||
|
|
ce8f4b4a48 | ||
|
|
15179d2450 | ||
|
|
9aae106970 | ||
|
|
c82044f6bc | ||
|
|
e4e6f59589 | ||
|
|
c8aa9d006f | ||
|
|
311538dcf8 | ||
|
|
324cf2469f | ||
|
|
01745ead1f | ||
|
|
4e705a52eb | ||
|
|
9bb58afee1 | ||
|
|
b45b33380c | ||
|
|
c86a036ac6 | ||
|
|
8694511d86 | ||
|
|
58c4a46f6e | ||
|
|
dfd26d88d4 | ||
|
|
dcf6959cff | ||
|
|
924affd978 | ||
|
|
ad6d233c11 | ||
|
|
d84ab1d215 | ||
|
|
a5fc04e0ce | ||
|
|
7f71513085 | ||
|
|
6004ef3f0d | ||
|
|
e76392a169 | ||
|
|
53f0b2e9b0 | ||
|
|
de79e36251 | ||
|
|
c84e984eae | ||
|
|
6d65c967b1 | ||
|
|
8199ea84f4 | ||
|
|
ce1b0da09d | ||
|
|
cd6bb848e9 | ||
|
|
34040fcd59 | ||
|
|
1969fb79fe | ||
|
|
904f64604b | ||
|
|
9b3509208d | ||
|
|
6700dc969f | ||
|
|
21714cc411 | ||
|
|
1a247d60e7 | ||
|
|
adf8b1f7aa | ||
|
|
69f640daa4 | ||
|
|
a43314f5be | ||
|
|
599e8a7e37 | ||
|
|
0e00451e1f | ||
|
|
866e5d2011 | ||
|
|
232674b1c1 | ||
|
|
ddf60d2512 | ||
|
|
3e6e0153cf | ||
|
|
6b984aac53 | ||
|
|
6d3ee8116e | ||
|
|
904a0f67dd | ||
|
|
81e1ce2688 | ||
|
|
7c422d2ed6 | ||
|
|
2a5129c481 | ||
|
|
28cdfca14b | ||
|
|
202aba048b | ||
|
|
3c2a4703bc | ||
|
|
4904aac2df | ||
|
|
303f88d6ee | ||
|
|
cccc74279d | ||
|
|
16db2abca5 | ||
|
|
859f2d9795 | ||
|
|
4fd9957bec | ||
|
|
0ac6e6ba2c | ||
|
|
785cc04126 | ||
|
|
f269eae774 | ||
|
|
6f19fcb702 | ||
|
|
b7b6218306 | ||
|
|
5cd809b48a | ||
|
|
5a1b894138 | ||
|
|
4d402617b6 | ||
|
|
666ef366e7 | ||
|
|
28d029a553 | ||
|
|
00763e986a | ||
|
|
f9a7cca478 | ||
|
|
d09b34e232 | ||
|
|
2737675c36 | ||
|
|
7591f40b2c | ||
|
|
c4af6fa72d | ||
|
|
19be7ac580 | ||
|
|
49404f763d | ||
|
|
87f2a67789 | ||
|
|
041edb6177 | ||
|
|
df2fad76c7 | ||
|
|
94020a8fbb | ||
|
|
cb1e19d28f | ||
|
|
0980dffb47 | ||
|
|
4051225ecb | ||
|
|
507518da04 | ||
|
|
d3bdbc0b81 |
@@ -5,3 +5,5 @@ data/*
|
|||||||
tmp/*
|
tmp/*
|
||||||
django.db
|
django.db
|
||||||
celerybeat.pid
|
celerybeat.pid
|
||||||
|
### Vagrant ###
|
||||||
|
.vagrant/
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -34,3 +34,5 @@ data/static
|
|||||||
docs/_build/
|
docs/_build/
|
||||||
xpack
|
xpack
|
||||||
logs/*
|
logs/*
|
||||||
|
### Vagrant ###
|
||||||
|
.vagrant/
|
||||||
11
Dockerfile
11
Dockerfile
@@ -6,10 +6,12 @@ RUN useradd jumpserver
|
|||||||
|
|
||||||
COPY ./requirements /tmp/requirements
|
COPY ./requirements /tmp/requirements
|
||||||
|
|
||||||
RUN yum -y install epel-release openldap-clients telnet && cd /tmp/requirements && \
|
RUN yum -y install epel-release && \
|
||||||
yum -y install $(cat rpm_requirements.txt)
|
echo -e "[mysql]\nname=mysql\nbaseurl=https://mirrors.tuna.tsinghua.edu.cn/mysql/yum/mysql57-community-el6/\ngpgcheck=0\nenabled=1" > /etc/yum.repos.d/mysql.repo
|
||||||
|
RUN cd /tmp/requirements && yum -y install $(cat rpm_requirements.txt)
|
||||||
RUN cd /tmp/requirements && pip install -r requirements.txt
|
RUN cd /tmp/requirements && pip install --upgrade pip setuptools && pip install wheel && \
|
||||||
|
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt || pip install -r requirements.txt
|
||||||
|
RUN mkdir -p /root/.ssh/ && echo -e "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config
|
||||||
|
|
||||||
COPY . /opt/jumpserver
|
COPY . /opt/jumpserver
|
||||||
RUN echo > config.yml
|
RUN echo > config.yml
|
||||||
@@ -19,5 +21,6 @@ VOLUME /opt/jumpserver/logs
|
|||||||
ENV LANG=zh_CN.UTF-8
|
ENV LANG=zh_CN.UTF-8
|
||||||
ENV LC_ALL=zh_CN.UTF-8
|
ENV LC_ALL=zh_CN.UTF-8
|
||||||
|
|
||||||
|
EXPOSE 8070
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
ENTRYPOINT ["./entrypoint.sh"]
|
ENTRYPOINT ["./entrypoint.sh"]
|
||||||
|
|||||||
232
README.md
232
README.md
@@ -1,52 +1,230 @@
|
|||||||
## Jumpserver
|
# JumpServer 多云环境下更好用的堡垒机
|
||||||
|
|
||||||
[](https://www.python.org/)
|
[](https://www.python.org/)
|
||||||
[](https://www.djangoproject.com/)
|
[](https://www.djangoproject.com/)
|
||||||
[](https://www.ansible.com/)
|
|
||||||
[](http://www.paramiko.org/)
|
|
||||||
|
|
||||||
|
JumpServer 是全球首款开源的堡垒机,使用 GNU GPL v2.0 开源协议,是符合 4A 规范的运维安全审计系统。
|
||||||
|
|
||||||
----
|
JumpServer 使用 Python / Django 为主进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web Terminal 方案,交互界面美观、用户体验好。
|
||||||
|
|
||||||
Jumpserver是全球首款完全开源的堡垒机,使用GNU GPL v2.0开源协议,是符合 4A 的专业运维审计系统。
|
JumpServer 采纳分布式架构,支持多机房跨区域部署,支持横向扩展,无资产数量及并发限制。
|
||||||
|
|
||||||
Jumpserver使用Python / Django 进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web Terminal 解决方案,交互界面美观、用户体验好。
|
|
||||||
|
|
||||||
Jumpserver采纳分布式架构,支持多机房跨区域部署,中心节点提供 API,各机房部署登录节点,可横向扩展、无并发限制。
|
|
||||||
|
|
||||||
改变世界,从一点点开始。
|
改变世界,从一点点开始。
|
||||||
|
|
||||||
----
|
> 注: [KubeOperator](https://github.com/KubeOperator/KubeOperator) 是 JumpServer 团队在 Kubernetes 领域的的又一全新力作,欢迎关注和使用。
|
||||||
|
|
||||||
### 功能
|
## 特色优势
|
||||||
|
|
||||||

|
- 开源: 零门槛,线上快速获取和安装, 修复版本视情况而定;
|
||||||
|
, 修复版本视情况而定- 分布式: 轻松支持大规模并发访问;
|
||||||
|
- 无插件: 仅需浏览器,极致的 Web Terminal 使用体验;
|
||||||
|
- 多云支持: 一套系统,同时管理不同云上面的资产;
|
||||||
|
- 云端存储: 审计录像云端存储,永不丢失;
|
||||||
|
- 多租户: 一套系统,多个子公司和部门同时使用。
|
||||||
|
|
||||||
### 开始使用
|
## 版本说明
|
||||||
|
|
||||||
快速开始文档 [Docker安装](http://docs.jumpserver.org/zh/docs/dockerinstall.html)
|
自 v2.0.0 发布后, JumpServer 版本号命名将变更为:v大版本.功能版本.Bug修复版本。比如:
|
||||||
|
|
||||||
一步一步安装文档 [详细部署](http://docs.jumpserver.org/zh/docs/step_by_step.html)
|
```
|
||||||
|
v2.0.1 是 v2.0.0 之后的Bug修复版本;
|
||||||
|
v2.1.0 是 v2.0.0 之后的功能版本。
|
||||||
|
```
|
||||||
|
|
||||||
也可以查看我们完整文档包括了使用和开发 [文档](http://docs.jumpserver.org)
|
像其它优秀开源项目一样,JumpServer 每个月会发布一个功能版本,并同时维护 3 个功能版本。比如:
|
||||||
|
|
||||||
### Demo 和 截图
|
```
|
||||||
|
在 v2.4 发布前,我们会同时维护 v2.1、v2.2、v2.3;
|
||||||
|
在 v2.4 发布后,我们会同时维护 v2.2、v2.3、v2.4;v2.1 会停止维护。
|
||||||
|
```
|
||||||
|
|
||||||
我们提供了DEMO和截图可以让你快速了解Jumpserver
|
## 功能列表
|
||||||
|
|
||||||
[DEMO](https://demo.jumpserver.org)
|
<table>
|
||||||
[截图](http://docs.jumpserver.org/zh/docs/snapshot.html)
|
<tr>
|
||||||
|
<td rowspan="8">身份认证<br>Authentication</td>
|
||||||
|
<td rowspan="5">登录认证</td>
|
||||||
|
<td>资源统一登录与认证</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>LDAP/AD 认证</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>RADIUS 认证</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>OpenID 认证(实现单点登录)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>CAS 认证 (实现单点登录)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td rowspan="2">MFA认证</td>
|
||||||
|
<td>MFA 二次认证(Google Authenticator)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>RADIUS 二次认证</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>登录复核(X-PACK)</td>
|
||||||
|
<td>用户登录行为受管理员的监管与控制</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td rowspan="11">账号管理<br>Account</td>
|
||||||
|
<td rowspan="2">集中账号</td>
|
||||||
|
<td>管理用户管理</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>系统用户管理</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td rowspan="4">统一密码</td>
|
||||||
|
<td>资产密码托管</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>自动生成密码</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>自动推送密码</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>密码过期设置</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td rowspan="2">批量改密(X-PACK)</td>
|
||||||
|
<td>定期批量改密</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>多种密码策略</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>多云纳管(X-PACK)</td>
|
||||||
|
<td>对私有云、公有云资产自动统一纳管</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>收集用户(X-PACK)</td>
|
||||||
|
<td>自定义任务定期收集主机用户</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>密码匣子(X-PACK)</td>
|
||||||
|
<td>统一对资产主机的用户密码进行查看、更新、测试操作</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td rowspan="15">授权控制<br>Authorization</td>
|
||||||
|
<td>多维授权</td>
|
||||||
|
<td>对用户、用户组、资产、资产节点、应用以及系统用户进行授权</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td rowspan="4">资产授权</td>
|
||||||
|
<td>资产以树状结构进行展示</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>资产和节点均可灵活授权</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>节点内资产自动继承授权</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>子节点自动继承父节点授权</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td rowspan="2">应用授权</td>
|
||||||
|
<td>实现更细粒度的应用级授权</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>MySQL 数据库应用、RemoteApp 远程应用(X-PACK)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>动作授权</td>
|
||||||
|
<td>实现对授权资产的文件上传、下载以及连接动作的控制</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>时间授权</td>
|
||||||
|
<td>实现对授权资源使用时间段的限制</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>特权指令</td>
|
||||||
|
<td>实现对特权指令的使用(支持黑白名单)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>命令过滤</td>
|
||||||
|
<td>实现对授权系统用户所执行的命令进行控制</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>文件传输</td>
|
||||||
|
<td>SFTP 文件上传/下载</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>文件管理</td>
|
||||||
|
<td>实现 Web SFTP 文件管理</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>工单管理(X-PACK)</td>
|
||||||
|
<td>支持对用户登录请求行为进行控制</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>组织管理(X-PACK)</td>
|
||||||
|
<td>实现多租户管理与权限隔离</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td rowspan="7">安全审计<br>Audit</td>
|
||||||
|
<td>操作审计</td>
|
||||||
|
<td>用户操作行为审计</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td rowspan="2">会话审计</td>
|
||||||
|
<td>在线会话内容审计</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>历史会话内容审计</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td rowspan="2">录像审计</td>
|
||||||
|
<td>支持对 Linux、Windows 等资产操作的录像进行回放审计</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>支持对 RemoteApp(X-PACK)、MySQL 等应用操作的录像进行回放审计</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>指令审计</td>
|
||||||
|
<td>支持对资产和应用等操作的命令进行审计</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>文件传输</td>
|
||||||
|
<td>可对文件的上传、下载记录进行审计</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
### SDK
|
## 快速开始
|
||||||
|
|
||||||
我们还编写了一些SDK,供你其它系统快速和Jumpserver APi交互,
|
- [极速安装](https://docs.jumpserver.org/zh/master/install/setup_by_fast/)
|
||||||
|
- [完整文档](https://docs.jumpserver.org)
|
||||||
|
- [演示视频](https://jumpserver.oss-cn-hangzhou.aliyuncs.com/jms-media/%E3%80%90%E6%BC%94%E7%A4%BA%E8%A7%86%E9%A2%91%E3%80%91Jumpserver%20%E5%A0%A1%E5%9E%92%E6%9C%BA%20V1.5.0%20%E6%BC%94%E7%A4%BA%E8%A7%86%E9%A2%91%20-%20final.mp4)
|
||||||
|
|
||||||
- [python](https://github.com/jumpserver/jumpserver-python-sdk) Jumpserver其它组件使用这个SDK完成交互
|
## 案例研究
|
||||||
- [java](https://github.com/KaiJunYan/jumpserver-java-sdk.git) 恺珺同学提供的Java版本的SDK
|
|
||||||
|
|
||||||
|
- [JumpServer 堡垒机护航顺丰科技超大规模资产安全运维](https://blog.fit2cloud.com/?p=1147);
|
||||||
|
- [JumpServer 堡垒机让“大智慧”的混合 IT 运维更智慧](https://blog.fit2cloud.com/?p=882);
|
||||||
|
- [携程 JumpServer 堡垒机部署与运营实战](https://blog.fit2cloud.com/?p=851);
|
||||||
|
- [小红书的JumpServer堡垒机大规模资产跨版本迁移之路](https://blog.fit2cloud.com/?p=516);
|
||||||
|
- [JumpServer堡垒机助力中手游提升多云环境下安全运维能力](https://blog.fit2cloud.com/?p=732);
|
||||||
|
- [中通快递:JumpServer主机安全运维实践](https://blog.fit2cloud.com/?p=708);
|
||||||
|
- [东方明珠:JumpServer高效管控异构化、分布式云端资产](https://blog.fit2cloud.com/?p=687);
|
||||||
|
- [江苏农信:JumpServer堡垒机助力行业云安全运维](https://blog.fit2cloud.com/?p=666)。
|
||||||
|
|
||||||
### License & Copyright
|
## 安全说明
|
||||||
Copyright (c) 2014-2018 Beijing Duizhan Tech, Inc., All rights reserved.
|
|
||||||
|
JumpServer是一款安全产品,请参考 [基本安全建议](https://docs.jumpserver.org/zh/master/install/install_security/) 部署安装.
|
||||||
|
|
||||||
|
如果你发现安全问题,可以直接联系我们:
|
||||||
|
|
||||||
|
- ibuler@fit2cloud.com
|
||||||
|
- support@fit2cloud.com
|
||||||
|
- 400-052-0755
|
||||||
|
|
||||||
|
## License & Copyright
|
||||||
|
|
||||||
|
Copyright (c) 2014-2020 飞致云 FIT2CLOUD, All rights reserved.
|
||||||
|
|
||||||
Licensed under The GNU General Public License version 2 (GPLv2) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
Licensed under The GNU General Public License version 2 (GPLv2) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
|||||||
60
README_EN.md
Normal file
60
README_EN.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
## Jumpserver
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
[](https://www.python.org/)
|
||||||
|
[](https://www.djangoproject.com/)
|
||||||
|
[](https://www.ansible.com/)
|
||||||
|
[](http://www.paramiko.org/)
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
- [中文版](https://github.com/jumpserver/jumpserver/blob/master/README_EN.md)
|
||||||
|
|
||||||
|
Jumpserver is the first fully open source bastion in the world, based on the GNU GPL v2.0 open source protocol. Jumpserver is a professional operation and maintenance audit system conforms to 4A specifications.
|
||||||
|
|
||||||
|
Jumpserver is developed using Python / Django, conforms to the Web 2.0 specification, and is equipped with the industry-leading Web Terminal solution which have beautiful interface and great user experience.
|
||||||
|
|
||||||
|
Jumpserver adopts a distributed architecture to support multi-branch deployment across multiple areas. The central node provides APIs, and login nodes are deployed in each branch. It can be scaled horizontally without concurrency restrictions.
|
||||||
|
|
||||||
|
Change the world, starting from little things.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Start
|
||||||
|
|
||||||
|
Quick start [Docker Install](http://docs.jumpserver.org/zh/docs/dockerinstall.html)
|
||||||
|
|
||||||
|
Step by Step deployment. [Docs](http://docs.jumpserver.org/zh/docs/step_by_step.html)
|
||||||
|
|
||||||
|
Full documentation [Docs](http://docs.jumpserver.org)
|
||||||
|
|
||||||
|
### Demo、Video 和 Snapshot
|
||||||
|
|
||||||
|
We provide online demo, demo video and screenshots to get you started quickly.
|
||||||
|
|
||||||
|
[Demo](https://demo.jumpserver.org/auth/login/?next=/)
|
||||||
|
[Video](https://fit2cloud2-offline-installer.oss-cn-beijing.aliyuncs.com/tools/Jumpserver%20%E4%BB%8B%E7%BB%8Dv1.4.mp4)
|
||||||
|
[Snapshot](http://docs.jumpserver.org/zh/docs/snapshot.html)
|
||||||
|
|
||||||
|
### SDK
|
||||||
|
|
||||||
|
We provide the SDK for your other systems to quickly interact with the Jumpserver API.
|
||||||
|
|
||||||
|
- [Python](https://github.com/jumpserver/jumpserver-python-sdk) Jumpserver other components use this SDK to complete the interaction.
|
||||||
|
- [Java](https://github.com/KaiJunYan/jumpserver-java-sdk.git) 恺珺同学提供的Java版本的SDK thanks to 恺珺 for provide Java SDK
|
||||||
|
|
||||||
|
|
||||||
|
### License & Copyright
|
||||||
|
Copyright (c) 2014-2019 Beijing Duizhan Tech, Inc., All rights reserved.
|
||||||
|
|
||||||
|
Licensed under The GNU General Public License version 2 (GPLv2) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.gnu.org/licenses/gpl-2.0.html
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||||
56
Vagrantfile
vendored
Normal file
56
Vagrantfile
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# -*- mode: ruby -*-
|
||||||
|
# vi: set ft=ruby :
|
||||||
|
|
||||||
|
Vagrant.configure("2") do |config|
|
||||||
|
# The most common configuration options are documented and commented below.
|
||||||
|
# For a complete reference, please see the online documentation at
|
||||||
|
# https://docs.vagrantup.com.
|
||||||
|
|
||||||
|
# Every Vagrant development environment requires a box. You can search for
|
||||||
|
# boxes at https://vagrantcloud.com/search.
|
||||||
|
config.vm.box_check_update = false
|
||||||
|
config.vm.box = "centos/7"
|
||||||
|
config.vm.hostname = "jumpserver"
|
||||||
|
config.vm.network "private_network", ip: "172.17.8.101"
|
||||||
|
config.vm.provider "virtualbox" do |vb|
|
||||||
|
vb.memory = "4096"
|
||||||
|
vb.cpus = 2
|
||||||
|
vb.name = "jumpserver"
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.synced_folder ".", "/vagrant", type: "rsync",
|
||||||
|
rsync__verbose: true,
|
||||||
|
rsync__exclude: ['.git*', 'node_modules*','*.log','*.box','Vagrantfile']
|
||||||
|
|
||||||
|
config.vm.provision "shell", inline: <<-SHELL
|
||||||
|
## 设置yum的阿里云源
|
||||||
|
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
|
||||||
|
sudo sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo
|
||||||
|
sudo curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
|
||||||
|
sudo yum makecache
|
||||||
|
|
||||||
|
## 安装依赖包
|
||||||
|
sudo yum install -y python36 python36-devel python36-pip \
|
||||||
|
libtiff-devel libjpeg-devel libzip-devel freetype-devel \
|
||||||
|
lcms2-devel libwebp-devel tcl-devel tk-devel sshpass \
|
||||||
|
openldap-devel mariadb-devel mysql-devel libffi-devel \
|
||||||
|
openssh-clients telnet openldap-clients gcc
|
||||||
|
|
||||||
|
## 配置pip阿里云源
|
||||||
|
mkdir /home/vagrant/.pip
|
||||||
|
cat << EOF | sudo tee -a /home/vagrant/.pip/pip.conf
|
||||||
|
[global]
|
||||||
|
timeout = 6000
|
||||||
|
index-url = https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
|
||||||
|
[install]
|
||||||
|
use-mirrors = true
|
||||||
|
mirrors = https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
trusted-host=mirrors.aliyun.com
|
||||||
|
EOF
|
||||||
|
|
||||||
|
python3.6 -m venv /home/vagrant/venv
|
||||||
|
source /home/vagrant/venv/bin/activate
|
||||||
|
echo 'source /home/vagrant/venv/bin/activate' >> /home/vagrant/.bash_profile
|
||||||
|
SHELL
|
||||||
|
end
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
__version__ = "1.4.6"
|
|
||||||
|
|||||||
3
apps/applications/admin.py
Normal file
3
apps/applications/admin.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
2
apps/applications/api/__init__.py
Normal file
2
apps/applications/api/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from .remote_app import *
|
||||||
|
from .database_app import *
|
||||||
20
apps/applications/api/database_app.py
Normal file
20
apps/applications/api/database_app.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
|
||||||
|
from .. import models
|
||||||
|
from .. import serializers
|
||||||
|
from ..hands import IsOrgAdminOrAppUser
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'DatabaseAppViewSet',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseAppViewSet(OrgBulkModelViewSet):
|
||||||
|
model = models.DatabaseApp
|
||||||
|
filter_fields = ('name',)
|
||||||
|
search_fields = filter_fields
|
||||||
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
serializer_class = serializers.DatabaseAppSerializer
|
||||||
27
apps/applications/api/remote_app.py
Normal file
27
apps/applications/api/remote_app.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
from orgs.mixins import generics
|
||||||
|
from ..hands import IsOrgAdmin, IsAppUser
|
||||||
|
from ..models import RemoteApp
|
||||||
|
from ..serializers import RemoteAppSerializer, RemoteAppConnectionInfoSerializer
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'RemoteAppViewSet', 'RemoteAppConnectionInfoApi',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteAppViewSet(OrgBulkModelViewSet):
|
||||||
|
model = RemoteApp
|
||||||
|
filter_fields = ('name', 'type', 'comment')
|
||||||
|
search_fields = filter_fields
|
||||||
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
serializer_class = RemoteAppSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteAppConnectionInfoApi(generics.RetrieveAPIView):
|
||||||
|
model = RemoteApp
|
||||||
|
permission_classes = (IsAppUser, )
|
||||||
|
serializer_class = RemoteAppConnectionInfoSerializer
|
||||||
7
apps/applications/apps.py
Normal file
7
apps/applications/apps.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationsConfig(AppConfig):
|
||||||
|
name = 'applications'
|
||||||
63
apps/applications/const.py
Normal file
63
apps/applications/const.py
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
# RemoteApp
|
||||||
|
|
||||||
|
REMOTE_APP_BOOT_PROGRAM_NAME = '||jmservisor'
|
||||||
|
|
||||||
|
REMOTE_APP_TYPE_CHROME = 'chrome'
|
||||||
|
REMOTE_APP_TYPE_MYSQL_WORKBENCH = 'mysql_workbench'
|
||||||
|
REMOTE_APP_TYPE_VMWARE_CLIENT = 'vmware_client'
|
||||||
|
REMOTE_APP_TYPE_CUSTOM = 'custom'
|
||||||
|
|
||||||
|
# Fields attribute write_only default => False
|
||||||
|
|
||||||
|
REMOTE_APP_TYPE_CHROME_FIELDS = [
|
||||||
|
{'name': 'chrome_target'},
|
||||||
|
{'name': 'chrome_username'},
|
||||||
|
{'name': 'chrome_password', 'write_only': True}
|
||||||
|
]
|
||||||
|
REMOTE_APP_TYPE_MYSQL_WORKBENCH_FIELDS = [
|
||||||
|
{'name': 'mysql_workbench_ip'},
|
||||||
|
{'name': 'mysql_workbench_name'},
|
||||||
|
{'name': 'mysql_workbench_username'},
|
||||||
|
{'name': 'mysql_workbench_password', 'write_only': True}
|
||||||
|
]
|
||||||
|
REMOTE_APP_TYPE_VMWARE_CLIENT_FIELDS = [
|
||||||
|
{'name': 'vmware_target'},
|
||||||
|
{'name': 'vmware_username'},
|
||||||
|
{'name': 'vmware_password', 'write_only': True}
|
||||||
|
]
|
||||||
|
REMOTE_APP_TYPE_CUSTOM_FIELDS = [
|
||||||
|
{'name': 'custom_cmdline'},
|
||||||
|
{'name': 'custom_target'},
|
||||||
|
{'name': 'custom_username'},
|
||||||
|
{'name': 'custom_password', 'write_only': True}
|
||||||
|
]
|
||||||
|
|
||||||
|
REMOTE_APP_TYPE_FIELDS_MAP = {
|
||||||
|
REMOTE_APP_TYPE_CHROME: REMOTE_APP_TYPE_CHROME_FIELDS,
|
||||||
|
REMOTE_APP_TYPE_MYSQL_WORKBENCH: REMOTE_APP_TYPE_MYSQL_WORKBENCH_FIELDS,
|
||||||
|
REMOTE_APP_TYPE_VMWARE_CLIENT: REMOTE_APP_TYPE_VMWARE_CLIENT_FIELDS,
|
||||||
|
REMOTE_APP_TYPE_CUSTOM: REMOTE_APP_TYPE_CUSTOM_FIELDS
|
||||||
|
}
|
||||||
|
|
||||||
|
REMOTE_APP_TYPE_CHOICES = (
|
||||||
|
(REMOTE_APP_TYPE_CHROME, 'Chrome'),
|
||||||
|
(REMOTE_APP_TYPE_MYSQL_WORKBENCH, 'MySQL Workbench'),
|
||||||
|
(REMOTE_APP_TYPE_VMWARE_CLIENT, 'vSphere Client'),
|
||||||
|
(REMOTE_APP_TYPE_CUSTOM, _('Custom')),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# DatabaseApp
|
||||||
|
|
||||||
|
|
||||||
|
DATABASE_APP_TYPE_MYSQL = 'mysql'
|
||||||
|
|
||||||
|
DATABASE_APP_TYPE_CHOICES = (
|
||||||
|
(DATABASE_APP_TYPE_MYSQL, 'MySQL'),
|
||||||
|
)
|
||||||
15
apps/applications/hands.py
Normal file
15
apps/applications/hands.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
"""
|
||||||
|
jumpserver.__app__.hands.py
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This app depends other apps api, function .. should be import or write mack here.
|
||||||
|
|
||||||
|
Other module of this app shouldn't connect with other app.
|
||||||
|
|
||||||
|
:copyright: (c) 2014-2018 by JumpServer Team.
|
||||||
|
:license: GPL v2, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser
|
||||||
|
from users.models import User, UserGroup
|
||||||
42
apps/applications/migrations/0001_initial.py
Normal file
42
apps/applications/migrations/0001_initial.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-05-20 11:04
|
||||||
|
|
||||||
|
import common.fields.model
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0026_auto_20190325_2035'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='RemoteApp',
|
||||||
|
fields=[
|
||||||
|
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||||
|
('type', models.CharField(choices=[('Browser', (('chrome', 'Chrome'),)), ('Database tools', (('mysql_workbench', 'MySQL Workbench'),)), ('Virtualization tools', (('vmware_client', 'vSphere Client'),)), ('custom', 'Custom')], default='chrome', max_length=128, verbose_name='App type')),
|
||||||
|
('path', models.CharField(max_length=128, verbose_name='App path')),
|
||||||
|
('params', common.fields.model.EncryptJsonDictTextField(blank=True, default={}, max_length=4096, null=True, verbose_name='Parameters')),
|
||||||
|
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
|
||||||
|
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
|
||||||
|
('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),
|
||||||
|
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Asset', verbose_name='Asset')),
|
||||||
|
('system_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.SystemUser', verbose_name='System user')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'RemoteApp',
|
||||||
|
'ordering': ('name',),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='remoteapp',
|
||||||
|
unique_together={('org_id', 'name')},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-09-09 09:57
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('applications', '0001_initial'),
|
||||||
|
('perms', '0009_remoteapppermission_system_users'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='remoteapp',
|
||||||
|
name='system_user',
|
||||||
|
),
|
||||||
|
]
|
||||||
18
apps/applications/migrations/0003_auto_20191210_1659.py
Normal file
18
apps/applications/migrations/0003_auto_20191210_1659.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 2.1.11 on 2019-12-10 08:59
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('applications', '0002_remove_remoteapp_system_user'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='remoteapp',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom')], default='chrome', max_length=128, verbose_name='App type'),
|
||||||
|
),
|
||||||
|
]
|
||||||
38
apps/applications/migrations/0004_auto_20191218_1705.py
Normal file
38
apps/applications/migrations/0004_auto_20191218_1705.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Generated by Django 2.1.11 on 2019-12-18 09:05
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('applications', '0003_auto_20191210_1659'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='DatabaseApp',
|
||||||
|
fields=[
|
||||||
|
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
|
||||||
|
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
|
||||||
|
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
|
||||||
|
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||||
|
('type', models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=128, verbose_name='Type')),
|
||||||
|
('host', models.CharField(db_index=True, max_length=128, verbose_name='Host')),
|
||||||
|
('port', models.IntegerField(default=3306, verbose_name='Port')),
|
||||||
|
('database', models.CharField(blank=True, db_index=True, max_length=128, null=True, verbose_name='Database')),
|
||||||
|
('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'DatabaseApp',
|
||||||
|
'ordering': ('name',),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='databaseapp',
|
||||||
|
unique_together={('org_id', 'name')},
|
||||||
|
),
|
||||||
|
]
|
||||||
2
apps/applications/models/__init__.py
Normal file
2
apps/applications/models/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from .remote_app import *
|
||||||
|
from .database_app import *
|
||||||
42
apps/applications/models/database_app.py
Normal file
42
apps/applications/models/database_app.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from orgs.mixins.models import OrgModelMixin
|
||||||
|
from common.mixins import CommonModelMixin
|
||||||
|
from .. import const
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['DatabaseApp']
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseApp(CommonModelMixin, OrgModelMixin):
|
||||||
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
|
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||||
|
type = models.CharField(
|
||||||
|
default=const.DATABASE_APP_TYPE_MYSQL,
|
||||||
|
choices=const.DATABASE_APP_TYPE_CHOICES,
|
||||||
|
max_length=128, verbose_name=_('Type')
|
||||||
|
)
|
||||||
|
host = models.CharField(
|
||||||
|
max_length=128, verbose_name=_('Host'), db_index=True
|
||||||
|
)
|
||||||
|
port = models.IntegerField(default=3306, verbose_name=_('Port'))
|
||||||
|
database = models.CharField(
|
||||||
|
max_length=128, blank=True, null=True, verbose_name=_('Database'),
|
||||||
|
db_index=True
|
||||||
|
)
|
||||||
|
comment = models.TextField(
|
||||||
|
max_length=128, default='', blank=True, verbose_name=_('Comment')
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = [('org_id', 'name'), ]
|
||||||
|
verbose_name = _("DatabaseApp")
|
||||||
|
ordering = ('name', )
|
||||||
78
apps/applications/models/remote_app.py
Normal file
78
apps/applications/models/remote_app.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from orgs.mixins.models import OrgModelMixin
|
||||||
|
from common.fields.model import EncryptJsonDictTextField
|
||||||
|
|
||||||
|
from .. import const
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'RemoteApp',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteApp(OrgModelMixin):
|
||||||
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
|
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||||
|
asset = models.ForeignKey(
|
||||||
|
'assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset')
|
||||||
|
)
|
||||||
|
type = models.CharField(
|
||||||
|
default=const.REMOTE_APP_TYPE_CHROME,
|
||||||
|
choices=const.REMOTE_APP_TYPE_CHOICES,
|
||||||
|
max_length=128, verbose_name=_('App type')
|
||||||
|
)
|
||||||
|
path = models.CharField(
|
||||||
|
max_length=128, blank=False, null=False,
|
||||||
|
verbose_name=_('App path')
|
||||||
|
)
|
||||||
|
params = EncryptJsonDictTextField(
|
||||||
|
max_length=4096, default={}, blank=True, null=True,
|
||||||
|
verbose_name=_('Parameters')
|
||||||
|
)
|
||||||
|
created_by = models.CharField(
|
||||||
|
max_length=32, null=True, blank=True, verbose_name=_('Created by')
|
||||||
|
)
|
||||||
|
date_created = models.DateTimeField(
|
||||||
|
auto_now_add=True, null=True, blank=True, verbose_name=_('Date created')
|
||||||
|
)
|
||||||
|
comment = models.TextField(
|
||||||
|
max_length=128, default='', blank=True, verbose_name=_('Comment')
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("RemoteApp")
|
||||||
|
unique_together = [('org_id', 'name')]
|
||||||
|
ordering = ('name', )
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parameters(self):
|
||||||
|
"""
|
||||||
|
返回Guacamole需要的RemoteApp配置参数信息中的parameters参数
|
||||||
|
"""
|
||||||
|
_parameters = list()
|
||||||
|
_parameters.append(self.type)
|
||||||
|
path = '\"%s\"' % self.path
|
||||||
|
_parameters.append(path)
|
||||||
|
for field in const.REMOTE_APP_TYPE_FIELDS_MAP[self.type]:
|
||||||
|
value = self.params.get(field['name'])
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
_parameters.append(value)
|
||||||
|
_parameters = ' '.join(_parameters)
|
||||||
|
return _parameters
|
||||||
|
|
||||||
|
@property
|
||||||
|
def asset_info(self):
|
||||||
|
return {
|
||||||
|
'id': self.asset.id,
|
||||||
|
'hostname': self.asset.hostname
|
||||||
|
}
|
||||||
2
apps/applications/serializers/__init__.py
Normal file
2
apps/applications/serializers/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from .remote_app import *
|
||||||
|
from .database_app import *
|
||||||
26
apps/applications/serializers/database_app.py
Normal file
26
apps/applications/serializers/database_app.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
|
from common.serializers import AdaptedBulkListSerializer
|
||||||
|
|
||||||
|
from .. import models
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'DatabaseAppSerializer',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseAppSerializer(BulkOrgResourceModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.DatabaseApp
|
||||||
|
list_serializer_class = AdaptedBulkListSerializer
|
||||||
|
fields = [
|
||||||
|
'id', 'name', 'type', 'get_type_display', 'host', 'port',
|
||||||
|
'database', 'comment', 'created_by', 'date_created', 'date_updated',
|
||||||
|
]
|
||||||
|
read_only_fields = [
|
||||||
|
'created_by', 'date_created', 'date_updated'
|
||||||
|
'get_type_display',
|
||||||
|
]
|
||||||
86
apps/applications/serializers/remote_app.py
Normal file
86
apps/applications/serializers/remote_app.py
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
import copy
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from common.serializers import AdaptedBulkListSerializer
|
||||||
|
from common.fields.serializer import CustomMetaDictField
|
||||||
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
|
|
||||||
|
from .. import const
|
||||||
|
from ..models import RemoteApp
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'RemoteAppSerializer', 'RemoteAppConnectionInfoSerializer',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteAppParamsDictField(CustomMetaDictField):
|
||||||
|
type_fields_map = const.REMOTE_APP_TYPE_FIELDS_MAP
|
||||||
|
default_type = const.REMOTE_APP_TYPE_CHROME
|
||||||
|
convert_key_remove_type_prefix = False
|
||||||
|
convert_key_to_upper = False
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteAppSerializer(BulkOrgResourceModelSerializer):
|
||||||
|
params = RemoteAppParamsDictField()
|
||||||
|
type_fields_map = const.REMOTE_APP_TYPE_FIELDS_MAP
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = RemoteApp
|
||||||
|
list_serializer_class = AdaptedBulkListSerializer
|
||||||
|
fields = [
|
||||||
|
'id', 'name', 'asset', 'asset_info', 'type', 'get_type_display',
|
||||||
|
'path', 'params', 'date_created', 'created_by', 'comment',
|
||||||
|
]
|
||||||
|
read_only_fields = [
|
||||||
|
'created_by', 'date_created', 'asset_info',
|
||||||
|
'get_type_display'
|
||||||
|
]
|
||||||
|
|
||||||
|
def process_params(self, instance, validated_data):
|
||||||
|
new_params = copy.deepcopy(validated_data.get('params', {}))
|
||||||
|
tp = validated_data.get('type', '')
|
||||||
|
|
||||||
|
if tp != instance.type:
|
||||||
|
return new_params
|
||||||
|
|
||||||
|
old_params = instance.params
|
||||||
|
fields = self.type_fields_map.get(instance.type, [])
|
||||||
|
for field in fields:
|
||||||
|
if not field.get('write_only', False):
|
||||||
|
continue
|
||||||
|
field_name = field['name']
|
||||||
|
new_value = new_params.get(field_name, '')
|
||||||
|
old_value = old_params.get(field_name, '')
|
||||||
|
field_value = new_value if new_value else old_value
|
||||||
|
new_params[field_name] = field_value
|
||||||
|
|
||||||
|
return new_params
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
params = self.process_params(instance, validated_data)
|
||||||
|
validated_data['params'] = params
|
||||||
|
return super().update(instance, validated_data)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteAppConnectionInfoSerializer(serializers.ModelSerializer):
|
||||||
|
parameter_remote_app = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = RemoteApp
|
||||||
|
fields = [
|
||||||
|
'id', 'name', 'asset', 'parameter_remote_app',
|
||||||
|
]
|
||||||
|
read_only_fields = ['parameter_remote_app']
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_parameter_remote_app(obj):
|
||||||
|
parameter = {
|
||||||
|
'program': const.REMOTE_APP_BOOT_PROGRAM_NAME,
|
||||||
|
'working_directory': '',
|
||||||
|
'parameters': obj.parameters,
|
||||||
|
}
|
||||||
|
return parameter
|
||||||
7
apps/applications/urls/__init__.py
Normal file
7
apps/applications/urls/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
|
||||||
|
]
|
||||||
24
apps/applications/urls/api_urls.py
Normal file
24
apps/applications/urls/api_urls.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# coding:utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
from django.urls import path, re_path
|
||||||
|
from rest_framework_bulk.routes import BulkRouter
|
||||||
|
|
||||||
|
from common import api as capi
|
||||||
|
from .. import api
|
||||||
|
|
||||||
|
app_name = 'applications'
|
||||||
|
|
||||||
|
router = BulkRouter()
|
||||||
|
router.register(r'remote-apps', api.RemoteAppViewSet, 'remote-app')
|
||||||
|
router.register(r'database-apps', api.DatabaseAppViewSet, 'database-app')
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('remote-apps/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'),
|
||||||
|
]
|
||||||
|
|
||||||
|
old_version_urlpatterns = [
|
||||||
|
re_path('(?P<resource>remote-app)/.*', capi.redirect_plural_name_api)
|
||||||
|
]
|
||||||
|
|
||||||
|
urlpatterns += router.urls + old_version_urlpatterns
|
||||||
7
apps/applications/urls/views_urls.py
Normal file
7
apps/applications/urls/views_urls.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# coding:utf-8
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
app_name = 'applications'
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
]
|
||||||
@@ -2,6 +2,10 @@ from .admin_user import *
|
|||||||
from .asset import *
|
from .asset import *
|
||||||
from .label import *
|
from .label import *
|
||||||
from .system_user import *
|
from .system_user import *
|
||||||
|
from .system_user_relation import *
|
||||||
from .node import *
|
from .node import *
|
||||||
from .domain import *
|
from .domain import *
|
||||||
from .cmd_filter import *
|
from .cmd_filter import *
|
||||||
|
from .asset_user import *
|
||||||
|
from .gathered_user import *
|
||||||
|
from .favorite_asset import *
|
||||||
|
|||||||
@@ -1,26 +1,14 @@
|
|||||||
# ~*~ coding: utf-8 ~*~
|
|
||||||
# Copyright (C) 2014-2018 Beijing DuiZhan Technology Co.,Ltd. All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the GNU General Public License v2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.gnu.org/licenses/gpl-2.0.html
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.db.models import Count
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework import generics
|
from django.utils.translation import ugettext as _
|
||||||
|
from rest_framework import status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
from rest_framework.pagination import LimitOffsetPagination
|
from orgs.mixins import generics
|
||||||
|
|
||||||
from common.mixins import IDInFilterMixin
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from ..hands import IsOrgAdmin
|
from ..hands import IsOrgAdmin
|
||||||
from ..models import AdminUser, Asset
|
from ..models import AdminUser, Asset
|
||||||
@@ -36,31 +24,38 @@ __all__ = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class AdminUserViewSet(IDInFilterMixin, BulkModelViewSet):
|
class AdminUserViewSet(OrgBulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
Admin user api set, for add,delete,update,list,retrieve resource
|
Admin user api set, for add,delete,update,list,retrieve resource
|
||||||
"""
|
"""
|
||||||
|
model = AdminUser
|
||||||
filter_fields = ("name", "username")
|
filter_fields = ("name", "username")
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
queryset = AdminUser.objects.all()
|
|
||||||
serializer_class = serializers.AdminUserSerializer
|
serializer_class = serializers.AdminUserSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
pagination_class = LimitOffsetPagination
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = super().get_queryset().all()
|
queryset = super().get_queryset()
|
||||||
|
queryset = queryset.annotate(assets_amount=Count('assets'))
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
instance = self.get_object()
|
||||||
|
has_related_asset = instance.assets.exists()
|
||||||
|
if has_related_asset:
|
||||||
|
data = {'msg': _('Deleted failed, There are related assets')}
|
||||||
|
return Response(data=data, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AdminUserAuthApi(generics.UpdateAPIView):
|
class AdminUserAuthApi(generics.UpdateAPIView):
|
||||||
queryset = AdminUser.objects.all()
|
model = AdminUser
|
||||||
serializer_class = serializers.AdminUserAuthSerializer
|
serializer_class = serializers.AdminUserAuthSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
|
|
||||||
class ReplaceNodesAdminUserApi(generics.UpdateAPIView):
|
class ReplaceNodesAdminUserApi(generics.UpdateAPIView):
|
||||||
queryset = AdminUser.objects.all()
|
model = AdminUser
|
||||||
serializer_class = serializers.ReplaceNodeAdminUserSerializer
|
serializer_class = serializers.ReplaceNodeAdminUserSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
@@ -85,7 +80,7 @@ class AdminUserTestConnectiveApi(generics.RetrieveAPIView):
|
|||||||
"""
|
"""
|
||||||
Test asset admin user assets_connectivity
|
Test asset admin user assets_connectivity
|
||||||
"""
|
"""
|
||||||
queryset = AdminUser.objects.all()
|
model = AdminUser
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.TaskIDSerializer
|
serializer_class = serializers.TaskIDSerializer
|
||||||
|
|
||||||
@@ -98,7 +93,6 @@ class AdminUserTestConnectiveApi(generics.RetrieveAPIView):
|
|||||||
class AdminUserAssetsListView(generics.ListAPIView):
|
class AdminUserAssetsListView(generics.ListAPIView):
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.AssetSimpleSerializer
|
serializer_class = serializers.AssetSimpleSerializer
|
||||||
pagination_class = LimitOffsetPagination
|
|
||||||
filter_fields = ("hostname", "ip")
|
filter_fields = ("hostname", "ip")
|
||||||
http_method_names = ['get']
|
http_method_names = ['get']
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
|
|||||||
@@ -1,140 +1,126 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
import random
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
from rest_framework.generics import RetrieveAPIView
|
||||||
from rest_framework import generics
|
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
|
||||||
from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView
|
|
||||||
from rest_framework.pagination import LimitOffsetPagination
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.db.models import Q
|
|
||||||
|
|
||||||
from common.mixins import IDInFilterMixin
|
from common.utils import get_logger, get_object_or_none
|
||||||
from common.utils import get_logger
|
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsSuperUser
|
||||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
from ..models import Asset, AdminUser, Node
|
from orgs.mixins import generics
|
||||||
|
from ..models import Asset, Node, Platform
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
from ..tasks import update_asset_hardware_info_manual, \
|
from ..tasks import (
|
||||||
test_asset_connectivity_manual
|
update_asset_hardware_info_manual, test_asset_connectivity_manual
|
||||||
from ..utils import LabelFilter
|
)
|
||||||
|
from ..filters import AssetByNodeFilterBackend, LabelFilterBackend
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AssetViewSet', 'AssetListUpdateApi',
|
'AssetViewSet', 'AssetPlatformRetrieveApi',
|
||||||
'AssetRefreshHardwareApi', 'AssetAdminUserTestApi',
|
'AssetGatewayListApi', 'AssetPlatformViewSet',
|
||||||
'AssetGatewayApi'
|
'AssetTaskCreateApi',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
|
class AssetViewSet(OrgBulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoint that allows Asset to be viewed or edited.
|
API endpoint that allows Asset to be viewed or edited.
|
||||||
"""
|
"""
|
||||||
filter_fields = ("hostname", "ip")
|
model = Asset
|
||||||
search_fields = filter_fields
|
filter_fields = (
|
||||||
|
"hostname", "ip", "systemuser__id", "admin_user__id", "platform__base",
|
||||||
|
"is_active"
|
||||||
|
)
|
||||||
|
search_fields = ("hostname", "ip")
|
||||||
ordering_fields = ("hostname", "ip", "port", "cpu_cores")
|
ordering_fields = ("hostname", "ip", "port", "cpu_cores")
|
||||||
queryset = Asset.objects.all()
|
serializer_classes = {
|
||||||
serializer_class = serializers.AssetSerializer
|
'default': serializers.AssetSerializer,
|
||||||
pagination_class = LimitOffsetPagination
|
'display': serializers.AssetDisplaySerializer,
|
||||||
|
}
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
extra_filter_backends = [AssetByNodeFilterBackend, LabelFilterBackend]
|
||||||
|
|
||||||
def filter_node(self, queryset):
|
def set_assets_node(self, assets):
|
||||||
node_id = self.request.query_params.get("node_id")
|
if not isinstance(assets, list):
|
||||||
|
assets = [assets]
|
||||||
|
node_id = self.request.query_params.get('node_id')
|
||||||
if not node_id:
|
if not node_id:
|
||||||
return queryset
|
return
|
||||||
|
node = get_object_or_none(Node, pk=node_id)
|
||||||
|
if not node:
|
||||||
|
return
|
||||||
|
node.assets.add(*assets)
|
||||||
|
|
||||||
node = get_object_or_404(Node, id=node_id)
|
def perform_create(self, serializer):
|
||||||
show_current_asset = self.request.query_params.get("show_current_asset") in ('1', 'true')
|
assets = serializer.save()
|
||||||
|
self.set_assets_node(assets)
|
||||||
|
|
||||||
if node.is_root() and show_current_asset:
|
|
||||||
queryset = queryset.filter(
|
class AssetPlatformRetrieveApi(RetrieveAPIView):
|
||||||
Q(nodes=node_id) | Q(nodes__isnull=True)
|
queryset = Platform.objects.all()
|
||||||
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
serializer_class = serializers.PlatformSerializer
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
asset_pk = self.kwargs.get('pk')
|
||||||
|
asset = get_object_or_404(Asset, pk=asset_pk)
|
||||||
|
return asset.platform
|
||||||
|
|
||||||
|
|
||||||
|
class AssetPlatformViewSet(ModelViewSet):
|
||||||
|
queryset = Platform.objects.all()
|
||||||
|
permission_classes = (IsSuperUser,)
|
||||||
|
serializer_class = serializers.PlatformSerializer
|
||||||
|
filter_fields = ['name', 'base']
|
||||||
|
search_fields = ['name']
|
||||||
|
|
||||||
|
def get_permissions(self):
|
||||||
|
if self.request.method.lower() in ['get', 'options']:
|
||||||
|
self.permission_classes = (IsOrgAdmin,)
|
||||||
|
return super().get_permissions()
|
||||||
|
|
||||||
|
def check_object_permissions(self, request, obj):
|
||||||
|
if request.method.lower() in ['delete', 'put', 'patch'] and obj.internal:
|
||||||
|
self.permission_denied(
|
||||||
|
request, message={"detail": "Internal platform"}
|
||||||
)
|
)
|
||||||
elif node.is_root() and not show_current_asset:
|
return super().check_object_permissions(request, obj)
|
||||||
pass
|
|
||||||
elif not node.is_root() and show_current_asset:
|
|
||||||
queryset = queryset.filter(nodes=node)
|
|
||||||
else:
|
|
||||||
queryset = queryset.filter(
|
|
||||||
nodes__key__regex='^{}(:[0-9]+)*$'.format(node.key),
|
|
||||||
)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def filter_admin_user_id(self, queryset):
|
|
||||||
admin_user_id = self.request.query_params.get('admin_user_id')
|
|
||||||
if not admin_user_id:
|
|
||||||
return queryset
|
|
||||||
admin_user = get_object_or_404(AdminUser, id=admin_user_id)
|
|
||||||
queryset = queryset.filter(admin_user=admin_user)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
|
||||||
queryset = super().filter_queryset(queryset)
|
|
||||||
queryset = self.filter_node(queryset)
|
|
||||||
queryset = self.filter_admin_user_id(queryset)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = super().get_queryset().distinct()
|
|
||||||
queryset = self.get_serializer_class().setup_eager_loading(queryset)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
|
class AssetTaskCreateApi(generics.CreateAPIView):
|
||||||
"""
|
model = Asset
|
||||||
Asset bulk update api
|
serializer_class = serializers.AssetTaskSerializer
|
||||||
"""
|
|
||||||
queryset = Asset.objects.all()
|
|
||||||
serializer_class = serializers.AssetSerializer
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
pk = self.kwargs.get("pk")
|
||||||
|
instance = get_object_or_404(Asset, pk=pk)
|
||||||
|
return instance
|
||||||
|
|
||||||
class AssetRefreshHardwareApi(generics.RetrieveAPIView):
|
def perform_create(self, serializer):
|
||||||
"""
|
asset = self.get_object()
|
||||||
Refresh asset hardware info
|
action = serializer.validated_data["action"]
|
||||||
"""
|
if action == "refresh":
|
||||||
queryset = Asset.objects.all()
|
|
||||||
serializer_class = serializers.AssetSerializer
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
|
||||||
asset_id = kwargs.get('pk')
|
|
||||||
asset = get_object_or_404(Asset, pk=asset_id)
|
|
||||||
task = update_asset_hardware_info_manual.delay(asset)
|
task = update_asset_hardware_info_manual.delay(asset)
|
||||||
return Response({"task": task.id})
|
else:
|
||||||
|
|
||||||
|
|
||||||
class AssetAdminUserTestApi(generics.RetrieveAPIView):
|
|
||||||
"""
|
|
||||||
Test asset admin user assets_connectivity
|
|
||||||
"""
|
|
||||||
queryset = Asset.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
|
||||||
serializer_class = serializers.TaskIDSerializer
|
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
|
||||||
asset_id = kwargs.get('pk')
|
|
||||||
asset = get_object_or_404(Asset, pk=asset_id)
|
|
||||||
task = test_asset_connectivity_manual.delay(asset)
|
task = test_asset_connectivity_manual.delay(asset)
|
||||||
return Response({"task": task.id})
|
data = getattr(serializer, '_data', {})
|
||||||
|
data["task"] = task.id
|
||||||
|
setattr(serializer, '_data', data)
|
||||||
|
|
||||||
|
|
||||||
class AssetGatewayApi(generics.RetrieveAPIView):
|
class AssetGatewayListApi(generics.ListAPIView):
|
||||||
queryset = Asset.objects.all()
|
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.GatewayWithAuthSerializer
|
serializer_class = serializers.GatewayWithAuthSerializer
|
||||||
|
model = Asset
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def get_queryset(self):
|
||||||
asset_id = kwargs.get('pk')
|
asset_id = self.kwargs.get('pk')
|
||||||
asset = get_object_or_404(Asset, pk=asset_id)
|
asset = get_object_or_404(Asset, pk=asset_id)
|
||||||
|
if not asset.domain:
|
||||||
if asset.domain and \
|
return []
|
||||||
asset.domain.gateways.filter(protocol=asset.protocol).exists():
|
queryset = asset.domain.gateways.filter(protocol='ssh')
|
||||||
gateway = random.choice(asset.domain.gateways.filter(protocol=asset.protocol))
|
return queryset
|
||||||
serializer = serializers.GatewayWithAuthSerializer(instance=gateway)
|
|
||||||
return Response(serializer.data)
|
|
||||||
else:
|
|
||||||
return Response({"msg": "Not have gateway"}, status=404)
|
|
||||||
|
|||||||
157
apps/assets/api/asset_user.py
Normal file
157
apps/assets/api/asset_user.py
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
import coreapi
|
||||||
|
from django.conf import settings
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import generics, filters
|
||||||
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
|
|
||||||
|
from common.permissions import IsOrgAdminOrAppUser, NeedMFAVerify
|
||||||
|
from common.utils import get_object_or_none, get_logger
|
||||||
|
from common.mixins import CommonApiMixin
|
||||||
|
from ..backends import AssetUserManager
|
||||||
|
from ..models import Asset, Node, SystemUser
|
||||||
|
from .. import serializers
|
||||||
|
from ..tasks import (
|
||||||
|
test_asset_users_connectivity_manual, push_system_user_a_asset_manual
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'AssetUserViewSet', 'AssetUserAuthInfoViewSet', 'AssetUserTaskCreateAPI',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetUserFilterBackend(filters.BaseFilterBackend):
|
||||||
|
def filter_queryset(self, request, queryset, view):
|
||||||
|
kwargs = {}
|
||||||
|
for field in view.filter_fields:
|
||||||
|
value = request.GET.get(field)
|
||||||
|
if not value:
|
||||||
|
continue
|
||||||
|
if field == "node_id":
|
||||||
|
value = get_object_or_none(Node, pk=value)
|
||||||
|
kwargs["node"] = value
|
||||||
|
continue
|
||||||
|
elif field == "asset_id":
|
||||||
|
field = "asset"
|
||||||
|
kwargs[field] = value
|
||||||
|
if kwargs:
|
||||||
|
queryset = queryset.filter(**kwargs)
|
||||||
|
logger.debug("Filter {}".format(kwargs))
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class AssetUserSearchBackend(filters.BaseFilterBackend):
|
||||||
|
def filter_queryset(self, request, queryset, view):
|
||||||
|
value = request.GET.get('search')
|
||||||
|
if not value:
|
||||||
|
return queryset
|
||||||
|
queryset = queryset.search(value)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class AssetUserLatestFilterBackend(filters.BaseFilterBackend):
|
||||||
|
def get_schema_fields(self, view):
|
||||||
|
return [
|
||||||
|
coreapi.Field(
|
||||||
|
name='latest', location='query', required=False,
|
||||||
|
type='string', example='1',
|
||||||
|
description='Only the latest version'
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
def filter_queryset(self, request, queryset, view):
|
||||||
|
latest = request.GET.get('latest') == '1'
|
||||||
|
if latest:
|
||||||
|
queryset = queryset.distinct()
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class AssetUserViewSet(CommonApiMixin, BulkModelViewSet):
|
||||||
|
serializer_classes = {
|
||||||
|
'default': serializers.AssetUserWriteSerializer,
|
||||||
|
'display': serializers.AssetUserReadSerializer,
|
||||||
|
'retrieve': serializers.AssetUserReadSerializer,
|
||||||
|
}
|
||||||
|
permission_classes = [IsOrgAdminOrAppUser]
|
||||||
|
filter_fields = [
|
||||||
|
"id", "ip", "hostname", "username",
|
||||||
|
"asset_id", "node_id",
|
||||||
|
"prefer", "prefer_id",
|
||||||
|
]
|
||||||
|
search_fields = ["ip", "hostname", "username"]
|
||||||
|
filter_backends = [
|
||||||
|
AssetUserFilterBackend, AssetUserSearchBackend,
|
||||||
|
AssetUserLatestFilterBackend,
|
||||||
|
]
|
||||||
|
|
||||||
|
def allow_bulk_destroy(self, qs, filtered):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
pk = self.kwargs.get("pk")
|
||||||
|
if pk is None:
|
||||||
|
return
|
||||||
|
queryset = self.get_queryset()
|
||||||
|
obj = queryset.get(id=pk)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def get_exception_handler(self):
|
||||||
|
def handler(e, context):
|
||||||
|
logger.error(e, exc_info=True)
|
||||||
|
return Response({"error": str(e)}, status=400)
|
||||||
|
return handler
|
||||||
|
|
||||||
|
def perform_destroy(self, instance):
|
||||||
|
manager = AssetUserManager()
|
||||||
|
manager.delete(instance)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
manager = AssetUserManager()
|
||||||
|
queryset = manager.all()
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class AssetUserAuthInfoViewSet(AssetUserViewSet):
|
||||||
|
serializer_classes = {"default": serializers.AssetUserAuthInfoSerializer}
|
||||||
|
http_method_names = ['get', 'post']
|
||||||
|
permission_classes = [IsOrgAdminOrAppUser]
|
||||||
|
|
||||||
|
def get_permissions(self):
|
||||||
|
if settings.SECURITY_VIEW_AUTH_NEED_MFA:
|
||||||
|
self.permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
|
||||||
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
|
class AssetUserTaskCreateAPI(generics.CreateAPIView):
|
||||||
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
serializer_class = serializers.AssetUserTaskSerializer
|
||||||
|
filter_backends = AssetUserViewSet.filter_backends
|
||||||
|
filter_fields = AssetUserViewSet.filter_fields
|
||||||
|
|
||||||
|
def get_asset_users(self):
|
||||||
|
manager = AssetUserManager()
|
||||||
|
queryset = manager.all()
|
||||||
|
for cls in self.filter_backends:
|
||||||
|
queryset = cls().filter_queryset(self.request, queryset, self)
|
||||||
|
return list(queryset)
|
||||||
|
|
||||||
|
def perform_create(self, serializer):
|
||||||
|
asset_users = self.get_asset_users()
|
||||||
|
# action = serializer.validated_data["action"]
|
||||||
|
# only this
|
||||||
|
# if action == "test":
|
||||||
|
task = test_asset_users_connectivity_manual.delay(asset_users)
|
||||||
|
data = getattr(serializer, '_data', {})
|
||||||
|
data["task"] = task.id
|
||||||
|
setattr(serializer, '_data', data)
|
||||||
|
return task
|
||||||
|
|
||||||
|
def get_exception_handler(self):
|
||||||
|
def handler(e, context):
|
||||||
|
return Response({"error": str(e)}, status=400)
|
||||||
|
return handler
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
|
||||||
from rest_framework.pagination import LimitOffsetPagination
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
from ..hands import IsOrgAdmin
|
from ..hands import IsOrgAdmin
|
||||||
from ..models import CommandFilter, CommandFilterRule
|
from ..models import CommandFilter, CommandFilterRule
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
@@ -13,21 +12,20 @@ from .. import serializers
|
|||||||
__all__ = ['CommandFilterViewSet', 'CommandFilterRuleViewSet']
|
__all__ = ['CommandFilterViewSet', 'CommandFilterRuleViewSet']
|
||||||
|
|
||||||
|
|
||||||
class CommandFilterViewSet(BulkModelViewSet):
|
class CommandFilterViewSet(OrgBulkModelViewSet):
|
||||||
|
model = CommandFilter
|
||||||
filter_fields = ("name",)
|
filter_fields = ("name",)
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
queryset = CommandFilter.objects.all()
|
|
||||||
serializer_class = serializers.CommandFilterSerializer
|
serializer_class = serializers.CommandFilterSerializer
|
||||||
pagination_class = LimitOffsetPagination
|
|
||||||
|
|
||||||
|
|
||||||
class CommandFilterRuleViewSet(BulkModelViewSet):
|
class CommandFilterRuleViewSet(OrgBulkModelViewSet):
|
||||||
|
model = CommandFilterRule
|
||||||
filter_fields = ("content",)
|
filter_fields = ("content",)
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.CommandFilterRuleSerializer
|
serializer_class = serializers.CommandFilterRuleSerializer
|
||||||
pagination_class = LimitOffsetPagination
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
fpk = self.kwargs.get('filter_pk')
|
fpk = self.kwargs.get('filter_pk')
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
|
||||||
from rest_framework.views import APIView, Response
|
from rest_framework.views import APIView, Response
|
||||||
from rest_framework.pagination import LimitOffsetPagination
|
|
||||||
|
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from common.permissions import IsOrgAdmin, IsAppUser, IsOrgAdminOrAppUser
|
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
||||||
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
from ..models import Domain, Gateway
|
from ..models import Domain, Gateway
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
|
|
||||||
@@ -16,45 +14,36 @@ logger = get_logger(__file__)
|
|||||||
__all__ = ['DomainViewSet', 'GatewayViewSet', "GatewayTestConnectionApi"]
|
__all__ = ['DomainViewSet', 'GatewayViewSet', "GatewayTestConnectionApi"]
|
||||||
|
|
||||||
|
|
||||||
class DomainViewSet(BulkModelViewSet):
|
class DomainViewSet(OrgBulkModelViewSet):
|
||||||
queryset = Domain.objects.all()
|
model = Domain
|
||||||
permission_classes = (IsOrgAdmin,)
|
filter_fields = ("name", )
|
||||||
|
search_fields = filter_fields
|
||||||
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.DomainSerializer
|
serializer_class = serializers.DomainSerializer
|
||||||
pagination_class = LimitOffsetPagination
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = super().get_queryset().all()
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
if self.request.query_params.get('gateway'):
|
if self.request.query_params.get('gateway'):
|
||||||
return serializers.DomainWithGatewaySerializer
|
return serializers.DomainWithGatewaySerializer
|
||||||
return super().get_serializer_class()
|
return super().get_serializer_class()
|
||||||
|
|
||||||
def get_permissions(self):
|
|
||||||
if self.request.query_params.get('gateway'):
|
|
||||||
self.permission_classes = (IsOrgAdminOrAppUser,)
|
|
||||||
return super().get_permissions()
|
|
||||||
|
|
||||||
|
class GatewayViewSet(OrgBulkModelViewSet):
|
||||||
class GatewayViewSet(BulkModelViewSet):
|
model = Gateway
|
||||||
filter_fields = ("domain__name", "name", "username", "ip", "domain")
|
filter_fields = ("domain__name", "name", "username", "ip", "domain")
|
||||||
search_fields = filter_fields
|
search_fields = ("domain__name", "name", "username", "ip")
|
||||||
queryset = Gateway.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.GatewaySerializer
|
serializer_class = serializers.GatewaySerializer
|
||||||
pagination_class = LimitOffsetPagination
|
|
||||||
|
|
||||||
|
|
||||||
class GatewayTestConnectionApi(SingleObjectMixin, APIView):
|
class GatewayTestConnectionApi(SingleObjectMixin, APIView):
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
model = Gateway
|
|
||||||
object = None
|
object = None
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object(Gateway.objects.all())
|
self.object = self.get_object(Gateway.objects.all())
|
||||||
ok, e = self.object.test_connective()
|
local_port = self.request.data.get('port') or self.object.port
|
||||||
|
ok, e = self.object.test_connective(local_port=local_port)
|
||||||
if ok:
|
if ok:
|
||||||
return Response("ok")
|
return Response("ok")
|
||||||
else:
|
else:
|
||||||
return Response({"failed": e}, status=404)
|
return Response({"error": e}, status=400)
|
||||||
|
|||||||
27
apps/assets/api/favorite_asset.py
Normal file
27
apps/assets/api/favorite_asset.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
|
|
||||||
|
from common.permissions import IsValidUser
|
||||||
|
from orgs.utils import tmp_to_root_org
|
||||||
|
from ..models import FavoriteAsset
|
||||||
|
from ..serializers import FavoriteAssetSerializer
|
||||||
|
|
||||||
|
__all__ = ['FavoriteAssetViewSet']
|
||||||
|
|
||||||
|
|
||||||
|
class FavoriteAssetViewSet(BulkModelViewSet):
|
||||||
|
serializer_class = FavoriteAssetSerializer
|
||||||
|
permission_classes = (IsValidUser,)
|
||||||
|
filter_fields = ['asset']
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
with tmp_to_root_org():
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = FavoriteAsset.objects.filter(user=self.request.user)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def allow_bulk_destroy(self, qs, filtered):
|
||||||
|
return filtered.count() == 1
|
||||||
22
apps/assets/api/gathered_user.py
Normal file
22
apps/assets/api/gathered_user.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from orgs.mixins.api import OrgModelViewSet
|
||||||
|
from assets.models import GatheredUser
|
||||||
|
from common.permissions import IsOrgAdmin
|
||||||
|
|
||||||
|
from ..serializers import GatheredUserSerializer
|
||||||
|
from ..filters import AssetRelatedByNodeFilterBackend
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['GatheredUserViewSet']
|
||||||
|
|
||||||
|
|
||||||
|
class GatheredUserViewSet(OrgModelViewSet):
|
||||||
|
model = GatheredUser
|
||||||
|
serializer_class = GatheredUserSerializer
|
||||||
|
permission_classes = [IsOrgAdmin]
|
||||||
|
extra_filter_backends = [AssetRelatedByNodeFilterBackend]
|
||||||
|
|
||||||
|
filter_fields = ['asset', 'username', 'present', 'asset__ip', 'asset__hostname']
|
||||||
|
search_fields = ['username', 'asset__ip', 'asset__hostname']
|
||||||
@@ -13,11 +13,10 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
|
||||||
from rest_framework.pagination import LimitOffsetPagination
|
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
from ..hands import IsOrgAdmin
|
from ..hands import IsOrgAdmin
|
||||||
from ..models import Label
|
from ..models import Label
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
@@ -27,12 +26,12 @@ logger = get_logger(__file__)
|
|||||||
__all__ = ['LabelViewSet']
|
__all__ = ['LabelViewSet']
|
||||||
|
|
||||||
|
|
||||||
class LabelViewSet(BulkModelViewSet):
|
class LabelViewSet(OrgBulkModelViewSet):
|
||||||
|
model = Label
|
||||||
filter_fields = ("name", "value")
|
filter_fields = ("name", "value")
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.LabelSerializer
|
serializer_class = serializers.LabelSerializer
|
||||||
pagination_class = LimitOffsetPagination
|
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
if request.query_params.get("distinct"):
|
if request.query_params.get("distinct"):
|
||||||
|
|||||||
@@ -1,30 +1,22 @@
|
|||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
# Copyright (C) 2014-2018 Beijing DuiZhan Technology Co.,Ltd. All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the GNU General Public License v2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.gnu.org/licenses/gpl-2.0.html
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from rest_framework import generics, mixins, viewsets
|
from collections import namedtuple
|
||||||
|
from rest_framework import status
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
from rest_framework.views import APIView
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404, Http404
|
||||||
|
|
||||||
from common.utils import get_logger, get_object_or_none
|
from common.utils import get_logger, get_object_or_none
|
||||||
from common.tree import TreeNodeSerializer
|
from common.tree import TreeNodeSerializer
|
||||||
|
from orgs.mixins.api import OrgModelViewSet
|
||||||
|
from orgs.mixins import generics
|
||||||
from ..hands import IsOrgAdmin
|
from ..hands import IsOrgAdmin
|
||||||
from ..models import Node
|
from ..models import Node
|
||||||
from ..tasks import update_assets_hardware_info_util, test_asset_connectivity_util
|
from ..tasks import (
|
||||||
|
update_node_assets_hardware_info_manual,
|
||||||
|
test_node_assets_connectivity_manual,
|
||||||
|
)
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
|
|
||||||
|
|
||||||
@@ -32,33 +24,38 @@ logger = get_logger(__file__)
|
|||||||
__all__ = [
|
__all__ = [
|
||||||
'NodeViewSet', 'NodeChildrenApi', 'NodeAssetsApi',
|
'NodeViewSet', 'NodeChildrenApi', 'NodeAssetsApi',
|
||||||
'NodeAddAssetsApi', 'NodeRemoveAssetsApi', 'NodeReplaceAssetsApi',
|
'NodeAddAssetsApi', 'NodeRemoveAssetsApi', 'NodeReplaceAssetsApi',
|
||||||
'NodeAddChildrenApi', 'RefreshNodeHardwareInfoApi',
|
'NodeAddChildrenApi', 'NodeListAsTreeApi',
|
||||||
'TestNodeConnectiveApi', 'NodeListAsTreeApi',
|
'NodeChildrenAsTreeApi',
|
||||||
'NodeChildrenAsTreeApi', 'RefreshAssetsAmount',
|
'NodeTaskCreateApi',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NodeViewSet(viewsets.ModelViewSet):
|
class NodeViewSet(OrgModelViewSet):
|
||||||
queryset = Node.objects.all()
|
model = Node
|
||||||
|
filter_fields = ('value', 'key', 'id')
|
||||||
|
search_fields = ('value', )
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.NodeSerializer
|
serializer_class = serializers.NodeSerializer
|
||||||
|
|
||||||
|
# 仅支持根节点指直接创建,子节点下的节点需要通过children接口创建
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
child_key = Node.root().get_next_child_key()
|
child_key = Node.org_root().get_next_child_key()
|
||||||
serializer.validated_data["key"] = child_key
|
serializer.validated_data["key"] = child_key
|
||||||
serializer.save()
|
serializer.save()
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def perform_update(self, serializer):
|
||||||
node = self.get_object()
|
node = self.get_object()
|
||||||
if node.is_root():
|
if node.is_org_root() and node.value != serializer.validated_data['value']:
|
||||||
node_value = node.value
|
msg = _("You can't update the root node name")
|
||||||
post_value = request.data.get('value')
|
raise ValidationError({"error": msg})
|
||||||
if node_value != post_value:
|
return super().perform_update(serializer)
|
||||||
return Response(
|
|
||||||
{"msg": _("You can't update the root node name")},
|
def destroy(self, request, *args, **kwargs):
|
||||||
status=400
|
node = self.get_object()
|
||||||
)
|
if node.has_children_or_has_assets():
|
||||||
return super().update(request, *args, **kwargs)
|
error = _("Deletion failed and the node contains children or assets")
|
||||||
|
return Response(data={'error': error}, status=status.HTTP_403_FORBIDDEN)
|
||||||
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class NodeListAsTreeApi(generics.ListAPIView):
|
class NodeListAsTreeApi(generics.ListAPIView):
|
||||||
@@ -73,26 +70,73 @@ class NodeListAsTreeApi(generics.ListAPIView):
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
|
model = Node
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = TreeNodeSerializer
|
serializer_class = TreeNodeSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
@staticmethod
|
||||||
queryset = [node.as_tree_node() for node in Node.objects.all()]
|
def to_tree_queryset(queryset):
|
||||||
|
queryset = [node.as_tree_node() for node in queryset]
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
def filter_queryset(self, queryset):
|
||||||
if self.request.query_params.get('refresh', '0') == '1':
|
queryset = super().filter_queryset(queryset)
|
||||||
queryset = self.refresh_nodes(queryset)
|
queryset = self.to_tree_queryset(queryset)
|
||||||
return queryset
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def refresh_nodes(queryset):
|
|
||||||
Node.expire_nodes_assets_amount()
|
|
||||||
Node.expire_nodes_full_value()
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class NodeChildrenAsTreeApi(generics.ListAPIView):
|
class NodeChildrenApi(generics.ListCreateAPIView):
|
||||||
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
serializer_class = serializers.NodeSerializer
|
||||||
|
instance = None
|
||||||
|
is_initial = False
|
||||||
|
|
||||||
|
def initial(self, request, *args, **kwargs):
|
||||||
|
self.instance = self.get_object()
|
||||||
|
return super().initial(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def perform_create(self, serializer):
|
||||||
|
data = serializer.validated_data
|
||||||
|
_id = data.get("id")
|
||||||
|
value = data.get("value")
|
||||||
|
if not value:
|
||||||
|
value = self.instance.get_next_child_preset_name()
|
||||||
|
node = self.instance.create_child(value=value, _id=_id)
|
||||||
|
# 避免查询 full value
|
||||||
|
node._full_value = node.value
|
||||||
|
serializer.instance = node
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
pk = self.kwargs.get('pk') or self.request.query_params.get('id')
|
||||||
|
key = self.request.query_params.get("key")
|
||||||
|
if not pk and not key:
|
||||||
|
node = Node.org_root()
|
||||||
|
self.is_initial = True
|
||||||
|
return node
|
||||||
|
if pk:
|
||||||
|
node = get_object_or_404(Node, pk=pk)
|
||||||
|
else:
|
||||||
|
node = get_object_or_404(Node, key=key)
|
||||||
|
return node
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
query_all = self.request.query_params.get("all", "0") == "all"
|
||||||
|
if not self.instance:
|
||||||
|
return Node.objects.none()
|
||||||
|
|
||||||
|
if self.is_initial:
|
||||||
|
with_self = True
|
||||||
|
else:
|
||||||
|
with_self = False
|
||||||
|
|
||||||
|
if query_all:
|
||||||
|
queryset = self.instance.get_all_children(with_self=with_self)
|
||||||
|
else:
|
||||||
|
queryset = self.instance.get_children(with_self=with_self)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class NodeChildrenAsTreeApi(NodeChildrenApi):
|
||||||
"""
|
"""
|
||||||
节点子节点作为树返回,
|
节点子节点作为树返回,
|
||||||
[
|
[
|
||||||
@@ -105,97 +149,33 @@ class NodeChildrenAsTreeApi(generics.ListAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
permission_classes = (IsOrgAdmin,)
|
model = Node
|
||||||
serializer_class = TreeNodeSerializer
|
serializer_class = TreeNodeSerializer
|
||||||
node = None
|
http_method_names = ['get']
|
||||||
is_root = False
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
node_key = self.request.query_params.get('key')
|
queryset = super().get_queryset()
|
||||||
if node_key:
|
|
||||||
self.node = Node.objects.get(key=node_key)
|
|
||||||
queryset = self.node.get_children(with_self=False)
|
|
||||||
else:
|
|
||||||
self.is_root = True
|
|
||||||
self.node = Node.root()
|
|
||||||
queryset = list(self.node.get_children(with_self=True))
|
|
||||||
nodes_invalid = Node.objects.exclude(key__startswith=self.node.key)
|
|
||||||
queryset.extend(list(nodes_invalid))
|
|
||||||
queryset = [node.as_tree_node() for node in queryset]
|
queryset = [node.as_tree_node() for node in queryset]
|
||||||
|
queryset = self.add_assets_if_need(queryset)
|
||||||
|
queryset = sorted(queryset)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def filter_assets(self, queryset):
|
def add_assets_if_need(self, queryset):
|
||||||
include_assets = self.request.query_params.get('assets', '0') == '1'
|
include_assets = self.request.query_params.get('assets', '0') == '1'
|
||||||
if not include_assets:
|
if not include_assets:
|
||||||
return queryset
|
return queryset
|
||||||
assets = self.node.get_assets()
|
assets = self.instance.get_assets().only(
|
||||||
for asset in assets:
|
"id", "hostname", "ip", "os",
|
||||||
queryset.append(asset.as_tree_node(self.node))
|
"org_id", "protocols",
|
||||||
return queryset
|
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
|
||||||
queryset = self.filter_assets(queryset)
|
|
||||||
queryset = self.filter_refresh_nodes(queryset)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def filter_refresh_nodes(self, queryset):
|
|
||||||
if self.request.query_params.get('refresh', '0') == '1':
|
|
||||||
Node.expire_nodes_assets_amount()
|
|
||||||
Node.expire_nodes_full_value()
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
|
|
||||||
queryset = Node.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
|
||||||
serializer_class = serializers.NodeSerializer
|
|
||||||
instance = None
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
return self.list(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
instance = self.get_object()
|
|
||||||
if not request.data.get("value"):
|
|
||||||
request.data["value"] = instance.get_next_child_preset_name()
|
|
||||||
return super().post(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
|
||||||
instance = self.get_object()
|
|
||||||
value = request.data.get("value")
|
|
||||||
values = [child.value for child in instance.get_children()]
|
|
||||||
if value in values:
|
|
||||||
raise ValidationError(
|
|
||||||
'The same level node name cannot be the same'
|
|
||||||
)
|
)
|
||||||
node = instance.create_child(value=value)
|
for asset in assets:
|
||||||
return Response(self.serializer_class(instance=node).data, status=201)
|
queryset.append(asset.as_tree_node(self.instance))
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
pk = self.kwargs.get('pk') or self.request.query_params.get('id')
|
|
||||||
if not pk:
|
|
||||||
node = Node.root()
|
|
||||||
else:
|
|
||||||
node = get_object_or_404(Node, pk=pk)
|
|
||||||
return node
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = []
|
|
||||||
query_all = self.request.query_params.get("all")
|
|
||||||
node = self.get_object()
|
|
||||||
|
|
||||||
if node is None:
|
|
||||||
node = Node.root()
|
|
||||||
node.assets__count = node.get_all_assets().count()
|
|
||||||
queryset.append(node)
|
|
||||||
|
|
||||||
if query_all:
|
|
||||||
children = node.get_all_children()
|
|
||||||
else:
|
|
||||||
children = node.get_children()
|
|
||||||
queryset.extend(list(children))
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
def check_need_refresh_nodes(self):
|
||||||
|
if self.request.query_params.get('refresh', '0') == '1':
|
||||||
|
Node.refresh_nodes()
|
||||||
|
|
||||||
|
|
||||||
class NodeAssetsApi(generics.ListAPIView):
|
class NodeAssetsApi(generics.ListAPIView):
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
@@ -212,7 +192,7 @@ class NodeAssetsApi(generics.ListAPIView):
|
|||||||
|
|
||||||
|
|
||||||
class NodeAddChildrenApi(generics.UpdateAPIView):
|
class NodeAddChildrenApi(generics.UpdateAPIView):
|
||||||
queryset = Node.objects.all()
|
model = Node
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.NodeAddChildrenSerializer
|
serializer_class = serializers.NodeAddChildrenSerializer
|
||||||
instance = None
|
instance = None
|
||||||
@@ -229,8 +209,8 @@ class NodeAddChildrenApi(generics.UpdateAPIView):
|
|||||||
|
|
||||||
|
|
||||||
class NodeAddAssetsApi(generics.UpdateAPIView):
|
class NodeAddAssetsApi(generics.UpdateAPIView):
|
||||||
|
model = Node
|
||||||
serializer_class = serializers.NodeAssetsSerializer
|
serializer_class = serializers.NodeAssetsSerializer
|
||||||
queryset = Node.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
instance = None
|
instance = None
|
||||||
|
|
||||||
@@ -241,15 +221,15 @@ class NodeAddAssetsApi(generics.UpdateAPIView):
|
|||||||
|
|
||||||
|
|
||||||
class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
||||||
|
model = Node
|
||||||
serializer_class = serializers.NodeAssetsSerializer
|
serializer_class = serializers.NodeAssetsSerializer
|
||||||
queryset = Node.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
instance = None
|
instance = None
|
||||||
|
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
assets = serializer.validated_data.get('assets')
|
assets = serializer.validated_data.get('assets')
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
if instance != Node.root():
|
if instance != Node.org_root():
|
||||||
instance.assets.remove(*tuple(assets))
|
instance.assets.remove(*tuple(assets))
|
||||||
else:
|
else:
|
||||||
assets = [asset for asset in assets if asset.nodes.count() > 1]
|
assets = [asset for asset in assets if asset.nodes.count() > 1]
|
||||||
@@ -257,8 +237,8 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
|||||||
|
|
||||||
|
|
||||||
class NodeReplaceAssetsApi(generics.UpdateAPIView):
|
class NodeReplaceAssetsApi(generics.UpdateAPIView):
|
||||||
|
model = Node
|
||||||
serializer_class = serializers.NodeAssetsSerializer
|
serializer_class = serializers.NodeAssetsSerializer
|
||||||
queryset = Node.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
instance = None
|
instance = None
|
||||||
|
|
||||||
@@ -269,38 +249,41 @@ class NodeReplaceAssetsApi(generics.UpdateAPIView):
|
|||||||
asset.nodes.set([instance])
|
asset.nodes.set([instance])
|
||||||
|
|
||||||
|
|
||||||
class RefreshNodeHardwareInfoApi(APIView):
|
class NodeTaskCreateApi(generics.CreateAPIView):
|
||||||
permission_classes = (IsOrgAdmin,)
|
|
||||||
model = Node
|
model = Node
|
||||||
|
serializer_class = serializers.NodeTaskSerializer
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
node_id = kwargs.get('pk')
|
|
||||||
node = get_object_or_404(self.model, id=node_id)
|
|
||||||
assets = node.get_all_assets()
|
|
||||||
# task_name = _("更新节点资产硬件信息: {}".format(node.name))
|
|
||||||
task_name = _("Update node asset hardware information: {}").format(node.name)
|
|
||||||
task = update_assets_hardware_info_util.delay(assets, task_name=task_name)
|
|
||||||
return Response({"task": task.id})
|
|
||||||
|
|
||||||
|
|
||||||
class TestNodeConnectiveApi(APIView):
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
model = Node
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get_object(self):
|
||||||
node_id = kwargs.get('pk')
|
node_id = self.kwargs.get('pk')
|
||||||
node = get_object_or_404(self.model, id=node_id)
|
node = get_object_or_none(self.model, id=node_id)
|
||||||
assets = node.get_all_assets()
|
return node
|
||||||
# task_name = _("测试节点下资产是否可连接: {}".format(node.name))
|
|
||||||
task_name = _("Test if the assets under the node are connectable: {}".format(node.name))
|
|
||||||
task = test_asset_connectivity_util.delay(assets, task_name=task_name)
|
|
||||||
return Response({"task": task.id})
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_serializer_data(s, task):
|
||||||
|
data = getattr(s, '_data', {})
|
||||||
|
data["task"] = task.id
|
||||||
|
setattr(s, '_data', data)
|
||||||
|
|
||||||
class RefreshAssetsAmount(APIView):
|
@staticmethod
|
||||||
permission_classes = (IsOrgAdmin,)
|
def refresh_nodes_cache():
|
||||||
model = Node
|
Node.refresh_nodes()
|
||||||
|
Task = namedtuple('Task', ['id'])
|
||||||
|
task = Task(id="0")
|
||||||
|
return task
|
||||||
|
|
||||||
|
def perform_create(self, serializer):
|
||||||
|
action = serializer.validated_data["action"]
|
||||||
|
node = self.get_object()
|
||||||
|
if action == "refresh_cache" and node is None:
|
||||||
|
task = self.refresh_nodes_cache()
|
||||||
|
self.set_serializer_data(serializer, task)
|
||||||
|
return
|
||||||
|
if node is None:
|
||||||
|
raise Http404()
|
||||||
|
if action == "refresh":
|
||||||
|
task = update_node_assets_hardware_info_manual.delay(node)
|
||||||
|
else:
|
||||||
|
task = test_node_assets_connectivity_manual.delay(node)
|
||||||
|
self.set_serializer_data(serializer, task)
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
self.model.expire_nodes_assets_amount()
|
|
||||||
return Response("Ok")
|
|
||||||
|
|||||||
@@ -1,66 +1,50 @@
|
|||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
# Copyright (C) 2014-2018 Beijing DuiZhan Technology Co.,Ltd. All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the GNU General Public License v2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.gnu.org/licenses/gpl-2.0.html
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework import generics
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
|
||||||
from rest_framework.pagination import LimitOffsetPagination
|
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsAppUser
|
||||||
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
from orgs.mixins import generics
|
||||||
|
from orgs.utils import tmp_to_org
|
||||||
from ..models import SystemUser, Asset
|
from ..models import SystemUser, Asset
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
from ..tasks import push_system_user_to_assets_manual, \
|
from ..serializers import SystemUserWithAuthInfoSerializer
|
||||||
test_system_user_connectivity_manual, push_system_user_a_asset_manual, \
|
from ..tasks import (
|
||||||
test_system_user_connectivity_a_asset
|
push_system_user_to_assets_manual, test_system_user_connectivity_manual,
|
||||||
|
push_system_user_a_asset_manual,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'SystemUserViewSet', 'SystemUserAuthInfoApi',
|
'SystemUserViewSet', 'SystemUserAuthInfoApi', 'SystemUserAssetAuthInfoApi',
|
||||||
'SystemUserPushApi', 'SystemUserTestConnectiveApi',
|
'SystemUserCommandFilterRuleListApi', 'SystemUserTaskApi',
|
||||||
'SystemUserAssetsListView', 'SystemUserPushToAssetApi',
|
|
||||||
'SystemUserTestAssetConnectivityApi', 'SystemUserCommandFilterRuleListApi',
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SystemUserViewSet(BulkModelViewSet):
|
class SystemUserViewSet(OrgBulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
System user api set, for add,delete,update,list,retrieve resource
|
System user api set, for add,delete,update,list,retrieve resource
|
||||||
"""
|
"""
|
||||||
|
model = SystemUser
|
||||||
filter_fields = ("name", "username")
|
filter_fields = ("name", "username")
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
queryset = SystemUser.objects.all()
|
|
||||||
serializer_class = serializers.SystemUserSerializer
|
serializer_class = serializers.SystemUserSerializer
|
||||||
|
serializer_classes = {
|
||||||
|
'default': serializers.SystemUserSerializer,
|
||||||
|
'list': serializers.SystemUserListSerializer,
|
||||||
|
}
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
pagination_class = LimitOffsetPagination
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = super().get_queryset().all()
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
||||||
"""
|
"""
|
||||||
Get system user auth info
|
Get system user auth info
|
||||||
"""
|
"""
|
||||||
queryset = SystemUser.objects.all()
|
model = SystemUser
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.SystemUserAuthSerializer
|
serializer_class = SystemUserWithAuthInfoSerializer
|
||||||
|
|
||||||
def destroy(self, request, *args, **kwargs):
|
def destroy(self, request, *args, **kwargs):
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
@@ -68,76 +52,66 @@ class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return Response(status=204)
|
return Response(status=204)
|
||||||
|
|
||||||
|
|
||||||
class SystemUserPushApi(generics.RetrieveAPIView):
|
class SystemUserAssetAuthInfoApi(generics.RetrieveAPIView):
|
||||||
"""
|
"""
|
||||||
Push system user to cluster assets api
|
Get system user with asset auth info
|
||||||
"""
|
"""
|
||||||
queryset = SystemUser.objects.all()
|
model = SystemUser
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
serializer_class = SystemUserWithAuthInfoSerializer
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def get_exception_handler(self):
|
||||||
system_user = self.get_object()
|
def handler(e, context):
|
||||||
nodes = system_user.nodes.all()
|
return Response({"error": str(e)}, status=400)
|
||||||
for node in nodes:
|
return handler
|
||||||
system_user.assets.add(*tuple(node.get_all_assets()))
|
|
||||||
|
def get_object(self):
|
||||||
|
instance = super().get_object()
|
||||||
|
username = instance.username
|
||||||
|
if instance.username_same_with_user:
|
||||||
|
username = self.request.query_params.get("username")
|
||||||
|
asset_id = self.kwargs.get('aid')
|
||||||
|
asset = get_object_or_404(Asset, pk=asset_id)
|
||||||
|
|
||||||
|
with tmp_to_org(asset.org_id):
|
||||||
|
instance.load_asset_special_auth(asset=asset, username=username)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserTaskApi(generics.CreateAPIView):
|
||||||
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
serializer_class = serializers.SystemUserTaskSerializer
|
||||||
|
|
||||||
|
def do_push(self, system_user, asset=None):
|
||||||
|
if asset is None:
|
||||||
task = push_system_user_to_assets_manual.delay(system_user)
|
task = push_system_user_to_assets_manual.delay(system_user)
|
||||||
return Response({"task": task.id})
|
else:
|
||||||
|
username = self.request.query_params.get('username')
|
||||||
|
task = push_system_user_a_asset_manual.delay(
|
||||||
|
system_user, asset, username=username
|
||||||
|
)
|
||||||
|
return task
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
class SystemUserTestConnectiveApi(generics.RetrieveAPIView):
|
def do_test(system_user, asset=None):
|
||||||
"""
|
|
||||||
Push system user to cluster assets api
|
|
||||||
"""
|
|
||||||
queryset = SystemUser.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
|
||||||
system_user = self.get_object()
|
|
||||||
task = test_system_user_connectivity_manual.delay(system_user)
|
task = test_system_user_connectivity_manual.delay(system_user)
|
||||||
return Response({"task": task.id})
|
return task
|
||||||
|
|
||||||
|
|
||||||
class SystemUserAssetsListView(generics.ListAPIView):
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
|
||||||
serializer_class = serializers.AssetSimpleSerializer
|
|
||||||
pagination_class = LimitOffsetPagination
|
|
||||||
filter_fields = ("hostname", "ip")
|
|
||||||
http_method_names = ['get']
|
|
||||||
search_fields = filter_fields
|
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
pk = self.kwargs.get('pk')
|
pk = self.kwargs.get('pk')
|
||||||
return get_object_or_404(SystemUser, pk=pk)
|
return get_object_or_404(SystemUser, pk=pk)
|
||||||
|
|
||||||
def get_queryset(self):
|
def perform_create(self, serializer):
|
||||||
|
action = serializer.validated_data["action"]
|
||||||
|
asset = serializer.validated_data.get('asset')
|
||||||
system_user = self.get_object()
|
system_user = self.get_object()
|
||||||
return system_user.assets.all()
|
if action == 'push':
|
||||||
|
task = self.do_push(system_user, asset)
|
||||||
|
else:
|
||||||
class SystemUserPushToAssetApi(generics.RetrieveAPIView):
|
task = self.do_test(system_user, asset)
|
||||||
queryset = SystemUser.objects.all()
|
data = getattr(serializer, '_data', {})
|
||||||
permission_classes = (IsOrgAdmin,)
|
data["task"] = task.id
|
||||||
serializer_class = serializers.TaskIDSerializer
|
setattr(serializer, '_data', data)
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
|
||||||
system_user = self.get_object()
|
|
||||||
asset_id = self.kwargs.get('aid')
|
|
||||||
asset = get_object_or_404(Asset, id=asset_id)
|
|
||||||
task = push_system_user_a_asset_manual.delay(system_user, asset)
|
|
||||||
return Response({"task": task.id})
|
|
||||||
|
|
||||||
|
|
||||||
class SystemUserTestAssetConnectivityApi(generics.RetrieveAPIView):
|
|
||||||
queryset = SystemUser.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
|
||||||
serializer_class = serializers.TaskIDSerializer
|
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
|
||||||
system_user = self.get_object()
|
|
||||||
asset_id = self.kwargs.get('aid')
|
|
||||||
asset = get_object_or_404(Asset, id=asset_id)
|
|
||||||
task = test_system_user_connectivity_a_asset.delay(system_user, asset)
|
|
||||||
return Response({"task": task.id})
|
|
||||||
|
|
||||||
|
|
||||||
class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
|
class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
|
||||||
|
|||||||
136
apps/assets/api/system_user_relation.py
Normal file
136
apps/assets/api/system_user_relation.py
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from collections import defaultdict
|
||||||
|
from django.db.models import F, Value
|
||||||
|
from django.db.models.signals import m2m_changed
|
||||||
|
from django.db.models.functions import Concat
|
||||||
|
|
||||||
|
from common.permissions import IsOrgAdmin
|
||||||
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
from orgs.utils import current_org
|
||||||
|
from .. import models, serializers
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'SystemUserAssetRelationViewSet', 'SystemUserNodeRelationViewSet',
|
||||||
|
'SystemUserUserRelationViewSet',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class RelationMixin:
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = self.model.objects.all()
|
||||||
|
org_id = current_org.org_id()
|
||||||
|
if org_id is not None:
|
||||||
|
queryset = queryset.filter(systemuser__org_id=org_id)
|
||||||
|
queryset = queryset.annotate(systemuser_display=Concat(
|
||||||
|
F('systemuser__name'), Value('('), F('systemuser__username'),
|
||||||
|
Value(')')
|
||||||
|
))
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def send_post_add_signal(self, instance):
|
||||||
|
if not isinstance(instance, list):
|
||||||
|
instance = [instance]
|
||||||
|
|
||||||
|
system_users_objects_map = defaultdict(list)
|
||||||
|
model, object_field = self.get_objects_attr()
|
||||||
|
|
||||||
|
for i in instance:
|
||||||
|
_id = getattr(i, object_field).id
|
||||||
|
system_users_objects_map[i.systemuser].append(_id)
|
||||||
|
|
||||||
|
sender = self.get_sender()
|
||||||
|
for system_user, objects in system_users_objects_map.items():
|
||||||
|
m2m_changed.send(
|
||||||
|
sender=sender, instance=system_user, action='post_add',
|
||||||
|
reverse=False, model=model, pk_set=objects
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_sender(self):
|
||||||
|
return self.model
|
||||||
|
|
||||||
|
def get_objects_attr(self):
|
||||||
|
return models.Asset, 'asset'
|
||||||
|
|
||||||
|
def perform_create(self, serializer):
|
||||||
|
instance = serializer.save()
|
||||||
|
self.send_post_add_signal(instance)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseRelationViewSet(RelationMixin, OrgBulkModelViewSet):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserAssetRelationViewSet(BaseRelationViewSet):
|
||||||
|
serializer_class = serializers.SystemUserAssetRelationSerializer
|
||||||
|
model = models.SystemUser.assets.through
|
||||||
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
filter_fields = [
|
||||||
|
'id', 'asset', 'systemuser',
|
||||||
|
]
|
||||||
|
search_fields = [
|
||||||
|
"id", "asset__hostname", "asset__ip",
|
||||||
|
"systemuser__name", "systemuser__username"
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_objects_attr(self):
|
||||||
|
return models.Asset, 'asset'
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset()
|
||||||
|
queryset = queryset.annotate(
|
||||||
|
asset_display=Concat(
|
||||||
|
F('asset__hostname'), Value('('),
|
||||||
|
F('asset__ip'), Value(')')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserNodeRelationViewSet(BaseRelationViewSet):
|
||||||
|
serializer_class = serializers.SystemUserNodeRelationSerializer
|
||||||
|
model = models.SystemUser.nodes.through
|
||||||
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
filter_fields = [
|
||||||
|
'id', 'node', 'systemuser',
|
||||||
|
]
|
||||||
|
search_fields = [
|
||||||
|
"node__value", "systemuser__name", "systemuser_username"
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_objects_attr(self):
|
||||||
|
return models.Node, 'node'
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset()
|
||||||
|
queryset = queryset \
|
||||||
|
.annotate(node_key=F('node__key'))
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserUserRelationViewSet(BaseRelationViewSet):
|
||||||
|
serializer_class = serializers.SystemUserUserRelationSerializer
|
||||||
|
model = models.SystemUser.users.through
|
||||||
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
filter_fields = [
|
||||||
|
'id', 'user', 'systemuser',
|
||||||
|
]
|
||||||
|
search_fields = [
|
||||||
|
"user__username", "user__name",
|
||||||
|
"systemuser__name", "systemuser__username",
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_objects_attr(self):
|
||||||
|
from users.models import User
|
||||||
|
return User, 'user'
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset()
|
||||||
|
queryset = queryset.annotate(
|
||||||
|
user_display=Concat(
|
||||||
|
F('user__name'), Value('('),
|
||||||
|
F('user__username'), Value(')')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return queryset
|
||||||
|
|
||||||
@@ -1,11 +1,25 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
from django.db.models.signals import post_migrate
|
||||||
|
|
||||||
|
|
||||||
|
def initial_some_nodes():
|
||||||
|
from .models import Node
|
||||||
|
Node.initial_some_nodes()
|
||||||
|
|
||||||
|
|
||||||
|
def initial_some_nodes_callback(sender, **kwargs):
|
||||||
|
initial_some_nodes()
|
||||||
|
|
||||||
|
|
||||||
class AssetsConfig(AppConfig):
|
class AssetsConfig(AppConfig):
|
||||||
name = 'assets'
|
name = 'assets'
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
from . import signals_handler
|
|
||||||
super().ready()
|
super().ready()
|
||||||
|
from . import signals_handler
|
||||||
|
try:
|
||||||
|
initial_some_nodes()
|
||||||
|
except Exception:
|
||||||
|
post_migrate.connect(initial_some_nodes_callback, sender=self)
|
||||||
|
|||||||
1
apps/assets/backends/__init__.py
Normal file
1
apps/assets/backends/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .manager import AssetUserManager
|
||||||
48
apps/assets/backends/base.py
Normal file
48
apps/assets/backends/base.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
from ..models import Asset
|
||||||
|
|
||||||
|
|
||||||
|
class BaseBackend:
|
||||||
|
@abstractmethod
|
||||||
|
def all(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def filter(self, username=None, hostname=None, ip=None, assets=None,
|
||||||
|
node=None, prefer_id=None, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def search(self, item):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_queryset(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def delete(self, union_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def qs_to_values(qs):
|
||||||
|
values = qs.values(
|
||||||
|
'hostname', 'ip', "asset_id",
|
||||||
|
'username', 'password', 'private_key', 'public_key',
|
||||||
|
'score', 'version',
|
||||||
|
"asset_username", "union_id",
|
||||||
|
'date_created', 'date_updated',
|
||||||
|
'org_id', 'backend',
|
||||||
|
)
|
||||||
|
return values
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_assets_as_id(assets):
|
||||||
|
if not assets:
|
||||||
|
return []
|
||||||
|
if isinstance(assets[0], Asset):
|
||||||
|
assets = [a.id for a in assets]
|
||||||
|
return assets
|
||||||
318
apps/assets/backends/db.py
Normal file
318
apps/assets/backends/db.py
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
from functools import reduce
|
||||||
|
from django.db.models import F, CharField, Value, IntegerField, Q, Count
|
||||||
|
from django.db.models.functions import Concat
|
||||||
|
|
||||||
|
from common.utils import get_object_or_none
|
||||||
|
from orgs.utils import current_org
|
||||||
|
from ..models import AuthBook, SystemUser, Asset, AdminUser
|
||||||
|
from .base import BaseBackend
|
||||||
|
|
||||||
|
|
||||||
|
class DBBackend(BaseBackend):
|
||||||
|
union_id_length = 2
|
||||||
|
|
||||||
|
def __init__(self, queryset=None):
|
||||||
|
if queryset is None:
|
||||||
|
queryset = self.all()
|
||||||
|
self.queryset = queryset
|
||||||
|
|
||||||
|
def _clone(self):
|
||||||
|
return self.__class__(self.queryset)
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
return AuthBook.objects.none()
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
return self.queryset.count()
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return self.queryset
|
||||||
|
|
||||||
|
def delete(self, union_id):
|
||||||
|
cleaned_union_id = union_id.split('_')
|
||||||
|
# 如果union_id通不过本检查,代表可能不是本backend, 应该返回空
|
||||||
|
if not self._check_union_id(union_id, cleaned_union_id):
|
||||||
|
return
|
||||||
|
return self._perform_delete_by_union_id(cleaned_union_id)
|
||||||
|
|
||||||
|
def _perform_delete_by_union_id(self, union_id_cleaned):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def filter(self, assets=None, node=None, prefer=None, prefer_id=None,
|
||||||
|
union_id=None, id__in=None, **kwargs):
|
||||||
|
clone = self._clone()
|
||||||
|
clone._filter_union_id(union_id)
|
||||||
|
clone._filter_prefer(prefer, prefer_id)
|
||||||
|
clone._filter_node(node)
|
||||||
|
clone._filter_assets(assets)
|
||||||
|
clone._filter_other(kwargs)
|
||||||
|
clone._filter_id_in(id__in)
|
||||||
|
return clone
|
||||||
|
|
||||||
|
def _filter_union_id(self, union_id):
|
||||||
|
if not union_id:
|
||||||
|
return
|
||||||
|
cleaned_union_id = union_id.split('_')
|
||||||
|
# 如果union_id通不过本检查,代表可能不是本backend, 应该返回空
|
||||||
|
if not self._check_union_id(union_id, cleaned_union_id):
|
||||||
|
self.queryset = self.queryset.none()
|
||||||
|
return
|
||||||
|
return self._perform_filter_union_id(union_id, cleaned_union_id)
|
||||||
|
|
||||||
|
def _check_union_id(self, union_id, cleaned_union_id):
|
||||||
|
return union_id and len(cleaned_union_id) == self.union_id_length
|
||||||
|
|
||||||
|
def _perform_filter_union_id(self, union_id, union_id_cleaned):
|
||||||
|
self.queryset = self.queryset.filter(union_id=union_id)
|
||||||
|
|
||||||
|
def _filter_assets(self, assets):
|
||||||
|
assets_id = self.make_assets_as_id(assets)
|
||||||
|
if assets_id:
|
||||||
|
self.queryset = self.queryset.filter(asset_id__in=assets_id)
|
||||||
|
|
||||||
|
def _filter_node(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _filter_id_in(self, ids):
|
||||||
|
if ids and isinstance(ids, list):
|
||||||
|
self.queryset = self.queryset.filter(union_id__in=ids)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def clean_kwargs(kwargs):
|
||||||
|
return {k: v for k, v in kwargs.items() if v}
|
||||||
|
|
||||||
|
def _filter_other(self, kwargs):
|
||||||
|
kwargs = self.clean_kwargs(kwargs)
|
||||||
|
if kwargs:
|
||||||
|
self.queryset = self.queryset.filter(**kwargs)
|
||||||
|
|
||||||
|
def _filter_prefer(self, prefer, prefer_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def search(self, item):
|
||||||
|
qs = []
|
||||||
|
for i in ['hostname', 'ip', 'username']:
|
||||||
|
kwargs = {i + '__startswith': item}
|
||||||
|
qs.append(Q(**kwargs))
|
||||||
|
q = reduce(lambda x, y: x | y, qs)
|
||||||
|
clone = self._clone()
|
||||||
|
clone.queryset = clone.queryset.filter(q).distinct()
|
||||||
|
return clone
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserBackend(DBBackend):
|
||||||
|
model = SystemUser.assets.through
|
||||||
|
backend = 'system_user'
|
||||||
|
prefer = backend
|
||||||
|
base_score = 0
|
||||||
|
union_id_length = 2
|
||||||
|
|
||||||
|
def _filter_prefer(self, prefer, prefer_id):
|
||||||
|
if prefer and prefer != self.prefer:
|
||||||
|
self.queryset = self.queryset.none()
|
||||||
|
|
||||||
|
if prefer_id:
|
||||||
|
self.queryset = self.queryset.filter(systemuser__id=prefer_id)
|
||||||
|
|
||||||
|
def _perform_filter_union_id(self, union_id, union_id_cleaned):
|
||||||
|
system_user_id, asset_id = union_id_cleaned
|
||||||
|
self.queryset = self.queryset.filter(
|
||||||
|
asset_id=asset_id, systemuser__id=system_user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _perform_delete_by_union_id(self, union_id_cleaned):
|
||||||
|
system_user_id, asset_id = union_id_cleaned
|
||||||
|
system_user = get_object_or_none(SystemUser, pk=system_user_id)
|
||||||
|
asset = get_object_or_none(Asset, pk=asset_id)
|
||||||
|
if all((system_user, asset)):
|
||||||
|
system_user.assets.remove(asset)
|
||||||
|
|
||||||
|
def _filter_node(self, node):
|
||||||
|
if node:
|
||||||
|
self.queryset = self.queryset.filter(asset__nodes__id=node.id)
|
||||||
|
|
||||||
|
def get_annotate(self):
|
||||||
|
kwargs = dict(
|
||||||
|
hostname=F("asset__hostname"),
|
||||||
|
ip=F("asset__ip"),
|
||||||
|
username=F("systemuser__username"),
|
||||||
|
password=F("systemuser__password"),
|
||||||
|
private_key=F("systemuser__private_key"),
|
||||||
|
public_key=F("systemuser__public_key"),
|
||||||
|
score=F("systemuser__priority") + self.base_score,
|
||||||
|
version=Value(0, IntegerField()),
|
||||||
|
date_created=F("systemuser__date_created"),
|
||||||
|
date_updated=F("systemuser__date_updated"),
|
||||||
|
asset_username=Concat(F("asset__id"), Value("_"),
|
||||||
|
F("systemuser__username"),
|
||||||
|
output_field=CharField()),
|
||||||
|
union_id=Concat(F("systemuser_id"), Value("_"), F("asset_id"),
|
||||||
|
output_field=CharField()),
|
||||||
|
org_id=F("asset__org_id"),
|
||||||
|
backend=Value(self.backend, CharField())
|
||||||
|
)
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def get_filter(self):
|
||||||
|
return dict(
|
||||||
|
systemuser__username_same_with_user=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
kwargs = self.get_annotate()
|
||||||
|
filters = self.get_filter()
|
||||||
|
qs = self.model.objects.all().annotate(**kwargs)
|
||||||
|
if current_org.org_id() is not None:
|
||||||
|
filters['org_id'] = current_org.org_id()
|
||||||
|
qs = qs.filter(**filters)
|
||||||
|
qs = self.qs_to_values(qs)
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicSystemUserBackend(SystemUserBackend):
|
||||||
|
backend = 'system_user_dynamic'
|
||||||
|
prefer = 'system_user'
|
||||||
|
union_id_length = 3
|
||||||
|
|
||||||
|
def get_annotate(self):
|
||||||
|
kwargs = super().get_annotate()
|
||||||
|
kwargs.update(dict(
|
||||||
|
username=F("systemuser__users__username"),
|
||||||
|
asset_username=Concat(
|
||||||
|
F("asset__id"), Value("_"),
|
||||||
|
F("systemuser__users__username"),
|
||||||
|
output_field=CharField()
|
||||||
|
),
|
||||||
|
union_id=Concat(
|
||||||
|
F("systemuser_id"), Value("_"), F("asset_id"),
|
||||||
|
Value("_"), F("systemuser__users__id"),
|
||||||
|
output_field=CharField()
|
||||||
|
),
|
||||||
|
users_count=Count('systemuser__users'),
|
||||||
|
))
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def _perform_filter_union_id(self, union_id, union_id_cleaned):
|
||||||
|
system_user_id, asset_id, user_id = union_id_cleaned
|
||||||
|
self.queryset = self.queryset.filter(
|
||||||
|
asset_id=asset_id, systemuser_id=system_user_id,
|
||||||
|
union_id=union_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _perform_delete_by_union_id(self, union_id_cleaned):
|
||||||
|
system_user_id, asset_id, user_id = union_id_cleaned
|
||||||
|
system_user = get_object_or_none(SystemUser, pk=system_user_id)
|
||||||
|
if not system_user:
|
||||||
|
return
|
||||||
|
system_user.users.remove(user_id)
|
||||||
|
if system_user.users.count() == 0:
|
||||||
|
system_user.assets.remove(asset_id)
|
||||||
|
|
||||||
|
def get_filter(self):
|
||||||
|
return dict(
|
||||||
|
users_count__gt=0,
|
||||||
|
systemuser__username_same_with_user=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AdminUserBackend(DBBackend):
|
||||||
|
model = Asset
|
||||||
|
backend = 'admin_user'
|
||||||
|
prefer = backend
|
||||||
|
base_score = 200
|
||||||
|
|
||||||
|
def _filter_prefer(self, prefer, prefer_id):
|
||||||
|
if prefer and prefer != self.backend:
|
||||||
|
self.queryset = self.queryset.none()
|
||||||
|
if prefer_id:
|
||||||
|
self.queryset = self.queryset.filter(admin_user__id=prefer_id)
|
||||||
|
|
||||||
|
def _filter_node(self, node):
|
||||||
|
if node:
|
||||||
|
self.queryset = self.queryset.filter(nodes__id=node.id)
|
||||||
|
|
||||||
|
def _perform_filter_union_id(self, union_id, union_id_cleaned):
|
||||||
|
admin_user_id, asset_id = union_id_cleaned
|
||||||
|
self.queryset = self.queryset.filter(
|
||||||
|
id=asset_id, admin_user_id=admin_user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _perform_delete_by_union_id(self, union_id_cleaned):
|
||||||
|
raise PermissionError(_("Could not remove asset admin user"))
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
qs = self.model.objects.all().annotate(
|
||||||
|
asset_id=F("id"),
|
||||||
|
username=F("admin_user__username"),
|
||||||
|
password=F("admin_user__password"),
|
||||||
|
private_key=F("admin_user__private_key"),
|
||||||
|
public_key=F("admin_user__public_key"),
|
||||||
|
score=Value(self.base_score, IntegerField()),
|
||||||
|
version=Value(0, IntegerField()),
|
||||||
|
date_updated=F("admin_user__date_updated"),
|
||||||
|
asset_username=Concat(F("id"), Value("_"), F("admin_user__username"), output_field=CharField()),
|
||||||
|
union_id=Concat(F("admin_user_id"), Value("_"), F("id"), output_field=CharField()),
|
||||||
|
backend=Value(self.backend, CharField()),
|
||||||
|
)
|
||||||
|
qs = self.qs_to_values(qs)
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
class AuthbookBackend(DBBackend):
|
||||||
|
model = AuthBook
|
||||||
|
backend = 'db'
|
||||||
|
prefer = backend
|
||||||
|
base_score = 400
|
||||||
|
|
||||||
|
def _filter_node(self, node):
|
||||||
|
if node:
|
||||||
|
self.queryset = self.queryset.filter(asset__nodes__id=node.id)
|
||||||
|
|
||||||
|
def _filter_prefer(self, prefer, prefer_id):
|
||||||
|
if not prefer or not prefer_id:
|
||||||
|
return
|
||||||
|
if prefer.lower() == "admin_user":
|
||||||
|
model = AdminUser
|
||||||
|
elif prefer.lower() == "system_user":
|
||||||
|
model = SystemUser
|
||||||
|
else:
|
||||||
|
self.queryset = self.queryset.none()
|
||||||
|
return
|
||||||
|
obj = get_object_or_none(model, pk=prefer_id)
|
||||||
|
if obj is None:
|
||||||
|
self.queryset = self.queryset.none()
|
||||||
|
return
|
||||||
|
username = obj.get_username()
|
||||||
|
if isinstance(username, str):
|
||||||
|
self.queryset = self.queryset.filter(username=username)
|
||||||
|
# dynamic system user return more username
|
||||||
|
else:
|
||||||
|
self.queryset = self.queryset.filter(username__in=username)
|
||||||
|
|
||||||
|
def _perform_filter_union_id(self, union_id, union_id_cleaned):
|
||||||
|
authbook_id, asset_id = union_id_cleaned
|
||||||
|
self.queryset = self.queryset.filter(
|
||||||
|
id=authbook_id, asset_id=asset_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _perform_delete_by_union_id(self, union_id_cleaned):
|
||||||
|
authbook_id, asset_id = union_id_cleaned
|
||||||
|
authbook = get_object_or_none(AuthBook, pk=authbook_id)
|
||||||
|
if authbook.is_latest:
|
||||||
|
raise PermissionError(_("Latest version could not be delete"))
|
||||||
|
AuthBook.objects.filter(id=authbook_id).delete()
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
qs = self.model.objects.all().annotate(
|
||||||
|
hostname=F("asset__hostname"),
|
||||||
|
ip=F("asset__ip"),
|
||||||
|
score=F('version') + self.base_score,
|
||||||
|
asset_username=Concat(F("asset__id"), Value("_"), F("username"), output_field=CharField()),
|
||||||
|
union_id=Concat(F("id"), Value("_"), F("asset_id"), output_field=CharField()),
|
||||||
|
backend=Value(self.backend, CharField()),
|
||||||
|
)
|
||||||
|
qs = self.qs_to_values(qs)
|
||||||
|
return qs
|
||||||
162
apps/assets/backends/manager.py
Normal file
162
apps/assets/backends/manager.py
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from itertools import chain, groupby
|
||||||
|
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
|
||||||
|
|
||||||
|
from orgs.utils import current_org
|
||||||
|
from common.utils import get_logger, lazyproperty
|
||||||
|
from common.struct import QuerySetChain
|
||||||
|
|
||||||
|
from ..models import AssetUser, AuthBook
|
||||||
|
from .db import (
|
||||||
|
AuthbookBackend, SystemUserBackend, AdminUserBackend,
|
||||||
|
DynamicSystemUserBackend
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class NotSupportError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AssetUserQueryset:
|
||||||
|
ObjectDoesNotExist = ObjectDoesNotExist
|
||||||
|
MultipleObjectsReturned = MultipleObjectsReturned
|
||||||
|
|
||||||
|
def __init__(self, backends=()):
|
||||||
|
self.backends = backends
|
||||||
|
self._distinct_queryset = None
|
||||||
|
|
||||||
|
def backends_queryset(self):
|
||||||
|
return [b.get_queryset() for b in self.backends]
|
||||||
|
|
||||||
|
@lazyproperty
|
||||||
|
def backends_counts(self):
|
||||||
|
return [b.count() for b in self.backends]
|
||||||
|
|
||||||
|
def filter(self, hostname=None, ip=None, username=None,
|
||||||
|
assets=None, asset=None, node=None,
|
||||||
|
id=None, prefer_id=None, prefer=None, id__in=None):
|
||||||
|
if not assets and asset:
|
||||||
|
assets = [asset]
|
||||||
|
|
||||||
|
kwargs = dict(
|
||||||
|
hostname=hostname, ip=ip, username=username,
|
||||||
|
assets=assets, node=node, prefer=prefer, prefer_id=prefer_id,
|
||||||
|
id__in=id__in, union_id=id,
|
||||||
|
)
|
||||||
|
logger.debug("Filter: {}".format(kwargs))
|
||||||
|
backends = []
|
||||||
|
for backend in self.backends:
|
||||||
|
clone = backend.filter(**kwargs)
|
||||||
|
backends.append(clone)
|
||||||
|
return self._clone(backends)
|
||||||
|
|
||||||
|
def _clone(self, backends=None):
|
||||||
|
if backends is None:
|
||||||
|
backends = self.backends
|
||||||
|
return self.__class__(backends)
|
||||||
|
|
||||||
|
def search(self, item):
|
||||||
|
backends = []
|
||||||
|
for backend in self.backends:
|
||||||
|
new = backend.search(item)
|
||||||
|
backends.append(new)
|
||||||
|
return self._clone(backends)
|
||||||
|
|
||||||
|
def distinct(self):
|
||||||
|
logger.debug("Distinct asset user queryset")
|
||||||
|
queryset_chain = chain(*(backend.get_queryset() for backend in self.backends))
|
||||||
|
queryset_sorted = sorted(
|
||||||
|
queryset_chain,
|
||||||
|
key=lambda item: (item["asset_username"], item["score"]),
|
||||||
|
reverse=True,
|
||||||
|
)
|
||||||
|
results = groupby(queryset_sorted, key=lambda item: item["asset_username"])
|
||||||
|
final = [next(result[1]) for result in results]
|
||||||
|
self._distinct_queryset = final
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get(self, latest=False, **kwargs):
|
||||||
|
queryset = self.filter(**kwargs)
|
||||||
|
if latest:
|
||||||
|
queryset = queryset.distinct()
|
||||||
|
queryset = list(queryset)
|
||||||
|
count = len(queryset)
|
||||||
|
if count == 1:
|
||||||
|
data = queryset[0]
|
||||||
|
return data
|
||||||
|
elif count > 1:
|
||||||
|
msg = 'Should return 1 record, but get {}'.format(count)
|
||||||
|
raise MultipleObjectsReturned(msg)
|
||||||
|
else:
|
||||||
|
msg = 'No record found(org is {})'.format(current_org.name)
|
||||||
|
raise ObjectDoesNotExist(msg)
|
||||||
|
|
||||||
|
def get_latest(self, **kwargs):
|
||||||
|
return self.get(latest=True, **kwargs)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def to_asset_user(data):
|
||||||
|
obj = AssetUser()
|
||||||
|
for k, v in data.items():
|
||||||
|
setattr(obj, k, v)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
@property
|
||||||
|
def queryset(self):
|
||||||
|
if self._distinct_queryset is not None:
|
||||||
|
return self._distinct_queryset
|
||||||
|
return QuerySetChain(self.backends_queryset())
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
if self._distinct_queryset is not None:
|
||||||
|
return len(self._distinct_queryset)
|
||||||
|
else:
|
||||||
|
return sum(self.backends_counts)
|
||||||
|
|
||||||
|
def __getitem__(self, ndx):
|
||||||
|
return self.queryset.__getitem__(ndx)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
self._data = iter(self.queryset)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
return self.to_asset_user(next(self._data))
|
||||||
|
|
||||||
|
|
||||||
|
class AssetUserManager:
|
||||||
|
support_backends = (
|
||||||
|
('db', AuthbookBackend),
|
||||||
|
('system_user', SystemUserBackend),
|
||||||
|
('admin_user', AdminUserBackend),
|
||||||
|
('system_user_dynamic', DynamicSystemUserBackend),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.backends = [backend() for name, backend in self.support_backends]
|
||||||
|
self._queryset = AssetUserQueryset(self.backends)
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
return self._queryset
|
||||||
|
|
||||||
|
def delete(self, obj):
|
||||||
|
name_backends_map = dict(self.support_backends)
|
||||||
|
backend_name = obj.backend
|
||||||
|
backend_cls = name_backends_map.get(backend_name)
|
||||||
|
union_id = obj.union_id
|
||||||
|
if backend_cls:
|
||||||
|
backend_cls().delete(union_id)
|
||||||
|
else:
|
||||||
|
raise ObjectDoesNotExist("Not backend found")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create(**kwargs):
|
||||||
|
# 使用create方法创建AuthBook对象,解决并发创建问题(添加锁机制)
|
||||||
|
authbook = AuthBook.create(**kwargs)
|
||||||
|
return authbook
|
||||||
|
|
||||||
|
def __getattr__(self, item):
|
||||||
|
return getattr(self._queryset, item)
|
||||||
7
apps/assets/backends/utils.py
Normal file
7
apps/assets/backends/utils.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
# from django.conf import settings
|
||||||
|
|
||||||
|
# from .vault import VaultBackend
|
||||||
|
|
||||||
4
apps/assets/backends/vault.py
Normal file
4
apps/assets/backends/vault.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
@@ -1,38 +1,2 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
UPDATE_ASSETS_HARDWARE_TASKS = [
|
|
||||||
{
|
|
||||||
'name': "setup",
|
|
||||||
'action': {
|
|
||||||
'module': 'setup'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
ADMIN_USER_CONN_CACHE_KEY = "ADMIN_USER_CONN_{}"
|
|
||||||
TEST_ADMIN_USER_CONN_TASKS = [
|
|
||||||
{
|
|
||||||
"name": "ping",
|
|
||||||
"action": {
|
|
||||||
"module": "ping",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
ASSET_ADMIN_CONN_CACHE_KEY = "ASSET_ADMIN_USER_CONN_{}"
|
|
||||||
|
|
||||||
SYSTEM_USER_CONN_CACHE_KEY = "SYSTEM_USER_CONN_{}"
|
|
||||||
TEST_SYSTEM_USER_CONN_TASKS = [
|
|
||||||
{
|
|
||||||
"name": "ping",
|
|
||||||
"action": {
|
|
||||||
"module": "ping",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
TASK_OPTIONS = {
|
|
||||||
'timeout': 10,
|
|
||||||
'forks': 10,
|
|
||||||
}
|
|
||||||
|
|||||||
119
apps/assets/filters.py
Normal file
119
apps/assets/filters.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
import coreapi
|
||||||
|
from rest_framework import filters
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
from common.utils import dict_get_any, is_uuid, get_object_or_none
|
||||||
|
from .models import Node, Label
|
||||||
|
|
||||||
|
|
||||||
|
class AssetByNodeFilterBackend(filters.BaseFilterBackend):
|
||||||
|
fields = ['node', 'all']
|
||||||
|
|
||||||
|
def get_schema_fields(self, view):
|
||||||
|
return [
|
||||||
|
coreapi.Field(
|
||||||
|
name=field, location='query', required=False,
|
||||||
|
type='string', example='', description='', schema=None,
|
||||||
|
)
|
||||||
|
for field in self.fields
|
||||||
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_query_all(request):
|
||||||
|
query_all_arg = request.query_params.get('all')
|
||||||
|
show_current_asset_arg = request.query_params.get('show_current_asset')
|
||||||
|
|
||||||
|
query_all = query_all_arg == '1'
|
||||||
|
if show_current_asset_arg is not None:
|
||||||
|
query_all = show_current_asset_arg != '1'
|
||||||
|
return query_all
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_query_node(request):
|
||||||
|
node_id = dict_get_any(request.query_params, ['node', 'node_id'])
|
||||||
|
if not node_id:
|
||||||
|
return None, False
|
||||||
|
|
||||||
|
if is_uuid(node_id):
|
||||||
|
node = get_object_or_none(Node, id=node_id)
|
||||||
|
else:
|
||||||
|
node = get_object_or_none(Node, key=node_id)
|
||||||
|
return node, True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def perform_query(pattern, queryset):
|
||||||
|
return queryset.filter(nodes__key__regex=pattern).distinct()
|
||||||
|
|
||||||
|
def filter_queryset(self, request, queryset, view):
|
||||||
|
node, has_query_arg = self.get_query_node(request)
|
||||||
|
if not has_query_arg:
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
if node is None:
|
||||||
|
return queryset
|
||||||
|
query_all = self.is_query_all(request)
|
||||||
|
if query_all:
|
||||||
|
pattern = node.get_all_children_pattern(with_self=True)
|
||||||
|
else:
|
||||||
|
# pattern = node.get_children_key_pattern(with_self=True)
|
||||||
|
# 只显示当前节点下资产
|
||||||
|
pattern = r"^{}$".format(node.key)
|
||||||
|
return self.perform_query(pattern, queryset)
|
||||||
|
|
||||||
|
|
||||||
|
class LabelFilterBackend(filters.BaseFilterBackend):
|
||||||
|
sep = ':'
|
||||||
|
query_arg = 'label'
|
||||||
|
|
||||||
|
def get_schema_fields(self, view):
|
||||||
|
example = self.sep.join(['os', 'linux'])
|
||||||
|
return [
|
||||||
|
coreapi.Field(
|
||||||
|
name=self.query_arg, location='query', required=False,
|
||||||
|
type='string', example=example, description=''
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_query_labels(self, request):
|
||||||
|
labels_query = request.query_params.getlist(self.query_arg)
|
||||||
|
if not labels_query:
|
||||||
|
return None
|
||||||
|
|
||||||
|
q = None
|
||||||
|
for kv in labels_query:
|
||||||
|
if '#' in kv:
|
||||||
|
self.sep = '#'
|
||||||
|
if self.sep not in kv:
|
||||||
|
continue
|
||||||
|
key, value = kv.strip().split(self.sep)[:2]
|
||||||
|
if not all([key, value]):
|
||||||
|
continue
|
||||||
|
if q:
|
||||||
|
q |= Q(name=key, value=value)
|
||||||
|
else:
|
||||||
|
q = Q(name=key, value=value)
|
||||||
|
if not q:
|
||||||
|
return []
|
||||||
|
labels = Label.objects.filter(q, is_active=True)\
|
||||||
|
.values_list('id', flat=True)
|
||||||
|
return labels
|
||||||
|
|
||||||
|
def filter_queryset(self, request, queryset, view):
|
||||||
|
labels = self.get_query_labels(request)
|
||||||
|
if labels is None:
|
||||||
|
return queryset
|
||||||
|
if len(labels) == 0:
|
||||||
|
return queryset.none()
|
||||||
|
for label in labels:
|
||||||
|
queryset = queryset.filter(labels=label)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class AssetRelatedByNodeFilterBackend(AssetByNodeFilterBackend):
|
||||||
|
@staticmethod
|
||||||
|
def perform_query(pattern, queryset):
|
||||||
|
return queryset.filter(asset__nodes__key__regex=pattern).distinct()
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
from .asset import *
|
|
||||||
from .label import *
|
|
||||||
from .user import *
|
|
||||||
from .domain import *
|
|
||||||
from .cmd_filter import *
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
from django import forms
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from common.utils import get_logger
|
|
||||||
from orgs.mixins import OrgModelForm
|
|
||||||
|
|
||||||
from ..models import Asset, AdminUser
|
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
|
||||||
__all__ = ['AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm']
|
|
||||||
|
|
||||||
|
|
||||||
class AssetCreateForm(OrgModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Asset
|
|
||||||
fields = [
|
|
||||||
'hostname', 'ip', 'public_ip', 'port', 'comment',
|
|
||||||
'nodes', 'is_active', 'admin_user', 'labels', 'platform',
|
|
||||||
'domain', 'protocol',
|
|
||||||
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
'nodes': forms.SelectMultiple(attrs={
|
|
||||||
'class': 'select2', 'data-placeholder': _('Nodes')
|
|
||||||
}),
|
|
||||||
'admin_user': forms.Select(attrs={
|
|
||||||
'class': 'select2', 'data-placeholder': _('Admin user')
|
|
||||||
}),
|
|
||||||
'labels': forms.SelectMultiple(attrs={
|
|
||||||
'class': 'select2', 'data-placeholder': _('Label')
|
|
||||||
}),
|
|
||||||
'port': forms.TextInput(),
|
|
||||||
'domain': forms.Select(attrs={
|
|
||||||
'class': 'select2', 'data-placeholder': _('Domain')
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
labels = {
|
|
||||||
'nodes': _("Node"),
|
|
||||||
}
|
|
||||||
help_texts = {
|
|
||||||
'admin_user': _(
|
|
||||||
'root or other NOPASSWD sudo privilege user existed in asset,'
|
|
||||||
'If asset is windows or other set any one, more see admin user left menu'
|
|
||||||
),
|
|
||||||
'platform': _("Windows 2016 RDP protocol is different, If is window 2016, set it"),
|
|
||||||
'domain': _("If your have some network not connect with each other, you can set domain")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class AssetUpdateForm(OrgModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Asset
|
|
||||||
fields = [
|
|
||||||
'hostname', 'ip', 'port', 'nodes', 'is_active', 'platform',
|
|
||||||
'public_ip', 'number', 'comment', 'admin_user', 'labels',
|
|
||||||
'domain', 'protocol',
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
'nodes': forms.SelectMultiple(attrs={
|
|
||||||
'class': 'select2', 'data-placeholder': _('Node')
|
|
||||||
}),
|
|
||||||
'admin_user': forms.Select(attrs={
|
|
||||||
'class': 'select2', 'data-placeholder': _('Admin user')
|
|
||||||
}),
|
|
||||||
'labels': forms.SelectMultiple(attrs={
|
|
||||||
'class': 'select2', 'data-placeholder': _('Label')
|
|
||||||
}),
|
|
||||||
'port': forms.TextInput(),
|
|
||||||
'domain': forms.Select(attrs={
|
|
||||||
'class': 'select2', 'data-placeholder': _('Domain')
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
labels = {
|
|
||||||
'nodes': _("Node"),
|
|
||||||
}
|
|
||||||
help_texts = {
|
|
||||||
'admin_user': _(
|
|
||||||
'root or other NOPASSWD sudo privilege user existed in asset,'
|
|
||||||
'If asset is windows or other set any one, more see admin user left menu'
|
|
||||||
),
|
|
||||||
'platform': _("Windows 2016 RDP protocol is different, If is window 2016, set it"),
|
|
||||||
'domain': _("If your have some network not connect with each other, you can set domain")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class AssetBulkUpdateForm(OrgModelForm):
|
|
||||||
assets = forms.ModelMultipleChoiceField(
|
|
||||||
required=True,
|
|
||||||
label=_('Select assets'), queryset=Asset.objects.all(),
|
|
||||||
widget=forms.SelectMultiple(
|
|
||||||
attrs={
|
|
||||||
'class': 'select2',
|
|
||||||
'data-placeholder': _('Select assets')
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
port = forms.IntegerField(
|
|
||||||
label=_('Port'), required=False, min_value=1, max_value=65535,
|
|
||||||
)
|
|
||||||
admin_user = forms.ModelChoiceField(
|
|
||||||
required=False, queryset=AdminUser.objects,
|
|
||||||
label=_("Admin user"),
|
|
||||||
widget=forms.Select(
|
|
||||||
attrs={
|
|
||||||
'class': 'select2',
|
|
||||||
'data-placeholder': _('Admin user')
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Asset
|
|
||||||
fields = [
|
|
||||||
'assets', 'port', 'admin_user', 'labels', 'nodes', 'platform'
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
'labels': forms.SelectMultiple(
|
|
||||||
attrs={'class': 'select2', 'data-placeholder': _('Label')}
|
|
||||||
),
|
|
||||||
'nodes': forms.SelectMultiple(
|
|
||||||
attrs={'class': 'select2', 'data-placeholder': _('Node')}
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
def save(self, commit=True):
|
|
||||||
changed_fields = []
|
|
||||||
for field in self._meta.fields:
|
|
||||||
if self.data.get(field) not in [None, '']:
|
|
||||||
changed_fields.append(field)
|
|
||||||
|
|
||||||
cleaned_data = {k: v for k, v in self.cleaned_data.items()
|
|
||||||
if k in changed_fields}
|
|
||||||
assets = cleaned_data.pop('assets')
|
|
||||||
labels = cleaned_data.pop('labels', [])
|
|
||||||
nodes = cleaned_data.pop('nodes', None)
|
|
||||||
assets = Asset.objects.filter(id__in=[asset.id for asset in assets])
|
|
||||||
assets.update(**cleaned_data)
|
|
||||||
|
|
||||||
if labels:
|
|
||||||
for asset in assets:
|
|
||||||
asset.labels.set(labels)
|
|
||||||
if nodes:
|
|
||||||
for asset in assets:
|
|
||||||
asset.nodes.set(nodes)
|
|
||||||
return assets
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
from django import forms
|
|
||||||
|
|
||||||
from orgs.mixins import OrgModelForm
|
|
||||||
from ..models import CommandFilter, CommandFilterRule
|
|
||||||
|
|
||||||
__all__ = ['CommandFilterForm', 'CommandFilterRuleForm']
|
|
||||||
|
|
||||||
|
|
||||||
class CommandFilterForm(OrgModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = CommandFilter
|
|
||||||
fields = ['name', 'comment']
|
|
||||||
|
|
||||||
|
|
||||||
class CommandFilterRuleForm(OrgModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = CommandFilterRule
|
|
||||||
fields = [
|
|
||||||
'filter', 'type', 'content', 'priority', 'action', 'comment'
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
'content': forms.Textarea(attrs={
|
|
||||||
'placeholder': 'eg:\r\nreboot\r\nrm -rf'
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
from django import forms
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from orgs.mixins import OrgModelForm
|
|
||||||
from ..models import Domain, Asset, Gateway
|
|
||||||
from .user import PasswordAndKeyAuthForm
|
|
||||||
|
|
||||||
__all__ = ['DomainForm', 'GatewayForm']
|
|
||||||
|
|
||||||
|
|
||||||
class DomainForm(forms.ModelForm):
|
|
||||||
assets = forms.ModelMultipleChoiceField(
|
|
||||||
queryset=Asset.objects.all(), label=_('Asset'), required=False,
|
|
||||||
widget=forms.SelectMultiple(
|
|
||||||
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Domain
|
|
||||||
fields = ['name', 'comment', 'assets']
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
if kwargs.get('instance', None):
|
|
||||||
initial = kwargs.get('initial', {})
|
|
||||||
initial['assets'] = kwargs['instance'].assets.all()
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# 前端渲染优化, 防止过多资产
|
|
||||||
assets_field = self.fields.get('assets')
|
|
||||||
if not self.data:
|
|
||||||
instance = kwargs.get('instance')
|
|
||||||
if instance:
|
|
||||||
assets_field.queryset = instance.assets.all()
|
|
||||||
else:
|
|
||||||
assets_field.queryset = Asset.objects.none()
|
|
||||||
|
|
||||||
def save(self, commit=True):
|
|
||||||
instance = super().save(commit=commit)
|
|
||||||
assets = self.cleaned_data['assets']
|
|
||||||
instance.assets.set(assets)
|
|
||||||
return instance
|
|
||||||
|
|
||||||
|
|
||||||
class GatewayForm(PasswordAndKeyAuthForm, OrgModelForm):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
password_field = self.fields.get('password')
|
|
||||||
password_field.help_text = _('Password should not contain special characters')
|
|
||||||
protocol_field = self.fields.get('protocol')
|
|
||||||
protocol_field.choices = [Gateway.PROTOCOL_CHOICES[0]]
|
|
||||||
|
|
||||||
def save(self, commit=True):
|
|
||||||
# Because we define custom field, so we need rewrite :method: `save`
|
|
||||||
instance = super().save()
|
|
||||||
password = self.cleaned_data.get('password')
|
|
||||||
private_key, public_key = super().gen_keys()
|
|
||||||
instance.set_auth(password=password, private_key=private_key)
|
|
||||||
return instance
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Gateway
|
|
||||||
fields = [
|
|
||||||
'name', 'ip', 'port', 'username', 'protocol', 'domain', 'password',
|
|
||||||
'private_key_file', 'is_active', 'comment',
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
|
|
||||||
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
from django import forms
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from ..models import Label, Asset
|
|
||||||
|
|
||||||
__all__ = ['LabelForm']
|
|
||||||
|
|
||||||
|
|
||||||
class LabelForm(forms.ModelForm):
|
|
||||||
assets = forms.ModelMultipleChoiceField(
|
|
||||||
queryset=Asset.objects.all(), label=_('Asset'), required=False,
|
|
||||||
widget=forms.SelectMultiple(
|
|
||||||
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Label
|
|
||||||
fields = ['name', 'value', 'assets']
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
if kwargs.get('instance', None):
|
|
||||||
initial = kwargs.get('initial', {})
|
|
||||||
initial['assets'] = kwargs['instance'].assets.all()
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# 前端渲染优化, 防止过多资产
|
|
||||||
assets_field = self.fields.get('assets')
|
|
||||||
if not self.data:
|
|
||||||
instance = kwargs.get('instance')
|
|
||||||
if instance:
|
|
||||||
assets_field.queryset = instance.assets.all()
|
|
||||||
else:
|
|
||||||
assets_field.queryset = Asset.objects.none()
|
|
||||||
|
|
||||||
def save(self, commit=True):
|
|
||||||
label = super().save(commit=commit)
|
|
||||||
assets = self.cleaned_data['assets']
|
|
||||||
label.assets.set(assets)
|
|
||||||
return label
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
from django import forms
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from common.utils import validate_ssh_private_key, ssh_pubkey_gen, get_logger
|
|
||||||
from orgs.mixins import OrgModelForm
|
|
||||||
from ..models import AdminUser, SystemUser
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
|
||||||
__all__ = [
|
|
||||||
'FileForm', 'SystemUserForm', 'AdminUserForm', 'PasswordAndKeyAuthForm',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class FileForm(forms.Form):
|
|
||||||
file = forms.FileField()
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordAndKeyAuthForm(forms.ModelForm):
|
|
||||||
# Form field name can not start with `_`, so redefine it,
|
|
||||||
password = forms.CharField(
|
|
||||||
widget=forms.PasswordInput, max_length=128,
|
|
||||||
strip=True, required=False,
|
|
||||||
help_text=_('Password or private key passphrase'),
|
|
||||||
label=_("Password"),
|
|
||||||
)
|
|
||||||
# Need use upload private key file except paste private key content
|
|
||||||
private_key_file = forms.FileField(required=False, label=_("Private key"))
|
|
||||||
|
|
||||||
def clean_private_key_file(self):
|
|
||||||
private_key_file = self.cleaned_data['private_key_file']
|
|
||||||
password = self.cleaned_data['password']
|
|
||||||
|
|
||||||
if private_key_file:
|
|
||||||
key_string = private_key_file.read()
|
|
||||||
private_key_file.seek(0)
|
|
||||||
if not validate_ssh_private_key(key_string, password):
|
|
||||||
raise forms.ValidationError(_('Invalid private key'))
|
|
||||||
return private_key_file
|
|
||||||
|
|
||||||
def validate_password_key(self):
|
|
||||||
password = self.cleaned_data['password']
|
|
||||||
private_key_file = self.cleaned_data.get('private_key_file', '')
|
|
||||||
|
|
||||||
if not password and not private_key_file:
|
|
||||||
raise forms.ValidationError(_(
|
|
||||||
'Password and private key file must be input one'
|
|
||||||
))
|
|
||||||
|
|
||||||
def gen_keys(self):
|
|
||||||
password = self.cleaned_data.get('password', '') or None
|
|
||||||
private_key_file = self.cleaned_data['private_key_file']
|
|
||||||
public_key = private_key = None
|
|
||||||
|
|
||||||
if private_key_file:
|
|
||||||
private_key = private_key_file.read().strip().decode('utf-8')
|
|
||||||
public_key = ssh_pubkey_gen(private_key=private_key, password=password)
|
|
||||||
return private_key, public_key
|
|
||||||
|
|
||||||
|
|
||||||
class AdminUserForm(PasswordAndKeyAuthForm):
|
|
||||||
def save(self, commit=True):
|
|
||||||
# Because we define custom field, so we need rewrite :method: `save`
|
|
||||||
admin_user = super().save(commit=commit)
|
|
||||||
password = self.cleaned_data.get('password', '') or None
|
|
||||||
private_key, public_key = super().gen_keys()
|
|
||||||
admin_user.set_auth(password=password, public_key=public_key, private_key=private_key)
|
|
||||||
return admin_user
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
super().clean()
|
|
||||||
if not self.instance:
|
|
||||||
super().validate_password_key()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = AdminUser
|
|
||||||
fields = ['name', 'username', 'password', 'private_key_file', 'comment']
|
|
||||||
widgets = {
|
|
||||||
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
|
|
||||||
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class SystemUserForm(OrgModelForm, PasswordAndKeyAuthForm):
|
|
||||||
# Admin user assets define, let user select, save it in form not in view
|
|
||||||
auto_generate_key = forms.BooleanField(initial=True, required=False)
|
|
||||||
|
|
||||||
def save(self, commit=True):
|
|
||||||
# Because we define custom field, so we need rewrite :method: `save`
|
|
||||||
system_user = super().save()
|
|
||||||
password = self.cleaned_data.get('password', '') or None
|
|
||||||
login_mode = self.cleaned_data.get('login_mode', '') or None
|
|
||||||
protocol = self.cleaned_data.get('protocol') or None
|
|
||||||
auto_generate_key = self.cleaned_data.get('auto_generate_key', False)
|
|
||||||
private_key, public_key = super().gen_keys()
|
|
||||||
|
|
||||||
if login_mode == SystemUser.LOGIN_MANUAL or \
|
|
||||||
protocol in [SystemUser.PROTOCOL_RDP,
|
|
||||||
SystemUser.PROTOCOL_TELNET,
|
|
||||||
SystemUser.PROTOCOL_VNC]:
|
|
||||||
system_user.auto_push = 0
|
|
||||||
auto_generate_key = False
|
|
||||||
system_user.save()
|
|
||||||
|
|
||||||
if auto_generate_key:
|
|
||||||
logger.info('Auto generate key and set system user auth')
|
|
||||||
system_user.auto_gen_auth()
|
|
||||||
else:
|
|
||||||
system_user.set_auth(password=password, private_key=private_key,
|
|
||||||
public_key=public_key)
|
|
||||||
|
|
||||||
return system_user
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
super().clean()
|
|
||||||
auto_generate = self.cleaned_data.get('auto_generate_key')
|
|
||||||
if not self.instance and not auto_generate:
|
|
||||||
super().validate_password_key()
|
|
||||||
|
|
||||||
def clean_username(self):
|
|
||||||
username = self.data.get('username')
|
|
||||||
login_mode = self.data.get('login_mode')
|
|
||||||
protocol = self.data.get('protocol')
|
|
||||||
|
|
||||||
if username:
|
|
||||||
return username
|
|
||||||
if login_mode == SystemUser.LOGIN_AUTO and \
|
|
||||||
protocol != SystemUser.PROTOCOL_VNC:
|
|
||||||
msg = _('* Automatic login mode must fill in the username.')
|
|
||||||
raise forms.ValidationError(msg)
|
|
||||||
return username
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = SystemUser
|
|
||||||
fields = [
|
|
||||||
'name', 'username', 'protocol', 'auto_generate_key',
|
|
||||||
'password', 'private_key_file', 'auto_push', 'sudo',
|
|
||||||
'comment', 'shell', 'priority', 'login_mode', 'cmd_filters',
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
|
|
||||||
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
|
|
||||||
'cmd_filters': forms.SelectMultiple(attrs={
|
|
||||||
'class': 'select2', 'data-placeholder': _('Command filter')
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
help_texts = {
|
|
||||||
'auto_push': _('Auto push system user to asset'),
|
|
||||||
'priority': _('1-100, High level will be using login asset as default, '
|
|
||||||
'if user was granted more than 2 system user'),
|
|
||||||
'login_mode': _('If you choose manual login mode, you do not '
|
|
||||||
'need to fill in the username and password.')
|
|
||||||
}
|
|
||||||
@@ -6,11 +6,10 @@
|
|||||||
|
|
||||||
Other module of this app shouldn't connect with other app.
|
Other module of this app shouldn't connect with other app.
|
||||||
|
|
||||||
:copyright: (c) 2014-2018 by Jumpserver Team.
|
:copyright: (c) 2014-2018 by JumpServer Team.
|
||||||
:license: GPL v2, see LICENSE for more details.
|
:license: GPL v2, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
from common.permissions import AdminUserRequiredMixin
|
|
||||||
from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser
|
from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser
|
||||||
from users.models import User, UserGroup
|
from users.models import User, UserGroup
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-01-05 10:07
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='adminuser',
|
|
||||||
options={'ordering': ['name'], 'verbose_name': 'Admin user'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='asset',
|
|
||||||
options={'verbose_name': 'Asset'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='assetgroup',
|
|
||||||
options={'ordering': ['name'], 'verbose_name': 'Asset group'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='cluster',
|
|
||||||
options={'ordering': ['name'], 'verbose_name': 'Cluster'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='systemuser',
|
|
||||||
options={'ordering': ['name'], 'verbose_name': 'System user'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-02-28 10:16
|
||||||
|
|
||||||
|
import assets.models.asset
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('assets', '0002_auto_20180105_1807'), ('assets', '0003_auto_20180109_2331'), ('assets', '0004_auto_20180125_1218'), ('assets', '0005_auto_20180126_1637'), ('assets', '0006_auto_20180130_1502'), ('assets', '0007_auto_20180225_1815'), ('assets', '0008_auto_20180306_1804'), ('assets', '0009_auto_20180307_1212')]
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='adminuser',
|
||||||
|
options={'ordering': ['name'], 'verbose_name': 'Admin user'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='asset',
|
||||||
|
options={'verbose_name': 'Asset'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='assetgroup',
|
||||||
|
options={'ordering': ['name'], 'verbose_name': 'Asset group'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='cluster',
|
||||||
|
options={'ordering': ['name'], 'verbose_name': 'Cluster'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='systemuser',
|
||||||
|
options={'ordering': ['name'], 'verbose_name': 'System user'},
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='asset',
|
||||||
|
name='cluster',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='assetgroup',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Label',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||||
|
('value', models.CharField(max_length=128, verbose_name='Value')),
|
||||||
|
('category', models.CharField(choices=[('S', 'System'), ('U', 'User')], default='U', max_length=128, verbose_name='Category')),
|
||||||
|
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
|
||||||
|
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
|
||||||
|
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'db_table': 'assets_label',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='label',
|
||||||
|
unique_together={('name', 'value')},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='asset',
|
||||||
|
name='labels',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='assets', to='assets.Label', verbose_name='Labels'),
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='asset',
|
||||||
|
name='cabinet_no',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='asset',
|
||||||
|
name='cabinet_pos',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='asset',
|
||||||
|
name='env',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='asset',
|
||||||
|
name='remote_card_ip',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='asset',
|
||||||
|
name='status',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='asset',
|
||||||
|
name='type',
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Node',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
|
('key', models.CharField(max_length=64, unique=True, verbose_name='Key')),
|
||||||
|
('value', models.CharField(max_length=128, verbose_name='Value')),
|
||||||
|
('child_mark', models.IntegerField(default=0)),
|
||||||
|
('date_create', models.DateTimeField(auto_now_add=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='asset',
|
||||||
|
name='groups',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='cluster',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='asset',
|
||||||
|
name='admin_user',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='assets.AdminUser', verbose_name='Admin user'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='protocol',
|
||||||
|
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp')], default='ssh', max_length=16, verbose_name='Protocol'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='asset',
|
||||||
|
name='nodes',
|
||||||
|
field=models.ManyToManyField(default=assets.models.asset.default_node, related_name='assets', to='assets.Node', verbose_name='Nodes'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='nodes',
|
||||||
|
field=models.ManyToManyField(blank=True, to='assets.Node', verbose_name='Nodes'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(max_length=128, verbose_name='Username'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='asset',
|
||||||
|
name='platform',
|
||||||
|
field=models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Other', 'Other')], default='Linux', max_length=128, verbose_name='Platform'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='created_by',
|
||||||
|
field=models.CharField(max_length=128, null=True, verbose_name='Created by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(max_length=128, verbose_name='Username'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-01-09 15:31
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import assets.models.asset
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0002_auto_20180105_1807'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='asset',
|
|
||||||
name='cluster',
|
|
||||||
field=models.ForeignKey(default=assets.models.asset.default_cluster, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='assets', to='assets.Cluster', verbose_name='Cluster'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-01-25 04:18
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0003_auto_20180109_2331'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='assetgroup',
|
|
||||||
name='created_by',
|
|
||||||
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-01-26 08:37
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0004_auto_20180125_1218'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Label',
|
|
||||||
fields=[
|
|
||||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
|
||||||
('name', models.CharField(max_length=128, verbose_name='Name')),
|
|
||||||
('value', models.CharField(max_length=128, verbose_name='Value')),
|
|
||||||
('category', models.CharField(choices=[('S', 'System'), ('U', 'User')], default='U', max_length=128, verbose_name='Category')),
|
|
||||||
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
|
|
||||||
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
|
|
||||||
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'db_table': 'assets_label',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='label',
|
|
||||||
unique_together=set([('name', 'value')]),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='asset',
|
|
||||||
name='labels',
|
|
||||||
field=models.ManyToManyField(blank=True, related_name='assets', to='assets.Label', verbose_name='Labels'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-01-30 07:02
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0005_auto_20180126_1637'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='asset',
|
|
||||||
name='cabinet_no',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='asset',
|
|
||||||
name='cabinet_pos',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='asset',
|
|
||||||
name='env',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='asset',
|
|
||||||
name='remote_card_ip',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='asset',
|
|
||||||
name='status',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='asset',
|
|
||||||
name='type',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-02-25 10:15
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import assets.models.asset
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0006_auto_20180130_1502'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Node',
|
|
||||||
fields=[
|
|
||||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
|
||||||
('key', models.CharField(max_length=64, unique=True, verbose_name='Key')),
|
|
||||||
('value', models.CharField(max_length=128, unique=True, verbose_name='Value')),
|
|
||||||
('child_mark', models.IntegerField(default=0)),
|
|
||||||
('date_create', models.DateTimeField(auto_now_add=True)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='asset',
|
|
||||||
name='cluster',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='asset',
|
|
||||||
name='groups',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='cluster',
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='asset',
|
|
||||||
name='admin_user',
|
|
||||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='assets.AdminUser', verbose_name='Admin user'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='protocol',
|
|
||||||
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp')], default='ssh', max_length=16, verbose_name='Protocol'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='asset',
|
|
||||||
name='nodes',
|
|
||||||
field=models.ManyToManyField(default=assets.models.asset.default_node, related_name='assets', to='assets.Node', verbose_name='Nodes'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='nodes',
|
|
||||||
field=models.ManyToManyField(blank=True, to='assets.Node', verbose_name='Nodes'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-03-06 10:04
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0007_auto_20180225_1815'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='adminuser',
|
|
||||||
name='created_by',
|
|
||||||
field=models.CharField(max_length=128, null=True, verbose_name='Created by'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='adminuser',
|
|
||||||
name='username',
|
|
||||||
field=models.CharField(max_length=128, verbose_name='Username'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='asset',
|
|
||||||
name='platform',
|
|
||||||
field=models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Other', 'Other')], default='Linux', max_length=128, verbose_name='Platform'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='created_by',
|
|
||||||
field=models.CharField(max_length=128, null=True, verbose_name='Created by'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='username',
|
|
||||||
field=models.CharField(max_length=128, verbose_name='Username'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-03-07 04:12
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0008_auto_20180306_1804'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='node',
|
|
||||||
name='value',
|
|
||||||
field=models.CharField(max_length=128, verbose_name='Value'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-03-07 09:49
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0009_auto_20180307_1212'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='node',
|
|
||||||
name='value',
|
|
||||||
field=models.CharField(max_length=128, unique=True, verbose_name='Value'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-02-28 10:16
|
||||||
|
|
||||||
|
import assets.models.utils
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
# Functions from the following migrations need manual copying.
|
||||||
|
# Move them and any dependencies into this file, then update the
|
||||||
|
# RunPython operations to refer to the local versions:
|
||||||
|
# assets.migrations.0017_auto_20180702_1415
|
||||||
|
|
||||||
|
def migrate_win_to_ssh_protocol(apps, schema_editor):
|
||||||
|
asset_model = apps.get_model("assets", "Asset")
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
asset_model.objects.using(db_alias).filter(platform__startswith='Win').update(protocol='rdp')
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('assets', '0010_auto_20180307_1749'), ('assets', '0011_auto_20180326_0957'), ('assets', '0012_auto_20180404_1302'), ('assets', '0013_auto_20180411_1135'), ('assets', '0014_auto_20180427_1245'), ('assets', '0015_auto_20180510_1235'), ('assets', '0016_auto_20180511_1203'), ('assets', '0017_auto_20180702_1415'), ('assets', '0018_auto_20180807_1116'), ('assets', '0019_auto_20180816_1320')]
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0009_auto_20180307_1212'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='node',
|
||||||
|
name='value',
|
||||||
|
field=models.CharField(max_length=128, unique=True, verbose_name='Value'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Domain',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
|
||||||
|
('comment', models.TextField(blank=True, verbose_name='Comment')),
|
||||||
|
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Gateway',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
|
||||||
|
('username', models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username')),
|
||||||
|
('_password', models.CharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
||||||
|
('_private_key', models.TextField(blank=True, max_length=4096, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key')),
|
||||||
|
('_public_key', models.TextField(blank=True, max_length=4096, verbose_name='SSH public key')),
|
||||||
|
('date_created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('date_updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
||||||
|
('ip', models.GenericIPAddressField(db_index=True, verbose_name='IP')),
|
||||||
|
('port', models.IntegerField(default=22, verbose_name='Port')),
|
||||||
|
('protocol', models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp')], default='ssh', max_length=16, verbose_name='Protocol')),
|
||||||
|
('comment', models.CharField(blank=True, max_length=128, null=True, verbose_name='Comment')),
|
||||||
|
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
|
||||||
|
('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Domain', verbose_name='Domain')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='asset',
|
||||||
|
name='domain',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assets', to='assets.Domain', verbose_name='Domain'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='assets',
|
||||||
|
field=models.ManyToManyField(blank=True, to='assets.Asset', verbose_name='Assets'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='sudo',
|
||||||
|
field=models.TextField(default='/bin/whoami', verbose_name='Sudo'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_-]*$', 'Special char not allowed')], verbose_name='Username'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_-]*$', 'Special char not allowed')], verbose_name='Username'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='node',
|
||||||
|
name='value',
|
||||||
|
field=models.CharField(max_length=128, verbose_name='Value'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='asset',
|
||||||
|
name='protocol',
|
||||||
|
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)')], default='ssh', max_length=128, verbose_name='Protocol'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='login_mode',
|
||||||
|
field=models.CharField(choices=[('auto', 'Automatic login'), ('manual', 'Manually login')], default='auto', max_length=10, verbose_name='Login mode'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='asset',
|
||||||
|
name='platform',
|
||||||
|
field=models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Windows2016', 'Windows(2016)'), ('Other', 'Other')], default='Linux', max_length=128, verbose_name='Platform'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='protocol',
|
||||||
|
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)')], default='ssh', max_length=16, verbose_name='Protocol'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=migrate_win_to_ssh_protocol,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='org_id',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='asset',
|
||||||
|
name='org_id',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='domain',
|
||||||
|
name='org_id',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='gateway',
|
||||||
|
name='org_id',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='label',
|
||||||
|
name='org_id',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='node',
|
||||||
|
name='org_id',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='org_id',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=128, verbose_name='Name'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='asset',
|
||||||
|
name='hostname',
|
||||||
|
field=models.CharField(max_length=128, verbose_name='Hostname'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='gateway',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=128, verbose_name='Name'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=128, verbose_name='Name'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='adminuser',
|
||||||
|
unique_together={('name', 'org_id')},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='asset',
|
||||||
|
name='cpu_vcpus',
|
||||||
|
field=models.IntegerField(null=True, verbose_name='CPU vcpus'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='asset',
|
||||||
|
unique_together={('org_id', 'hostname')},
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='gateway',
|
||||||
|
unique_together={('name', 'org_id')},
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='systemuser',
|
||||||
|
unique_together={('name', 'org_id')},
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='label',
|
||||||
|
unique_together={('name', 'value', 'org_id')},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-03-26 01:57
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import assets.models.utils
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0010_auto_20180307_1749'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Domain',
|
|
||||||
fields=[
|
|
||||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
|
||||||
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
|
|
||||||
('comment', models.TextField(blank=True, verbose_name='Comment')),
|
|
||||||
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Gateway',
|
|
||||||
fields=[
|
|
||||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
|
||||||
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
|
|
||||||
('username', models.CharField(max_length=128, verbose_name='Username')),
|
|
||||||
('_password', models.CharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
|
||||||
('_private_key', models.TextField(blank=True, max_length=4096, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key')),
|
|
||||||
('_public_key', models.TextField(blank=True, max_length=4096, verbose_name='SSH public key')),
|
|
||||||
('date_created', models.DateTimeField(auto_now_add=True)),
|
|
||||||
('date_updated', models.DateTimeField(auto_now=True)),
|
|
||||||
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
|
||||||
('ip', models.GenericIPAddressField(db_index=True, verbose_name='IP')),
|
|
||||||
('port', models.IntegerField(default=22, verbose_name='Port')),
|
|
||||||
('protocol', models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp')], default='ssh', max_length=16, verbose_name='Protocol')),
|
|
||||||
('comment', models.CharField(blank=True, max_length=128, null=True, verbose_name='Comment')),
|
|
||||||
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
|
|
||||||
('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Domain', verbose_name='Domain')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='asset',
|
|
||||||
name='domain',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='assets', to='assets.Domain', verbose_name='Domain'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-04-04 05:02
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0011_auto_20180326_0957'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='asset',
|
|
||||||
name='domain',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assets', to='assets.Domain', verbose_name='Domain'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-04-11 03:35
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0012_auto_20180404_1302'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='assets',
|
|
||||||
field=models.ManyToManyField(blank=True, to='assets.Asset', verbose_name='Assets'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='sudo',
|
|
||||||
field=models.TextField(default='/bin/whoami', verbose_name='Sudo'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-04-27 04:45
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import django.core.validators
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0013_auto_20180411_1135'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='adminuser',
|
|
||||||
name='username',
|
|
||||||
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_-]*$', 'Special char not allowed')], verbose_name='Username'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='gateway',
|
|
||||||
name='username',
|
|
||||||
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_-]*$', 'Special char not allowed')], verbose_name='Username'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='username',
|
|
||||||
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_-]*$', 'Special char not allowed')], verbose_name='Username'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-05-10 04:35
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import django.core.validators
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0014_auto_20180427_1245'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='adminuser',
|
|
||||||
name='username',
|
|
||||||
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='gateway',
|
|
||||||
name='username',
|
|
||||||
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='username',
|
|
||||||
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-05-11 04:03
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0015_auto_20180510_1235'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='node',
|
|
||||||
name='value',
|
|
||||||
field=models.CharField(max_length=128, verbose_name='Value'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11 on 2018-07-02 06:15
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import django.core.validators
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
def migrate_win_to_ssh_protocol(apps, schema_editor):
|
|
||||||
asset_model = apps.get_model("assets", "Asset")
|
|
||||||
db_alias = schema_editor.connection.alias
|
|
||||||
asset_model.objects.using(db_alias).filter(platform__startswith='Win').update(protocol='rdp')
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0016_auto_20180511_1203'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='asset',
|
|
||||||
name='protocol',
|
|
||||||
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)')], default='ssh', max_length=128, verbose_name='Protocol'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='login_mode',
|
|
||||||
field=models.CharField(choices=[('auto', 'Automatic login'), ('manual', 'Manually login')], default='auto', max_length=10, verbose_name='Login mode'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='adminuser',
|
|
||||||
name='username',
|
|
||||||
field=models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='asset',
|
|
||||||
name='platform',
|
|
||||||
field=models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Windows2016', 'Windows(2016)'), ('Other', 'Other')], default='Linux', max_length=128, verbose_name='Platform'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='gateway',
|
|
||||||
name='username',
|
|
||||||
field=models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='protocol',
|
|
||||||
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)')], default='ssh', max_length=16, verbose_name='Protocol'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='username',
|
|
||||||
field=models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
|
||||||
),
|
|
||||||
migrations.RunPython(migrate_win_to_ssh_protocol),
|
|
||||||
]
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
# Generated by Django 2.0.7 on 2018-08-07 03:16
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0017_auto_20180702_1415'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='adminuser',
|
|
||||||
name='org_id',
|
|
||||||
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='asset',
|
|
||||||
name='org_id',
|
|
||||||
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='domain',
|
|
||||||
name='org_id',
|
|
||||||
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='gateway',
|
|
||||||
name='org_id',
|
|
||||||
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='label',
|
|
||||||
name='org_id',
|
|
||||||
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='node',
|
|
||||||
name='org_id',
|
|
||||||
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='org_id',
|
|
||||||
field=models.CharField(blank=True, default=None, max_length=36, null=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='adminuser',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(max_length=128, verbose_name='Name'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='asset',
|
|
||||||
name='hostname',
|
|
||||||
field=models.CharField(max_length=128, verbose_name='Hostname'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='gateway',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(max_length=128, verbose_name='Name'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='systemuser',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(max_length=128, verbose_name='Name'),
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='adminuser',
|
|
||||||
unique_together={('name', 'org_id')},
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='asset',
|
|
||||||
unique_together={('org_id', 'hostname')},
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='gateway',
|
|
||||||
unique_together={('name', 'org_id')},
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='systemuser',
|
|
||||||
unique_together={('name', 'org_id')},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# Generated by Django 2.0.7 on 2018-08-16 05:20
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0018_auto_20180807_1116'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='asset',
|
|
||||||
name='cpu_vcpus',
|
|
||||||
field=models.IntegerField(null=True, verbose_name='CPU vcpus'),
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='label',
|
|
||||||
unique_together={('name', 'value', 'org_id')},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
21
apps/assets/migrations/0025_auto_20190221_1902.py
Normal file
21
apps/assets/migrations/0025_auto_20190221_1902.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-02-21 11:02
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0024_auto_20181219_1614'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='commandfilter',
|
||||||
|
options={'verbose_name': 'Command filter'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='commandfilterrule',
|
||||||
|
options={'ordering': ('-priority', 'action'), 'verbose_name': 'Command filter rule'},
|
||||||
|
),
|
||||||
|
]
|
||||||
43
apps/assets/migrations/0026_auto_20190325_2035.py
Normal file
43
apps/assets/migrations/0026_auto_20190325_2035.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-03-25 12:35
|
||||||
|
|
||||||
|
import assets.models.utils
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0025_auto_20190221_1902'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AuthBook',
|
||||||
|
fields=[
|
||||||
|
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||||
|
('username', models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username')),
|
||||||
|
('_password', models.CharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
||||||
|
('_private_key', models.TextField(blank=True, max_length=4096, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key')),
|
||||||
|
('_public_key', models.TextField(blank=True, max_length=4096, verbose_name='SSH public key')),
|
||||||
|
('comment', models.TextField(blank=True, verbose_name='Comment')),
|
||||||
|
('date_created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('date_updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
||||||
|
('is_latest', models.BooleanField(default=False, verbose_name='Latest version')),
|
||||||
|
('version', models.IntegerField(default=1, verbose_name='Version')),
|
||||||
|
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Asset', verbose_name='Asset')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'AuthBook',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='node',
|
||||||
|
options={'ordering': ['key'], 'verbose_name': 'Node'},
|
||||||
|
),
|
||||||
|
]
|
||||||
23
apps/assets/migrations/0027_auto_20190521_1703.py
Normal file
23
apps/assets/migrations/0027_auto_20190521_1703.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-05-21 09:03
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0026_auto_20190325_2035'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='asset',
|
||||||
|
name='ip',
|
||||||
|
field=models.CharField(db_index=True, max_length=128, verbose_name='IP'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='asset',
|
||||||
|
name='public_ip',
|
||||||
|
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Public IP'),
|
||||||
|
),
|
||||||
|
]
|
||||||
29
apps/assets/migrations/0028_protocol.py
Normal file
29
apps/assets/migrations/0028_protocol.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-05-22 02:58
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0027_auto_20190521_1703'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Protocol',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)'), ('vnc', 'vnc')], default='ssh', max_length=16, verbose_name='Name')),
|
||||||
|
('port', models.IntegerField(default=22, validators=[django.core.validators.MaxValueValidator(65535), django.core.validators.MinValueValidator(1)], verbose_name='Port')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='asset',
|
||||||
|
name='protocols',
|
||||||
|
field=models.ManyToManyField(to='assets.Protocol',
|
||||||
|
verbose_name='Protocol'),
|
||||||
|
),
|
||||||
|
]
|
||||||
13
apps/assets/migrations/0029_auto_20190522_1114.py
Normal file
13
apps/assets/migrations/0029_auto_20190522_1114.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-05-22 03:14
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0028_protocol'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
]
|
||||||
19
apps/assets/migrations/0030_auto_20190619_1135.py
Normal file
19
apps/assets/migrations/0030_auto_20190619_1135.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-06-19 03:35
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0029_auto_20190522_1114'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='asset',
|
||||||
|
name='admin_user',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='assets', to='assets.AdminUser', verbose_name='Admin user'),
|
||||||
|
),
|
||||||
|
]
|
||||||
53
apps/assets/migrations/0031_auto_20190621_1332.py
Normal file
53
apps/assets/migrations/0031_auto_20190621_1332.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-06-21 05:32
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0030_auto_20190619_1135'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='date_created',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, verbose_name='Date created'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='date_updated',
|
||||||
|
field=models.DateTimeField(auto_now=True, verbose_name='Date updated'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authbook',
|
||||||
|
name='date_created',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, verbose_name='Date created'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authbook',
|
||||||
|
name='date_updated',
|
||||||
|
field=models.DateTimeField(auto_now=True, verbose_name='Date updated'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='gateway',
|
||||||
|
name='date_created',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, verbose_name='Date created'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='gateway',
|
||||||
|
name='date_updated',
|
||||||
|
field=models.DateTimeField(auto_now=True, verbose_name='Date updated'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='date_created',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, verbose_name='Date created'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='date_updated',
|
||||||
|
field=models.DateTimeField(auto_now=True, verbose_name='Date updated'),
|
||||||
|
),
|
||||||
|
]
|
||||||
75
apps/assets/migrations/0032_auto_20190624_2108.py
Normal file
75
apps/assets/migrations/0032_auto_20190624_2108.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-06-24 13:08
|
||||||
|
|
||||||
|
import assets.models.utils
|
||||||
|
import common.fields.model
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0031_auto_20190621_1332'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='_password',
|
||||||
|
field=common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='_private_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='_public_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authbook',
|
||||||
|
name='_password',
|
||||||
|
field=common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authbook',
|
||||||
|
name='_private_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authbook',
|
||||||
|
name='_public_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='gateway',
|
||||||
|
name='_password',
|
||||||
|
field=common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='gateway',
|
||||||
|
name='_private_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='gateway',
|
||||||
|
name='_public_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='_password',
|
||||||
|
field=common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='_private_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='_public_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
|
||||||
|
),
|
||||||
|
]
|
||||||
73
apps/assets/migrations/0033_auto_20190624_2108.py
Normal file
73
apps/assets/migrations/0033_auto_20190624_2108.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-06-24 13:08
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0032_auto_20190624_2108'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='adminuser',
|
||||||
|
old_name='_private_key',
|
||||||
|
new_name='private_key',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='adminuser',
|
||||||
|
old_name='_public_key',
|
||||||
|
new_name='public_key',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='authbook',
|
||||||
|
old_name='_private_key',
|
||||||
|
new_name='private_key',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='authbook',
|
||||||
|
old_name='_public_key',
|
||||||
|
new_name='public_key',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='gateway',
|
||||||
|
old_name='_private_key',
|
||||||
|
new_name='private_key',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='gateway',
|
||||||
|
old_name='_public_key',
|
||||||
|
new_name='public_key',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='systemuser',
|
||||||
|
old_name='_private_key',
|
||||||
|
new_name='private_key',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='systemuser',
|
||||||
|
old_name='_public_key',
|
||||||
|
new_name='public_key',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='adminuser',
|
||||||
|
old_name='_password',
|
||||||
|
new_name='password',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='authbook',
|
||||||
|
old_name='_password',
|
||||||
|
new_name='password',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='gateway',
|
||||||
|
old_name='_password',
|
||||||
|
new_name='password',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='systemuser',
|
||||||
|
old_name='_password',
|
||||||
|
new_name='password',
|
||||||
|
),
|
||||||
|
]
|
||||||
39
apps/assets/migrations/0034_auto_20190705_1348.py
Normal file
39
apps/assets/migrations/0034_auto_20190705_1348.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-07-05 05:48
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db.models import F
|
||||||
|
from django.db.models import CharField, Value as V
|
||||||
|
from django.db.models.functions import Concat
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_assets_protocol(apps, schema_editor):
|
||||||
|
asset_model = apps.get_model("assets", "Asset")
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
assets = asset_model.objects.using(db_alias).all().annotate(
|
||||||
|
protocols_new=Concat(
|
||||||
|
'protocol', V('/'), 'port',
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assets.update(protocols=F('protocols_new'))
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0033_auto_20190624_2108'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='asset',
|
||||||
|
name='protocols',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='asset',
|
||||||
|
name='protocols',
|
||||||
|
field=CharField(blank=True, default='ssh/22', max_length=128, verbose_name='Protocols'),
|
||||||
|
),
|
||||||
|
migrations.RunPython(migrate_assets_protocol),
|
||||||
|
migrations.DeleteModel(name='Protocol'),
|
||||||
|
]
|
||||||
34
apps/assets/migrations/0035_auto_20190711_2018.py
Normal file
34
apps/assets/migrations/0035_auto_20190711_2018.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-07-11 12:18
|
||||||
|
|
||||||
|
import common.fields.model
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0034_auto_20190705_1348'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='private_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authbook',
|
||||||
|
name='private_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='gateway',
|
||||||
|
name='private_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='private_key',
|
||||||
|
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
apps/assets/migrations/0036_auto_20190716_1535.py
Normal file
18
apps/assets/migrations/0036_auto_20190716_1535.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-07-16 07:35
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0035_auto_20190711_2018'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='commandfilter',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=64, unique=True, verbose_name='Name'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
apps/assets/migrations/0037_auto_20190724_2002.py
Normal file
18
apps/assets/migrations/0037_auto_20190724_2002.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-07-24 12:02
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0036_auto_20190716_1535'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='_become_pass',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=128),
|
||||||
|
),
|
||||||
|
]
|
||||||
23
apps/assets/migrations/0038_auto_20190911_1634.py
Normal file
23
apps/assets/migrations/0038_auto_20190911_1634.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-09-11 08:34
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0037_auto_20190724_2002'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='asset',
|
||||||
|
name='protocol',
|
||||||
|
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet'), ('vnc', 'vnc')], default='ssh', max_length=128, verbose_name='Protocol'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='protocol',
|
||||||
|
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet'), ('vnc', 'vnc')], default='ssh', max_length=16, verbose_name='Protocol'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
apps/assets/migrations/0039_authbook_is_active.py
Normal file
18
apps/assets/migrations/0039_authbook_is_active.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-09-17 12:22
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0038_auto_20190911_1634'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='authbook',
|
||||||
|
name='is_active',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='Is active'),
|
||||||
|
),
|
||||||
|
]
|
||||||
36
apps/assets/migrations/0040_auto_20190917_2056.py
Normal file
36
apps/assets/migrations/0040_auto_20190917_2056.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-09-17 12:56
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0039_authbook_is_active'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='adminuser',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(blank=True, db_index=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authbook',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(blank=True, db_index=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='gateway',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(blank=True, db_index=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='username',
|
||||||
|
field=models.CharField(blank=True, db_index=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
|
||||||
|
),
|
||||||
|
]
|
||||||
28
apps/assets/migrations/0041_gathereduser.py
Normal file
28
apps/assets/migrations/0041_gathereduser.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Generated by Django 2.1.7 on 2019-09-18 04:10
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0040_auto_20190917_2056'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='GatheredUser',
|
||||||
|
fields=[
|
||||||
|
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
|
('username', models.CharField(blank=True, db_index=True, max_length=32, verbose_name='Username')),
|
||||||
|
('present', models.BooleanField(default=True, verbose_name='Present')),
|
||||||
|
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
|
||||||
|
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||||
|
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Asset', verbose_name='Asset')),
|
||||||
|
],
|
||||||
|
options={'ordering': ['asset'], 'verbose_name': 'GatherUser'},
|
||||||
|
),
|
||||||
|
]
|
||||||
31
apps/assets/migrations/0042_favoriteasset.py
Normal file
31
apps/assets/migrations/0042_favoriteasset.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Generated by Django 2.2.5 on 2019-10-16 08:38
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('assets', '0041_gathereduser'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FavoriteAsset',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
|
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
|
||||||
|
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
|
||||||
|
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||||
|
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Asset')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'unique_together': {('user', 'asset')},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
23
apps/assets/migrations/0043_auto_20191114_1111.py
Normal file
23
apps/assets/migrations/0043_auto_20191114_1111.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 2.2.5 on 2019-11-14 03:11
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0042_favoriteasset'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='gathereduser',
|
||||||
|
name='date_last_login',
|
||||||
|
field=models.DateTimeField(null=True, verbose_name='Date last login'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='gathereduser',
|
||||||
|
name='ip_last_login',
|
||||||
|
field=models.CharField(default='', max_length=39, verbose_name='IP last login'),
|
||||||
|
),
|
||||||
|
]
|
||||||
48
apps/assets/migrations/0044_platform.py
Normal file
48
apps/assets/migrations/0044_platform.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Generated by Django 2.2.7 on 2019-12-06 07:26
|
||||||
|
|
||||||
|
import common.fields.model
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
def create_internal_platform(apps, schema_editor):
|
||||||
|
model = apps.get_model("assets", "Platform")
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
type_platforms = (
|
||||||
|
('Linux', 'Linux', None),
|
||||||
|
('Unix', 'Unix', None),
|
||||||
|
('MacOS', 'MacOS', None),
|
||||||
|
('BSD', 'BSD', None),
|
||||||
|
('Windows', 'Windows', None),
|
||||||
|
('Windows2016', 'Windows', {'security': 'tls'}),
|
||||||
|
('Other', 'Other', None),
|
||||||
|
)
|
||||||
|
for name, base, meta in type_platforms:
|
||||||
|
model.objects.using(db_alias).create(
|
||||||
|
name=name, base=base, internal=True, meta=meta
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0043_auto_20191114_1111'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Platform',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.SlugField(allow_unicode=True, unique=True, verbose_name='Name')),
|
||||||
|
('base', models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Other', 'Other')], default='Linux', max_length=16, verbose_name='Base')),
|
||||||
|
('charset', models.CharField(choices=[('utf8', 'UTF-8'), ('gbk', 'GBK')], default='utf8', max_length=8, verbose_name='Charset')),
|
||||||
|
('meta', common.fields.model.JsonDictTextField(blank=True, null=True, verbose_name='Meta')),
|
||||||
|
('internal', models.BooleanField(default=False, verbose_name='Internal')),
|
||||||
|
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Platform'
|
||||||
|
}
|
||||||
|
),
|
||||||
|
migrations.RunPython(create_internal_platform)
|
||||||
|
]
|
||||||
47
apps/assets/migrations/0045_auto_20191206_1607.py
Normal file
47
apps/assets/migrations/0045_auto_20191206_1607.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Generated by Django 2.2.7 on 2019-12-06 08:07
|
||||||
|
|
||||||
|
import assets.models.asset
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_platform_to_asset_type(apps, schema_editor):
|
||||||
|
asset_model = apps.get_model("assets", "Asset")
|
||||||
|
platform_model = apps.get_model("assets", "Platform")
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
|
||||||
|
platforms = platform_model.objects.using(db_alias).all()
|
||||||
|
platforms_map = {p.name: p for p in platforms}
|
||||||
|
for name, p in platforms_map.items():
|
||||||
|
asset_model.objects.using(db_alias)\
|
||||||
|
.filter(_platform=name)\
|
||||||
|
.update(platform=p)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0044_platform'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='asset',
|
||||||
|
old_name='platform',
|
||||||
|
new_name='_platform',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='asset',
|
||||||
|
name='platform',
|
||||||
|
field=models.ForeignKey(
|
||||||
|
default=assets.models.asset.Platform.default,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name='assets', to='assets.Platform',
|
||||||
|
verbose_name='Platform'),
|
||||||
|
),
|
||||||
|
migrations.RunPython(migrate_platform_to_asset_type),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='asset',
|
||||||
|
name='_platform',
|
||||||
|
),
|
||||||
|
]
|
||||||
18
apps/assets/migrations/0046_auto_20191218_1705.py
Normal file
18
apps/assets/migrations/0046_auto_20191218_1705.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 2.1.11 on 2019-12-18 09:05
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0045_auto_20191206_1607'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='systemuser',
|
||||||
|
name='protocol',
|
||||||
|
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet'), ('vnc', 'vnc'), ('mysql', 'mysql')], default='ssh', max_length=16, verbose_name='Protocol'),
|
||||||
|
),
|
||||||
|
]
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user