mirror of
https://github.com/altlinux/gpupdate.git
synced 2025-11-07 12:24:07 +03:00
Compare commits
364 Commits
astra
...
freeipa_ba
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbbc0b8289 | ||
|
|
d975cd2f10 | ||
|
|
cb9c70d6c1 | ||
|
|
99feb569a2 | ||
|
|
cd1a2fc042 | ||
|
|
5e918900c6 | ||
|
|
63e5ffc3f8 | ||
|
|
01d219cb8e | ||
|
|
6af54ff17d | ||
|
|
238d1f4784 | ||
|
|
b3253bd684 | ||
|
|
66b17be85b | ||
|
|
bea7fe9803 | ||
|
|
f36b362523 | ||
|
|
abfb756edb | ||
|
|
0578e21521 | ||
|
|
02bd6773aa | ||
|
|
927c3ceb2f | ||
|
|
a329f601f7 | ||
|
|
d4f12dacfa | ||
|
|
4d05358790 | ||
|
|
bc4bb96b03 | ||
|
|
5588be1daa | ||
|
|
bde48cbedf | ||
|
|
43d32c3882 | ||
|
|
0932d1da26 | ||
|
|
2a4375c6fb | ||
|
|
ba11149983 | ||
|
|
56008e7e1c | ||
|
|
9325c241ef | ||
|
|
3c0c722818 | ||
|
|
9424c2c8e8 | ||
|
|
9065352bb0 | ||
|
|
9034d4ba4c | ||
|
|
2e6c76337b | ||
|
|
a3398e0307 | ||
|
|
c7192773fd | ||
|
|
93bcac5f19 | ||
|
|
967687497c | ||
|
|
3797993209 | ||
|
|
04831c4dbd | ||
|
|
316c0881a9 | ||
|
|
22d0c87538 | ||
|
|
2c66ad9bc1 | ||
|
|
5fe0b6f418 | ||
|
|
829825060b | ||
|
|
463620ff25 | ||
|
|
ab632a8177 | ||
|
|
5c47ebb6c5 | ||
|
|
6a840674ca | ||
|
|
a6f6b021fa | ||
|
|
0f4066e0f0 | ||
|
|
030e69cb86 | ||
|
|
5f94fad90b | ||
|
|
156918ad3b | ||
|
|
6df5a5754f | ||
|
|
dda57ed179 | ||
|
|
99595c85d3 | ||
|
|
e25c5844a9 | ||
|
|
8e1a76552f | ||
|
|
1f6776912d | ||
|
|
3e889622b1 | ||
|
|
1c827d4533 | ||
|
|
ce660afcbd | ||
|
|
5b1a928291 | ||
|
|
a77a6e3c6f | ||
|
|
25a784fa2e | ||
|
|
6378c8c78b | ||
|
|
9ad7440c8b | ||
|
|
2a5642a76d | ||
| dbff83050b | |||
| ed1b2aa39e | |||
|
|
02701136c0 | ||
|
|
408d221c3d | ||
|
|
67a02a4623 | ||
| 7a0af6ab9b | |||
|
|
ce6e49443f | ||
|
|
433d312c0f | ||
|
|
2ec68dd95a | ||
|
|
3990f876a4 | ||
|
|
1f541914cd | ||
|
|
dc054008fd | ||
|
|
aa4bf9a7c8 | ||
|
|
99a6e85ccf | ||
|
|
79ef884f7d | ||
|
|
0abc5b0282 | ||
| dce52c4d9c | |||
|
|
4d5969a5fa | ||
|
|
3263a4cfd3 | ||
|
|
0685b9e492 | ||
|
|
7188c70a77 | ||
|
|
2edc5c326c | ||
|
|
39b92ce763 | ||
|
|
620010e1ab | ||
|
|
b87e8b218f | ||
|
|
df0f806035 | ||
|
|
7e8657939f | ||
|
|
a879d5ad52 | ||
|
|
c097769681 | ||
|
|
a85158ce3c | ||
|
|
f79b283574 | ||
|
|
b791f3d5eb | ||
|
|
b16460309a | ||
|
|
40cf97989e | ||
|
|
71eeb1d5a0 | ||
|
|
f45fc7092d | ||
|
|
e537b3846a | ||
|
|
64581f60d2 | ||
|
|
1436ee201e | ||
|
|
0051e001a8 | ||
|
|
d4eb4263fa | ||
|
|
a99ed2db2a | ||
|
|
8bc4375339 | ||
|
|
f24038b288 | ||
|
|
96ec5cc690 | ||
|
|
e88278fb47 | ||
|
|
4be89029aa | ||
|
|
b981744d75 | ||
|
|
760a1d8b90 | ||
|
|
cb035fd56e | ||
|
|
e56293e768 | ||
|
|
0c0f7d223b | ||
|
|
3c09737aa7 | ||
|
|
0027b5aa96 | ||
| df8984dd65 | |||
|
|
5f8c75e27c | ||
|
|
03b031734a | ||
| 77c0d60b7d | |||
| 51b744f94b | |||
| cdd9d84037 | |||
|
|
4de1946e32 | ||
|
|
73759857b3 | ||
|
|
b3e222ae55 | ||
|
|
8a2c9554f7 | ||
|
|
862b3b358b | ||
|
|
0d2c70da35 | ||
|
|
2953e4b0c6 | ||
|
|
c8585ac932 | ||
|
|
981d883ed0 | ||
|
|
3ddd9462ea | ||
|
|
ab79f169e8 | ||
| 5a3ba30910 | |||
|
|
d554b1fdf9 | ||
|
|
3960c4b094 | ||
|
|
5f178651f7 | ||
|
|
674e1d176b | ||
|
|
afe6ef04d4 | ||
|
|
fa98fef5a3 | ||
|
|
c6c34accff | ||
|
|
dba6a58c6a | ||
|
|
a02969c686 | ||
|
|
e040bbbd69 | ||
|
|
1775bfa08c | ||
|
|
165f4bfc83 | ||
|
|
316f5d1e49 | ||
|
|
150f3441fd | ||
|
|
769b520d47 | ||
|
|
517ed6d56b | ||
|
|
40635f9a01 | ||
|
|
2eb6e0c632 | ||
|
|
710b78b79f | ||
|
|
f308539a5a | ||
|
|
ca8cb9ce78 | ||
|
|
3c7d45cd52 | ||
|
|
6e77d54aa3 | ||
|
|
3c72786bd8 | ||
|
|
8a36e01fbb | ||
|
|
32cb959f0b | ||
|
|
3fb24dbd99 | ||
|
|
b737c9f0aa | ||
|
|
48d94ae046 | ||
|
|
4ed05cb481 | ||
|
|
cddc7d70fb | ||
|
|
64c305c544 | ||
|
|
4ee10c1560 | ||
|
|
5e5c5d45a6 | ||
|
|
56ee1334af | ||
|
|
de5ef65c16 | ||
|
|
453934621d | ||
|
|
2132c3676f | ||
|
|
e9adb9b298 | ||
|
|
3e3957d693 | ||
|
|
554147b57f | ||
|
|
6b632e851c | ||
|
|
3e99bfcb60 | ||
|
|
2c48b3a6a4 | ||
|
|
2e22d7abc9 | ||
|
|
e645fa4e86 | ||
|
|
cdcac9e4db | ||
|
|
d3a316c1c0 | ||
|
|
f081ec6454 | ||
|
|
60d6996db2 | ||
|
|
ea52e9671b | ||
|
|
92df692559 | ||
|
|
3b4f92997e | ||
|
|
98d02a4da0 | ||
|
|
eb951cbd5e | ||
|
|
9ce68f2acc | ||
|
|
54239c339c | ||
|
|
2b108e2029 | ||
|
|
2a21983b13 | ||
|
|
b6e84b3d9e | ||
|
|
bb314fb553 | ||
|
|
28718e8ad6 | ||
|
|
2857cfb899 | ||
| 8717e1b9a3 | |||
| d3c9b95331 | |||
|
|
4d6a5d750c | ||
|
|
84e1340362 | ||
| 5ee05df574 | |||
|
|
2a993f0400 | ||
|
|
b878b7e1b3 | ||
|
|
c57d1bac9e | ||
| b9b5239448 | |||
| aae2776790 | |||
|
|
a20aa841d6 | ||
|
|
8c7819d96f | ||
|
|
3d9473f979 | ||
|
|
01f48be853 | ||
|
|
1638098fd4 | ||
|
|
047e5459af | ||
|
|
5baa4245e3 | ||
| ec6b9f7887 | |||
| 22d0d23b89 | |||
| fd3a32e8e1 | |||
|
|
9e849e8fe3 | ||
|
|
d65f3ed942 | ||
|
|
31298be840 | ||
|
|
5c889fd57e | ||
|
|
4e2874c972 | ||
|
|
63e50ac2df | ||
|
|
ad2a87e20d | ||
|
|
e9c3a4262a | ||
|
|
b5706ec6e1 | ||
|
|
61e7350429 | ||
|
|
c9a274fc79 | ||
|
|
127c9f7183 | ||
|
|
a27f8ba5dd | ||
|
|
fafe2c34b4 | ||
|
|
9c91ddc7ba | ||
|
|
1f02ed650b | ||
|
|
fc47df4649 | ||
|
|
42b8bdb82a | ||
|
|
2a174edeef | ||
|
|
9b8529b39b | ||
|
|
062ff742c3 | ||
|
|
1764560c49 | ||
|
|
b439e04a2f | ||
|
|
e413f95633 | ||
|
|
675f37ab85 | ||
|
|
9932c682ef | ||
|
|
018b30cdc4 | ||
|
|
249eb69ade | ||
|
|
1ab8c7aee0 | ||
|
|
400a5fab7d | ||
|
|
e7851e88b3 | ||
|
|
0761637666 | ||
|
|
dda4d987cb | ||
|
|
609ec0e8b8 | ||
|
|
c0b28a0655 | ||
|
|
78aad11e06 | ||
|
|
59bebbc45e | ||
|
|
e92656add0 | ||
|
|
5d24579d2f | ||
|
|
ce284b61be | ||
|
|
7a8118ac63 | ||
|
|
18d8e73acd | ||
|
|
58235cb1a1 | ||
|
|
e0d88cc076 | ||
|
|
c8b0927090 | ||
|
|
a4a79d8c99 | ||
|
|
408609fa58 | ||
|
|
6efebfad89 | ||
| 12865b0b43 | |||
| 9117dddcee | |||
| 1e267f5cb6 | |||
| 62ed015ea9 | |||
| 3e6b7cd040 | |||
| 209eb84d6d | |||
| 7f3b47a23c | |||
| 08ba87c8d8 | |||
| f2a45a2a6d | |||
| 9c544adc94 | |||
| a225c9aa7f | |||
| 51c8711da6 | |||
| 54eb4188a7 | |||
|
|
89d5e36d6c | ||
|
|
6cd5ab4ee2 | ||
|
|
0c913c68e3 | ||
|
|
12d746a1dc | ||
|
|
0a25f3a1d6 | ||
|
|
1eaab893c8 | ||
|
|
05ea872831 | ||
|
|
d0506dba29 | ||
|
|
dd28587b20 | ||
|
|
1a288c84f5 | ||
|
|
cadc3eda52 | ||
|
|
8d3e6691d4 | ||
|
|
cb54fa5d78 | ||
|
|
53ffc072f0 | ||
|
|
7a59bcb65b | ||
|
|
b81a727cd4 | ||
|
|
11b33dd148 | ||
|
|
1ccc18a31f | ||
|
|
9a3afeebdf | ||
|
|
0720471cca | ||
|
|
dd43ddaad6 | ||
|
|
6fc059aaac | ||
|
|
8cfb6f0bb3 | ||
|
|
ddcdc322f8 | ||
|
|
4ee52f06d6 | ||
|
|
603efc2deb | ||
|
|
9fc5007590 | ||
|
|
a6210f8b29 | ||
|
|
175f244a5f | ||
|
|
0d4ce533bc | ||
|
|
8e22235df2 | ||
|
|
0519d2703c | ||
|
|
1ca9b006e1 | ||
|
|
8cc5a8904b | ||
|
|
70cdef2e71 | ||
|
|
3baffeb12d | ||
|
|
a0d9dc585f | ||
|
|
388125415b | ||
|
|
14c7e5db21 | ||
|
|
582a85df88 | ||
|
|
18ddc54626 | ||
|
|
6bad9a331d | ||
|
|
16b5747620 | ||
|
|
47015ec312 | ||
|
|
666c88bdf1 | ||
|
|
bd5262353b | ||
|
|
e1d5712b83 | ||
|
|
bcb9108424 | ||
|
|
82bb88ca34 | ||
|
|
518685f361 | ||
|
|
39e3d15fa8 | ||
|
|
7a755bbb3e | ||
|
|
41260df1a1 | ||
|
|
0d1b60158a | ||
|
|
b244df8f2d | ||
|
|
e48ca4fc8e | ||
|
|
82d52d1c9f | ||
|
|
e6a51d02fb | ||
|
|
28e2d9c94b | ||
|
|
60137feed0 | ||
|
|
a86c49e471 | ||
|
|
8c5d0bbb06 | ||
|
|
c26fbf8042 | ||
|
|
83e70d5e7a | ||
|
|
c383b8df9b | ||
|
|
fc810c3362 | ||
|
|
7e225c837a | ||
|
|
b053544512 | ||
|
|
9b4527d334 | ||
|
|
3794ffa5be | ||
|
|
fe68f0cca8 | ||
|
|
d83cf4d29d | ||
|
|
47dc1df796 | ||
|
|
5d2fb3f719 | ||
|
|
3fded83c75 | ||
|
|
aeab315c3d | ||
|
|
446fa532db | ||
|
|
ac2190809a |
22
completions/gpoa
Normal file
22
completions/gpoa
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
_gpoa()
|
||||||
|
{
|
||||||
|
local cur prev words cword split
|
||||||
|
_init_completion -s || return
|
||||||
|
|
||||||
|
case $prev in
|
||||||
|
--dc)
|
||||||
|
_filedir
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
--loglevel)
|
||||||
|
COMPREPLY=($(compgen -W '0 1 2 3 4 5' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COMPREPLY=($(compgen -W '--dc --nodomain --noupdate --noplugins --list-backends --loglevel --help --force' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _gpoa gpoa
|
||||||
27
completions/gpupdate
Normal file
27
completions/gpupdate
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
_gpupdate()
|
||||||
|
{
|
||||||
|
local cur prev words cword split
|
||||||
|
_init_completion -s || return
|
||||||
|
|
||||||
|
case $prev in
|
||||||
|
-u|--user)
|
||||||
|
_filedir
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
-t|--target)
|
||||||
|
COMPREPLY=($(compgen -W 'ALL USER COMPUTER' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
-l|--loglevel)
|
||||||
|
COMPREPLY=($(compgen -W '0 1 2 3 4 5' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COMPREPLY=($(compgen -W '--user --target --loglevel --system --help --force' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _gpupdate gpupdate
|
||||||
|
|
||||||
18
completions/gpupdate-setup
Normal file
18
completions/gpupdate-setup
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
_gpupdate-setup()
|
||||||
|
{
|
||||||
|
local cur prev words cword split
|
||||||
|
_init_completion -s || return
|
||||||
|
|
||||||
|
case $prev in
|
||||||
|
set-backend)
|
||||||
|
COMPREPLY=($(compgen -W 'local samba' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COMPREPLY=($(compgen -W 'list list-backends status enable disable update write set-backend default-policy active-policy active-backend' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _gpupdate-setup gpupdate-setup
|
||||||
@@ -45,6 +45,9 @@ Don't run plugins.
|
|||||||
.TP
|
.TP
|
||||||
\fB--loglevel \fILOGLEVEL\fP
|
\fB--loglevel \fILOGLEVEL\fP
|
||||||
Set logging verbosity from 0 to 5.
|
Set logging verbosity from 0 to 5.
|
||||||
|
.TP
|
||||||
|
\fB--force\fP
|
||||||
|
Force GPT download.
|
||||||
.
|
.
|
||||||
.SH FILES
|
.SH FILES
|
||||||
\fB/usr/sbin/gpoa\fR utility uses \fB/usr/share/local-policy/default\fR
|
\fB/usr/sbin/gpoa\fR utility uses \fB/usr/share/local-policy/default\fR
|
||||||
@@ -55,8 +58,10 @@ All data is located in \fB/var/cache/gpupdate\fR. Also domain GPTs are
|
|||||||
taken from Samba's \fB/var/cache/samba\fR.
|
taken from Samba's \fB/var/cache/samba\fR.
|
||||||
.
|
.
|
||||||
The settings read from Samba are stored in
|
The settings read from Samba are stored in
|
||||||
\fB/var/cache/gpupdate/registry.sqlite\fR and "Local Policy" settings
|
Dconf. Machine policies are stored in the \fB/etc/dconf/db/policy.d/policy.ini\fR file,
|
||||||
read from \fB/usr/local/share/local-policy/default\fR are converted
|
user policies are stored in the \fB/etc/dconf/db/policy<UID>.d/policy<UID>.ini\fR file
|
||||||
|
(where UID is the user ID in the system)."Local Policy" settings
|
||||||
|
read from \fB/usr/share/local-policy/\fR are converted
|
||||||
into GPT and stored as \fB/var/cache/gpupdate/local-policy\fR.
|
into GPT and stored as \fB/var/cache/gpupdate/local-policy\fR.
|
||||||
.SH "SEE ALSO"
|
.SH "SEE ALSO"
|
||||||
gpupdate(1)
|
gpupdate(1)
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ Show help.
|
|||||||
.TP
|
.TP
|
||||||
\fB--user \fIusername\fR
|
\fB--user \fIusername\fR
|
||||||
Run \fBgpupdate\fP for \fIusername\fP.
|
Run \fBgpupdate\fP for \fIusername\fP.
|
||||||
|
.TP
|
||||||
|
\fB--force\fP
|
||||||
|
Force GPT download.
|
||||||
.
|
.
|
||||||
.SS "EXIT CODES"
|
.SS "EXIT CODES"
|
||||||
.TP
|
.TP
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ from .samba_backend import samba_backend
|
|||||||
from .nodomain_backend import nodomain_backend
|
from .nodomain_backend import nodomain_backend
|
||||||
from util.logging import log
|
from util.logging import log
|
||||||
from util.config import GPConfig
|
from util.config import GPConfig
|
||||||
|
from util.util import get_uid_by_username, touch_file
|
||||||
|
from util.paths import get_dconf_config_file
|
||||||
|
from util.ipacreds import ipacreds
|
||||||
|
from .freeipa_backend import freeipa_backend
|
||||||
|
from storage.dconf_registry import Dconf_registry, create_dconf_ini_file, add_preferences_to_global_registry_dict
|
||||||
|
|
||||||
def backend_factory(dc, username, is_machine, no_domain = False):
|
def backend_factory(dc, username, is_machine, no_domain = False):
|
||||||
'''
|
'''
|
||||||
@@ -49,6 +54,20 @@ def backend_factory(dc, username, is_machine, no_domain = False):
|
|||||||
logdata = dict({'error': str(exc)})
|
logdata = dict({'error': str(exc)})
|
||||||
log('E7', logdata)
|
log('E7', logdata)
|
||||||
|
|
||||||
|
if config.get_backend() == 'freeipa' and not no_domain:
|
||||||
|
try:
|
||||||
|
if not dc:
|
||||||
|
dc = config.get_dc()
|
||||||
|
if dc:
|
||||||
|
ld = {'dc': dc}
|
||||||
|
log('D52', ld)
|
||||||
|
ipac = ipacreds()
|
||||||
|
domain = ipac.get_domain()
|
||||||
|
back = freeipa_backend(ipac, username, domain, is_machine)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'error': str(exc)}
|
||||||
|
log('E77', logdata)
|
||||||
|
|
||||||
if config.get_backend() == 'local' or no_domain:
|
if config.get_backend() == 'local' or no_domain:
|
||||||
log('D8')
|
log('D8')
|
||||||
try:
|
try:
|
||||||
@@ -59,3 +78,14 @@ def backend_factory(dc, username, is_machine, no_domain = False):
|
|||||||
|
|
||||||
return back
|
return back
|
||||||
|
|
||||||
|
def save_dconf(username, is_machine, nodomain=None):
|
||||||
|
if is_machine:
|
||||||
|
uid = None
|
||||||
|
else:
|
||||||
|
uid = get_uid_by_username(username) if not is_machine else None
|
||||||
|
target_file = get_dconf_config_file(uid)
|
||||||
|
touch_file(target_file)
|
||||||
|
Dconf_registry.apply_template(uid)
|
||||||
|
add_preferences_to_global_registry_dict(username, is_machine)
|
||||||
|
Dconf_registry.update_dict_to_previous()
|
||||||
|
create_dconf_ini_file(target_file,Dconf_registry.global_registry_dict, uid, nodomain)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,10 +16,231 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import smbc
|
||||||
|
import re
|
||||||
|
|
||||||
from .applier_backend import applier_backend
|
from .applier_backend import applier_backend
|
||||||
|
from pathlib import Path
|
||||||
|
from gpt.gpt import gpt, get_local_gpt
|
||||||
|
from gpt.gpo_dconf_mapping import GpoInfoDconf
|
||||||
|
from storage import registry_factory
|
||||||
|
from storage.dconf_registry import Dconf_registry, extract_display_name_version
|
||||||
|
from storage.fs_file_cache import fs_file_cache
|
||||||
|
from util.logging import log
|
||||||
|
from util.util import get_uid_by_username
|
||||||
|
from util.kerberos import (
|
||||||
|
machine_kinit
|
||||||
|
, machine_kdestroy
|
||||||
|
)
|
||||||
|
|
||||||
class freeipa_backend(applier_backend):
|
class freeipa_backend(applier_backend):
|
||||||
def __init__(self):
|
def __init__(self, ipacreds, username, domain, is_machine):
|
||||||
pass
|
self.ipacreds = ipacreds
|
||||||
|
self.cache_path = '/var/cache/gpupdate/creds/krb5cc_{}'.format(os.getpid())
|
||||||
|
self.__kinit_successful = machine_kinit(self.cache_path, "freeipa")
|
||||||
|
if not self.__kinit_successful:
|
||||||
|
raise Exception('kinit is not successful')
|
||||||
|
|
||||||
|
self.storage = registry_factory()
|
||||||
|
self.storage.set_info('domain', domain)
|
||||||
|
|
||||||
|
machine_name = self.ipacreds.get_machine_name()
|
||||||
|
self.storage.set_info('machine_name', machine_name)
|
||||||
|
self.username = machine_name if is_machine else username
|
||||||
|
self._is_machine_username = is_machine
|
||||||
|
|
||||||
|
self.cache_dir = self.ipacreds.get_cache_dir()
|
||||||
|
self.gpo_cache_part = 'gpo_cache'
|
||||||
|
self.gpo_cache_dir = os.path.join(self.cache_dir, self.gpo_cache_part)
|
||||||
|
self.storage.set_info('cache_dir', self.gpo_cache_dir)
|
||||||
|
self.file_cache = fs_file_cache("freeipa_gpo", username)
|
||||||
|
logdata = {'cachedir': self.cache_dir}
|
||||||
|
log('D7', logdata)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self.__kinit_successful:
|
||||||
|
machine_kdestroy()
|
||||||
|
|
||||||
|
def retrieve_and_store(self):
|
||||||
|
'''
|
||||||
|
Retrieve settings and store it in a database - FreeIPA version
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
if self._is_machine_username:
|
||||||
|
dconf_dict = Dconf_registry.get_dictionary_from_dconf_file_db(save_dconf_db=True)
|
||||||
|
else:
|
||||||
|
uid = get_uid_by_username(self.username)
|
||||||
|
dconf_dict = Dconf_registry.get_dictionary_from_dconf_file_db(uid, save_dconf_db=True)
|
||||||
|
except Exception as e:
|
||||||
|
logdata = {'msg': str(e)}
|
||||||
|
log('E72', logdata)
|
||||||
|
|
||||||
|
if self._is_machine_username:
|
||||||
|
machine_gpts = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
machine_name = self.storage.get_info('machine_name')
|
||||||
|
machine_gpts = self._get_gpts(machine_name)
|
||||||
|
machine_gpts.reverse()
|
||||||
|
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'msg': str(exc)}
|
||||||
|
log('E17', logdata)
|
||||||
|
|
||||||
|
for i, gptobj in enumerate(machine_gpts):
|
||||||
|
try:
|
||||||
|
gptobj.merge_machine()
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'msg': str(exc)}
|
||||||
|
log('E26', logdata)
|
||||||
|
else:
|
||||||
|
user_gpts = []
|
||||||
|
try:
|
||||||
|
user_gpts = self._get_gpts(self.username)
|
||||||
|
user_gpts.reverse()
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'msg': str(exc)}
|
||||||
|
log('E17', logdata)
|
||||||
|
for i, gptobj in enumerate(user_gpts):
|
||||||
|
try:
|
||||||
|
gptobj.merge_user()
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'msg': str(exc)}
|
||||||
|
log('E27', logdata)
|
||||||
|
|
||||||
|
def _get_gpts(self, username):
|
||||||
|
gpts = []
|
||||||
|
gpos, server = self.ipacreds.update_gpos(username)
|
||||||
|
if not gpos:
|
||||||
|
return gpts
|
||||||
|
if not server:
|
||||||
|
return gpts
|
||||||
|
|
||||||
|
cached_gpos = []
|
||||||
|
download_gpos = []
|
||||||
|
|
||||||
|
for i, gpo in enumerate(gpos):
|
||||||
|
if gpo.file_sys_path.startswith('/'):
|
||||||
|
if os.path.exists(gpo.file_sys_path):
|
||||||
|
logdata = {'gpo_name': gpo.display_name, 'path': gpo.file_sys_path}
|
||||||
|
log('D11', logdata)
|
||||||
|
cached_gpos.append(gpo)
|
||||||
|
else:
|
||||||
|
download_gpos.append(gpo)
|
||||||
|
else:
|
||||||
|
if self._check_sysvol_present(gpo):
|
||||||
|
download_gpos.append(gpo)
|
||||||
|
else:
|
||||||
|
logdata = {'gpo_name': gpo.display_name}
|
||||||
|
log('W4', logdata)
|
||||||
|
|
||||||
|
if download_gpos:
|
||||||
|
try:
|
||||||
|
self._download_gpos(download_gpos, server)
|
||||||
|
logdata = {'count': len(download_gpos)}
|
||||||
|
log('D50', logdata)
|
||||||
|
except Exception as e:
|
||||||
|
logdata = {'msg': str(e), 'count': len(download_gpos)}
|
||||||
|
log('E35', logdata)
|
||||||
|
else:
|
||||||
|
log('D211', {})
|
||||||
|
|
||||||
|
all_gpos = cached_gpos + download_gpos
|
||||||
|
for gpo in all_gpos:
|
||||||
|
gpt_abspath = gpo.file_sys_path
|
||||||
|
if not os.path.exists(gpt_abspath):
|
||||||
|
logdata = {'path': gpt_abspath, 'gpo_name': gpo.display_name}
|
||||||
|
log('W12', logdata)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self._is_machine_username:
|
||||||
|
obj = gpt(gpt_abspath, None, GpoInfoDconf(gpo))
|
||||||
|
else:
|
||||||
|
obj = gpt(gpt_abspath, self.username, GpoInfoDconf(gpo))
|
||||||
|
|
||||||
|
obj.set_name(gpo.display_name)
|
||||||
|
gpts.append(obj)
|
||||||
|
|
||||||
|
local_gpt = get_local_gpt()
|
||||||
|
gpts.append(local_gpt)
|
||||||
|
logdata = {'total_count': len(gpts), 'downloaded_count': len(download_gpos)}
|
||||||
|
log('I2', logdata)
|
||||||
|
return gpts
|
||||||
|
|
||||||
|
def _check_sysvol_present(self, gpo):
|
||||||
|
if not gpo.file_sys_path:
|
||||||
|
if getattr(gpo, 'name', '') != 'Local Policy':
|
||||||
|
logdata = {'gponame': getattr(gpo, 'name', 'Unknown')}
|
||||||
|
log('W4', logdata)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if gpo.file_sys_path.startswith('\\\\'):
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif gpo.file_sys_path.startswith('/'):
|
||||||
|
if os.path.exists(gpo.file_sys_path):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _download_gpos(self, gpos, server):
|
||||||
|
cache_dir = self.ipacreds.get_cache_dir()
|
||||||
|
domain = self.ipacreds.get_domain().upper()
|
||||||
|
gpo_cache_dir = os.path.join(cache_dir, domain, 'POLICIES')
|
||||||
|
os.makedirs(gpo_cache_dir, exist_ok=True)
|
||||||
|
|
||||||
|
for gpo in gpos:
|
||||||
|
if not gpo.file_sys_path:
|
||||||
|
continue
|
||||||
|
smb_remote_path = None
|
||||||
|
try:
|
||||||
|
smb_remote_path = self._convert_to_smb_path(gpo.file_sys_path, server)
|
||||||
|
local_gpo_path = os.path.join(gpo_cache_dir, gpo.name)
|
||||||
|
|
||||||
|
self._download_gpo_directory(smb_remote_path, local_gpo_path)
|
||||||
|
gpo.file_sys_path = local_gpo_path
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logdata = {
|
||||||
|
'msg': str(e),
|
||||||
|
'gpo_name': gpo.display_name,
|
||||||
|
'smb_path': smb_remote_path,
|
||||||
|
}
|
||||||
|
log('E38', logdata)
|
||||||
|
|
||||||
|
def _convert_to_smb_path(self, windows_path, server):
|
||||||
|
match = re.search(r'\\\\[^\\]+\\(.+)', windows_path)
|
||||||
|
if not match:
|
||||||
|
raise Exception(f"Invalid Windows path format: {windows_path}")
|
||||||
|
relative_path = match.group(1).replace('\\', '/').lower()
|
||||||
|
smb_url = f"smb://{server}/{relative_path}"
|
||||||
|
|
||||||
|
return smb_url
|
||||||
|
|
||||||
|
def _download_gpo_directory(self, remote_smb_path, local_path):
|
||||||
|
os.makedirs(local_path, exist_ok=True)
|
||||||
|
try:
|
||||||
|
entries = self.file_cache.samba_context.opendir(remote_smb_path).getdents()
|
||||||
|
for entry in entries:
|
||||||
|
if entry.name in [".", ".."]:
|
||||||
|
continue
|
||||||
|
remote_entry_path = f"{remote_smb_path}/{entry.name}"
|
||||||
|
local_entry_path = os.path.join(local_path, entry.name)
|
||||||
|
if entry.smbc_type == smbc.DIR:
|
||||||
|
self._download_gpo_directory(remote_entry_path, local_entry_path)
|
||||||
|
elif entry.smbc_type == smbc.FILE:
|
||||||
|
try:
|
||||||
|
os.makedirs(os.path.dirname(local_entry_path), exist_ok=True)
|
||||||
|
self.file_cache.store(remote_entry_path, Path(local_entry_path))
|
||||||
|
except Exception as e:
|
||||||
|
logdata = {'exception': str(e), 'file': entry.name}
|
||||||
|
log('W30', logdata)
|
||||||
|
except Exception as e:
|
||||||
|
logdata = {'exception': str(e), 'remote_folder_path': remote_smb_path}
|
||||||
|
log('W31', logdata)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,33 +16,17 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
|
|
||||||
from .applier_backend import applier_backend
|
from .applier_backend import applier_backend
|
||||||
from storage import registry_factory
|
from storage import registry_factory
|
||||||
from gpt.gpt import gpt, get_local_gpt
|
from gpt.gpt import get_local_gpt
|
||||||
from util.util import (
|
|
||||||
get_machine_name
|
|
||||||
)
|
|
||||||
from util.sid import get_sid
|
|
||||||
import util.preg
|
|
||||||
from util.logging import slogm
|
|
||||||
|
|
||||||
class nodomain_backend(applier_backend):
|
class nodomain_backend(applier_backend):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
domain = None
|
self.storage = registry_factory()
|
||||||
machine_name = get_machine_name()
|
|
||||||
machine_sid = get_sid(domain, machine_name, True)
|
|
||||||
self.storage = registry_factory('registry')
|
|
||||||
self.storage.set_info('domain', domain)
|
|
||||||
self.storage.set_info('machine_name', machine_name)
|
|
||||||
self.storage.set_info('machine_sid', machine_sid)
|
|
||||||
|
|
||||||
# User SID to work with HKCU hive
|
|
||||||
self.username = machine_name
|
|
||||||
self.sid = machine_sid
|
|
||||||
|
|
||||||
def retrieve_and_store(self):
|
def retrieve_and_store(self):
|
||||||
'''
|
'''
|
||||||
@@ -50,8 +34,7 @@ class nodomain_backend(applier_backend):
|
|||||||
'''
|
'''
|
||||||
# Get policies for machine at first.
|
# Get policies for machine at first.
|
||||||
self.storage.wipe_hklm()
|
self.storage.wipe_hklm()
|
||||||
self.storage.wipe_user(self.storage.get_info('machine_sid'))
|
local_policy = get_local_gpt()
|
||||||
local_policy = get_local_gpt(self.sid)
|
|
||||||
local_policy.merge_machine()
|
local_policy.merge_machine()
|
||||||
local_policy.merge_user()
|
local_policy.merge_user()
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -24,29 +24,29 @@ except ImportError:
|
|||||||
from samba.gp.gpclass import check_safe_path
|
from samba.gp.gpclass import check_safe_path
|
||||||
|
|
||||||
from .applier_backend import applier_backend
|
from .applier_backend import applier_backend
|
||||||
from storage import cache_factory, registry_factory
|
from storage import registry_factory
|
||||||
from gpt.gpt import gpt, get_local_gpt
|
from gpt.gpt import gpt, get_local_gpt
|
||||||
|
from gpt.gpo_dconf_mapping import GpoInfoDconf
|
||||||
from util.util import (
|
from util.util import (
|
||||||
get_machine_name,
|
get_machine_name
|
||||||
is_machine_name
|
|
||||||
)
|
)
|
||||||
from util.kerberos import (
|
from util.kerberos import (
|
||||||
machine_kinit
|
machine_kinit
|
||||||
, machine_kdestroy
|
, machine_kdestroy
|
||||||
)
|
)
|
||||||
from util.sid import get_sid
|
from util.sid import get_sid
|
||||||
import util.preg
|
|
||||||
from util.logging import log
|
from util.logging import log
|
||||||
|
|
||||||
class samba_backend(applier_backend):
|
class samba_backend(applier_backend):
|
||||||
__user_policy_mode_key = 'Software\\Policies\\Microsoft\\Windows\\System\\UserPolicyMode'
|
__user_policy_mode_key = '/SOFTWARE/Policies/Microsoft/Windows/System/UserPolicyMode'
|
||||||
|
__user_policy_mode_key_win = '/Software/Policies/Microsoft/Windows/System/UserPolicyMode'
|
||||||
|
|
||||||
def __init__(self, sambacreds, username, domain, is_machine):
|
def __init__(self, sambacreds, username, domain, is_machine):
|
||||||
self.cache_path = '/var/cache/gpupdate/creds/krb5cc_{}'.format(os.getpid())
|
self.cache_path = '/var/cache/gpupdate/creds/krb5cc_{}'.format(os.getpid())
|
||||||
self.__kinit_successful = machine_kinit(self.cache_path)
|
self.__kinit_successful = machine_kinit(self.cache_path)
|
||||||
if not self.__kinit_successful:
|
if not self.__kinit_successful:
|
||||||
raise Exception('kinit is not successful')
|
raise Exception('kinit is not successful')
|
||||||
self.storage = registry_factory('registry')
|
self.storage = registry_factory()
|
||||||
self.storage.set_info('domain', domain)
|
self.storage.set_info('domain', domain)
|
||||||
machine_name = get_machine_name()
|
machine_name = get_machine_name()
|
||||||
machine_sid = get_sid(domain, machine_name, is_machine)
|
machine_sid = get_sid(domain, machine_name, is_machine)
|
||||||
@@ -55,20 +55,20 @@ class samba_backend(applier_backend):
|
|||||||
|
|
||||||
# User SID to work with HKCU hive
|
# User SID to work with HKCU hive
|
||||||
self.username = username
|
self.username = username
|
||||||
self._is_machine_username = is_machine
|
self._is_machine = is_machine
|
||||||
if is_machine:
|
if is_machine:
|
||||||
self.sid = machine_sid
|
self.sid = machine_sid
|
||||||
else:
|
else:
|
||||||
self.sid = get_sid(self.storage.get_info('domain'), self.username)
|
self.sid = get_sid(self.storage.get_info('domain'), self.username)
|
||||||
|
|
||||||
self.cache = cache_factory('regpol_cache')
|
|
||||||
self.gpo_names = cache_factory('gpo_names')
|
|
||||||
|
|
||||||
# Samba objects - LoadParm() and CredentialsOptions()
|
# Samba objects - LoadParm() and CredentialsOptions()
|
||||||
self.sambacreds = sambacreds
|
self.sambacreds = sambacreds
|
||||||
|
|
||||||
self.cache_dir = self.sambacreds.get_cache_dir()
|
self.cache_dir = self.sambacreds.get_cache_dir()
|
||||||
logdata = dict({'cachedir': self.cache_dir})
|
self.gpo_cache_part ='gpo_cache'
|
||||||
|
self._cached = False
|
||||||
|
self.storage.set_info('cache_dir', os.path.join(self.cache_dir, self.gpo_cache_part))
|
||||||
|
logdata = {'cachedir': self.cache_dir}
|
||||||
log('D7', logdata)
|
log('D7', logdata)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
@@ -81,9 +81,11 @@ class samba_backend(applier_backend):
|
|||||||
is possible to work with user's part of GPT. This value is
|
is possible to work with user's part of GPT. This value is
|
||||||
checked only if working for user's SID.
|
checked only if working for user's SID.
|
||||||
'''
|
'''
|
||||||
upm = self.storage.get_hklm_entry(self.__user_policy_mode_key)
|
upm_key = self.storage.get_key_value(self.__user_policy_mode_key)
|
||||||
if upm and upm.data:
|
upm_win_key = self.storage.get_key_value(self.__user_policy_mode_key_win)
|
||||||
upm = int(upm.data)
|
upm = upm_key if upm_key else upm_win_key
|
||||||
|
if upm:
|
||||||
|
upm = int(upm)
|
||||||
if upm < 0 or upm > 2:
|
if upm < 0 or upm > 2:
|
||||||
upm = 0
|
upm = 0
|
||||||
else:
|
else:
|
||||||
@@ -96,39 +98,36 @@ class samba_backend(applier_backend):
|
|||||||
Retrieve settings and strore it in a database
|
Retrieve settings and strore it in a database
|
||||||
'''
|
'''
|
||||||
# Get policies for machine at first.
|
# Get policies for machine at first.
|
||||||
machine_gpts = list()
|
machine_gpts = []
|
||||||
try:
|
try:
|
||||||
machine_gpts = self._get_gpts(get_machine_name(), self.storage.get_info('machine_sid'))
|
machine_gpts = self._get_gpts()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
log('F2')
|
log('F2')
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
if self._is_machine_username:
|
if self._is_machine:
|
||||||
self.storage.wipe_hklm()
|
|
||||||
self.storage.wipe_user(self.storage.get_info('machine_sid'))
|
|
||||||
for gptobj in machine_gpts:
|
for gptobj in machine_gpts:
|
||||||
try:
|
try:
|
||||||
gptobj.merge_machine()
|
gptobj.merge_machine()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['msg'] = str(exc)
|
logdata['msg'] = str(exc)
|
||||||
log('E26', logdata)
|
log('E26', logdata)
|
||||||
|
|
||||||
# Load user GPT values in case user's name specified
|
# Load user GPT values in case user's name specified
|
||||||
# This is a buggy implementation and should be tested more
|
# This is a buggy implementation and should be tested more
|
||||||
else:
|
else:
|
||||||
user_gpts = list()
|
user_gpts = []
|
||||||
try:
|
try:
|
||||||
user_gpts = self._get_gpts(self.username, self.sid)
|
user_gpts = self._get_gpts(self.username)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
log('F3')
|
log('F3')
|
||||||
raise exc
|
raise exc
|
||||||
self.storage.wipe_user(self.sid)
|
|
||||||
|
|
||||||
# Merge user settings if UserPolicyMode set accordingly
|
# Merge user settings if UserPolicyMode set accordingly
|
||||||
# and user settings (for HKCU) are exist.
|
# and user settings (for HKCU) are exist.
|
||||||
policy_mode = self.get_policy_mode()
|
policy_mode = self.get_policy_mode()
|
||||||
logdata = dict({'mode': upm2str(policy_mode), 'sid': self.sid})
|
logdata = {'mode': upm2str(policy_mode)}
|
||||||
log('D152', logdata)
|
log('D152', logdata)
|
||||||
|
|
||||||
if policy_mode < 2:
|
if policy_mode < 2:
|
||||||
@@ -136,17 +135,16 @@ class samba_backend(applier_backend):
|
|||||||
try:
|
try:
|
||||||
gptobj.merge_user()
|
gptobj.merge_user()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['msg'] = str(exc)
|
logdata['msg'] = str(exc)
|
||||||
log('E27', logdata)
|
log('E27', logdata)
|
||||||
|
|
||||||
if policy_mode > 0:
|
if policy_mode > 0:
|
||||||
for gptobj in machine_gpts:
|
for gptobj in machine_gpts:
|
||||||
try:
|
try:
|
||||||
gptobj.sid = self.sid
|
|
||||||
gptobj.merge_user()
|
gptobj.merge_user()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['msg'] = str(exc)
|
logdata['msg'] = str(exc)
|
||||||
log('E63', logdata)
|
log('E63', logdata)
|
||||||
|
|
||||||
@@ -154,34 +152,47 @@ class samba_backend(applier_backend):
|
|||||||
'''
|
'''
|
||||||
Check if there is SYSVOL path for GPO assigned
|
Check if there is SYSVOL path for GPO assigned
|
||||||
'''
|
'''
|
||||||
|
self._cached = False
|
||||||
if not gpo.file_sys_path:
|
if not gpo.file_sys_path:
|
||||||
# GPO named "Local Policy" has no entry by its nature so
|
# GPO named "Local Policy" has no entry by its nature so
|
||||||
# no reason to print warning.
|
# no reason to print warning.
|
||||||
if 'Local Policy' != gpo.name:
|
if gpo.display_name in self.storage._dict_gpo_name_version_cache.keys():
|
||||||
logdata = dict({'gponame': gpo.name})
|
gpo.file_sys_path = self.storage._dict_gpo_name_version_cache.get(gpo.display_name, {}).get('correct_path')
|
||||||
|
self._cached = True
|
||||||
|
return True
|
||||||
|
elif 'Local Policy' != gpo.name:
|
||||||
|
logdata = {'gponame': gpo.name}
|
||||||
log('W4', logdata)
|
log('W4', logdata)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _get_gpts(self, username, sid):
|
def _get_gpts(self, username=None):
|
||||||
gpts = list()
|
gpts = []
|
||||||
|
if not username:
|
||||||
log('D45', {'username': username, 'sid': sid})
|
username = get_machine_name()
|
||||||
|
log('D45', {'username': username})
|
||||||
# util.windows.smbcreds
|
# util.windows.smbcreds
|
||||||
gpos = self.sambacreds.update_gpos(username)
|
gpos = self.sambacreds.update_gpos(username)
|
||||||
log('D46')
|
log('D46')
|
||||||
for gpo in gpos:
|
for gpo in gpos:
|
||||||
if self._check_sysvol_present(gpo):
|
if self._check_sysvol_present(gpo):
|
||||||
path = check_safe_path(gpo.file_sys_path).upper()
|
if not self._cached:
|
||||||
slogdata = dict({'sysvol_path': gpo.file_sys_path, 'gpo_name': gpo.display_name, 'gpo_path': path})
|
path = check_safe_path(gpo.file_sys_path).upper()
|
||||||
log('D30', slogdata)
|
slogdata = {'sysvol_path': gpo.file_sys_path, 'gpo_name': gpo.display_name, 'gpo_path': path}
|
||||||
gpt_abspath = os.path.join(self.cache_dir, 'gpo_cache', path)
|
log('D30', slogdata)
|
||||||
obj = gpt(gpt_abspath, sid)
|
gpt_abspath = os.path.join(self.cache_dir, self.gpo_cache_part, path)
|
||||||
|
else:
|
||||||
|
gpt_abspath = gpo.file_sys_path
|
||||||
|
log('D211', {'sysvol_path': gpo.file_sys_path, 'gpo_name': gpo.display_name})
|
||||||
|
if self._is_machine:
|
||||||
|
obj = gpt(gpt_abspath, None, GpoInfoDconf(gpo))
|
||||||
|
else:
|
||||||
|
obj = gpt(gpt_abspath, self.username, GpoInfoDconf(gpo))
|
||||||
obj.set_name(gpo.display_name)
|
obj.set_name(gpo.display_name)
|
||||||
gpts.append(obj)
|
gpts.append(obj)
|
||||||
else:
|
else:
|
||||||
if 'Local Policy' == gpo.name:
|
if 'Local Policy' == gpo.name:
|
||||||
gpts.append(get_local_gpt(sid))
|
gpts.append(get_local_gpt())
|
||||||
|
|
||||||
return gpts
|
return gpts
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2024 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,43 +18,41 @@
|
|||||||
|
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
|
|
||||||
import logging
|
|
||||||
from util.logging import slogm
|
|
||||||
|
|
||||||
def check_experimental_enabled(storage):
|
def check_experimental_enabled(storage):
|
||||||
experimental_enable_flag = 'Software\\BaseALT\\Policies\\GPUpdate\\GlobalExperimental'
|
experimental_enable_flag = '/Software/BaseALT/Policies/GPUpdate/GlobalExperimental'
|
||||||
flag = storage.get_hklm_entry(experimental_enable_flag)
|
flag = storage.get_key_value(experimental_enable_flag)
|
||||||
|
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
if flag and '1' == flag.data:
|
if flag and '1' == str(flag):
|
||||||
result = True
|
result = True
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def check_windows_mapping_enabled(storage):
|
def check_windows_mapping_enabled(storage):
|
||||||
windows_mapping_enable_flag = 'Software\\BaseALT\\Policies\\GPUpdate\\WindowsPoliciesMapping'
|
windows_mapping_enable_flag = '/Software/BaseALT/Policies/GPUpdate/WindowsPoliciesMapping'
|
||||||
flag = storage.get_hklm_entry(windows_mapping_enable_flag)
|
flag = storage.get_key_value(windows_mapping_enable_flag)
|
||||||
|
|
||||||
result = True
|
result = True
|
||||||
|
flag = str(flag)
|
||||||
if flag and '0' == flag.data:
|
if flag and '0' == flag:
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def check_module_enabled(storage, module_name):
|
def check_module_enabled(storage, module_name):
|
||||||
gpupdate_module_enable_branch = 'Software\\BaseALT\\Policies\\GPUpdate'
|
gpupdate_module_enable_branch = '/Software/BaseALT/Policies/GPUpdate'
|
||||||
gpupdate_module_flag = '{}\\{}'.format(gpupdate_module_enable_branch, module_name)
|
gpupdate_module_flag = '{}/{}'.format(gpupdate_module_enable_branch, module_name)
|
||||||
flag = storage.get_hklm_entry(gpupdate_module_flag)
|
flag = storage.get_key_value(gpupdate_module_flag)
|
||||||
|
|
||||||
result = None
|
result = None
|
||||||
|
flag = str(flag)
|
||||||
if flag:
|
if flag and flag!='None':
|
||||||
if '1' == flag.data:
|
if '1' == flag:
|
||||||
result = True
|
result = True
|
||||||
if '0' == flag.data:
|
else:
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
@@ -17,16 +17,14 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
from util.logging import log
|
||||||
import logging
|
|
||||||
from util.logging import slogm, log
|
|
||||||
|
|
||||||
def control_subst(preg_name):
|
def control_subst(preg_name):
|
||||||
'''
|
'''
|
||||||
This is a workaround for control names which can't be used in
|
This is a workaround for control names which can't be used in
|
||||||
PReg/ADMX files.
|
PReg/ADMX files.
|
||||||
'''
|
'''
|
||||||
control_triggers = dict()
|
control_triggers = {}
|
||||||
control_triggers['dvd_rw-format'] = 'dvd+rw-format'
|
control_triggers['dvd_rw-format'] = 'dvd+rw-format'
|
||||||
control_triggers['dvd_rw-mediainfo'] = 'dvd+rw-mediainfo'
|
control_triggers['dvd_rw-mediainfo'] = 'dvd+rw-mediainfo'
|
||||||
control_triggers['dvd_rw-booktype'] = 'dvd+rw-booktype'
|
control_triggers['dvd_rw-booktype'] = 'dvd+rw-booktype'
|
||||||
@@ -52,7 +50,7 @@ class control:
|
|||||||
Query possible values from control in order to perform check of
|
Query possible values from control in order to perform check of
|
||||||
parameter passed to constructor.
|
parameter passed to constructor.
|
||||||
'''
|
'''
|
||||||
values = list()
|
values = []
|
||||||
|
|
||||||
popen_call = ['/usr/sbin/control', self.control_name, 'list']
|
popen_call = ['/usr/sbin/control', self.control_name, 'list']
|
||||||
with subprocess.Popen(popen_call, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc:
|
with subprocess.Popen(popen_call, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc:
|
||||||
@@ -70,7 +68,7 @@ class control:
|
|||||||
try:
|
try:
|
||||||
str_status = self.possible_values[int_status]
|
str_status = self.possible_values[int_status]
|
||||||
except IndexError as exc:
|
except IndexError as exc:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['control'] = self.control_name
|
logdata['control'] = self.control_name
|
||||||
logdata['value from'] = self.possible_values
|
logdata['value from'] = self.possible_values
|
||||||
logdata['by index'] = int_status
|
logdata['by index'] = int_status
|
||||||
@@ -99,20 +97,20 @@ class control:
|
|||||||
if type(self.control_value) == int:
|
if type(self.control_value) == int:
|
||||||
status = self._map_control_status(self.control_value)
|
status = self._map_control_status(self.control_value)
|
||||||
if status == None:
|
if status == None:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['control'] = self.control_name
|
logdata['control'] = self.control_name
|
||||||
logdata['inpossible values'] = self.self.control_value
|
logdata['inpossible values'] = self.control_value
|
||||||
log('E42', logdata)
|
log('E42', logdata)
|
||||||
return
|
return
|
||||||
elif type(self.control_value) == str:
|
elif type(self.control_value) == str:
|
||||||
if self.control_value not in self.possible_values:
|
if self.control_value not in self.possible_values:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['control'] = self.control_name
|
logdata['control'] = self.control_name
|
||||||
logdata['inpossible values'] = self.self.control_value
|
logdata['inpossible values'] = self.control_value
|
||||||
log('E59', logdata)
|
log('E59', logdata)
|
||||||
return
|
return
|
||||||
status = self.control_value
|
status = self.control_value
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['control'] = self.control_name
|
logdata['control'] = self.control_name
|
||||||
logdata['status'] = status
|
logdata['status'] = status
|
||||||
log('D68', logdata)
|
log('D68', logdata)
|
||||||
@@ -122,7 +120,7 @@ class control:
|
|||||||
with subprocess.Popen(popen_call, stdout=subprocess.PIPE) as proc:
|
with subprocess.Popen(popen_call, stdout=subprocess.PIPE) as proc:
|
||||||
proc.wait()
|
proc.wait()
|
||||||
except:
|
except:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['control'] = self.control_name
|
logdata['control'] = self.control_name
|
||||||
logdata['status'] = status
|
logdata['status'] = status
|
||||||
log('E43', logdata)
|
log('E43', logdata)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2024 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,27 +17,40 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from os.path import isfile
|
from os.path import isfile
|
||||||
from util.logging import slogm
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from gpt.envvars import (
|
from util.arguments import (
|
||||||
FileAction
|
FileAction
|
||||||
, action_letter2enum
|
, action_letter2enum
|
||||||
)
|
)
|
||||||
from util.windows import expand_windows_var
|
from util.windows import expand_windows_var
|
||||||
from util.util import (
|
from util.util import get_homedir
|
||||||
get_homedir,
|
from util.logging import log
|
||||||
homedir_exists
|
|
||||||
)
|
|
||||||
|
|
||||||
class Envvar:
|
class Envvar:
|
||||||
|
__envvar_file_path = '/etc/gpupdate/environment'
|
||||||
|
__envvar_file_path_user = '/.gpupdate_environment'
|
||||||
|
|
||||||
def __init__(self, envvars, username=''):
|
def __init__(self, envvars, username=''):
|
||||||
self.username = username
|
self.username = username
|
||||||
self.envvars = envvars
|
self.envvars = envvars
|
||||||
if self.username == 'root':
|
if self.username == 'root':
|
||||||
self.envvar_file_path = '/etc/gpupdate/environment'
|
self.envvar_file_path = Envvar.__envvar_file_path
|
||||||
else:
|
else:
|
||||||
self.envvar_file_path = get_homedir(self.username) + '/.gpupdate_environment'
|
self.envvar_file_path = get_homedir(self.username) + Envvar.__envvar_file_path_user
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def clear_envvar_file(username = False):
|
||||||
|
if username:
|
||||||
|
file_path = get_homedir(username) + Envvar.__envvar_file_path_user
|
||||||
|
else:
|
||||||
|
file_path = Envvar.__envvar_file_path
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(file_path, 'w') as file:
|
||||||
|
file.write('')
|
||||||
|
log('D215', {'path':file_path})
|
||||||
|
except Exception as exc:
|
||||||
|
log('D216', {'path': file_path, 'exc': exc})
|
||||||
|
|
||||||
def _open_envvar_file(self):
|
def _open_envvar_file(self):
|
||||||
fd = None
|
fd = None
|
||||||
@@ -51,7 +64,7 @@ class Envvar:
|
|||||||
|
|
||||||
def _create_action(self, create_dict, envvar_file):
|
def _create_action(self, create_dict, envvar_file):
|
||||||
lines_old = envvar_file.readlines()
|
lines_old = envvar_file.readlines()
|
||||||
lines_new = list()
|
lines_new = []
|
||||||
for name in create_dict:
|
for name in create_dict:
|
||||||
exist = False
|
exist = False
|
||||||
for line in lines_old:
|
for line in lines_old:
|
||||||
@@ -80,7 +93,7 @@ class Envvar:
|
|||||||
with open(self.envvar_file_path, 'r') as f:
|
with open(self.envvar_file_path, 'r') as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
else:
|
else:
|
||||||
lines = list()
|
lines = []
|
||||||
|
|
||||||
file_changed = False
|
file_changed = False
|
||||||
for envvar_object in self.envvars:
|
for envvar_object in self.envvars:
|
||||||
@@ -92,6 +105,8 @@ class Envvar:
|
|||||||
value = value.replace('\\', '/')
|
value = value.replace('\\', '/')
|
||||||
exist_line = None
|
exist_line = None
|
||||||
for line in lines:
|
for line in lines:
|
||||||
|
if line == '\n':
|
||||||
|
continue
|
||||||
if line.split()[0] == name:
|
if line.split()[0] == name:
|
||||||
exist_line = line
|
exist_line = line
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2022 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
from gpt.folders import (
|
from util.arguments import (
|
||||||
FileAction
|
FileAction
|
||||||
, action_letter2enum
|
, action_letter2enum
|
||||||
)
|
)
|
||||||
@@ -30,6 +30,8 @@ from util.util import get_homedir
|
|||||||
from util.exceptions import NotUNCPathError
|
from util.exceptions import NotUNCPathError
|
||||||
from util.paths import UNCPath
|
from util.paths import UNCPath
|
||||||
import fnmatch
|
import fnmatch
|
||||||
|
import pwd
|
||||||
|
import grp
|
||||||
|
|
||||||
class Files_cp:
|
class Files_cp:
|
||||||
def __init__(self, file_obj, file_cache, exe_check, username=None):
|
def __init__(self, file_obj, file_cache, exe_check, username=None):
|
||||||
@@ -49,7 +51,8 @@ class Files_cp:
|
|||||||
self.suppress = str2bool(file_obj.suppress)
|
self.suppress = str2bool(file_obj.suppress)
|
||||||
self.executable = str2bool(file_obj.executable)
|
self.executable = str2bool(file_obj.executable)
|
||||||
self.username = username
|
self.username = username
|
||||||
self.fromPathFiles = list()
|
self.pw = pwd.getpwnam(username) if username else None
|
||||||
|
self.fromPathFiles = []
|
||||||
if self.fromPath:
|
if self.fromPath:
|
||||||
if targetPath[-1] == '/' or self.is_pattern(Path(self.fromPath).name):
|
if targetPath[-1] == '/' or self.is_pattern(Path(self.fromPath).name):
|
||||||
self.isTargetPathDirectory = True
|
self.isTargetPathDirectory = True
|
||||||
@@ -77,7 +80,7 @@ class Files_cp:
|
|||||||
else:
|
else:
|
||||||
return targetPath.parent.joinpath('.' + targetPath.name)
|
return targetPath.parent.joinpath('.' + targetPath.name)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['targetPath'] = targetPath
|
logdata['targetPath'] = targetPath
|
||||||
logdata['fromFile'] = fromFile
|
logdata['fromFile'] = fromFile
|
||||||
logdata['exc'] = exc
|
logdata['exc'] = exc
|
||||||
@@ -94,7 +97,7 @@ class Files_cp:
|
|||||||
if fromFilePath.exists():
|
if fromFilePath.exists():
|
||||||
targetFile.write_bytes(fromFilePath.read_bytes())
|
targetFile.write_bytes(fromFilePath.read_bytes())
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['targetFile'] = targetFile
|
logdata['targetFile'] = targetFile
|
||||||
logdata['fromFile'] = fromFile
|
logdata['fromFile'] = fromFile
|
||||||
logdata['exc'] = exc
|
logdata['exc'] = exc
|
||||||
@@ -125,7 +128,7 @@ class Files_cp:
|
|||||||
shutil.os.chmod(targetFile, 0o644)
|
shutil.os.chmod(targetFile, 0o644)
|
||||||
|
|
||||||
def _create_action(self):
|
def _create_action(self):
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
for fromFile in self.fromPathFiles:
|
for fromFile in self.fromPathFiles:
|
||||||
targetFile = None
|
targetFile = None
|
||||||
|
|
||||||
@@ -134,7 +137,8 @@ class Files_cp:
|
|||||||
if targetFile and not targetFile.exists():
|
if targetFile and not targetFile.exists():
|
||||||
self.copy_target_file(targetFile, fromFile)
|
self.copy_target_file(targetFile, fromFile)
|
||||||
if self.username:
|
if self.username:
|
||||||
shutil.chown(targetFile, self.username)
|
group_name = grp.getgrgid(self.pw.pw_gid).gr_name
|
||||||
|
chown_home_path(targetFile, username=self.username, group=group_name)
|
||||||
self.set_mod_file(targetFile, fromFile)
|
self.set_mod_file(targetFile, fromFile)
|
||||||
logdata['File'] = targetFile
|
logdata['File'] = targetFile
|
||||||
log('D191', logdata)
|
log('D191', logdata)
|
||||||
@@ -149,7 +153,7 @@ class Files_cp:
|
|||||||
list_target = [self.targetPath.name]
|
list_target = [self.targetPath.name]
|
||||||
if self.is_pattern(self.targetPath.name) and self.targetPath.parent.exists() and self.targetPath.parent.is_dir():
|
if self.is_pattern(self.targetPath.name) and self.targetPath.parent.exists() and self.targetPath.parent.is_dir():
|
||||||
list_target = fnmatch.filter([str(x.name) for x in self.targetPath.parent.iterdir() if x.is_file()], self.targetPath.name)
|
list_target = fnmatch.filter([str(x.name) for x in self.targetPath.parent.iterdir() if x.is_file()], self.targetPath.name)
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
for targetFile in list_target:
|
for targetFile in list_target:
|
||||||
targetFile = self.targetPath.parent.joinpath(targetFile)
|
targetFile = self.targetPath.parent.joinpath(targetFile)
|
||||||
try:
|
try:
|
||||||
@@ -165,13 +169,15 @@ class Files_cp:
|
|||||||
log('D165', logdata)
|
log('D165', logdata)
|
||||||
|
|
||||||
def _update_action(self):
|
def _update_action(self):
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
for fromFile in self.fromPathFiles:
|
for fromFile in self.fromPathFiles:
|
||||||
targetFile = self.get_target_file(self.targetPath, fromFile)
|
targetFile = self.get_target_file(self.targetPath, fromFile)
|
||||||
try:
|
try:
|
||||||
self.copy_target_file(targetFile, fromFile)
|
self.copy_target_file(targetFile, fromFile)
|
||||||
if self.username:
|
if self.username:
|
||||||
shutil.chown(self.targetPath, self.username)
|
shutil.chown(self.targetPath, self.username)
|
||||||
|
group_name = grp.getgrgid(self.pw.pw_gid).gr_name
|
||||||
|
chown_home_path(targetFile, username=self.username, group=group_name)
|
||||||
self.set_mod_file(targetFile, fromFile)
|
self.set_mod_file(targetFile, fromFile)
|
||||||
logdata['File'] = targetFile
|
logdata['File'] = targetFile
|
||||||
log('D192', logdata)
|
log('D192', logdata)
|
||||||
@@ -200,7 +206,7 @@ class Files_cp:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def get_list_files(self):
|
def get_list_files(self):
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['targetPath'] = str(self.targetPath)
|
logdata['targetPath'] = str(self.targetPath)
|
||||||
fromFilePath = Path(self.fromPath)
|
fromFilePath = Path(self.fromPath)
|
||||||
if not self.is_pattern(fromFilePath.name):
|
if not self.is_pattern(fromFilePath.name):
|
||||||
@@ -254,8 +260,8 @@ class Execution_check():
|
|||||||
marker_usage_path_branch = '{}\\{}%'.format(self.__hklm_branch, self.__marker_usage_path_key_name)
|
marker_usage_path_branch = '{}\\{}%'.format(self.__hklm_branch, self.__marker_usage_path_key_name)
|
||||||
self.etension_marker = storage.filter_hklm_entries(etension_marker_branch)
|
self.etension_marker = storage.filter_hklm_entries(etension_marker_branch)
|
||||||
self.marker_usage_path = storage.filter_hklm_entries(marker_usage_path_branch)
|
self.marker_usage_path = storage.filter_hklm_entries(marker_usage_path_branch)
|
||||||
self.list_paths = list()
|
self.list_paths = []
|
||||||
self.list_markers = list()
|
self.list_markers = []
|
||||||
for marker in self.etension_marker:
|
for marker in self.etension_marker:
|
||||||
self.list_markers.append(marker.data)
|
self.list_markers.append(marker.data)
|
||||||
for usage_path in self.marker_usage_path:
|
for usage_path in self.marker_usage_path:
|
||||||
@@ -266,3 +272,32 @@ class Execution_check():
|
|||||||
|
|
||||||
def get_list_markers(self):
|
def get_list_markers(self):
|
||||||
return self.list_markers
|
return self.list_markers
|
||||||
|
|
||||||
|
|
||||||
|
def chown_home_path(path: Path, username: str, group: str) -> None:
|
||||||
|
"""
|
||||||
|
Change ownership (user and group) of the given path and all its parent
|
||||||
|
directories up to (but NOT including) the user's home directory.
|
||||||
|
|
||||||
|
If the path is not inside the user's home directory, do nothing.
|
||||||
|
|
||||||
|
:param path: Path to a file or directory.
|
||||||
|
:param user: Username to set as owner.
|
||||||
|
:param group: Group name to set as group.
|
||||||
|
"""
|
||||||
|
path = path.resolve()
|
||||||
|
home_root = Path(get_homedir(username))
|
||||||
|
|
||||||
|
# Check if the path is inside user's home directory
|
||||||
|
if home_root not in path.parents:
|
||||||
|
return # Not inside user's home - do nothing
|
||||||
|
|
||||||
|
# Walk upwards from the given path until just above home_root
|
||||||
|
current = path
|
||||||
|
while True:
|
||||||
|
if current == home_root:
|
||||||
|
break # do not change ownership of the home directory itself
|
||||||
|
shutil.chown(current, user=username, group=group)
|
||||||
|
if current.parent == current: # Safety check: reached root (/)
|
||||||
|
break
|
||||||
|
current = current.parent
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from enum import Enum
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
def getprops(param_list):
|
def getprops(param_list):
|
||||||
props = dict()
|
props = {}
|
||||||
|
|
||||||
for entry in param_list:
|
for entry in param_list:
|
||||||
lentry = entry.lower()
|
lentry = entry.lower()
|
||||||
@@ -35,7 +35,7 @@ def getprops(param_list):
|
|||||||
|
|
||||||
|
|
||||||
def get_ports(param_list):
|
def get_ports(param_list):
|
||||||
portlist = list()
|
portlist = []
|
||||||
|
|
||||||
for entry in param_list:
|
for entry in param_list:
|
||||||
lentry = entry.lower()
|
lentry = entry.lower()
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
from gpt.folders import (
|
from util.arguments import (
|
||||||
FileAction
|
FileAction
|
||||||
, action_letter2enum
|
, action_letter2enum
|
||||||
)
|
)
|
||||||
@@ -28,7 +28,7 @@ from util.windows import expand_windows_var
|
|||||||
from util.util import get_homedir
|
from util.util import get_homedir
|
||||||
|
|
||||||
def remove_dir_tree(path, delete_files=False, delete_folder=False, delete_sub_folders=False):
|
def remove_dir_tree(path, delete_files=False, delete_folder=False, delete_sub_folders=False):
|
||||||
content = list()
|
content = []
|
||||||
for entry in path.iterdir():
|
for entry in path.iterdir():
|
||||||
content.append(entry)
|
content.append(entry)
|
||||||
if entry.is_file() and delete_files:
|
if entry.is_file() and delete_files:
|
||||||
@@ -44,14 +44,16 @@ def remove_dir_tree(path, delete_files=False, delete_folder=False, delete_sub_fo
|
|||||||
return content
|
return content
|
||||||
|
|
||||||
def str2bool(boolstr):
|
def str2bool(boolstr):
|
||||||
if boolstr and boolstr.lower() in ['true', 'yes', '1']:
|
if isinstance(boolstr, bool):
|
||||||
|
return boolstr
|
||||||
|
elif boolstr and boolstr.lower() in ['true', 'yes', '1']:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Folder:
|
class Folder:
|
||||||
def __init__(self, folder_object, username=None):
|
def __init__(self, folder_object, username=None):
|
||||||
folder_path = expand_windows_var(folder_object.path, username).replace('\\', '/')
|
folder_path = expand_windows_var(folder_object.path, username).replace('\\', '/').replace('//', '/')
|
||||||
if username:
|
if username:
|
||||||
folder_path = folder_path.replace(get_homedir(username), '')
|
folder_path = folder_path.replace(get_homedir(username), '')
|
||||||
self.folder_path = Path(get_homedir(username)).joinpath(folder_path if folder_path [0] != '/' else folder_path [1:])
|
self.folder_path = Path(get_homedir(username)).joinpath(folder_path if folder_path [0] != '/' else folder_path [1:])
|
||||||
@@ -75,9 +77,10 @@ class Folder:
|
|||||||
self.delete_folder,
|
self.delete_folder,
|
||||||
self.delete_sub_folders)
|
self.delete_sub_folders)
|
||||||
|
|
||||||
|
|
||||||
def act(self):
|
def act(self):
|
||||||
if self.hidden_folder == True and str(self.folder_path.name)[0] != '.':
|
if self.hidden_folder == True and str(self.folder_path.name)[0] != '.':
|
||||||
path_components = list(self.folder_path.parts)
|
path_components = [*self.folder_path.parts]
|
||||||
path_components[-1] = '.' + path_components[-1]
|
path_components[-1] = '.' + path_components[-1]
|
||||||
new_folder_path = Path(*path_components)
|
new_folder_path = Path(*path_components)
|
||||||
self.folder_path = new_folder_path
|
self.folder_path = new_folder_path
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2021 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,10 +18,9 @@
|
|||||||
|
|
||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
import logging
|
|
||||||
from gi.repository import Gio, GLib
|
from gi.repository import Gio, GLib
|
||||||
|
|
||||||
from util.logging import slogm, log
|
from util.logging import log
|
||||||
|
|
||||||
class system_gsetting:
|
class system_gsetting:
|
||||||
def __init__(self, schema, path, value, lock, helper_function=None):
|
def __init__(self, schema, path, value, lock, helper_function=None):
|
||||||
@@ -54,15 +53,15 @@ class system_gsettings:
|
|||||||
__profile_data = 'user-db:user\nsystem-db:policy\nsystem-db:local\n'
|
__profile_data = 'user-db:user\nsystem-db:policy\nsystem-db:local\n'
|
||||||
|
|
||||||
def __init__(self, override_file_path):
|
def __init__(self, override_file_path):
|
||||||
self.gsettings = list()
|
self.gsettings = []
|
||||||
self.locks = list()
|
self.locks = []
|
||||||
self.override_file_path = override_file_path
|
self.override_file_path = override_file_path
|
||||||
|
|
||||||
def append(self, schema, path, data, lock, helper):
|
def append(self, schema, path, data, lock, helper):
|
||||||
if check_existing_gsettings(schema, path):
|
if check_existing_gsettings(schema, path):
|
||||||
self.gsettings.append(system_gsetting(schema, path, data, lock, helper))
|
self.gsettings.append(system_gsetting(schema, path, data, lock, helper))
|
||||||
else:
|
else:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['schema'] = schema
|
logdata['schema'] = schema
|
||||||
logdata['path'] = path
|
logdata['path'] = path
|
||||||
logdata['data'] = data
|
logdata['data'] = data
|
||||||
@@ -73,7 +72,7 @@ class system_gsettings:
|
|||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
|
|
||||||
for gsetting in self.gsettings:
|
for gsetting in self.gsettings:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['gsetting.schema'] = gsetting.schema
|
logdata['gsetting.schema'] = gsetting.schema
|
||||||
logdata['gsetting.path'] = gsetting.path
|
logdata['gsetting.path'] = gsetting.path
|
||||||
logdata['gsetting.value'] = gsetting.value
|
logdata['gsetting.value'] = gsetting.value
|
||||||
@@ -133,13 +132,13 @@ def check_existing_gsettings (schema, path):
|
|||||||
|
|
||||||
class user_gsettings:
|
class user_gsettings:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.gsettings = list()
|
self.gsettings = []
|
||||||
|
|
||||||
def append(self, schema, path, value, helper=None):
|
def append(self, schema, path, value, helper=None):
|
||||||
if check_existing_gsettings(schema, path):
|
if check_existing_gsettings(schema, path):
|
||||||
self.gsettings.append(user_gsetting(schema, path, value, helper))
|
self.gsettings.append(user_gsetting(schema, path, value, helper))
|
||||||
else:
|
else:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['schema'] = schema
|
logdata['schema'] = schema
|
||||||
logdata['path'] = path
|
logdata['path'] = path
|
||||||
logdata['data'] = value
|
logdata['data'] = value
|
||||||
@@ -147,7 +146,7 @@ class user_gsettings:
|
|||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
for gsetting in self.gsettings:
|
for gsetting in self.gsettings:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['gsetting.schema'] = gsetting.schema
|
logdata['gsetting.schema'] = gsetting.schema
|
||||||
logdata['gsetting.path'] = gsetting.path
|
logdata['gsetting.path'] = gsetting.path
|
||||||
logdata['gsetting.value'] = gsetting.value
|
logdata['gsetting.value'] = gsetting.value
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
from gpt.folders import (
|
from util.arguments import (
|
||||||
FileAction
|
FileAction
|
||||||
, action_letter2enum
|
, action_letter2enum
|
||||||
)
|
)
|
||||||
@@ -54,7 +54,7 @@ class Ini_file:
|
|||||||
if self.path.is_dir():
|
if self.path.is_dir():
|
||||||
return
|
return
|
||||||
if self.section not in self.config:
|
if self.section not in self.config:
|
||||||
self.config[self.section] = dict()
|
self.config[self.section] = {}
|
||||||
|
|
||||||
self.config[self.section][self.key] = self.value
|
self.config[self.section][self.key] = self.value
|
||||||
self.config.write()
|
self.config.write()
|
||||||
@@ -85,7 +85,7 @@ class Ini_file:
|
|||||||
if self.action == FileAction.REPLACE:
|
if self.action == FileAction.REPLACE:
|
||||||
self._create_action()
|
self._create_action()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['action'] = self.action
|
logdata['action'] = self.action
|
||||||
logdata['exc'] = exc
|
logdata['exc'] = exc
|
||||||
log('W23', logdata)
|
log('W23', logdata)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from gpt.folders import (
|
from util.arguments import (
|
||||||
FileAction
|
FileAction
|
||||||
, action_letter2enum
|
, action_letter2enum
|
||||||
)
|
)
|
||||||
@@ -31,7 +31,7 @@ class Networkshare:
|
|||||||
def __init__(self, networkshare_obj, username = None):
|
def __init__(self, networkshare_obj, username = None):
|
||||||
self.net_full_cmd = ['/usr/bin/net', 'usershare']
|
self.net_full_cmd = ['/usr/bin/net', 'usershare']
|
||||||
self.net_cmd_check = ['/usr/bin/net', 'usershare', 'list']
|
self.net_cmd_check = ['/usr/bin/net', 'usershare', 'list']
|
||||||
self.cmd = list()
|
self.cmd = []
|
||||||
self.name = networkshare_obj.name
|
self.name = networkshare_obj.name
|
||||||
self.path = expand_windows_var(networkshare_obj.path, username).replace('\\', '/') if networkshare_obj.path else None
|
self.path = expand_windows_var(networkshare_obj.path, username).replace('\\', '/') if networkshare_obj.path else None
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ class Networkshare:
|
|||||||
return exc
|
return exc
|
||||||
|
|
||||||
def _run_net_full_cmd(self):
|
def _run_net_full_cmd(self):
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
try:
|
try:
|
||||||
res = subprocess.check_output(self.net_full_cmd, stderr=subprocess.DEVNULL, encoding='utf-8')
|
res = subprocess.check_output(self.net_full_cmd, stderr=subprocess.DEVNULL, encoding='utf-8')
|
||||||
if res:
|
if res:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -50,6 +50,7 @@ class polkit:
|
|||||||
if os.path.isfile(self.outfile):
|
if os.path.isfile(self.outfile):
|
||||||
os.remove(self.outfile)
|
os.remove(self.outfile)
|
||||||
return
|
return
|
||||||
|
logdata = {}
|
||||||
try:
|
try:
|
||||||
template = self.__template_environment.get_template(self.infilename)
|
template = self.__template_environment.get_template(self.infilename)
|
||||||
text = template.render(**self.args)
|
text = template.render(**self.args)
|
||||||
@@ -57,12 +58,10 @@ class polkit:
|
|||||||
with open(self.outfile, 'w') as f:
|
with open(self.outfile, 'w') as f:
|
||||||
f.write(text)
|
f.write(text)
|
||||||
|
|
||||||
logdata = dict()
|
|
||||||
logdata['file'] = self.outfile
|
logdata['file'] = self.outfile
|
||||||
logdata['arguments'] = self.args
|
logdata['arguments'] = self.args
|
||||||
log('D77', logdata)
|
log('D77', logdata)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
|
||||||
logdata['file'] = self.outfile
|
logdata['file'] = self.outfile
|
||||||
logdata['arguments'] = self.args
|
logdata['arguments'] = self.args
|
||||||
log('E44', logdata)
|
log('E44', logdata)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,9 +17,8 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import dbus
|
import dbus
|
||||||
import logging
|
|
||||||
|
|
||||||
from util.logging import slogm, log
|
from util.logging import log
|
||||||
|
|
||||||
class systemd_unit:
|
class systemd_unit:
|
||||||
def __init__(self, unit_name, state):
|
def __init__(self, unit_name, state):
|
||||||
@@ -35,12 +34,14 @@ class systemd_unit:
|
|||||||
self.unit_properties = dbus.Interface(self.unit_proxy, dbus_interface='org.freedesktop.DBus.Properties')
|
self.unit_properties = dbus.Interface(self.unit_proxy, dbus_interface='org.freedesktop.DBus.Properties')
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
|
logdata = {'unit': self.unit_name}
|
||||||
if self.desired_state == 1:
|
if self.desired_state == 1:
|
||||||
self.manager.UnmaskUnitFiles([self.unit_name], dbus.Boolean(False))
|
self.manager.UnmaskUnitFiles([self.unit_name], dbus.Boolean(False))
|
||||||
self.manager.EnableUnitFiles([self.unit_name], dbus.Boolean(False), dbus.Boolean(True))
|
self.manager.EnableUnitFiles([self.unit_name], dbus.Boolean(False), dbus.Boolean(True))
|
||||||
|
if self.unit_name == 'gpupdate.service':
|
||||||
|
if self.manager.GetUnitFileState(dbus.String(self.unit_name)) == 'enabled':
|
||||||
|
return
|
||||||
self.manager.StartUnit(self.unit_name, 'replace')
|
self.manager.StartUnit(self.unit_name, 'replace')
|
||||||
logdata = dict()
|
|
||||||
logdata['unit'] = self.unit_name
|
|
||||||
log('I6', logdata)
|
log('I6', logdata)
|
||||||
|
|
||||||
# In case the service has 'RestartSec' property set it
|
# In case the service has 'RestartSec' property set it
|
||||||
@@ -48,27 +49,21 @@ class systemd_unit:
|
|||||||
# 'active' so we consider 'activating' a valid state too.
|
# 'active' so we consider 'activating' a valid state too.
|
||||||
service_state = self._get_state()
|
service_state = self._get_state()
|
||||||
|
|
||||||
if not service_state in ['active', 'activating']:
|
if service_state not in ('active', 'activating'):
|
||||||
service_timer_name = self.unit_name.replace(".service", ".timer")
|
service_timer_name = self.unit_name.replace(".service", ".timer")
|
||||||
self.unit = self.manager.LoadUnit(dbus.String(service_timer_name))
|
self.unit = self.manager.LoadUnit(dbus.String(service_timer_name))
|
||||||
service_state = self._get_state()
|
service_state = self._get_state()
|
||||||
if not service_state in ['active', 'activating']:
|
if service_state not in ('active', 'activating'):
|
||||||
logdata = dict()
|
|
||||||
logdata['unit'] = self.unit_name
|
|
||||||
log('E46', logdata)
|
log('E46', logdata)
|
||||||
else:
|
else:
|
||||||
self.manager.StopUnit(self.unit_name, 'replace')
|
self.manager.StopUnit(self.unit_name, 'replace')
|
||||||
self.manager.DisableUnitFiles([self.unit_name], dbus.Boolean(False))
|
self.manager.DisableUnitFiles([self.unit_name], dbus.Boolean(False))
|
||||||
self.manager.MaskUnitFiles([self.unit_name], dbus.Boolean(False), dbus.Boolean(True))
|
self.manager.MaskUnitFiles([self.unit_name], dbus.Boolean(False), dbus.Boolean(True))
|
||||||
logdata = dict()
|
|
||||||
logdata['unit'] = self.unit_name
|
|
||||||
log('I6', logdata)
|
log('I6', logdata)
|
||||||
|
|
||||||
service_state = self._get_state()
|
service_state = self._get_state()
|
||||||
|
|
||||||
if not service_state in ['stopped', 'deactivating', 'inactive']:
|
if service_state not in ('stopped', 'deactivating', 'inactive'):
|
||||||
logdata = dict()
|
|
||||||
logdata['unit'] = self.unit_name
|
|
||||||
log('E46', logdata)
|
log('E46', logdata)
|
||||||
|
|
||||||
def _get_state(self):
|
def _get_state(self):
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -30,19 +30,17 @@ class chromium_applier(applier_frontend):
|
|||||||
__module_name = 'ChromiumApplier'
|
__module_name = 'ChromiumApplier'
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__registry_branch = 'Software\\Policies\\Google\\Chrome'
|
__registry_branch = 'Software/Policies/Google/Chrome'
|
||||||
__managed_policies_path = '/etc/chromium/policies/managed'
|
__managed_policies_path = '/etc/chromium/policies/managed'
|
||||||
__recommended_policies_path = '/etc/chromium/policies/recommended'
|
__recommended_policies_path = '/etc/chromium/policies/recommended'
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self._is_machine_name = is_machine_name(self.username)
|
self._is_machine_name = is_machine_name(self.username)
|
||||||
chromium_filter = '{}%'.format(self.__registry_branch)
|
self.chromium_keys = self.storage.filter_hklm_entries(self.__registry_branch)
|
||||||
self.chromium_keys = self.storage.filter_hklm_entries(chromium_filter)
|
|
||||||
|
|
||||||
self.policies_json = dict()
|
self.policies_json = {}
|
||||||
|
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
@@ -70,7 +68,7 @@ class chromium_applier(applier_frontend):
|
|||||||
os.makedirs(self.__managed_policies_path, exist_ok=True)
|
os.makedirs(self.__managed_policies_path, exist_ok=True)
|
||||||
with open(destfile, 'w') as f:
|
with open(destfile, 'w') as f:
|
||||||
json.dump(dict_item_to_list(self.policies_json), f)
|
json.dump(dict_item_to_list(self.policies_json), f)
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['destfile'] = destfile
|
logdata['destfile'] = destfile
|
||||||
log('D97', logdata)
|
log('D97', logdata)
|
||||||
|
|
||||||
@@ -78,7 +76,7 @@ class chromium_applier(applier_frontend):
|
|||||||
os.makedirs(self.__recommended_policies_path, exist_ok=True)
|
os.makedirs(self.__recommended_policies_path, exist_ok=True)
|
||||||
with open(destfilerec, 'w') as f:
|
with open(destfilerec, 'w') as f:
|
||||||
json.dump(dict_item_to_list(recommended__json), f)
|
json.dump(dict_item_to_list(recommended__json), f)
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['destfilerec'] = destfilerec
|
logdata['destfilerec'] = destfilerec
|
||||||
log('D97', logdata)
|
log('D97', logdata)
|
||||||
|
|
||||||
@@ -122,6 +120,11 @@ class chromium_applier(applier_frontend):
|
|||||||
'ProxyServerMode',
|
'ProxyServerMode',
|
||||||
'ExtensionManifestV2Availability',
|
'ExtensionManifestV2Availability',
|
||||||
'ExtensionUnpublishedAvailability',
|
'ExtensionUnpublishedAvailability',
|
||||||
|
'CreateThemesSettings',
|
||||||
|
'DevToolsGenAiSettings',
|
||||||
|
'GenAILocalFoundationalModelSettings',
|
||||||
|
'HelpMeWriteSettings',
|
||||||
|
'TabOrganizerSettings',
|
||||||
'BrowserSwitcherParsingMode',
|
'BrowserSwitcherParsingMode',
|
||||||
'CloudAPAuthEnabled',
|
'CloudAPAuthEnabled',
|
||||||
'AdsSettingForIntrusiveAdsSites',
|
'AdsSettingForIntrusiveAdsSites',
|
||||||
@@ -135,10 +138,14 @@ class chromium_applier(applier_frontend):
|
|||||||
'HeadlessMode',
|
'HeadlessMode',
|
||||||
'IncognitoModeAvailability',
|
'IncognitoModeAvailability',
|
||||||
'IntranetRedirectBehavior',
|
'IntranetRedirectBehavior',
|
||||||
|
'LensOverlaySettings',
|
||||||
|
'MemorySaverModeSavings',
|
||||||
'NetworkPredictionOptions',
|
'NetworkPredictionOptions',
|
||||||
'ProfilePickerOnStartupAvailability',
|
'ProfilePickerOnStartupAvailability',
|
||||||
|
'ProfileReauthPrompt',
|
||||||
'RelaunchNotification',
|
'RelaunchNotification',
|
||||||
'SafeSitesFilterBehavior',
|
'SafeSitesFilterBehavior',
|
||||||
|
'ToolbarAvatarLabelSettings',
|
||||||
'UserAgentReduction',
|
'UserAgentReduction',
|
||||||
'BatterySaverModeAvailability_recommended',
|
'BatterySaverModeAvailability_recommended',
|
||||||
'DownloadRestrictions_recommended',
|
'DownloadRestrictions_recommended',
|
||||||
@@ -168,7 +175,7 @@ class chromium_applier(applier_frontend):
|
|||||||
'''
|
'''
|
||||||
Parse registry path string and leave key parameters
|
Parse registry path string and leave key parameters
|
||||||
'''
|
'''
|
||||||
parts = hivekeyname.replace(self.__registry_branch, '').split('\\')
|
parts = hivekeyname.replace(self.__registry_branch, '').split('/')
|
||||||
return parts
|
return parts
|
||||||
|
|
||||||
|
|
||||||
@@ -176,7 +183,7 @@ class chromium_applier(applier_frontend):
|
|||||||
'''
|
'''
|
||||||
Collect dictionaries from registry keys into a general dictionary
|
Collect dictionaries from registry keys into a general dictionary
|
||||||
'''
|
'''
|
||||||
counts = dict()
|
counts = {}
|
||||||
#getting the list of keys to read as an integer
|
#getting the list of keys to read as an integer
|
||||||
valuename_typeint = self.get_valuename_typeint()
|
valuename_typeint = self.get_valuename_typeint()
|
||||||
for it_data in chromium_keys:
|
for it_data in chromium_keys:
|
||||||
@@ -204,7 +211,7 @@ class chromium_applier(applier_frontend):
|
|||||||
branch[parts[-1]] = str(it_data.data).replace('\\', '/')
|
branch[parts[-1]] = str(it_data.data).replace('\\', '/')
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['Exception'] = exc
|
logdata['Exception'] = exc
|
||||||
logdata['keyname'] = it_data.keyname
|
logdata['keyname'] = it_data.keyname
|
||||||
log('D178', logdata)
|
log('D178', logdata)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2022 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
import os
|
import os
|
||||||
|
import pwd
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import string
|
import string
|
||||||
@@ -26,12 +27,12 @@ from .applier_frontend import (
|
|||||||
applier_frontend
|
applier_frontend
|
||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
from util.util import get_homedir
|
from util.util import get_homedir, get_uid_by_username, get_machine_name
|
||||||
from util.logging import log
|
from util.logging import log
|
||||||
|
|
||||||
def storage_get_drives(storage, sid):
|
def storage_get_drives(storage):
|
||||||
drives = storage.get_drives(sid)
|
drives = storage.get_drives()
|
||||||
drive_list = list()
|
drive_list = []
|
||||||
|
|
||||||
for drv_obj in drives:
|
for drv_obj in drives:
|
||||||
drive_list.append(drv_obj)
|
drive_list.append(drv_obj)
|
||||||
@@ -64,7 +65,7 @@ def remove_escaped_quotes(input_string):
|
|||||||
class Drive_list:
|
class Drive_list:
|
||||||
__alphabet = string.ascii_uppercase
|
__alphabet = string.ascii_uppercase
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.dict_drives = dict()
|
self.dict_drives = {}
|
||||||
|
|
||||||
def __get_letter(self, letter):
|
def __get_letter(self, letter):
|
||||||
slice_letters = set(self.__alphabet[self.__alphabet.find(letter) + 1:]) - set(self.dict_drives.keys())
|
slice_letters = set(self.__alphabet[self.__alphabet.find(letter) + 1:]) - set(self.dict_drives.keys())
|
||||||
@@ -124,14 +125,28 @@ class cifs_applier(applier_frontend):
|
|||||||
__module_name = 'CIFSApplier'
|
__module_name = 'CIFSApplier'
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
|
__dir4clean = '/etc/auto.master.gpupdate.d'
|
||||||
|
|
||||||
def __init__(self, storage, sid):
|
def __init__(self, storage):
|
||||||
self.applier_cifs = cifs_applier_user(storage, sid, None)
|
self.clear_directory_auto_dir()
|
||||||
|
self.applier_cifs = cifs_applier_user(storage, None)
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
storage
|
storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
, self.__module_experimental
|
, self.__module_experimental
|
||||||
)
|
)
|
||||||
|
def clear_directory_auto_dir(self):
|
||||||
|
path = Path(self.__dir4clean)
|
||||||
|
if not path.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
for item in path.iterdir():
|
||||||
|
try:
|
||||||
|
if item.is_file() or item.is_symlink():
|
||||||
|
item.unlink()
|
||||||
|
except Exception as exc:
|
||||||
|
log('W37', {'exc': exc})
|
||||||
|
log('D231')
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
@@ -152,32 +167,75 @@ class cifs_applier_user(applier_frontend):
|
|||||||
__template_auto = 'autofs_auto.j2'
|
__template_auto = 'autofs_auto.j2'
|
||||||
__template_mountpoints_hide = 'autofs_mountpoints_hide.j2'
|
__template_mountpoints_hide = 'autofs_mountpoints_hide.j2'
|
||||||
__template_auto_hide = 'autofs_auto_hide.j2'
|
__template_auto_hide = 'autofs_auto_hide.j2'
|
||||||
__enable_home_link = 'Software\\BaseALT\\Policies\\GPUpdate\\DriveMapsHome'
|
__enable_home_link = '/Software/BaseALT/Policies/GPUpdate/DriveMapsHome'
|
||||||
__enable_home_link_user = 'Software\\BaseALT\\Policies\\GPUpdate\\DriveMapsHomeUser'
|
__enable_home_link_user = '/Software/BaseALT/Policies/GPUpdate/DriveMapsHomeUser'
|
||||||
|
__name_dir = '/Software/BaseALT/Policies/GPUpdate'
|
||||||
|
__name_link_prefix = '/Software/BaseALT/Policies/GPUpdate/DriveMapsHomeDisableNet'
|
||||||
|
__name_link_prefix_user = '/Software/BaseALT/Policies/GPUpdate/DriveMapsHomeDisableNetUser'
|
||||||
|
__key_link_prefix = 'DriveMapsHomeDisableNet'
|
||||||
|
__key_link_prefix_user = 'DriveMapsHomeDisableNetUser'
|
||||||
|
__timeout_user_key = '/Software/BaseALT/Policies/GPUpdate/TimeoutAutofsUser'
|
||||||
|
__timeout_key = '/Software/BaseALT/Policies/GPUpdate/TimeoutAutofs'
|
||||||
|
__cifsacl_key = '/Software/BaseALT/Policies/GPUpdate/CifsaclDisable'
|
||||||
__target_mountpoint = '/media/gpupdate'
|
__target_mountpoint = '/media/gpupdate'
|
||||||
__target_mountpoint_user = '/run/media'
|
__target_mountpoint_user = '/run/media'
|
||||||
__mountpoint_dirname = 'drives.system'
|
__mountpoint_dirname = 'drives.system'
|
||||||
__mountpoint_dirname_user = 'drives'
|
__mountpoint_dirname_user = 'drives'
|
||||||
|
__key_cifs_previous_value = 'Previous/Software/BaseALT/Policies/GPUpdate'
|
||||||
|
__key_preferences = 'Software/BaseALT/Policies/Preferences/'
|
||||||
|
__key_preferences_previous = 'Previous/Software/BaseALT/Policies/Preferences/'
|
||||||
|
__name_value = 'DriveMapsName'
|
||||||
|
__name_value_user = 'DriveMapsNameUser'
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self.state_home_link = False
|
self.state_home_link = False
|
||||||
self.state_home_link_user = False
|
self.state_home_link_user = False
|
||||||
|
self.dict_registry_machine = self.storage.get_dictionary_from_dconf_file_db()
|
||||||
|
self.homedir = ''
|
||||||
|
name_dir = self.__name_dir[1:]
|
||||||
|
|
||||||
if username:
|
if username:
|
||||||
|
self.dict_registry_user = self.storage.get_dictionary_from_dconf_file_db(get_uid_by_username(username))
|
||||||
self.home = self.__target_mountpoint_user + '/' + username
|
self.home = self.__target_mountpoint_user + '/' + username
|
||||||
self.state_home_link = self.check_enable_home_link(self.__enable_home_link)
|
self.state_home_link = self.storage.check_enable_key(self.__enable_home_link)
|
||||||
self.state_home_link_user = self.check_enable_home_link(self.__enable_home_link_user)
|
self.state_home_link_disable_net = self.storage.check_enable_key(self.__name_link_prefix)
|
||||||
|
self.state_home_link_disable_net_user = self.storage.check_enable_key(self.__name_link_prefix_user)
|
||||||
|
|
||||||
|
self.state_home_link_user = self.storage.check_enable_key(self.__enable_home_link_user)
|
||||||
|
self.timeout = self.storage.get_entry(self.__timeout_user_key)
|
||||||
|
dirname = self.storage.get_entry(self.__name_dir + '/' + self.__name_value_user)
|
||||||
|
dirname_system_from_machine = self.dict_registry_machine.get(name_dir, dict()).get(self.__name_value, None)
|
||||||
|
self.__mountpoint_dirname_user = dirname.data if dirname and dirname.data else self.__mountpoint_dirname_user
|
||||||
|
self.__mountpoint_dirname = dirname_system_from_machine if dirname_system_from_machine else self.__mountpoint_dirname
|
||||||
|
mntTarget = self.__mountpoint_dirname_user
|
||||||
|
|
||||||
|
self.keys_cifs_previous_values_user = self.dict_registry_user.get(self.__key_cifs_previous_value,{})
|
||||||
|
self.keys_cifs_values_user = self.dict_registry_user.get(name_dir,{})
|
||||||
|
self.keys_the_preferences_previous_values_user = self.dict_registry_user.get((self.__key_preferences_previous+self.username),{}).get('Drives', {})
|
||||||
|
self.keys_the_preferences_values_user = self.dict_registry_user.get((self.__key_preferences+self.username),{}).get('Drives', {})
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.home = self.__target_mountpoint
|
self.home = self.__target_mountpoint
|
||||||
|
self.timeout = self.storage.get_entry(self.__timeout_key)
|
||||||
|
dirname_system = self.storage.get_entry(self.__name_dir + '/' + self.__name_value)
|
||||||
|
self.__mountpoint_dirname = dirname_system.data if dirname_system and dirname_system.data else self.__mountpoint_dirname
|
||||||
|
mntTarget = self.__mountpoint_dirname
|
||||||
|
|
||||||
conf_file = '{}.conf'.format(sid)
|
self.keys_cifs_previous_values_machine = self.dict_registry_machine.get(self.__key_cifs_previous_value,{})
|
||||||
conf_hide_file = '{}_hide.conf'.format(sid)
|
self.keys_cifs_values_machine = self.dict_registry_machine.get(name_dir,{})
|
||||||
autofs_file = '{}.autofs'.format(sid)
|
self.keys_the_preferences_previous_values = self.dict_registry_machine.get((self.__key_preferences_previous+'Machine'),{}).get('Drives', {})
|
||||||
autofs_hide_file = '{}_hide.autofs'.format(sid)
|
self.keys_the_preferences_values = self.dict_registry_machine.get((self.__key_preferences+'Machine'),{}).get('Drives', {})
|
||||||
cred_file = '{}.creds'.format(sid)
|
self.cifsacl_disable = self.storage.get_entry(self.__cifsacl_key, preg=False)
|
||||||
|
|
||||||
|
self.mntTarget = mntTarget.translate(str.maketrans({" ": r"\ "}))
|
||||||
|
file_name = username if username else get_machine_name()
|
||||||
|
conf_file = '{}.conf'.format(file_name)
|
||||||
|
conf_hide_file = '{}_hide.conf'.format(file_name)
|
||||||
|
autofs_file = '{}.autofs'.format(file_name)
|
||||||
|
autofs_hide_file = '{}_hide.autofs'.format(file_name)
|
||||||
|
cred_file = '{}.creds'.format(file_name)
|
||||||
|
|
||||||
self.auto_master_d = Path(self.__auto_dir)
|
self.auto_master_d = Path(self.__auto_dir)
|
||||||
|
|
||||||
@@ -195,13 +253,9 @@ class cifs_applier_user(applier_frontend):
|
|||||||
self.user_autofs_hide.unlink()
|
self.user_autofs_hide.unlink()
|
||||||
self.user_creds = self.auto_master_d / cred_file
|
self.user_creds = self.auto_master_d / cred_file
|
||||||
|
|
||||||
if username:
|
|
||||||
self.mntTarget = self.__mountpoint_dirname_user
|
|
||||||
else:
|
|
||||||
self.mntTarget = self.__mountpoint_dirname
|
|
||||||
|
|
||||||
self.mount_dir = Path(os.path.join(self.home))
|
self.mount_dir = Path(os.path.join(self.home))
|
||||||
self.drives = storage_get_drives(self.storage, self.sid)
|
self.drives = storage_get_drives(self.storage)
|
||||||
|
|
||||||
self.template_loader = jinja2.FileSystemLoader(searchpath=self.__template_path)
|
self.template_loader = jinja2.FileSystemLoader(searchpath=self.__template_path)
|
||||||
self.template_env = jinja2.Environment(loader=self.template_loader)
|
self.template_env = jinja2.Environment(loader=self.template_loader)
|
||||||
@@ -219,12 +273,20 @@ class cifs_applier_user(applier_frontend):
|
|||||||
, self.__module_experimental
|
, self.__module_experimental
|
||||||
)
|
)
|
||||||
|
|
||||||
def check_enable_home_link(self, enable_home_link):
|
|
||||||
if self.storage.get_hkcu_entry(self.sid, enable_home_link):
|
def is_mount_point_dirname(self):
|
||||||
data = self.storage.get_hkcu_entry(self.sid, enable_home_link).data
|
if self.username:
|
||||||
return bool(int(data))
|
return self.mount_dir.joinpath(self.__mountpoint_dirname_user).is_mount()
|
||||||
else:
|
else:
|
||||||
return False
|
return self.mount_dir.joinpath(self.__mountpoint_dirname).is_mount()
|
||||||
|
|
||||||
|
def is_changed_keys(self):
|
||||||
|
if self.username:
|
||||||
|
return (self.keys_cifs_previous_values_user.get(self.__name_value_user) != self.keys_cifs_values_user.get(self.__name_value_user) or
|
||||||
|
self.keys_the_preferences_previous_values_user != self.keys_the_preferences_values_user)
|
||||||
|
else:
|
||||||
|
return (self.keys_cifs_previous_values_machine.get(self.__name_value) != self.keys_cifs_values_machine.get(self.__name_value) or
|
||||||
|
self.keys_the_preferences_previous_values != self.keys_the_preferences_values)
|
||||||
|
|
||||||
def user_context_apply(self):
|
def user_context_apply(self):
|
||||||
'''
|
'''
|
||||||
@@ -237,6 +299,10 @@ class cifs_applier_user(applier_frontend):
|
|||||||
self.auto_master_d.mkdir(parents=True, exist_ok=True)
|
self.auto_master_d.mkdir(parents=True, exist_ok=True)
|
||||||
# Create user's destination mount directory
|
# Create user's destination mount directory
|
||||||
self.mount_dir.mkdir(parents=True, exist_ok=True)
|
self.mount_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
uid = pwd.getpwnam(self.username).pw_uid if self.username else None
|
||||||
|
if uid:
|
||||||
|
os.chown(self.mount_dir, uid=uid, gid=-1)
|
||||||
|
self.mount_dir.chmod(0o700)
|
||||||
|
|
||||||
# Add pointer to /etc/auto.master.gpiupdate.d in /etc/auto.master
|
# Add pointer to /etc/auto.master.gpiupdate.d in /etc/auto.master
|
||||||
auto_destdir = '+dir:{}'.format(self.__auto_dir)
|
auto_destdir = '+dir:{}'.format(self.__auto_dir)
|
||||||
@@ -245,7 +311,7 @@ class cifs_applier_user(applier_frontend):
|
|||||||
# Collect data for drive settings
|
# Collect data for drive settings
|
||||||
drive_list = Drive_list()
|
drive_list = Drive_list()
|
||||||
for drv in self.drives:
|
for drv in self.drives:
|
||||||
drive_settings = dict()
|
drive_settings = {}
|
||||||
drive_settings['dir'] = drv.dir
|
drive_settings['dir'] = drv.dir
|
||||||
drive_settings['login'] = drv.login
|
drive_settings['login'] = drv.login
|
||||||
drive_settings['password'] = drv.password
|
drive_settings['password'] = drv.password
|
||||||
@@ -256,11 +322,13 @@ class cifs_applier_user(applier_frontend):
|
|||||||
drive_settings['label'] = remove_escaped_quotes(drv.label)
|
drive_settings['label'] = remove_escaped_quotes(drv.label)
|
||||||
drive_settings['persistent'] = drv.persistent
|
drive_settings['persistent'] = drv.persistent
|
||||||
drive_settings['useLetter'] = drv.useLetter
|
drive_settings['useLetter'] = drv.useLetter
|
||||||
|
drive_settings['username'] = self.username
|
||||||
|
drive_settings['cifsacl'] = False if self.cifsacl_disable else True
|
||||||
|
|
||||||
drive_list.append(drive_settings)
|
drive_list.append(drive_settings)
|
||||||
|
|
||||||
if drive_list.len() > 0:
|
if drive_list.len() > 0:
|
||||||
mount_settings = dict()
|
mount_settings = {}
|
||||||
mount_settings['drives'] = drive_list()
|
mount_settings['drives'] = drive_list()
|
||||||
mount_text = self.template_mountpoints.render(**mount_settings)
|
mount_text = self.template_mountpoints.render(**mount_settings)
|
||||||
|
|
||||||
@@ -276,10 +344,12 @@ class cifs_applier_user(applier_frontend):
|
|||||||
f.write(mount_text_hide)
|
f.write(mount_text_hide)
|
||||||
f.flush()
|
f.flush()
|
||||||
|
|
||||||
autofs_settings = dict()
|
autofs_settings = {}
|
||||||
autofs_settings['home_dir'] = self.home
|
autofs_settings['home_dir'] = self.home
|
||||||
autofs_settings['mntTarget'] = self.mntTarget
|
autofs_settings['mntTarget'] = self.mntTarget
|
||||||
autofs_settings['mount_file'] = self.user_config.resolve()
|
autofs_settings['mount_file'] = self.user_config.resolve()
|
||||||
|
autofs_settings['timeout'] = self.timeout.data if self.timeout and self.timeout.data else 120
|
||||||
|
|
||||||
autofs_text = self.template_auto.render(**autofs_settings)
|
autofs_text = self.template_auto.render(**autofs_settings)
|
||||||
|
|
||||||
with open(self.user_autofs.resolve(), 'w') as f:
|
with open(self.user_autofs.resolve(), 'w') as f:
|
||||||
@@ -294,59 +364,109 @@ class cifs_applier_user(applier_frontend):
|
|||||||
f.write(autofs_text)
|
f.write(autofs_text)
|
||||||
f.flush()
|
f.flush()
|
||||||
|
|
||||||
if self.username:
|
if self.is_changed_keys() or (self.drives and not self.is_mount_point_dirname()):
|
||||||
self.update_drivemaps_home_links()
|
self.restart_autofs()
|
||||||
|
|
||||||
|
if self.username:
|
||||||
|
self.update_drivemaps_home_links()
|
||||||
|
|
||||||
|
def restart_autofs(self):
|
||||||
|
try:
|
||||||
subprocess.check_call(['/bin/systemctl', 'restart', 'autofs'])
|
subprocess.check_call(['/bin/systemctl', 'restart', 'autofs'])
|
||||||
|
except Exception as exc:
|
||||||
|
log('E74', {'exc': exc})
|
||||||
|
|
||||||
|
|
||||||
|
def unlink_symlink(self, symlink:Path, previous=None):
|
||||||
|
try:
|
||||||
|
if symlink.exists() and symlink.is_symlink() and symlink.owner() == 'root':
|
||||||
|
symlink.unlink()
|
||||||
|
elif symlink.is_symlink() and not symlink.exists():
|
||||||
|
symlink.unlink()
|
||||||
|
elif previous:
|
||||||
|
symlink.unlink()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def del_previous_link(self, previous_value_link , mountpoint_dirname, prefix):
|
||||||
|
d_previous = Path(self.homedir + ('/' if prefix else '/net.') + previous_value_link)
|
||||||
|
if d_previous.name != mountpoint_dirname:
|
||||||
|
dHide_previous = Path(self.homedir + ('/.' if prefix else '/.net.') + previous_value_link)
|
||||||
|
self.unlink_symlink(d_previous, True)
|
||||||
|
self.unlink_symlink(dHide_previous, True)
|
||||||
|
|
||||||
def update_drivemaps_home_links(self):
|
def update_drivemaps_home_links(self):
|
||||||
dUser = Path(get_homedir(self.username)+'/net.' + self.__mountpoint_dirname_user)
|
if self.state_home_link_disable_net:
|
||||||
dUserHide = Path(get_homedir(self.username)+'/.net.' + self.__mountpoint_dirname_user)
|
prefix = ''
|
||||||
|
else:
|
||||||
|
prefix = 'net.'
|
||||||
|
if self.state_home_link_disable_net_user:
|
||||||
|
prefix_user = ''
|
||||||
|
else:
|
||||||
|
prefix_user = 'net.'
|
||||||
|
|
||||||
|
previous_value_link = self.keys_cifs_previous_values_machine.get(self.__name_value, self.__mountpoint_dirname)
|
||||||
|
previous_state_home_link_disable_net_user = self.keys_cifs_previous_values_user.get(self.__key_link_prefix_user)
|
||||||
|
previous_state_home_link_disable_net = self.keys_cifs_previous_values_user.get(self.__key_link_prefix)
|
||||||
|
previous_value_link_user = self.keys_cifs_previous_values_user.get(self.__name_value_user, self.__mountpoint_dirname_user)
|
||||||
|
|
||||||
|
self.homedir = get_homedir(self.username)
|
||||||
|
|
||||||
|
dUser = Path(self.homedir + '/' + prefix_user + self.__mountpoint_dirname_user)
|
||||||
|
dUserHide = Path(self.homedir + '/.' + prefix_user + self.__mountpoint_dirname_user)
|
||||||
|
dMachine = Path(self.homedir+'/' + prefix + self.__mountpoint_dirname)
|
||||||
|
dMachineHide = Path(self.homedir+'/.' + prefix + self.__mountpoint_dirname)
|
||||||
|
|
||||||
if self.state_home_link_user:
|
if self.state_home_link_user:
|
||||||
dUserMountpoint = Path(self.home).joinpath(self.__mountpoint_dirname_user)
|
dUserMountpoint = Path(self.home).joinpath(self.__mountpoint_dirname_user)
|
||||||
dUserMountpointHide = Path(self.home).joinpath('.' + self.__mountpoint_dirname_user)
|
dUserMountpointHide = Path(self.home).joinpath('.' + self.__mountpoint_dirname_user)
|
||||||
|
self.del_previous_link(previous_value_link_user, dUser.name, previous_state_home_link_disable_net_user)
|
||||||
if not dUser.exists():
|
if not dUser.exists() and dUserMountpoint.exists():
|
||||||
try:
|
try:
|
||||||
os.symlink(dUserMountpoint, dUser, True)
|
os.symlink(dUserMountpoint, dUser, True)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
log('D194', {'exc': exc})
|
log('D194', {'exc': exc})
|
||||||
|
elif dUser.is_symlink() and not dUserMountpoint.exists():
|
||||||
|
self.unlink_symlink(dUser)
|
||||||
|
|
||||||
if not dUserHide.exists():
|
if not dUserHide.exists() and dUserMountpointHide.exists():
|
||||||
try:
|
try:
|
||||||
os.symlink(dUserMountpointHide, dUserHide, True)
|
os.symlink(dUserMountpointHide, dUserHide, True)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
log('D196', {'exc': exc})
|
log('D196', {'exc': exc})
|
||||||
|
elif dUserHide.is_symlink() and not dUserMountpointHide.exists():
|
||||||
|
self.unlink_symlink(dUserHide)
|
||||||
else:
|
else:
|
||||||
if dUser.is_symlink() and dUser.owner() == 'root':
|
self.del_previous_link(previous_value_link_user, dUser.name, previous_state_home_link_disable_net_user)
|
||||||
dUser.unlink()
|
self.unlink_symlink(dUser)
|
||||||
if dUserHide.is_symlink() and dUserHide.owner() == 'root':
|
self.unlink_symlink(dUserHide)
|
||||||
dUserHide.unlink()
|
|
||||||
|
|
||||||
dMachine = Path(get_homedir(self.username)+'/net.' + self.__mountpoint_dirname)
|
|
||||||
dMachineHide = Path(get_homedir(self.username)+'/.net.' + self.__mountpoint_dirname)
|
|
||||||
|
|
||||||
if self.state_home_link:
|
if self.state_home_link:
|
||||||
dMachineMountpoint = Path(self.__target_mountpoint).joinpath(self.__mountpoint_dirname)
|
dMachineMountpoint = Path(self.__target_mountpoint).joinpath(self.__mountpoint_dirname)
|
||||||
dMachineMountpointHide = Path(self.__target_mountpoint).joinpath('.' + self.__mountpoint_dirname)
|
dMachineMountpointHide = Path(self.__target_mountpoint).joinpath('.' + self.__mountpoint_dirname)
|
||||||
|
self.del_previous_link(previous_value_link, dMachine.name, previous_state_home_link_disable_net)
|
||||||
|
|
||||||
if not dMachine.exists():
|
if not dMachine.exists() and dMachineMountpoint.exists():
|
||||||
try:
|
try:
|
||||||
os.symlink(dMachineMountpoint, dMachine, True)
|
os.symlink(dMachineMountpoint, dMachine, True)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
log('D195', {'exc': exc})
|
log('D195', {'exc': exc})
|
||||||
|
elif dMachine.is_symlink() and not dMachineMountpoint.exists():
|
||||||
|
self.unlink_symlink(dMachine)
|
||||||
|
|
||||||
if not dMachineHide.exists():
|
if not dMachineHide.exists() and dMachineMountpointHide.exists():
|
||||||
try:
|
try:
|
||||||
os.symlink(dMachineMountpointHide, dMachineHide, True)
|
os.symlink(dMachineMountpointHide, dMachineHide, True)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
log('D197', {'exc': exc})
|
log('D197', {'exc': exc})
|
||||||
|
elif dMachineHide.is_symlink() and not dMachineMountpointHide.exists():
|
||||||
|
self.unlink_symlink(dMachineHide)
|
||||||
else:
|
else:
|
||||||
if dMachine.is_symlink() and dMachine.owner() == 'root':
|
self.del_previous_link(previous_value_link, dMachine.name, previous_state_home_link_disable_net)
|
||||||
dMachine.unlink()
|
self.unlink_symlink(dMachine)
|
||||||
if dMachineHide.is_symlink() and dMachineHide.owner() == 'root':
|
self.unlink_symlink(dMachineHide)
|
||||||
dMachineHide.unlink()
|
|
||||||
|
|
||||||
def admin_context_apply(self):
|
def admin_context_apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,20 +21,19 @@ from .applier_frontend import (
|
|||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
from .appliers.control import control
|
from .appliers.control import control
|
||||||
from util.logging import slogm, log
|
from util.logging import log
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
class control_applier(applier_frontend):
|
class control_applier(applier_frontend):
|
||||||
__module_name = 'ControlApplier'
|
__module_name = 'ControlApplier'
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
_registry_branch = 'Software\\BaseALT\\Policies\\Control'
|
_registry_branch = 'Software/BaseALT/Policies/Control'
|
||||||
|
|
||||||
def __init__(self, storage):
|
def __init__(self, storage):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.control_settings = self.storage.filter_hklm_entries('Software\\BaseALT\\Policies\\Control%')
|
self.control_settings = self.storage.filter_hklm_entries(self._registry_branch)
|
||||||
self.controls = list()
|
self.controls = []
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
@@ -43,12 +42,10 @@ class control_applier(applier_frontend):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
for setting in self.control_settings:
|
for setting in self.control_settings:
|
||||||
valuename = setting.hive_key.rpartition('\\')[2]
|
valuename = setting.hive_key.rpartition('/')[2]
|
||||||
try:
|
try:
|
||||||
self.controls.append(control(valuename, int(setting.data)))
|
self.controls.append(control(valuename, int(setting.data)))
|
||||||
logdata = dict()
|
logdata = {'control': valuename, 'value': setting.data}
|
||||||
logdata['control'] = valuename
|
|
||||||
logdata['value'] = setting.data
|
|
||||||
log('I3', logdata)
|
log('I3', logdata)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
try:
|
try:
|
||||||
@@ -58,14 +55,10 @@ class control_applier(applier_frontend):
|
|||||||
log('I3', logdata)
|
log('I3', logdata)
|
||||||
continue
|
continue
|
||||||
self.controls.append(ctl)
|
self.controls.append(ctl)
|
||||||
logdata = dict()
|
logdata = {'control': valuename, 'with string value': setting.data}
|
||||||
logdata['control'] = valuename
|
|
||||||
logdata['with string value'] = setting.data
|
|
||||||
log('I3', logdata)
|
log('I3', logdata)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'control': valuename, 'exc': exc}
|
||||||
logdata['control'] = valuename
|
|
||||||
logdata['exc'] = exc
|
|
||||||
log('E39', logdata)
|
log('E39', logdata)
|
||||||
#for e in polfile.pol_file.entries:
|
#for e in polfile.pol_file.entries:
|
||||||
# print('{}:{}:{}:{}:{}'.format(e.type, e.data, e.valuename, e.keyname))
|
# print('{}:{}:{}:{}:{}'.format(e.type, e.data, e.valuename, e.keyname))
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,26 +16,22 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import cups
|
import cups
|
||||||
|
|
||||||
from .applier_frontend import (
|
from .applier_frontend import (
|
||||||
applier_frontend
|
applier_frontend
|
||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
from gpt.printers import json2printer
|
from gpt.printers import json2printer
|
||||||
from util.rpm import is_rpm_installed
|
from util.rpm import is_rpm_installed
|
||||||
from util.logging import slogm, log
|
from util.logging import log
|
||||||
|
|
||||||
def storage_get_printers(storage, sid):
|
def storage_get_printers(storage):
|
||||||
'''
|
'''
|
||||||
Query printers configuration from storage
|
Query printers configuration from storage
|
||||||
'''
|
'''
|
||||||
printer_objs = storage.get_printers(sid)
|
printer_objs = storage.get_printers()
|
||||||
printers = list()
|
printers = []
|
||||||
|
|
||||||
for prnj in printer_objs:
|
for prnj in printer_objs:
|
||||||
printers.append(prnj)
|
printers.append(prnj)
|
||||||
@@ -66,8 +62,8 @@ def connect_printer(connection, prn):
|
|||||||
|
|
||||||
class cups_applier(applier_frontend):
|
class cups_applier(applier_frontend):
|
||||||
__module_name = 'CUPSApplier'
|
__module_name = 'CUPSApplier'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
|
|
||||||
def __init__(self, storage):
|
def __init__(self, storage):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
@@ -84,10 +80,9 @@ class cups_applier(applier_frontend):
|
|||||||
try:
|
try:
|
||||||
self.cups_connection = cups.Connection()
|
self.cups_connection = cups.Connection()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'exc': exc}
|
||||||
logdata['exc', exc]
|
|
||||||
log('W20', logdata)
|
log('W20', logdata)
|
||||||
self.printers = storage_get_printers(self.storage, self.storage.get_info('machine_sid'))
|
self.printers = storage_get_printers(self.storage)
|
||||||
|
|
||||||
if self.printers:
|
if self.printers:
|
||||||
for prn in self.printers:
|
for prn in self.printers:
|
||||||
@@ -105,17 +100,16 @@ class cups_applier(applier_frontend):
|
|||||||
|
|
||||||
class cups_applier_user(applier_frontend):
|
class cups_applier_user(applier_frontend):
|
||||||
__module_name = 'CUPSApplierUser'
|
__module_name = 'CUPSApplierUser'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
, self.__module_enabled
|
, self.__module_experimental
|
||||||
)
|
)
|
||||||
|
|
||||||
def user_context_apply(self):
|
def user_context_apply(self):
|
||||||
@@ -131,7 +125,7 @@ class cups_applier_user(applier_frontend):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.cups_connection = cups.Connection()
|
self.cups_connection = cups.Connection()
|
||||||
self.printers = storage_get_printers(self.storage, self.sid)
|
self.printers = storage_get_printers(self.storage)
|
||||||
|
|
||||||
if self.printers:
|
if self.printers:
|
||||||
for prn in self.printers:
|
for prn in self.printers:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,20 +21,19 @@ from .applier_frontend import (
|
|||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
from .appliers.envvar import Envvar
|
from .appliers.envvar import Envvar
|
||||||
from util.logging import slogm, log
|
from util.logging import log
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
class envvar_applier(applier_frontend):
|
class envvar_applier(applier_frontend):
|
||||||
__module_name = 'EnvvarsApplier'
|
__module_name = 'EnvvarsApplier'
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
|
|
||||||
def __init__(self, storage, sid):
|
def __init__(self, storage):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
self.envvars = self.storage.get_envvars()
|
||||||
self.envvars = self.storage.get_envvars(self.sid)
|
Envvar.clear_envvar_file()
|
||||||
#self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_enabled)
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
@@ -49,17 +48,14 @@ class envvar_applier_user(applier_frontend):
|
|||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self.envvars = self.storage.get_envvars(self.sid)
|
self.envvars = self.storage.get_envvars()
|
||||||
#self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
Envvar.clear_envvar_file(username)
|
||||||
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
|
|
||||||
def admin_context_apply(self):
|
def admin_context_apply(self):
|
||||||
pass
|
|
||||||
|
|
||||||
def user_context_apply(self):
|
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
log('D136')
|
log('D136')
|
||||||
ev = Envvar(self.envvars, self.username)
|
ev = Envvar(self.envvars, self.username)
|
||||||
@@ -67,3 +63,6 @@ class envvar_applier_user(applier_frontend):
|
|||||||
else:
|
else:
|
||||||
log('D137')
|
log('D137')
|
||||||
|
|
||||||
|
def user_context_apply(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2022 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -28,15 +28,14 @@ from util.logging import log
|
|||||||
|
|
||||||
class file_applier(applier_frontend):
|
class file_applier(applier_frontend):
|
||||||
__module_name = 'FilesApplier'
|
__module_name = 'FilesApplier'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
|
|
||||||
def __init__(self, storage, file_cache, sid):
|
def __init__(self, storage, file_cache):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.exe_check = Execution_check(storage)
|
self.exe_check = Execution_check(storage)
|
||||||
self.sid = sid
|
|
||||||
self.file_cache = file_cache
|
self.file_cache = file_cache
|
||||||
self.files = self.storage.get_files(self.sid)
|
self.files = self.storage.get_files()
|
||||||
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@@ -52,16 +51,15 @@ class file_applier(applier_frontend):
|
|||||||
|
|
||||||
class file_applier_user(applier_frontend):
|
class file_applier_user(applier_frontend):
|
||||||
__module_name = 'FilesApplierUser'
|
__module_name = 'FilesApplierUser'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
|
|
||||||
def __init__(self, storage, file_cache, sid, username):
|
def __init__(self, storage, file_cache, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.file_cache = file_cache
|
self.file_cache = file_cache
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self.exe_check = Execution_check(storage)
|
self.exe_check = Execution_check(storage)
|
||||||
self.files = self.storage.get_files(self.sid)
|
self.files = self.storage.get_files()
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -33,112 +33,42 @@ from .applier_frontend import (
|
|||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
from util.logging import log
|
from util.logging import log
|
||||||
from util.util import is_machine_name
|
from util.util import is_machine_name, try_dict_to_literal_eval
|
||||||
|
|
||||||
class firefox_applier(applier_frontend):
|
class firefox_applier(applier_frontend):
|
||||||
__module_name = 'FirefoxApplier'
|
__module_name = 'FirefoxApplier'
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
__registry_branch = 'Software\\Policies\\Mozilla\\Firefox\\'
|
__registry_branch = 'Software/Policies/Mozilla/Firefox'
|
||||||
__firefox_installdir1 = '/usr/lib64/firefox/distribution'
|
__firefox_policies = '/etc/firefox/policies'
|
||||||
__firefox_installdir2 = '/etc/firefox/policies'
|
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self._is_machine_name = is_machine_name(self.username)
|
self._is_machine_name = is_machine_name(self.username)
|
||||||
self.policies = dict()
|
self.policies = {}
|
||||||
self.policies_json = dict({ 'policies': self.policies })
|
self.policies_json = {'policies': self.policies}
|
||||||
firefox_filter = '{}%'.format(self.__registry_branch)
|
self.firefox_keys = self.storage.filter_hklm_entries(self.__registry_branch)
|
||||||
self.firefox_keys = self.storage.filter_hklm_entries(firefox_filter)
|
self.policies_gen = {}
|
||||||
self.policies_gen = dict()
|
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
, self.__module_experimental
|
, self.__module_experimental
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_boolean(self,data):
|
|
||||||
if data in ['0', 'false', None, 'none', 0]:
|
|
||||||
return False
|
|
||||||
if data in ['1', 'true', 1]:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_parts(self, hivekeyname):
|
|
||||||
'''
|
|
||||||
Parse registry path string and leave key parameters
|
|
||||||
'''
|
|
||||||
parts = hivekeyname.replace(self.__registry_branch, '').split('\\')
|
|
||||||
return parts
|
|
||||||
|
|
||||||
def create_dict(self, firefox_keys):
|
|
||||||
'''
|
|
||||||
Collect dictionaries from registry keys into a general dictionary
|
|
||||||
'''
|
|
||||||
excp = ['SOCKSVersion']
|
|
||||||
counts = dict()
|
|
||||||
for it_data in firefox_keys:
|
|
||||||
branch = counts
|
|
||||||
try:
|
|
||||||
if type(it_data.data) is bytes:
|
|
||||||
it_data.data = it_data.data.decode(encoding='utf-16').replace('\x00','')
|
|
||||||
#Cases when it is necessary to create nested dictionaries
|
|
||||||
if it_data.valuename != it_data.data:
|
|
||||||
parts = self.get_parts(it_data.hive_key)
|
|
||||||
#creating a nested dictionary from elements
|
|
||||||
for part in parts[:-1]:
|
|
||||||
branch = branch.setdefault(part, {})
|
|
||||||
#dictionary key value initialization
|
|
||||||
if it_data.type == 4:
|
|
||||||
if it_data.valuename in excp:
|
|
||||||
branch[parts[-1]] = int(it_data.data)
|
|
||||||
else:
|
|
||||||
branch[parts[-1]] = self.get_boolean(it_data.data)
|
|
||||||
else:
|
|
||||||
branch[parts[-1]] = str(it_data.data).replace('\\', '/')
|
|
||||||
#Cases when it is necessary to create lists in a dictionary
|
|
||||||
else:
|
|
||||||
parts = self.get_parts(it_data.keyname)
|
|
||||||
for part in parts[:-1]:
|
|
||||||
branch = branch.setdefault(part, {})
|
|
||||||
if branch.get(parts[-1]) is None:
|
|
||||||
branch[parts[-1]] = list()
|
|
||||||
if it_data.type == 4:
|
|
||||||
branch[parts[-1]].append(self.get_boolean(it_data.data))
|
|
||||||
else:
|
|
||||||
if os.path.isdir(str(it_data.data).replace('\\', '/')):
|
|
||||||
branch[parts[-1]].append(str(it_data.data).replace('\\', '/'))
|
|
||||||
else:
|
|
||||||
branch[parts[-1]].append(str(it_data.data))
|
|
||||||
except Exception as exc:
|
|
||||||
logdata = dict()
|
|
||||||
logdata['Exception'] = exc
|
|
||||||
logdata['keyname'] = it_data.keyname
|
|
||||||
log('W14', logdata)
|
|
||||||
|
|
||||||
self.policies_json = {'policies': dict_item_to_list(counts)}
|
|
||||||
|
|
||||||
def machine_apply(self):
|
def machine_apply(self):
|
||||||
'''
|
'''
|
||||||
Write policies.json to Firefox installdir.
|
Write policies.json to Firefox.
|
||||||
'''
|
'''
|
||||||
self.create_dict(self.firefox_keys)
|
excp = ['SOCKSVersion']
|
||||||
destfile = os.path.join(self.__firefox_installdir1, 'policies.json')
|
self.policies_json = create_dict(self.firefox_keys, self.__registry_branch, excp)
|
||||||
|
|
||||||
os.makedirs(self.__firefox_installdir1, exist_ok=True)
|
destfile = os.path.join(self.__firefox_policies, 'policies.json')
|
||||||
|
os.makedirs(self.__firefox_policies, exist_ok=True)
|
||||||
with open(destfile, 'w') as f:
|
with open(destfile, 'w') as f:
|
||||||
json.dump(self.policies_json, f)
|
json.dump(self.policies_json, f)
|
||||||
logdata = dict()
|
logdata = {'destfile': destfile}
|
||||||
logdata['destfile'] = destfile
|
|
||||||
log('D91', logdata)
|
|
||||||
|
|
||||||
destfile = os.path.join(self.__firefox_installdir2, 'policies.json')
|
|
||||||
os.makedirs(self.__firefox_installdir2, exist_ok=True)
|
|
||||||
with open(destfile, 'w') as f:
|
|
||||||
json.dump(self.policies_json, f)
|
|
||||||
logdata = dict()
|
|
||||||
logdata['destfile'] = destfile
|
|
||||||
log('D91', logdata)
|
log('D91', logdata)
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
@@ -164,6 +94,9 @@ def dict_item_to_list(dictionary:dict) -> dict:
|
|||||||
'''
|
'''
|
||||||
Replacing dictionaries with numeric keys with a List
|
Replacing dictionaries with numeric keys with a List
|
||||||
'''
|
'''
|
||||||
|
if '' in dictionary:
|
||||||
|
dictionary = dictionary.pop('')
|
||||||
|
|
||||||
for key,val in dictionary.items():
|
for key,val in dictionary.items():
|
||||||
if type(val) == dict:
|
if type(val) == dict:
|
||||||
if key_dict_is_digit(val):
|
if key_dict_is_digit(val):
|
||||||
@@ -171,3 +104,63 @@ def dict_item_to_list(dictionary:dict) -> dict:
|
|||||||
else:
|
else:
|
||||||
dict_item_to_list(dictionary[key])
|
dict_item_to_list(dictionary[key])
|
||||||
return dictionary
|
return dictionary
|
||||||
|
|
||||||
|
def clean_data_firefox(data):
|
||||||
|
return data.replace("'", '\"')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def create_dict(firefox_keys, registry_branch, excp=[]):
|
||||||
|
'''
|
||||||
|
Collect dictionaries from registry keys into a general dictionary
|
||||||
|
'''
|
||||||
|
get_boolean = lambda data: data in ['1', 'true', 'True', True, 1] if isinstance(data, (str, int)) else False
|
||||||
|
get_parts = lambda hivekey, registry: hivekey.replace(registry, '').split('/')
|
||||||
|
counts = {}
|
||||||
|
for it_data in firefox_keys:
|
||||||
|
branch = counts
|
||||||
|
try:
|
||||||
|
if type(it_data.data) is bytes:
|
||||||
|
it_data.data = it_data.data.decode(encoding='utf-16').replace('\x00','')
|
||||||
|
json_data = try_dict_to_literal_eval(it_data.data)
|
||||||
|
if json_data:
|
||||||
|
it_data.data = json_data
|
||||||
|
it_data.type = 7
|
||||||
|
else:
|
||||||
|
if it_data.type == 1:
|
||||||
|
it_data.data = clean_data_firefox(it_data.data)
|
||||||
|
#Cases when it is necessary to create nested dictionaries
|
||||||
|
if it_data.valuename != it_data.data:
|
||||||
|
parts = get_parts(it_data.hive_key, registry_branch)
|
||||||
|
#creating a nested dictionary from elements
|
||||||
|
for part in parts[:-1]:
|
||||||
|
branch = branch.setdefault(part, {})
|
||||||
|
#dictionary key value initialization
|
||||||
|
if it_data.type == 4:
|
||||||
|
if it_data.valuename in excp:
|
||||||
|
branch[parts[-1]] = int(it_data.data)
|
||||||
|
else:
|
||||||
|
branch[parts[-1]] = get_boolean(it_data.data)
|
||||||
|
elif it_data.type == 7:
|
||||||
|
branch[parts[-1]] = it_data.data
|
||||||
|
else:
|
||||||
|
branch[parts[-1]] = str(it_data.data).replace('\\', '/')
|
||||||
|
#Cases when it is necessary to create lists in a dictionary
|
||||||
|
else:
|
||||||
|
parts = get_parts(it_data.keyname, registry_branch)
|
||||||
|
for part in parts[:-1]:
|
||||||
|
branch = branch.setdefault(part, {})
|
||||||
|
if branch.get(parts[-1]) is None:
|
||||||
|
branch[parts[-1]] = []
|
||||||
|
if it_data.type == 4:
|
||||||
|
branch[parts[-1]].append(get_boolean(it_data.data))
|
||||||
|
else:
|
||||||
|
if os.path.isdir(str(it_data.data).replace('\\', '/')):
|
||||||
|
branch[parts[-1]].append(str(it_data.data).replace('\\', '/'))
|
||||||
|
else:
|
||||||
|
branch[parts[-1]].append(str(it_data.data))
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'Exception': exc, 'keyname': it_data.keyname}
|
||||||
|
log('W14', logdata)
|
||||||
|
|
||||||
|
return {'policies': dict_item_to_list(counts)}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2024 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,10 +17,9 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from util.logging import slogm, log
|
from util.logging import log
|
||||||
from .applier_frontend import (
|
from .applier_frontend import (
|
||||||
applier_frontend
|
applier_frontend
|
||||||
, check_enabled
|
, check_enabled
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from .applier_frontend import (
|
from .applier_frontend import (
|
||||||
applier_frontend
|
applier_frontend
|
||||||
@@ -32,10 +31,9 @@ class folder_applier(applier_frontend):
|
|||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
|
|
||||||
def __init__(self, storage, sid):
|
def __init__(self, storage):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
self.folders = self.storage.get_folders()
|
||||||
self.folders = self.storage.get_folders(self.sid)
|
|
||||||
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
@@ -58,11 +56,10 @@ class folder_applier_user(applier_frontend):
|
|||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self.folders = self.storage.get_folders(self.sid)
|
self.folders = self.storage.get_folders()
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -26,6 +26,7 @@ from .polkit_applier import (
|
|||||||
)
|
)
|
||||||
from .systemd_applier import systemd_applier
|
from .systemd_applier import systemd_applier
|
||||||
from .firefox_applier import firefox_applier
|
from .firefox_applier import firefox_applier
|
||||||
|
from .thunderbird_applier import thunderbird_applier
|
||||||
from .chromium_applier import chromium_applier
|
from .chromium_applier import chromium_applier
|
||||||
from .cups_applier import cups_applier
|
from .cups_applier import cups_applier
|
||||||
from .package_applier import (
|
from .package_applier import (
|
||||||
@@ -72,11 +73,11 @@ from .kde_applier import (
|
|||||||
kde_applier
|
kde_applier
|
||||||
, kde_applier_user
|
, kde_applier_user
|
||||||
)
|
)
|
||||||
|
from .laps_applier import laps_applier
|
||||||
|
|
||||||
from .networkshare_applier import networkshare_applier
|
from .networkshare_applier import networkshare_applier
|
||||||
from .yandex_browser_applier import yandex_browser_applier
|
from .yandex_browser_applier import yandex_browser_applier
|
||||||
|
|
||||||
from util.sid import get_sid
|
|
||||||
from util.users import (
|
from util.users import (
|
||||||
is_root,
|
is_root,
|
||||||
get_process_user,
|
get_process_user,
|
||||||
@@ -95,16 +96,15 @@ def determine_username(username=None):
|
|||||||
|
|
||||||
# If username is not set then it will be the name
|
# If username is not set then it will be the name
|
||||||
# of process owner.
|
# of process owner.
|
||||||
|
logdata = {'username': name}
|
||||||
if not username:
|
if not username:
|
||||||
name = get_process_user()
|
name = get_process_user()
|
||||||
logdata = dict({'username': name})
|
|
||||||
log('D2', logdata)
|
log('D2', logdata)
|
||||||
|
|
||||||
if not username_match_uid(name):
|
if not username_match_uid(name):
|
||||||
if not is_root():
|
if not is_root():
|
||||||
raise Exception('Current process UID does not match specified username')
|
raise Exception('Current process UID does not match specified username')
|
||||||
|
|
||||||
logdata = dict({'username': name})
|
|
||||||
log('D15', logdata)
|
log('D15', logdata)
|
||||||
|
|
||||||
return name
|
return name
|
||||||
@@ -116,9 +116,7 @@ def apply_user_context(user_appliers):
|
|||||||
try:
|
try:
|
||||||
applier_object.user_context_apply()
|
applier_object.user_context_apply()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'applier': applier_name, 'exception': str(exc)}
|
||||||
logdata['applier'] = applier_name
|
|
||||||
logdata['exception'] = str(exc)
|
|
||||||
log('E20', logdata)
|
log('E20', logdata)
|
||||||
|
|
||||||
class frontend_manager:
|
class frontend_manager:
|
||||||
@@ -128,11 +126,10 @@ class frontend_manager:
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, username, is_machine):
|
def __init__(self, username, is_machine):
|
||||||
self.storage = registry_factory('registry')
|
|
||||||
self.username = determine_username(username)
|
self.username = determine_username(username)
|
||||||
|
self.storage = registry_factory('dconf', username=self.username)
|
||||||
self.is_machine = is_machine
|
self.is_machine = is_machine
|
||||||
self.process_uname = get_process_user()
|
self.process_uname = get_process_user()
|
||||||
self.sid = get_sid(self.storage.get_info('domain'), self.username, is_machine)
|
|
||||||
self.file_cache = fs_file_cache('file_cache', self.username)
|
self.file_cache = fs_file_cache('file_cache', self.username)
|
||||||
|
|
||||||
self.machine_appliers = dict()
|
self.machine_appliers = dict()
|
||||||
@@ -143,54 +140,52 @@ class frontend_manager:
|
|||||||
self._init_user_appliers()
|
self._init_user_appliers()
|
||||||
|
|
||||||
def _init_machine_appliers(self):
|
def _init_machine_appliers(self):
|
||||||
|
self.machine_appliers['laps_applier'] = laps_applier(self.storage)
|
||||||
self.machine_appliers['control'] = control_applier(self.storage)
|
self.machine_appliers['control'] = control_applier(self.storage)
|
||||||
self.machine_appliers['polkit'] = polkit_applier(self.storage)
|
self.machine_appliers['polkit'] = polkit_applier(self.storage)
|
||||||
self.machine_appliers['systemd'] = systemd_applier(self.storage)
|
self.machine_appliers['systemd'] = systemd_applier(self.storage)
|
||||||
self.machine_appliers['firefox'] = firefox_applier(self.storage, self.sid, self.username)
|
self.machine_appliers['firefox'] = firefox_applier(self.storage, self.username)
|
||||||
self.machine_appliers['chromium'] = chromium_applier(self.storage, self.sid, self.username)
|
self.machine_appliers['thunderbird'] = thunderbird_applier(self.storage, self.username)
|
||||||
self.machine_appliers['yandex_browser'] = yandex_browser_applier(self.storage, self.sid, self.username)
|
self.machine_appliers['chromium'] = chromium_applier(self.storage, self.username)
|
||||||
|
self.machine_appliers['yandex_browser'] = yandex_browser_applier(self.storage, self.username)
|
||||||
self.machine_appliers['shortcuts'] = shortcut_applier(self.storage)
|
self.machine_appliers['shortcuts'] = shortcut_applier(self.storage)
|
||||||
self.machine_appliers['gsettings'] = gsettings_applier(self.storage, self.file_cache)
|
self.machine_appliers['gsettings'] = gsettings_applier(self.storage, self.file_cache)
|
||||||
try:
|
try:
|
||||||
self.machine_appliers['cifs'] = cifs_applier(self.storage, self.sid)
|
self.machine_appliers['cifs'] = cifs_applier(self.storage)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'applier_name': 'cifs', 'msg': str(exc)}
|
||||||
logdata['applier_name'] = 'cifs'
|
|
||||||
logdata['msg'] = str(exc)
|
|
||||||
log('E24', logdata)
|
log('E24', logdata)
|
||||||
self.machine_appliers['cups'] = cups_applier(self.storage)
|
self.machine_appliers['cups'] = cups_applier(self.storage)
|
||||||
self.machine_appliers['firewall'] = firewall_applier(self.storage)
|
self.machine_appliers['firewall'] = firewall_applier(self.storage)
|
||||||
self.machine_appliers['folders'] = folder_applier(self.storage, self.sid)
|
self.machine_appliers['folders'] = folder_applier(self.storage)
|
||||||
self.machine_appliers['package'] = package_applier(self.storage)
|
|
||||||
self.machine_appliers['ntp'] = ntp_applier(self.storage)
|
self.machine_appliers['ntp'] = ntp_applier(self.storage)
|
||||||
self.machine_appliers['envvar'] = envvar_applier(self.storage, self.sid)
|
self.machine_appliers['envvar'] = envvar_applier(self.storage)
|
||||||
self.machine_appliers['networkshare'] = networkshare_applier(self.storage, self.sid)
|
self.machine_appliers['networkshare'] = networkshare_applier(self.storage)
|
||||||
self.machine_appliers['scripts'] = scripts_applier(self.storage, self.sid)
|
self.machine_appliers['scripts'] = scripts_applier(self.storage)
|
||||||
self.machine_appliers['files'] = file_applier(self.storage, self.file_cache, self.sid)
|
self.machine_appliers['files'] = file_applier(self.storage, self.file_cache)
|
||||||
self.machine_appliers['ini'] = ini_applier(self.storage, self.sid)
|
self.machine_appliers['ini'] = ini_applier(self.storage)
|
||||||
self.machine_appliers['kde'] = kde_applier(self.storage)
|
self.machine_appliers['kde'] = kde_applier(self.storage)
|
||||||
|
self.machine_appliers['package'] = package_applier(self.storage)
|
||||||
|
|
||||||
def _init_user_appliers(self):
|
def _init_user_appliers(self):
|
||||||
# User appliers are expected to work with user-writable
|
# User appliers are expected to work with user-writable
|
||||||
# files and settings, mostly in $HOME.
|
# files and settings, mostly in $HOME.
|
||||||
self.user_appliers['shortcuts'] = shortcut_applier_user(self.storage, self.sid, self.username)
|
self.user_appliers['shortcuts'] = shortcut_applier_user(self.storage, self.username)
|
||||||
self.user_appliers['folders'] = folder_applier_user(self.storage, self.sid, self.username)
|
self.user_appliers['folders'] = folder_applier_user(self.storage, self.username)
|
||||||
self.user_appliers['gsettings'] = gsettings_applier_user(self.storage, self.file_cache, self.sid, self.username)
|
self.user_appliers['gsettings'] = gsettings_applier_user(self.storage, self.file_cache, self.username)
|
||||||
try:
|
try:
|
||||||
self.user_appliers['cifs'] = cifs_applier_user(self.storage, self.sid, self.username)
|
self.user_appliers['cifs'] = cifs_applier_user(self.storage, self.username)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'applier_name': 'cifs', 'msg': str(exc)}
|
||||||
logdata['applier_name'] = 'cifs'
|
|
||||||
logdata['msg'] = str(exc)
|
|
||||||
log('E25', logdata)
|
log('E25', logdata)
|
||||||
self.user_appliers['package'] = package_applier_user(self.storage, self.sid, self.username)
|
self.user_appliers['polkit'] = polkit_applier_user(self.storage, self.username)
|
||||||
self.user_appliers['polkit'] = polkit_applier_user(self.storage, self.sid, self.username)
|
self.user_appliers['envvar'] = envvar_applier_user(self.storage, self.username)
|
||||||
self.user_appliers['envvar'] = envvar_applier_user(self.storage, self.sid, self.username)
|
self.user_appliers['networkshare'] = networkshare_applier(self.storage, self.username)
|
||||||
self.user_appliers['networkshare'] = networkshare_applier(self.storage, self.sid, self.username)
|
self.user_appliers['scripts'] = scripts_applier_user(self.storage, self.username)
|
||||||
self.user_appliers['scripts'] = scripts_applier_user(self.storage, self.sid, self.username)
|
self.user_appliers['files'] = file_applier_user(self.storage, self.file_cache, self.username)
|
||||||
self.user_appliers['files'] = file_applier_user(self.storage, self.file_cache, self.sid, self.username)
|
self.user_appliers['ini'] = ini_applier_user(self.storage, self.username)
|
||||||
self.user_appliers['ini'] = ini_applier_user(self.storage, self.sid, self.username)
|
self.user_appliers['kde'] = kde_applier_user(self.storage, self.username, self.file_cache)
|
||||||
self.user_appliers['kde'] = kde_applier_user(self.storage, self.sid, self.username, self.file_cache)
|
self.user_appliers['package'] = package_applier_user(self.storage, self.username)
|
||||||
|
|
||||||
def machine_apply(self):
|
def machine_apply(self):
|
||||||
'''
|
'''
|
||||||
@@ -205,9 +200,7 @@ class frontend_manager:
|
|||||||
try:
|
try:
|
||||||
applier_object.apply()
|
applier_object.apply()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'applier_name': applier_name, 'msg': str(exc)}
|
||||||
logdata['applier_name'] = applier_name
|
|
||||||
logdata['msg'] = str(exc)
|
|
||||||
log('E24', logdata)
|
log('E24', logdata)
|
||||||
|
|
||||||
def user_apply(self):
|
def user_apply(self):
|
||||||
@@ -219,24 +212,20 @@ class frontend_manager:
|
|||||||
try:
|
try:
|
||||||
applier_object.admin_context_apply()
|
applier_object.admin_context_apply()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'applier': applier_name, 'exception': str(exc)}
|
||||||
logdata['applier'] = applier_name
|
|
||||||
logdata['exception'] = str(exc)
|
|
||||||
log('E19', logdata)
|
log('E19', logdata)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with_privileges(self.username, lambda: apply_user_context(self.user_appliers))
|
with_privileges(self.username, lambda: apply_user_context(self.user_appliers))
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'username': self.username, 'exception': str(exc)}
|
||||||
logdata['username'] = self.username
|
|
||||||
logdata['exception'] = str(exc)
|
|
||||||
log('E30', logdata)
|
log('E30', logdata)
|
||||||
else:
|
else:
|
||||||
for applier_name, applier_object in self.user_appliers.items():
|
for applier_name, applier_object in self.user_appliers.items():
|
||||||
try:
|
try:
|
||||||
applier_object.user_context_apply()
|
applier_object.user_context_apply()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict({'applier_name': applier_name, 'message': str(exc)})
|
logdata = {'applier_name': applier_name, 'message': str(exc)}
|
||||||
log('E11', logdata)
|
log('E11', logdata)
|
||||||
|
|
||||||
def apply_parameters(self):
|
def apply_parameters(self):
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2021 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,10 +21,8 @@ import os
|
|||||||
import pwd
|
import pwd
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from gi.repository import (
|
from gi.repository import Gio
|
||||||
Gio
|
from storage.dconf_registry import Dconf_registry
|
||||||
, GLib
|
|
||||||
)
|
|
||||||
|
|
||||||
from .applier_frontend import (
|
from .applier_frontend import (
|
||||||
applier_frontend
|
applier_frontend
|
||||||
@@ -35,19 +33,18 @@ from .appliers.gsettings import (
|
|||||||
system_gsettings,
|
system_gsettings,
|
||||||
user_gsettings
|
user_gsettings
|
||||||
)
|
)
|
||||||
from util.logging import slogm ,log
|
from util.logging import log
|
||||||
|
|
||||||
def uri_fetch(schema, path, value, cache):
|
def uri_fetch(schema, path, value, cache):
|
||||||
'''
|
'''
|
||||||
Function to fetch and cache uri
|
Function to fetch and cache uri
|
||||||
'''
|
'''
|
||||||
retval = value
|
retval = value
|
||||||
logdata = dict()
|
logdata = {'schema': schema, 'path': path, 'src': value}
|
||||||
logdata['schema'] = schema
|
|
||||||
logdata['path'] = path
|
|
||||||
logdata['src'] = value
|
|
||||||
try:
|
try:
|
||||||
retval = cache.get(value)
|
retval = cache.get(value)
|
||||||
|
if not retval:
|
||||||
|
retval = ''
|
||||||
logdata['dst'] = retval
|
logdata['dst'] = retval
|
||||||
log('D90', logdata)
|
log('D90', logdata)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -59,14 +56,14 @@ class gsettings_applier(applier_frontend):
|
|||||||
__module_name = 'GSettingsApplier'
|
__module_name = 'GSettingsApplier'
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
__registry_branch = 'Software\\BaseALT\\Policies\\GSettings\\'
|
__registry_branch = 'Software\\BaseALT\\Policies\\gsettings\\'
|
||||||
__registry_locks_branch = 'Software\\BaseALT\\Policies\\GSettingsLocks\\'
|
__registry_locks_branch = 'Software\\BaseALT\\Policies\\GSettingsLocks\\'
|
||||||
__wallpaper_entry = 'Software\\BaseALT\\Policies\\GSettings\\org.mate.background.picture-filename'
|
__wallpaper_entry = 'Software/BaseALT/Policies/gsettings/org.mate.background.picture-filename'
|
||||||
__vino_authentication_methods_entry = 'Software\\BaseALT\\Policies\\GSettings\\org.gnome.Vino.authentication-methods'
|
__vino_authentication_methods_entry = 'Software/BaseALT/Policies/gsettings/org.gnome.Vino.authentication-methods'
|
||||||
__global_schema = '/usr/share/glib-2.0/schemas'
|
__global_schema = '/usr/share/glib-2.0/schemas'
|
||||||
__override_priority_file = 'zzz_policy.gschema.override'
|
__override_priority_file = 'zzz_policy.gschema.override'
|
||||||
__override_old_file = '0_policy.gschema.override'
|
__override_old_file = '0_policy.gschema.override'
|
||||||
__windows_settings = dict()
|
|
||||||
|
|
||||||
def __init__(self, storage, file_cache):
|
def __init__(self, storage, file_cache):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
@@ -78,7 +75,7 @@ class gsettings_applier(applier_frontend):
|
|||||||
self.override_file = os.path.join(self.__global_schema, self.__override_priority_file)
|
self.override_file = os.path.join(self.__global_schema, self.__override_priority_file)
|
||||||
self.override_old_file = os.path.join(self.__global_schema, self.__override_old_file)
|
self.override_old_file = os.path.join(self.__global_schema, self.__override_old_file)
|
||||||
self.gsettings = system_gsettings(self.override_file)
|
self.gsettings = system_gsettings(self.override_file)
|
||||||
self.locks = dict()
|
self.locks = {}
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
@@ -89,8 +86,7 @@ class gsettings_applier(applier_frontend):
|
|||||||
try:
|
try:
|
||||||
self.file_cache.store(data)
|
self.file_cache.store(data)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'exception': str(exc)}
|
||||||
logdata['exception'] = str(exc)
|
|
||||||
log('D145', logdata)
|
log('D145', logdata)
|
||||||
|
|
||||||
def uri_fetch_helper(self, schema, path, value):
|
def uri_fetch_helper(self, schema, path, value):
|
||||||
@@ -108,13 +104,13 @@ class gsettings_applier(applier_frontend):
|
|||||||
|
|
||||||
# Get all configured gsettings locks
|
# Get all configured gsettings locks
|
||||||
for lock in self.gsettings_locks:
|
for lock in self.gsettings_locks:
|
||||||
valuename = lock.hive_key.rpartition('\\')[2]
|
valuename = lock.hive_key.rpartition('/')[2]
|
||||||
self.locks[valuename] = int(lock.data)
|
self.locks[valuename] = int(lock.data)
|
||||||
|
|
||||||
# Calculate all configured gsettings
|
# Calculate all configured gsettings
|
||||||
for setting in self.gsettings_keys:
|
for setting in self.gsettings_keys:
|
||||||
helper = None
|
helper = None
|
||||||
valuename = setting.hive_key.rpartition('\\')[2]
|
valuename = setting.hive_key.rpartition('/')[2]
|
||||||
rp = valuename.rpartition('.')
|
rp = valuename.rpartition('.')
|
||||||
schema = rp[0]
|
schema = rp[0]
|
||||||
path = rp[2]
|
path = rp[2]
|
||||||
@@ -137,10 +133,7 @@ class gsettings_applier(applier_frontend):
|
|||||||
log('E48')
|
log('E48')
|
||||||
|
|
||||||
# Update desktop configuration system backend
|
# Update desktop configuration system backend
|
||||||
try:
|
Dconf_registry.dconf_update()
|
||||||
proc = subprocess.run(args=['/usr/bin/dconf', "update"], capture_output=True, check=True)
|
|
||||||
except Exception as exc:
|
|
||||||
log('E49')
|
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
@@ -161,10 +154,9 @@ class GSettingsMapping:
|
|||||||
self.gsettings_schema_key = self.schema.get_key(self.gsettings_key)
|
self.gsettings_schema_key = self.schema.get_key(self.gsettings_key)
|
||||||
self.gsettings_type = self.gsettings_schema_key.get_value_type()
|
self.gsettings_type = self.gsettings_schema_key.get_value_type()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'hive_key': self.hive_key,
|
||||||
logdata['hive_key'] = self.hive_key
|
'gsettings_schema': self.gsettings_schema,
|
||||||
logdata['gsettings_schema'] = self.gsettings_schema
|
'gsettings_key': self.gsettings_key}
|
||||||
logdata['gsettings_key'] = self.gsettings_key
|
|
||||||
log('W6', logdata)
|
log('W6', logdata)
|
||||||
|
|
||||||
def preg2gsettings(self):
|
def preg2gsettings(self):
|
||||||
@@ -184,45 +176,44 @@ class gsettings_applier_user(applier_frontend):
|
|||||||
__module_name = 'GSettingsApplierUser'
|
__module_name = 'GSettingsApplierUser'
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
__registry_branch = 'Software\\BaseALT\\Policies\\GSettings\\'
|
__registry_branch = 'Software\\BaseALT\\Policies\\gsettings\\'
|
||||||
__wallpaper_entry = 'Software\\BaseALT\\Policies\\GSettings\\org.mate.background.picture-filename'
|
__wallpaper_entry = 'Software/BaseALT/Policies/gsettings/org.mate.background.picture-filename'
|
||||||
__vino_authentication_methods_entry = 'Software\\BaseALT\\Policies\\GSettings\\org.gnome.Vino.authentication-methods'
|
__vino_authentication_methods_entry = 'Software/BaseALT/Policies/gsettings/org.gnome.Vino.authentication-methods'
|
||||||
|
|
||||||
def __init__(self, storage, file_cache, sid, username):
|
def __init__(self, storage, file_cache, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.file_cache = file_cache
|
self.file_cache = file_cache
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
gsettings_filter = '{}%'.format(self.__registry_branch)
|
gsettings_filter = '{}%'.format(self.__registry_branch)
|
||||||
self.gsettings_keys = self.storage.filter_hkcu_entries(self.sid, gsettings_filter)
|
self.gsettings_keys = self.storage.filter_hkcu_entries(gsettings_filter)
|
||||||
self.gsettings = user_gsettings()
|
self.gsettings = user_gsettings()
|
||||||
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
self.__windows_mapping_enabled = check_windows_mapping_enabled(self.storage)
|
self.__windows_mapping_enabled = check_windows_mapping_enabled(self.storage)
|
||||||
|
|
||||||
self.__windows_settings = dict()
|
self.__windows_settings = {}
|
||||||
self.windows_settings = list()
|
self.windows_settings = []
|
||||||
mapping = [
|
mapping = [
|
||||||
# Disable or enable screen saver
|
# Disable or enable screen saver
|
||||||
GSettingsMapping(
|
GSettingsMapping(
|
||||||
'Software\\Policies\\Microsoft\\Windows\\Control Panel\\Desktop\\ScreenSaveActive'
|
'Software/Policies/Microsoft/Windows/Control Panel/Desktop/ScreenSaveActive'
|
||||||
, 'org.mate.screensaver'
|
, 'org.mate.screensaver'
|
||||||
, 'idle-activation-enabled'
|
, 'idle-activation-enabled'
|
||||||
)
|
)
|
||||||
# Timeout in seconds for screen saver activation. The value of zero effectively disables screensaver start
|
# Timeout in seconds for screen saver activation. The value of zero effectively disables screensaver start
|
||||||
, GSettingsMapping(
|
, GSettingsMapping(
|
||||||
'Software\\Policies\\Microsoft\\Windows\\Control Panel\\Desktop\\ScreenSaveTimeOut'
|
'Software/Policies/Microsoft/Windows/Control Panel/Desktop/ScreenSaveTimeOut'
|
||||||
, 'org.mate.session'
|
, 'org.mate.session'
|
||||||
, 'idle-delay'
|
, 'idle-delay'
|
||||||
)
|
)
|
||||||
# Enable or disable password protection for screen saver
|
# Enable or disable password protection for screen saver
|
||||||
, GSettingsMapping(
|
, GSettingsMapping(
|
||||||
'Software\\Policies\\Microsoft\\Windows\\Control Panel\\Desktop\\ScreenSaverIsSecure'
|
'Software/Policies/Microsoft/Windows/Control Panel/Desktop/ScreenSaverIsSecure'
|
||||||
, 'org.mate.screensaver'
|
, 'org.mate.screensaver'
|
||||||
, 'lock-enabled'
|
, 'lock-enabled'
|
||||||
)
|
)
|
||||||
# Specify image which will be used as a wallpaper
|
# Specify image which will be used as a wallpaper
|
||||||
, GSettingsMapping(
|
, GSettingsMapping(
|
||||||
'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\\Wallpaper'
|
'Software/Microsoft/Windows/CurrentVersion/Policies/System/Wallpaper'
|
||||||
, 'org.mate.background'
|
, 'org.mate.background'
|
||||||
, 'picture-filename'
|
, 'picture-filename'
|
||||||
)
|
)
|
||||||
@@ -235,11 +226,9 @@ class gsettings_applier_user(applier_frontend):
|
|||||||
|
|
||||||
def windows_mapping_append(self):
|
def windows_mapping_append(self):
|
||||||
for setting_key in self.__windows_settings.keys():
|
for setting_key in self.__windows_settings.keys():
|
||||||
value = self.storage.get_hkcu_entry(self.sid, setting_key)
|
value = self.storage.get_hkcu_entry(setting_key)
|
||||||
if value:
|
if value:
|
||||||
logdata = dict()
|
logdata = {'setting_key': setting_key, 'value.data': value.data}
|
||||||
logdata['setting_key'] = setting_key
|
|
||||||
logdata['value.data'] = value.data
|
|
||||||
log('D86', logdata)
|
log('D86', logdata)
|
||||||
mapping = self.__windows_settings[setting_key]
|
mapping = self.__windows_settings[setting_key]
|
||||||
try:
|
try:
|
||||||
@@ -251,15 +240,6 @@ class gsettings_applier_user(applier_frontend):
|
|||||||
return uri_fetch(schema, path, value, self.file_cache)
|
return uri_fetch(schema, path, value, self.file_cache)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
#for setting in self.gsettings_keys:
|
|
||||||
# valuename = setting.hive_key.rpartition('\\')[2]
|
|
||||||
# rp = valuename.rpartition('.')
|
|
||||||
# schema = rp[0]
|
|
||||||
# path = rp[2]
|
|
||||||
# self.gsettings.append(user_gsetting(schema, path, setting.data))
|
|
||||||
|
|
||||||
|
|
||||||
# Calculate all mapped gsettings if mapping enabled
|
|
||||||
if self.__windows_mapping_enabled:
|
if self.__windows_mapping_enabled:
|
||||||
log('D83')
|
log('D83')
|
||||||
self.windows_mapping_append()
|
self.windows_mapping_append()
|
||||||
@@ -268,7 +248,7 @@ class gsettings_applier_user(applier_frontend):
|
|||||||
|
|
||||||
# Calculate all configured gsettings
|
# Calculate all configured gsettings
|
||||||
for setting in self.gsettings_keys:
|
for setting in self.gsettings_keys:
|
||||||
valuename = setting.hive_key.rpartition('\\')[2]
|
valuename = setting.hive_key.rpartition('/')[2]
|
||||||
rp = valuename.rpartition('.')
|
rp = valuename.rpartition('.')
|
||||||
schema = rp[0]
|
schema = rp[0]
|
||||||
path = rp[2]
|
path = rp[2]
|
||||||
@@ -292,14 +272,13 @@ class gsettings_applier_user(applier_frontend):
|
|||||||
# Cache files on remote locations
|
# Cache files on remote locations
|
||||||
try:
|
try:
|
||||||
entry = self.__wallpaper_entry
|
entry = self.__wallpaper_entry
|
||||||
filter_result = self.storage.get_hkcu_entry(self.sid, entry)
|
filter_result = self.storage.get_hkcu_entry(entry)
|
||||||
if filter_result:
|
if filter_result and filter_result.data:
|
||||||
self.file_cache.store(filter_result.data)
|
self.file_cache.store(filter_result.data)
|
||||||
except NotUNCPathError:
|
except NotUNCPathError:
|
||||||
...
|
...
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'exception': str(exc)}
|
||||||
logdata['exception'] = str(exc)
|
|
||||||
log('E50', logdata)
|
log('E50', logdata)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2022 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from .appliers.ini_file import Ini_file
|
from .appliers.ini_file import Ini_file
|
||||||
from .applier_frontend import (
|
from .applier_frontend import (
|
||||||
@@ -27,13 +26,12 @@ from util.logging import log
|
|||||||
|
|
||||||
class ini_applier(applier_frontend):
|
class ini_applier(applier_frontend):
|
||||||
__module_name = 'InifilesApplier'
|
__module_name = 'InifilesApplier'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
|
|
||||||
def __init__(self, storage, sid):
|
def __init__(self, storage):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
self.inifiles_info = self.storage.get_ini()
|
||||||
self.inifiles_info = self.storage.get_ini(self.sid)
|
|
||||||
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@@ -49,14 +47,13 @@ class ini_applier(applier_frontend):
|
|||||||
|
|
||||||
class ini_applier_user(applier_frontend):
|
class ini_applier_user(applier_frontend):
|
||||||
__module_name = 'InifilesApplierUser'
|
__module_name = 'InifilesApplierUser'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, username):
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.inifiles_info = self.storage.get_ini(self.sid)
|
self.inifiles_info = self.storage.get_ini()
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2023 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -24,13 +24,14 @@ import os
|
|||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
import dbus
|
import dbus
|
||||||
|
import shutil
|
||||||
|
|
||||||
class kde_applier(applier_frontend):
|
class kde_applier(applier_frontend):
|
||||||
__module_name = 'KdeApplier'
|
__module_name = 'KdeApplier'
|
||||||
__module_experimental = True
|
__module_experimental = True
|
||||||
__module_enabled = False
|
__module_enabled = False
|
||||||
__hklm_branch = 'Software\\BaseALT\\Policies\\KDE\\'
|
__hklm_branch = 'Software/BaseALT/Policies/KDE/'
|
||||||
__hklm_lock_branch = 'Software\\BaseALT\\Policies\\KDELocks\\'
|
__hklm_lock_branch = 'Software/BaseALT/Policies/KDELocks/'
|
||||||
|
|
||||||
def __init__(self, storage):
|
def __init__(self, storage):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
@@ -61,21 +62,25 @@ class kde_applier_user(applier_frontend):
|
|||||||
__module_name = 'KdeApplierUser'
|
__module_name = 'KdeApplierUser'
|
||||||
__module_experimental = True
|
__module_experimental = True
|
||||||
__module_enabled = False
|
__module_enabled = False
|
||||||
__hkcu_branch = 'Software\\BaseALT\\Policies\\KDE\\'
|
kde_version = None
|
||||||
__hkcu_lock_branch = 'Software\\BaseALT\\Policies\\KDELocks\\'
|
__hkcu_branch = 'Software/BaseALT/Policies/KDE'
|
||||||
|
__hkcu_lock_branch = 'Software/BaseALT/Policies/KDELocks'
|
||||||
|
__plasma_update_entry = 'Software/BaseALT/Policies/KDE/Plasma/Update'
|
||||||
|
|
||||||
def __init__(self, storage, sid=None, username=None, file_cache = None):
|
def __init__(self, storage, username=None, file_cache = None):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.username = username
|
self.username = username
|
||||||
self.sid = sid
|
|
||||||
self.file_cache = file_cache
|
self.file_cache = file_cache
|
||||||
self.locks_dict = {}
|
self.locks_dict = {}
|
||||||
self.locks_data_dict = {}
|
self.locks_data_dict = {}
|
||||||
self.all_kde_settings = {}
|
self.all_kde_settings = {}
|
||||||
|
kde_applier_user.kde_version = get_kde_version()
|
||||||
kde_filter = '{}%'.format(self.__hkcu_branch)
|
kde_filter = '{}%'.format(self.__hkcu_branch)
|
||||||
locks_filter = '{}%'.format(self.__hkcu_lock_branch)
|
locks_filter = '{}%'.format(self.__hkcu_lock_branch)
|
||||||
self.locks_settings = self.storage.filter_hkcu_entries(self.sid, locks_filter)
|
self.locks_settings = self.storage.filter_hkcu_entries(locks_filter)
|
||||||
self.kde_settings = self.storage.filter_hkcu_entries(self.sid, kde_filter)
|
self.plasma_update = self.storage.get_entry(self.__plasma_update_entry)
|
||||||
|
self.plasma_update_flag = self.plasma_update.data if self.plasma_update is not None else 0
|
||||||
|
self.kde_settings = self.storage.filter_hkcu_entries(kde_filter)
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage,
|
self.storage,
|
||||||
self.__module_name,
|
self.__module_name,
|
||||||
@@ -83,7 +88,15 @@ class kde_applier_user(applier_frontend):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def admin_context_apply(self):
|
def admin_context_apply(self):
|
||||||
pass
|
try:
|
||||||
|
for setting in self.kde_settings:
|
||||||
|
file_name = setting.keyname.split("/")[-2]
|
||||||
|
if file_name == 'wallpaper':
|
||||||
|
data = setting.data
|
||||||
|
break
|
||||||
|
self.file_cache.store(data)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'exc': exc}
|
||||||
|
|
||||||
def user_context_apply(self):
|
def user_context_apply(self):
|
||||||
'''
|
'''
|
||||||
@@ -91,54 +104,95 @@ class kde_applier_user(applier_frontend):
|
|||||||
'''
|
'''
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
log('D200')
|
log('D200')
|
||||||
create_dict(self.kde_settings, self.all_kde_settings, self.locks_settings, self.locks_dict, self.file_cache, self.username)
|
create_dict(self.kde_settings, self.all_kde_settings, self.locks_settings, self.locks_dict, self.file_cache, self.username, self.plasma_update_flag)
|
||||||
apply(self.all_kde_settings, self.locks_dict, self.username)
|
apply(self.all_kde_settings, self.locks_dict, self.username)
|
||||||
else:
|
else:
|
||||||
log('D201')
|
log('D201')
|
||||||
|
|
||||||
def create_dict(kde_settings, all_kde_settings, locks_settings, locks_dict, file_cache = None, username = None):
|
dbus_methods_mapping = {
|
||||||
|
'kscreenlockerrc': {
|
||||||
|
'dbus_service': 'org.kde.screensaver',
|
||||||
|
'dbus_path': '/ScreenSaver',
|
||||||
|
'dbus_interface': 'org.kde.screensaver',
|
||||||
|
'dbus_method': 'configure'
|
||||||
|
},
|
||||||
|
'wallpaper': {
|
||||||
|
'dbus_service': 'org.freedesktop.systemd1',
|
||||||
|
'dbus_path': '/org/freedesktop/systemd1',
|
||||||
|
'dbus_interface': 'org.freedesktop.systemd1.Manager',
|
||||||
|
'dbus_method': 'RestartUnit',
|
||||||
|
'dbus_args': ['plasma-plasmashell.service', 'replace']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_kde_version():
|
||||||
|
try:
|
||||||
|
kinfo_path = shutil.which("kinfo", path="/usr/lib/kf5/bin:/usr/bin")
|
||||||
|
if not kinfo_path:
|
||||||
|
raise FileNotFoundError("Unable to find kinfo")
|
||||||
|
output = subprocess.check_output([kinfo_path], text=True, env={'LANG':'C'})
|
||||||
|
for line in output.splitlines():
|
||||||
|
if "KDE Frameworks Version" in line:
|
||||||
|
frameworks_version = line.split(":", 1)[1].strip()
|
||||||
|
major_frameworks_version = int(frameworks_version.split(".")[0])
|
||||||
|
return major_frameworks_version
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def create_dict(kde_settings, all_kde_settings, locks_settings, locks_dict, file_cache = None, username = None, plasmaupdate = False):
|
||||||
for locks in locks_settings:
|
for locks in locks_settings:
|
||||||
locks_dict[locks.valuename] = locks.data
|
locks_dict[locks.valuename] = locks.data
|
||||||
for setting in kde_settings:
|
for setting in kde_settings:
|
||||||
try:
|
try:
|
||||||
file_name, section, value = setting.keyname.split("\\")[-2], setting.keyname.split("\\")[-1], setting.valuename
|
file_name, section, value = setting.keyname.split("/")[-2], setting.keyname.split("/")[-1], setting.valuename
|
||||||
data = setting.data
|
data = setting.data
|
||||||
if file_name == 'wallpaper':
|
if file_name == 'wallpaper':
|
||||||
apply_for_wallpaper(data, file_cache, username)
|
apply_for_wallpaper(data, file_cache, username, plasmaupdate)
|
||||||
else:
|
else:
|
||||||
if file_name not in all_kde_settings:
|
all_kde_settings.setdefault(file_name, {}).setdefault(section, {})[value] = data
|
||||||
all_kde_settings[file_name] = {}
|
|
||||||
if section not in all_kde_settings[file_name]:
|
|
||||||
all_kde_settings[file_name][section] = {}
|
|
||||||
all_kde_settings[file_name][section][value] = data
|
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'file_name': file_name,
|
||||||
logdata['file_name'] = file_name
|
'section': section,
|
||||||
logdata['section'] = section
|
'value': value,
|
||||||
logdata['value'] = value
|
'data': data,
|
||||||
logdata['data'] = data
|
'exc': exc}
|
||||||
logdata['exc'] = exc
|
|
||||||
log('W16', logdata)
|
log('W16', logdata)
|
||||||
|
|
||||||
def apply(all_kde_settings, locks_dict, username = None):
|
def apply(all_kde_settings, locks_dict, username = None):
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
|
modified_files = set()
|
||||||
if username is None:
|
if username is None:
|
||||||
|
system_path_settings = '/etc/xdg/'
|
||||||
|
system_files = [
|
||||||
|
"baloofilerc",
|
||||||
|
"kcminputrc",
|
||||||
|
"kded_device_automounterrc",
|
||||||
|
"kdeglobals",
|
||||||
|
"ksplashrc",
|
||||||
|
"kwinrc",
|
||||||
|
"plasma-localerc",
|
||||||
|
"plasmarc",
|
||||||
|
"powermanagementprofilesrc"
|
||||||
|
]
|
||||||
|
for file in system_files:
|
||||||
|
file_to_remove = f'{system_path_settings}{file}'
|
||||||
|
if os.path.exists(file_to_remove):
|
||||||
|
os.remove(file_to_remove)
|
||||||
for file_name, sections in all_kde_settings.items():
|
for file_name, sections in all_kde_settings.items():
|
||||||
file_path = f'/etc/xdg/{file_name}'
|
file_path = f'{system_path_settings}{file_name}'
|
||||||
if os.path.exists(file_path):
|
|
||||||
os.remove(file_path)
|
|
||||||
with open(file_path, 'w') as file:
|
with open(file_path, 'w') as file:
|
||||||
for section, keys in sections.items():
|
for section, keys in sections.items():
|
||||||
section = section.replace(')(', '][')
|
section = section.replace(')(', '][')
|
||||||
file.write(f'[{section}]\n')
|
file.write(f'[{section}]\n')
|
||||||
for key, value in keys.items():
|
for key, value in keys.items():
|
||||||
lock = f"{file_name}.{section}.{key}".replace('][', ')(')
|
lock = f"{file_name}.{section}.{key}".replace('][', ')(')
|
||||||
if lock in locks_dict and locks_dict[lock] == '1':
|
if locks_dict.get(lock) == 1:
|
||||||
file.write(f'{key}[$i]={value}\n')
|
file.write(f'{key}[$i]={value}\n')
|
||||||
else:
|
else:
|
||||||
file.write(f'{key}={value}\n')
|
file.write(f'{key}={value}\n')
|
||||||
file.write('\n')
|
file.write('\n')
|
||||||
|
modified_files.add(file_name)
|
||||||
else:
|
else:
|
||||||
for file_name, sections in all_kde_settings.items():
|
for file_name, sections in all_kde_settings.items():
|
||||||
path = f'{get_homedir(username)}/.config/{file_name}'
|
path = f'{get_homedir(username)}/.config/{file_name}'
|
||||||
@@ -148,10 +202,11 @@ def apply(all_kde_settings, locks_dict, username = None):
|
|||||||
pass
|
pass
|
||||||
for section, keys in sections.items():
|
for section, keys in sections.items():
|
||||||
for key, value in keys.items():
|
for key, value in keys.items():
|
||||||
|
value = str(value)
|
||||||
lock = f"{file_name}.{section}.{key}"
|
lock = f"{file_name}.{section}.{key}"
|
||||||
if lock in locks_dict and locks_dict[lock] == '1':
|
if lock in locks_dict and locks_dict[lock] == 1:
|
||||||
command = [
|
command = [
|
||||||
'kwriteconfig5',
|
f'kwriteconfig{kde_applier_user.kde_version}',
|
||||||
'--file', file_name,
|
'--file', file_name,
|
||||||
'--group', section,
|
'--group', section,
|
||||||
'--key', key +'/$i/',
|
'--key', key +'/$i/',
|
||||||
@@ -160,7 +215,7 @@ def apply(all_kde_settings, locks_dict, username = None):
|
|||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
command = [
|
command = [
|
||||||
'kwriteconfig5',
|
f'kwriteconfig{kde_applier_user.kde_version}',
|
||||||
'--file', file_name,
|
'--file', file_name,
|
||||||
'--group', section,
|
'--group', section,
|
||||||
'--key', key,
|
'--key', key,
|
||||||
@@ -169,9 +224,11 @@ def apply(all_kde_settings, locks_dict, username = None):
|
|||||||
]
|
]
|
||||||
try:
|
try:
|
||||||
clear_locks_settings(username, file_name, key)
|
clear_locks_settings(username, file_name, key)
|
||||||
subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
env_path = dict(os.environ)
|
||||||
|
env_path["PATH"] = "/usr/lib/kf5/bin:/usr/bin"
|
||||||
|
subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env_path)
|
||||||
except:
|
except:
|
||||||
logdata['command'] = command
|
logdata = {'command': command}
|
||||||
log('W22', logdata)
|
log('W22', logdata)
|
||||||
new_content = []
|
new_content = []
|
||||||
file_path = f'{get_homedir(username)}/.config/{file_name}'
|
file_path = f'{get_homedir(username)}/.config/{file_name}'
|
||||||
@@ -187,6 +244,9 @@ def apply(all_kde_settings, locks_dict, username = None):
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata['exc'] = exc
|
logdata['exc'] = exc
|
||||||
log('W19', logdata)
|
log('W19', logdata)
|
||||||
|
modified_files.add(file_name)
|
||||||
|
for file_name in modified_files:
|
||||||
|
call_dbus_method(file_name)
|
||||||
|
|
||||||
def clear_locks_settings(username, file_name, key):
|
def clear_locks_settings(username, file_name, key):
|
||||||
'''
|
'''
|
||||||
@@ -201,64 +261,71 @@ def clear_locks_settings(username, file_name, key):
|
|||||||
file.write(line)
|
file.write(line)
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if f'{key}[$i]=' in line:
|
if f'{key}[$i]=' in line:
|
||||||
logdata = dict()
|
logdata = {'line': line.strip()}
|
||||||
logdata['line'] = line.strip()
|
|
||||||
log('I10', logdata)
|
log('I10', logdata)
|
||||||
|
|
||||||
def apply_for_wallpaper(data, file_cache, username):
|
def apply_for_wallpaper(data, file_cache, username, plasmaupdate):
|
||||||
'''
|
'''
|
||||||
Method to change wallpaper
|
Method to change wallpaper
|
||||||
'''
|
'''
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
path_to_wallpaper = f'{get_homedir(username)}/.config/plasma-org.kde.plasma.desktop-appletsrc'
|
path_to_wallpaper = f'{get_homedir(username)}/.config/plasma-org.kde.plasma.desktop-appletsrc'
|
||||||
|
id_desktop = get_id_desktop(path_to_wallpaper)
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
file_cache.store(data)
|
data = str(file_cache.get(data))
|
||||||
data = file_cache.get(data)
|
|
||||||
except NotUNCPathError:
|
except NotUNCPathError:
|
||||||
data = data
|
data = str(data)
|
||||||
|
|
||||||
|
with open(path_to_wallpaper, 'r') as file:
|
||||||
|
current_wallpaper = file.read()
|
||||||
|
match = re.search(rf'\[Containments\]\[{id_desktop}\]\[Wallpaper\]\[org\.kde\.image\]\[General\]\s+Image=(.*)', current_wallpaper)
|
||||||
|
if match:
|
||||||
|
current_wallpaper_path = match.group(1)
|
||||||
|
flag = (current_wallpaper_path == data)
|
||||||
|
else:
|
||||||
|
flag = False
|
||||||
|
|
||||||
|
os.environ["LANGUAGE"] = os.environ["LANG"].split(".")[0]
|
||||||
os.environ["XDG_DATA_DIRS"] = "/usr/share/kf5:"
|
os.environ["XDG_DATA_DIRS"] = "/usr/share/kf5:"
|
||||||
#Variable for system detection of directories before files with .colors extension
|
#Variable for system detection of directories before files with .colors extension
|
||||||
os.environ["DISPLAY"] = ":0"
|
os.environ["DISPLAY"] = ":0"
|
||||||
#Variable for command execution plasma-apply-colorscheme
|
#Variable for command execution plasma-apply-colorscheme
|
||||||
os.environ["XDG_RUNTIME_DIR"] = f"/run/user/{os.getuid()}"
|
os.environ["XDG_RUNTIME_DIR"] = f"/run/user/{os.getuid()}"
|
||||||
os.environ["DBUS_SESSION_BUS_ADDRESS"] = f"unix:path=/run/user/{os.getuid()}/bus"#plasma-apply-wallpaperimage
|
|
||||||
os.environ["PATH"] = "/usr/lib/kf5/bin:"
|
os.environ["PATH"] = "/usr/lib/kf5/bin:"
|
||||||
|
os.environ["DBUS_SESSION_BUS_ADDRESS"] = f"unix:path=/run/user/{os.getuid()}/bus"#plasma-apply-wallpaperimage
|
||||||
|
env_path = dict(os.environ)
|
||||||
|
env_path["PATH"] = "/usr/lib/kf5/bin:/usr/bin"
|
||||||
#environment variable for accessing binary files without hard links
|
#environment variable for accessing binary files without hard links
|
||||||
if os.path.isfile(path_to_wallpaper):
|
if not flag:
|
||||||
id_desktop = get_id_desktop(path_to_wallpaper)
|
if os.path.isfile(path_to_wallpaper):
|
||||||
command = [
|
command = [
|
||||||
'kwriteconfig5',
|
f'kwriteconfig{kde_applier_user.kde_version}',
|
||||||
'--file', 'plasma-org.kde.plasma.desktop-appletsrc',
|
'--file', 'plasma-org.kde.plasma.desktop-appletsrc',
|
||||||
'--group', 'Containments',
|
'--group', 'Containments',
|
||||||
'--group', id_desktop,
|
'--group', id_desktop,
|
||||||
'--group', 'Wallpaper',
|
'--group', 'Wallpaper',
|
||||||
'--group', 'org.kde.image',
|
'--group', 'org.kde.image',
|
||||||
'--group', 'General',
|
'--group', 'General',
|
||||||
'--key', 'Image',
|
'--key', 'Image',
|
||||||
'--type', 'string',
|
'--type', 'string',
|
||||||
data
|
data
|
||||||
]
|
]
|
||||||
try:
|
try:
|
||||||
subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env_path)
|
||||||
except:
|
except:
|
||||||
logdata['command'] = command
|
logdata = {'command': command}
|
||||||
log('E68', logdata)
|
log('E68', logdata)
|
||||||
try:
|
if plasmaupdate == 1:
|
||||||
session_bus = dbus.SessionBus()
|
call_dbus_method("wallpaper")
|
||||||
plasma_shell = session_bus.get_object('org.kde.plasmashell', '/PlasmaShell', introspect='org.kde.PlasmaShell')
|
|
||||||
plasma_shell_iface = dbus.Interface(plasma_shell, 'org.kde.PlasmaShell')
|
|
||||||
plasma_shell_iface.refreshCurrentShell()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
logdata['file'] = path_to_wallpaper
|
logdata = {'file': path_to_wallpaper}
|
||||||
log('W21', logdata)
|
log('W21', logdata)
|
||||||
except OSError as exc:
|
except OSError as exc:
|
||||||
logdata['exc'] = exc
|
logdata = {'exc': exc}
|
||||||
log('W17', logdata)
|
log('W17', logdata)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata['exc'] = exc
|
logdata = {'exc': exc}
|
||||||
log('E67', logdata)
|
log('E67', logdata)
|
||||||
|
|
||||||
def get_id_desktop(path_to_wallpaper):
|
def get_id_desktop(path_to_wallpaper):
|
||||||
@@ -270,9 +337,27 @@ def get_id_desktop(path_to_wallpaper):
|
|||||||
with open(path_to_wallpaper, 'r') as file:
|
with open(path_to_wallpaper, 'r') as file:
|
||||||
file_content = file.read()
|
file_content = file.read()
|
||||||
match = re.search(pattern, file_content)
|
match = re.search(pattern, file_content)
|
||||||
if match:
|
return match.group(1) if match else None
|
||||||
return match.group(1)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
except:
|
except:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def call_dbus_method(file_name):
|
||||||
|
'''
|
||||||
|
Method to call D-Bus method based on the file name
|
||||||
|
'''
|
||||||
|
os.environ["DBUS_SESSION_BUS_ADDRESS"] = f"unix:path=/run/user/{os.getuid()}/bus"
|
||||||
|
if file_name in dbus_methods_mapping:
|
||||||
|
config = dbus_methods_mapping[file_name]
|
||||||
|
try:
|
||||||
|
session_bus = dbus.SessionBus()
|
||||||
|
dbus_object = session_bus.get_object(config['dbus_service'], config['dbus_path'])
|
||||||
|
dbus_iface = dbus.Interface(dbus_object, config['dbus_interface'])
|
||||||
|
if 'dbus_args' in config:
|
||||||
|
getattr(dbus_iface, config['dbus_method'])(*config['dbus_args'])
|
||||||
|
else:
|
||||||
|
getattr(dbus_iface, config['dbus_method'])()
|
||||||
|
except dbus.exceptions.DBusException as exc:
|
||||||
|
logdata = {'error': str(exc)}
|
||||||
|
log('E31', logdata)
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|||||||
755
gpoa/frontend/laps_applier.py
Normal file
755
gpoa/frontend/laps_applier.py
Normal file
@@ -0,0 +1,755 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2025 BaseALT Ltd.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from .applier_frontend import (
|
||||||
|
applier_frontend,
|
||||||
|
check_enabled
|
||||||
|
)
|
||||||
|
import struct
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import dpapi_ng
|
||||||
|
from util.util import remove_prefix_from_keys, check_local_user_exists
|
||||||
|
from util.sid import WellKnown21RID
|
||||||
|
import subprocess
|
||||||
|
import ldb
|
||||||
|
import string
|
||||||
|
import secrets
|
||||||
|
import os
|
||||||
|
import psutil
|
||||||
|
from util.logging import log
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
from datetime import timezone
|
||||||
|
from dateutil import tz
|
||||||
|
|
||||||
|
_DATEUTIL_AVAILABLE = False
|
||||||
|
try:
|
||||||
|
from dateutil import tz
|
||||||
|
_DATEUTIL_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class laps_applier(applier_frontend):
|
||||||
|
"""
|
||||||
|
LAPS (Local Administrator Password Solution) implementation for managing
|
||||||
|
and automatically rotating administrator passwords.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Time calculation constants
|
||||||
|
|
||||||
|
# Number of seconds between the Windows epoch (1601-01-01 00:00:00 UTC)
|
||||||
|
# and the Unix epoch (1970-01-01 00:00:00 UTC).
|
||||||
|
# Used to convert between Unix timestamps and Windows FileTime.
|
||||||
|
_EPOCH_TIMESTAMP = 11644473600
|
||||||
|
# Number of 100-nanosecond intervals per second.
|
||||||
|
# Used to convert seconds to Windows FileTime format.
|
||||||
|
_HUNDREDS_OF_NANOSECONDS = 10000000
|
||||||
|
# Number of 100-nanosecond intervals in one day
|
||||||
|
_DAY_FLOAT = 8.64e11
|
||||||
|
|
||||||
|
# Module configuration
|
||||||
|
__module_name = 'LapsApplier'
|
||||||
|
__module_experimental = True
|
||||||
|
__module_enabled = False
|
||||||
|
|
||||||
|
# Registry paths
|
||||||
|
_WINDOWS_REGISTRY_PATH = 'SOFTWARE/Microsoft/Windows/CurrentVersion/Policies/LAPS/'
|
||||||
|
_ALT_REGISTRY_PATH = 'Software/BaseALT/Policies/Laps/'
|
||||||
|
|
||||||
|
# LDAP attributes
|
||||||
|
_ATTR_ENCRYPTED_PASSWORD = 'msLAPS-EncryptedPassword'
|
||||||
|
_ATTR_PASSWORD_EXPIRATION_TIME = 'msLAPS-PasswordExpirationTime'
|
||||||
|
|
||||||
|
# dconf key for password modification time
|
||||||
|
_KEY_PASSWORD_LAST_MODIFIED = '/Software/BaseALT/Policies/Laps/PasswordLastModified/'
|
||||||
|
|
||||||
|
# Password complexity levels
|
||||||
|
_PASSWORD_COMPLEXITY = {
|
||||||
|
1: string.ascii_uppercase,
|
||||||
|
2: string.ascii_letters,
|
||||||
|
3: string.ascii_letters + string.digits,
|
||||||
|
4: string.ascii_letters + string.digits + string.punctuation
|
||||||
|
}
|
||||||
|
|
||||||
|
# Post-authentication actions
|
||||||
|
_ACTION_NONE = 0
|
||||||
|
_ACTION_CHANGE_PASSWORD = 1
|
||||||
|
_ACTION_TERMINATE_SESSIONS = 3
|
||||||
|
_ACTION_REBOOT = 5
|
||||||
|
|
||||||
|
def __init__(self, storage):
|
||||||
|
"""
|
||||||
|
Initialize the LAPS applier with configuration from registry.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
storage: Storage object containing registry entries and system information
|
||||||
|
"""
|
||||||
|
self.storage = storage
|
||||||
|
|
||||||
|
# Load registry configuration
|
||||||
|
if not self._load_configuration():
|
||||||
|
self.__module_enabled = False
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self._check_requirements():
|
||||||
|
log('W29')
|
||||||
|
self.__module_enabled = False
|
||||||
|
return
|
||||||
|
|
||||||
|
# Initialize system connections and parameters
|
||||||
|
self._initialize_system_parameters()
|
||||||
|
|
||||||
|
# Check if module is enabled in configuration
|
||||||
|
self.__module_enabled = check_enabled(
|
||||||
|
self.storage,
|
||||||
|
self.__module_name,
|
||||||
|
self.__module_experimental
|
||||||
|
)
|
||||||
|
|
||||||
|
def _load_configuration(self):
|
||||||
|
"""Load configuration settings from registry."""
|
||||||
|
alt_keys = remove_prefix_from_keys(
|
||||||
|
self.storage.filter_entries(self._ALT_REGISTRY_PATH),
|
||||||
|
self._ALT_REGISTRY_PATH
|
||||||
|
)
|
||||||
|
windows_keys = remove_prefix_from_keys(
|
||||||
|
self.storage.filter_entries(self._WINDOWS_REGISTRY_PATH),
|
||||||
|
self._WINDOWS_REGISTRY_PATH
|
||||||
|
)
|
||||||
|
|
||||||
|
# Combine configurations with BaseALT taking precedence
|
||||||
|
self.config = windows_keys
|
||||||
|
self.config.update(alt_keys)
|
||||||
|
|
||||||
|
# Extract commonly used configuration parameters
|
||||||
|
self.backup_directory = self.config.get('BackupDirectory', None)
|
||||||
|
self.encryption_enabled = self.config.get('ADPasswordEncryptionEnabled', 1)
|
||||||
|
self.password_expiration_protection = self.config.get('PasswordExpirationProtectionEnabled', 1)
|
||||||
|
self.password_age_days = self.config.get('PasswordAgeDays', 30)
|
||||||
|
self.post_authentication_actions = self.config.get('PostAuthenticationActions', 3)
|
||||||
|
self.post_authentication_reset_delay = self.config.get('PostAuthenticationResetDelay', 24)
|
||||||
|
name = self.config.get('AdministratorAccountName', 'root')
|
||||||
|
if name and check_local_user_exists(name):
|
||||||
|
self.target_user = name
|
||||||
|
else:
|
||||||
|
log('W36')
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _check_requirements(self):
|
||||||
|
"""
|
||||||
|
Check if the necessary requirements are met for the module to operate.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if requirements are met, False otherwise
|
||||||
|
"""
|
||||||
|
if self.backup_directory != 2 or not self.encryption_enabled:
|
||||||
|
logdata = {}
|
||||||
|
logdata['backup_directory'] = self.backup_directory
|
||||||
|
logdata['encryption_enabled'] = self.encryption_enabled
|
||||||
|
log('D223', logdata)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _initialize_system_parameters(self):
|
||||||
|
"""Initialize system parameters and connections."""
|
||||||
|
# Set up LDAP connections
|
||||||
|
self.samdb = self.storage.get_info('samdb')
|
||||||
|
self.domain_sid = self.samdb.get_domain_sid()
|
||||||
|
self.domain_dn = self.samdb.domain_dn()
|
||||||
|
self.computer_dn = self._get_computer_dn()
|
||||||
|
self.admin_group_sid = f'{self.domain_sid}-{WellKnown21RID.DOMAIN_ADMINS.value}'
|
||||||
|
|
||||||
|
# Set up time parameters
|
||||||
|
self.expiration_date = self._get_expiration_date()
|
||||||
|
self.expiration_date_int = self._convert_to_filetime(self.expiration_date)
|
||||||
|
self.current_time_int = self._convert_to_filetime(datetime.now())
|
||||||
|
|
||||||
|
# Get current system state
|
||||||
|
self.expiration_time_attr = self._get_expiration_time_attr()
|
||||||
|
self.pass_last_mod_int = self._read_dconf_pass_last_mod()
|
||||||
|
self.encryption_principal = self._get_encryption_principal()
|
||||||
|
self.last_login_hours_ago = self._get_admin_login_hours_ago_after_timestamp()
|
||||||
|
|
||||||
|
def _get_computer_dn(self):
|
||||||
|
"""
|
||||||
|
Get the Distinguished Name of the computer account.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Computer's distinguished name in LDAP
|
||||||
|
"""
|
||||||
|
machine_name = self.storage.get_info('machine_name')
|
||||||
|
search_filter = f'(sAMAccountName={machine_name})'
|
||||||
|
results = self.samdb.search(base=self.domain_dn, expression=search_filter, attrs=['dn'])
|
||||||
|
return results[0]['dn']
|
||||||
|
|
||||||
|
def _get_encryption_principal(self):
|
||||||
|
"""
|
||||||
|
Get the encryption principal for password encryption.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: SID of the encryption principal
|
||||||
|
"""
|
||||||
|
encryption_principal = self.config.get('ADPasswordEncryptionPrincipal', None)
|
||||||
|
if not encryption_principal:
|
||||||
|
return self.admin_group_sid
|
||||||
|
|
||||||
|
return self._verify_encryption_principal(encryption_principal)
|
||||||
|
|
||||||
|
def _verify_encryption_principal(self, principal_name):
|
||||||
|
"""
|
||||||
|
Verify the encryption principal exists and get its SID.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
principal_name: Principal name to verify
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: SID of the encryption principal if found, or admin group SID as fallback
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Try to resolve as domain\\user format
|
||||||
|
domain = self.storage.get_info('domain')
|
||||||
|
username = f'{domain}\\{principal_name}'
|
||||||
|
output = subprocess.check_output(['wbinfo', '-n', username])
|
||||||
|
sid = output.split()[0].decode('utf-8')
|
||||||
|
return sid
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
# Try to resolve directly as SID
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(['wbinfo', '-s', principal_name])
|
||||||
|
return principal_name
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
# Fallback to admin group SID
|
||||||
|
logdata = {}
|
||||||
|
logdata['principal_name'] = principal_name
|
||||||
|
log('W30', logdata)
|
||||||
|
return self.admin_group_sid
|
||||||
|
|
||||||
|
def _get_expiration_date(self, base_time=None):
|
||||||
|
"""
|
||||||
|
Calculate the password expiration date.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
base_time: Optional datetime to base calculation on, defaults to now
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
datetime: Password expiration date
|
||||||
|
"""
|
||||||
|
base = base_time or datetime.now()
|
||||||
|
# Set to beginning of day and add password age
|
||||||
|
return (base.replace(hour=0, minute=0, second=0, microsecond=0) +
|
||||||
|
timedelta(days=int(self.password_age_days)))
|
||||||
|
|
||||||
|
def _convert_to_filetime(self, dt):
|
||||||
|
"""
|
||||||
|
Convert datetime to Windows filetime format (100ns intervals since 1601-01-01).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dt: Datetime to convert
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: Windows filetime integer
|
||||||
|
"""
|
||||||
|
epoch_timedelta = timedelta(seconds=self._EPOCH_TIMESTAMP)
|
||||||
|
new_dt = dt + epoch_timedelta
|
||||||
|
return int(new_dt.timestamp() * self._HUNDREDS_OF_NANOSECONDS)
|
||||||
|
|
||||||
|
def _get_expiration_time_attr(self):
|
||||||
|
"""
|
||||||
|
Get the current password expiration time from LDAP.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: Password expiration time as integer, or 0 if not found
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
res = self.samdb.search(
|
||||||
|
base=self.computer_dn,
|
||||||
|
scope=ldb.SCOPE_BASE,
|
||||||
|
expression="(objectClass=*)",
|
||||||
|
attrs=[self._ATTR_PASSWORD_EXPIRATION_TIME]
|
||||||
|
)
|
||||||
|
return int(res[0].get(self._ATTR_PASSWORD_EXPIRATION_TIME, 0)[0])
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'exc': exc}
|
||||||
|
log('W31', logdata)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _read_dconf_pass_last_mod(self):
|
||||||
|
"""
|
||||||
|
Read the password last modified time from dconf.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: Timestamp of last password modification or current time if not found
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
key_path = self._KEY_PASSWORD_LAST_MODIFIED + self.target_user
|
||||||
|
last_modified = subprocess.check_output(
|
||||||
|
['dconf', 'read', key_path],
|
||||||
|
text=True
|
||||||
|
).strip().strip("'\"")
|
||||||
|
return int(last_modified)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'exc': exc}
|
||||||
|
log('W32', logdata)
|
||||||
|
return self.current_time_int
|
||||||
|
|
||||||
|
def _write_dconf_pass_last_mod(self):
|
||||||
|
"""
|
||||||
|
Write the password last modified time to dconf.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Ensure dbus session is available
|
||||||
|
self._ensure_dbus_session()
|
||||||
|
|
||||||
|
# Write current time to dconf
|
||||||
|
key_path = self._KEY_PASSWORD_LAST_MODIFIED + self.target_user
|
||||||
|
last_modified = f'"{self.current_time_int}"'
|
||||||
|
subprocess.check_output(['dconf', 'write', key_path, last_modified])
|
||||||
|
log('D222')
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'exc': exc}
|
||||||
|
log('W28', logdata)
|
||||||
|
|
||||||
|
def _ensure_dbus_session(self):
|
||||||
|
"""Ensure a D-Bus session is available for dconf operations."""
|
||||||
|
dbus_address = os.getenv("DBUS_SESSION_BUS_ADDRESS")
|
||||||
|
if not dbus_address:
|
||||||
|
result = subprocess.run(
|
||||||
|
["dbus-daemon", "--fork", "--session", "--print-address"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
dbus_address = result.stdout.strip()
|
||||||
|
os.environ["DBUS_SESSION_BUS_ADDRESS"] = dbus_address
|
||||||
|
|
||||||
|
|
||||||
|
def _get_changed_password_hours_ago(self):
|
||||||
|
"""
|
||||||
|
Calculate how many hours ago the password was last changed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: Hours since password was last changed, or 0 if error
|
||||||
|
"""
|
||||||
|
logdata = {}
|
||||||
|
logdata['target_user'] = self.target_user
|
||||||
|
try:
|
||||||
|
diff_time = self.current_time_int - self.pass_last_mod_int
|
||||||
|
hours_difference = diff_time // 3.6e10
|
||||||
|
hours_ago = int(hours_difference)
|
||||||
|
logdata['hours_ago'] = hours_ago
|
||||||
|
log('D225', logdata)
|
||||||
|
return hours_ago
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'exc': exc}
|
||||||
|
log('W34', logdata)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _generate_password(self):
|
||||||
|
"""
|
||||||
|
Generate a secure password based on policy settings.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Generated password meeting complexity requirements
|
||||||
|
"""
|
||||||
|
# Get password length from config
|
||||||
|
password_length = self.config.get('PasswordLength', 14)
|
||||||
|
if not isinstance(password_length, int) or not (8 <= password_length <= 64):
|
||||||
|
password_length = 14
|
||||||
|
|
||||||
|
# Get password complexity from config
|
||||||
|
password_complexity = self.config.get('PasswordComplexity', 4)
|
||||||
|
if not isinstance(password_complexity, int) or not (1 <= password_complexity <= 4):
|
||||||
|
password_complexity = 4
|
||||||
|
|
||||||
|
# Get character set based on complexity
|
||||||
|
char_set = self._PASSWORD_COMPLEXITY.get(password_complexity, self._PASSWORD_COMPLEXITY[4])
|
||||||
|
|
||||||
|
# Generate initial password
|
||||||
|
password = ''.join(secrets.choice(char_set) for _ in range(password_length))
|
||||||
|
|
||||||
|
# Ensure password meets complexity requirements
|
||||||
|
if password_complexity >= 3 and not any(c.isdigit() for c in password):
|
||||||
|
# Add a digit if required but missing
|
||||||
|
digit = secrets.choice(string.digits)
|
||||||
|
position = secrets.randbelow(len(password))
|
||||||
|
password = password[:position] + digit + password[position:]
|
||||||
|
|
||||||
|
if password_complexity == 4 and not any(c in string.punctuation for c in password):
|
||||||
|
# Add a special character if required but missing
|
||||||
|
special_char = secrets.choice(string.punctuation)
|
||||||
|
position = secrets.randbelow(len(password))
|
||||||
|
password = password[:position] + special_char + password[position:]
|
||||||
|
|
||||||
|
return password
|
||||||
|
|
||||||
|
def _get_json_password_data(self, password):
|
||||||
|
"""
|
||||||
|
Format password information as JSON.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
password: The password
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: JSON formatted password information
|
||||||
|
"""
|
||||||
|
return f'{{"n":"{self.target_user}","t":"{self.expiration_date_int}","p":"{password}"}}'
|
||||||
|
|
||||||
|
def _create_password_blob(self, password):
|
||||||
|
"""
|
||||||
|
Create encrypted password blob for LDAP storage.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
password: Password to encrypt
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bytes: Encrypted password blob
|
||||||
|
"""
|
||||||
|
# Create JSON data and encode as UTF-16LE with null terminator
|
||||||
|
json_data = self._get_json_password_data(password)
|
||||||
|
password_bytes = json_data.encode("utf-16-le") + b"\x00\x00"
|
||||||
|
# Save and change loglevel
|
||||||
|
logger = logging.getLogger()
|
||||||
|
old_level = logger.level
|
||||||
|
logger.setLevel(logging.ERROR)
|
||||||
|
# Encrypt the password
|
||||||
|
dpapi_blob = dpapi_ng.ncrypt_protect_secret(
|
||||||
|
password_bytes,
|
||||||
|
self.encryption_principal,
|
||||||
|
auth_protocol='kerberos'
|
||||||
|
)
|
||||||
|
# Restoreloglevel
|
||||||
|
logger.setLevel(old_level)
|
||||||
|
# Create full blob with metadata
|
||||||
|
return self._add_blob_metadata(dpapi_blob)
|
||||||
|
|
||||||
|
def _add_blob_metadata(self, dpapi_blob):
|
||||||
|
"""
|
||||||
|
Add metadata to the encrypted password blob.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dpapi_blob: Encrypted password blob
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bytes: Complete blob with metadata
|
||||||
|
"""
|
||||||
|
# Convert timestamp to correct format
|
||||||
|
left, right = struct.unpack('<LL', struct.pack('Q', self.current_time_int))
|
||||||
|
packed = struct.pack('<LL', right, left)
|
||||||
|
|
||||||
|
# Add blob length and padding
|
||||||
|
prefix = packed + struct.pack('<i', len(dpapi_blob)) + b'\x00\x00\x00\x00'
|
||||||
|
|
||||||
|
# Combine metadata and encrypted blob
|
||||||
|
return prefix + dpapi_blob
|
||||||
|
|
||||||
|
def _change_user_password(self, new_password):
|
||||||
|
"""
|
||||||
|
Change the password for the target user.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
new_password: New password to set
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if password was changed successfully, False otherwise
|
||||||
|
"""
|
||||||
|
logdata = {'target_user': self.target_user}
|
||||||
|
try:
|
||||||
|
# Use chpasswd to change the password
|
||||||
|
process = subprocess.Popen(
|
||||||
|
["chpasswd"],
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
process.communicate(f"{self.target_user}:{new_password}")
|
||||||
|
|
||||||
|
# Record the time of change
|
||||||
|
self._write_dconf_pass_last_mod()
|
||||||
|
log('D221', logdata)
|
||||||
|
return True
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'exc': exc}
|
||||||
|
log('W27', logdata)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _update_ldap_password(self, encrypted_blob):
|
||||||
|
"""
|
||||||
|
Update the encrypted password and expiration time in LDAP.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
encrypted_blob: Encrypted password blob
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if LDAP was updated successfully, False otherwise
|
||||||
|
"""
|
||||||
|
logdata = {'computer_dn': self.computer_dn}
|
||||||
|
try:
|
||||||
|
# Create LDAP modification message
|
||||||
|
mod_msg = ldb.Message()
|
||||||
|
mod_msg.dn = self.computer_dn
|
||||||
|
|
||||||
|
# Update password blob
|
||||||
|
mod_msg[self._ATTR_ENCRYPTED_PASSWORD] = ldb.MessageElement(
|
||||||
|
encrypted_blob,
|
||||||
|
ldb.FLAG_MOD_REPLACE,
|
||||||
|
self._ATTR_ENCRYPTED_PASSWORD
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update expiration time
|
||||||
|
mod_msg[self._ATTR_PASSWORD_EXPIRATION_TIME] = ldb.MessageElement(
|
||||||
|
str(self.expiration_date_int),
|
||||||
|
ldb.FLAG_MOD_REPLACE,
|
||||||
|
self._ATTR_PASSWORD_EXPIRATION_TIME
|
||||||
|
)
|
||||||
|
|
||||||
|
# Perform the LDAP modification
|
||||||
|
self.samdb.modify(mod_msg)
|
||||||
|
log('D226', logdata)
|
||||||
|
return True
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'exc': exc}
|
||||||
|
log('E75', logdata)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _should_update_password(self):
|
||||||
|
"""
|
||||||
|
Determine if the password should be updated based on policy.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: (bool: update needed, bool: perform post-action)
|
||||||
|
"""
|
||||||
|
# Check if password has expired
|
||||||
|
if not self._is_password_expired():
|
||||||
|
# Password not expired, check if post-login action needed
|
||||||
|
return self._check_post_login_action()
|
||||||
|
|
||||||
|
# Password has expired, update needed
|
||||||
|
return True, False
|
||||||
|
|
||||||
|
def _is_password_expired(self):
|
||||||
|
"""
|
||||||
|
Check if the password has expired according to policy.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if password has expired, False otherwise
|
||||||
|
"""
|
||||||
|
# Case 1: No expiration protection, check LDAP attribute
|
||||||
|
if not self.password_expiration_protection:
|
||||||
|
if self.expiration_time_attr > self.current_time_int:
|
||||||
|
return False
|
||||||
|
# Case 2: With expiration protection, check both policy and LDAP
|
||||||
|
elif self.password_expiration_protection:
|
||||||
|
policy_expiry = self.pass_last_mod_int + (self.password_age_days * int(self._DAY_FLOAT))
|
||||||
|
if policy_expiry > self.current_time_int and self.expiration_time_attr > self.current_time_int:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _check_post_login_action(self):
|
||||||
|
"""
|
||||||
|
Check if a post-login password change action should be performed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: (bool: update needed, bool: perform post-action)
|
||||||
|
"""
|
||||||
|
# Check if password was changed after last login
|
||||||
|
if self._get_changed_password_hours_ago() < self.last_login_hours_ago:
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
# Check if enough time has passed since login
|
||||||
|
if self.last_login_hours_ago < self.post_authentication_reset_delay:
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
# Check if action is configured
|
||||||
|
if self.post_authentication_actions == self._ACTION_NONE:
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
# Update needed, determine if post-action required
|
||||||
|
return True, self.post_authentication_actions > self._ACTION_CHANGE_PASSWORD
|
||||||
|
|
||||||
|
def _perform_post_action(self):
|
||||||
|
"""
|
||||||
|
Perform post-password-change action based on configuration.
|
||||||
|
"""
|
||||||
|
if self.post_authentication_actions == self._ACTION_TERMINATE_SESSIONS:
|
||||||
|
self._terminate_user_sessions()
|
||||||
|
elif self.post_authentication_actions == self._ACTION_REBOOT:
|
||||||
|
log('D220')
|
||||||
|
subprocess.run(["reboot"])
|
||||||
|
|
||||||
|
def _terminate_user_sessions(self):
|
||||||
|
"""
|
||||||
|
Terminates all processes associated with the active sessions of the target user.
|
||||||
|
"""
|
||||||
|
# Get active sessions for the target user
|
||||||
|
user_sessions = [user for user in psutil.users() if user.name == self.target_user]
|
||||||
|
logdata = {'target_user': self.target_user}
|
||||||
|
if not user_sessions:
|
||||||
|
log('D227', logdata)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Terminate each session
|
||||||
|
for session in user_sessions:
|
||||||
|
try:
|
||||||
|
# Get the process and terminate it
|
||||||
|
proc = psutil.Process(session.pid)
|
||||||
|
proc.kill() # Send SIGKILL
|
||||||
|
logdata['pid'] = session.pid
|
||||||
|
log('D228')
|
||||||
|
except (psutil.NoSuchProcess, psutil.AccessDenied) as exc:
|
||||||
|
logdata['pid'] = session.pid
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('W35', logdata)
|
||||||
|
|
||||||
|
def update_laps_password(self):
|
||||||
|
"""
|
||||||
|
Update the LAPS password if needed based on policy.
|
||||||
|
Checks expiration and login times to determine if update is needed.
|
||||||
|
"""
|
||||||
|
# Check if password update is needed
|
||||||
|
update_needed, perform_post_action = self._should_update_password()
|
||||||
|
|
||||||
|
if not update_needed:
|
||||||
|
log('D229')
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Generate new password
|
||||||
|
password = self._generate_password()
|
||||||
|
|
||||||
|
# Create encrypted password blob
|
||||||
|
encrypted_blob = self._create_password_blob(password)
|
||||||
|
|
||||||
|
# Update password in LDAP
|
||||||
|
ldap_success = self._update_ldap_password(encrypted_blob)
|
||||||
|
|
||||||
|
if not ldap_success:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Change local user password
|
||||||
|
local_success = self._change_user_password(password)
|
||||||
|
|
||||||
|
if not local_success:
|
||||||
|
log('E76')
|
||||||
|
return False
|
||||||
|
|
||||||
|
log('D230')
|
||||||
|
|
||||||
|
# Perform post-action if configured
|
||||||
|
if perform_post_action:
|
||||||
|
self._perform_post_action()
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
"""
|
||||||
|
Main entry point for the LAPS applier.
|
||||||
|
"""
|
||||||
|
if self.__module_enabled:
|
||||||
|
log('D218')
|
||||||
|
self.update_laps_password()
|
||||||
|
else:
|
||||||
|
log('D219')
|
||||||
|
|
||||||
|
def _parse_login_time_from_last_line(self, line: str) -> datetime:
|
||||||
|
match_login_dt = re.search(
|
||||||
|
r"((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+\d{4})",
|
||||||
|
line
|
||||||
|
)
|
||||||
|
|
||||||
|
if not match_login_dt:
|
||||||
|
return None
|
||||||
|
|
||||||
|
login_dt_str = match_login_dt.group(1)
|
||||||
|
try:
|
||||||
|
dt_naive = datetime.strptime(login_dt_str, "%a %b %d %H:%M:%S %Y")
|
||||||
|
login_dt_utc: datetime
|
||||||
|
if _DATEUTIL_AVAILABLE:
|
||||||
|
local_tz = tz.tzlocal()
|
||||||
|
dt_local = dt_naive.replace(tzinfo=local_tz)
|
||||||
|
login_dt_utc = dt_local.astimezone(timezone.utc)
|
||||||
|
else:
|
||||||
|
system_local_tz = datetime.now().astimezone().tzinfo
|
||||||
|
if system_local_tz:
|
||||||
|
dt_local = dt_naive.replace(tzinfo=system_local_tz)
|
||||||
|
login_dt_utc = dt_local.astimezone(timezone.utc)
|
||||||
|
else:
|
||||||
|
login_dt_utc = dt_naive.replace(tzinfo=timezone.utc)
|
||||||
|
log('W38')
|
||||||
|
return login_dt_utc
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _get_user_login_datetimes_utc(self) -> list[datetime]:
|
||||||
|
command = ["last", "-F", "-w", self.target_user]
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["LC_TIME"] = "C"
|
||||||
|
login_datetimes = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
process = subprocess.run(command, capture_output=True, text=True, check=False, env=env)
|
||||||
|
if process.returncode != 0 and not ("no login record" in process.stderr.lower() or "no users logged in" in process.stdout.lower()):
|
||||||
|
log('W39')
|
||||||
|
return []
|
||||||
|
output_lines = process.stdout.splitlines()
|
||||||
|
except FileNotFoundError:
|
||||||
|
log('W40')
|
||||||
|
return []
|
||||||
|
except Exception as e:
|
||||||
|
log('W41')
|
||||||
|
return []
|
||||||
|
|
||||||
|
for line in output_lines:
|
||||||
|
if not line.strip() or "wtmp begins" in line or "btmp begins" in line:
|
||||||
|
continue
|
||||||
|
if not line.startswith(self.target_user):
|
||||||
|
continue
|
||||||
|
login_dt_utc = self._parse_login_time_from_last_line(line)
|
||||||
|
if login_dt_utc:
|
||||||
|
login_datetimes.append(login_dt_utc)
|
||||||
|
|
||||||
|
return login_datetimes
|
||||||
|
|
||||||
|
def _get_admin_login_hours_ago_after_timestamp(self) -> int:
|
||||||
|
# Convert Windows FileTime to datetime
|
||||||
|
reference_dt_utc = datetime.fromtimestamp(
|
||||||
|
(self.pass_last_mod_int / self._HUNDREDS_OF_NANOSECONDS) - self._EPOCH_TIMESTAMP,
|
||||||
|
tz=timezone.utc
|
||||||
|
)
|
||||||
|
|
||||||
|
if not (reference_dt_utc.tzinfo is timezone.utc or
|
||||||
|
(reference_dt_utc.tzinfo is not None and reference_dt_utc.tzinfo.utcoffset(reference_dt_utc) == timedelta(0))):
|
||||||
|
log('W42')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
user_login_times_utc = self._get_user_login_datetimes_utc()
|
||||||
|
if not user_login_times_utc:
|
||||||
|
log('D232')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
most_recent_login_after_reference_utc = None
|
||||||
|
for login_time_utc in user_login_times_utc[::-1]:
|
||||||
|
if login_time_utc >= reference_dt_utc:
|
||||||
|
most_recent_login_after_reference_utc = login_time_utc
|
||||||
|
break
|
||||||
|
|
||||||
|
if most_recent_login_after_reference_utc:
|
||||||
|
now_utc = datetime.now(timezone.utc)
|
||||||
|
time_delta_seconds = (now_utc - most_recent_login_after_reference_utc).total_seconds()
|
||||||
|
hours_ago = int(time_delta_seconds / 3600.0)
|
||||||
|
log('D233')
|
||||||
|
return hours_ago
|
||||||
|
else:
|
||||||
|
log('D234')
|
||||||
|
return 0
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2022 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -29,11 +29,10 @@ class networkshare_applier(applier_frontend):
|
|||||||
__module_experimental = True
|
__module_experimental = True
|
||||||
__module_enabled = False
|
__module_enabled = False
|
||||||
|
|
||||||
def __init__(self, storage, sid, username = None):
|
def __init__(self, storage, username = None):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self.networkshare_info = self.storage.get_networkshare(self.sid)
|
self.networkshare_info = self.storage.get_networkshare()
|
||||||
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
self.__module_enabled_user = check_enabled(self.storage, self.__module_name_user, self.__module_experimental)
|
self.__module_enabled_user = check_enabled(self.storage, self.__module_name_user, self.__module_experimental)
|
||||||
|
|
||||||
|
|||||||
@@ -77,8 +77,7 @@ class ntp_applier(applier_frontend):
|
|||||||
srv = None
|
srv = None
|
||||||
if server:
|
if server:
|
||||||
srv = server.data.rpartition(',')[0]
|
srv = server.data.rpartition(',')[0]
|
||||||
logdata = dict()
|
logdata = {'srv': srv}
|
||||||
logdata['srv'] = srv
|
|
||||||
log('D122', logdata)
|
log('D122', logdata)
|
||||||
|
|
||||||
start_command = ['/usr/bin/systemctl', 'start', 'chronyd']
|
start_command = ['/usr/bin/systemctl', 'start', 'chronyd']
|
||||||
@@ -92,8 +91,7 @@ class ntp_applier(applier_frontend):
|
|||||||
proc.wait()
|
proc.wait()
|
||||||
|
|
||||||
if srv:
|
if srv:
|
||||||
logdata = dict()
|
logdata = {'srv': srv}
|
||||||
logdata['srv'] = srv
|
|
||||||
log('D124', logdata)
|
log('D124', logdata)
|
||||||
|
|
||||||
proc = subprocess.Popen(chrony_disconnect_all)
|
proc = subprocess.Popen(chrony_disconnect_all)
|
||||||
@@ -119,8 +117,7 @@ class ntp_applier(applier_frontend):
|
|||||||
|
|
||||||
if server_type and server_type.data:
|
if server_type and server_type.data:
|
||||||
if NTPServerType.NTP.value != server_type.data:
|
if NTPServerType.NTP.value != server_type.data:
|
||||||
logdata = dict()
|
logdata = {'server_type': server_type}
|
||||||
logdata['server_type'] = server_type
|
|
||||||
log('W10', logdata)
|
log('W10', logdata)
|
||||||
else:
|
else:
|
||||||
log('D126')
|
log('D126')
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,12 +18,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
from util.logging import slogm, log
|
from util.logging import log
|
||||||
from util.rpm import (
|
|
||||||
update
|
|
||||||
, install_rpm
|
|
||||||
, remove_rpm
|
|
||||||
)
|
|
||||||
|
|
||||||
from .applier_frontend import (
|
from .applier_frontend import (
|
||||||
applier_frontend
|
applier_frontend
|
||||||
@@ -32,8 +27,8 @@ from .applier_frontend import (
|
|||||||
|
|
||||||
class package_applier(applier_frontend):
|
class package_applier(applier_frontend):
|
||||||
__module_name = 'PackagesApplier'
|
__module_name = 'PackagesApplier'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
__install_key_name = 'Install'
|
__install_key_name = 'Install'
|
||||||
__remove_key_name = 'Remove'
|
__remove_key_name = 'Remove'
|
||||||
__sync_key_name = 'Sync'
|
__sync_key_name = 'Sync'
|
||||||
@@ -45,7 +40,7 @@ class package_applier(applier_frontend):
|
|||||||
install_branch = '{}\\{}%'.format(self.__hklm_branch, self.__install_key_name)
|
install_branch = '{}\\{}%'.format(self.__hklm_branch, self.__install_key_name)
|
||||||
remove_branch = '{}\\{}%'.format(self.__hklm_branch, self.__remove_key_name)
|
remove_branch = '{}\\{}%'.format(self.__hklm_branch, self.__remove_key_name)
|
||||||
sync_branch = '{}\\{}%'.format(self.__hklm_branch, self.__sync_key_name)
|
sync_branch = '{}\\{}%'.format(self.__hklm_branch, self.__sync_key_name)
|
||||||
self.fulcmd = list()
|
self.fulcmd = []
|
||||||
self.fulcmd.append('/usr/libexec/gpupdate/pkcon_runner')
|
self.fulcmd.append('/usr/libexec/gpupdate/pkcon_runner')
|
||||||
self.fulcmd.append('--loglevel')
|
self.fulcmd.append('--loglevel')
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
@@ -62,23 +57,20 @@ class package_applier(applier_frontend):
|
|||||||
)
|
)
|
||||||
def run(self):
|
def run(self):
|
||||||
for flag in self.sync_packages_setting:
|
for flag in self.sync_packages_setting:
|
||||||
if flag.data:
|
self.flagSync = bool(flag.data)
|
||||||
self.flagSync = bool(int(flag.data))
|
|
||||||
|
|
||||||
if 0 < self.install_packages_setting.count() or 0 < self.remove_packages_setting.count():
|
if 0 < self.install_packages_setting.count() or 0 < self.remove_packages_setting.count():
|
||||||
if self.flagSync:
|
if self.flagSync:
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(self.fulcmd)
|
subprocess.check_call(self.fulcmd)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'msg': str(exc)}
|
||||||
logdata['msg'] = str(exc)
|
|
||||||
log('E55', logdata)
|
log('E55', logdata)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
subprocess.Popen(self.fulcmd,close_fds=False)
|
subprocess.Popen(self.fulcmd,close_fds=False)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'msg': str(exc)}
|
||||||
logdata['msg'] = str(exc)
|
|
||||||
log('E61', logdata)
|
log('E61', logdata)
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
@@ -91,21 +83,20 @@ class package_applier(applier_frontend):
|
|||||||
|
|
||||||
class package_applier_user(applier_frontend):
|
class package_applier_user(applier_frontend):
|
||||||
__module_name = 'PackagesApplierUser'
|
__module_name = 'PackagesApplierUser'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
__install_key_name = 'Install'
|
__install_key_name = 'Install'
|
||||||
__remove_key_name = 'Remove'
|
__remove_key_name = 'Remove'
|
||||||
__sync_key_name = 'Sync'
|
__sync_key_name = 'Sync'
|
||||||
__hkcu_branch = 'Software\\BaseALT\\Policies\\Packages'
|
__hkcu_branch = 'Software\\BaseALT\\Policies\\Packages'
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self.fulcmd = list()
|
self.fulcmd = []
|
||||||
self.fulcmd.append('/usr/libexec/gpupdate/pkcon_runner')
|
self.fulcmd.append('/usr/libexec/gpupdate/pkcon_runner')
|
||||||
self.fulcmd.append('--sid')
|
self.fulcmd.append('--user')
|
||||||
self.fulcmd.append(self.sid)
|
self.fulcmd.append(self.username)
|
||||||
self.fulcmd.append('--loglevel')
|
self.fulcmd.append('--loglevel')
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
self.fulcmd.append(str(logger.level))
|
self.fulcmd.append(str(logger.level))
|
||||||
@@ -114,12 +105,12 @@ class package_applier_user(applier_frontend):
|
|||||||
remove_branch = '{}\\{}%'.format(self.__hkcu_branch, self.__remove_key_name)
|
remove_branch = '{}\\{}%'.format(self.__hkcu_branch, self.__remove_key_name)
|
||||||
sync_branch = '{}\\{}%'.format(self.__hkcu_branch, self.__sync_key_name)
|
sync_branch = '{}\\{}%'.format(self.__hkcu_branch, self.__sync_key_name)
|
||||||
|
|
||||||
self.install_packages_setting = self.storage.filter_hkcu_entries(self.sid, install_branch)
|
self.install_packages_setting = self.storage.filter_hkcu_entries(install_branch)
|
||||||
self.remove_packages_setting = self.storage.filter_hkcu_entries(self.sid, remove_branch)
|
self.remove_packages_setting = self.storage.filter_hkcu_entries(remove_branch)
|
||||||
self.sync_packages_setting = self.storage.filter_hkcu_entries(self.sid, sync_branch)
|
self.sync_packages_setting = self.storage.filter_hkcu_entries(sync_branch)
|
||||||
self.flagSync = False
|
self.flagSync = False
|
||||||
|
|
||||||
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_enabled)
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
|
|
||||||
def user_context_apply(self):
|
def user_context_apply(self):
|
||||||
'''
|
'''
|
||||||
@@ -137,15 +128,13 @@ class package_applier_user(applier_frontend):
|
|||||||
try:
|
try:
|
||||||
subprocess.check_call(self.fulcmd)
|
subprocess.check_call(self.fulcmd)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'msg': str(exc)}
|
||||||
logdata['msg'] = str(exc)
|
|
||||||
log('E60', logdata)
|
log('E60', logdata)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
subprocess.Popen(self.fulcmd,close_fds=False)
|
subprocess.Popen(self.fulcmd,close_fds=False)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'msg': str(exc)}
|
||||||
logdata['msg'] = str(exc)
|
|
||||||
log('E62', logdata)
|
log('E62', logdata)
|
||||||
|
|
||||||
def admin_context_apply(self):
|
def admin_context_apply(self):
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ from util.logging import log
|
|||||||
|
|
||||||
class polkit_applier(applier_frontend):
|
class polkit_applier(applier_frontend):
|
||||||
__module_name = 'PolkitApplier'
|
__module_name = 'PolkitApplier'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
__deny_all_win = 'Software\\Policies\\Microsoft\\Windows\\RemovableStorageDevices\\Deny_All'
|
__deny_all_win = 'Software\\Policies\\Microsoft\\Windows\\RemovableStorageDevices\\Deny_All'
|
||||||
__registry_branch = 'Software\\BaseALT\\Policies\\Polkit\\'
|
__registry_branch = 'Software\\BaseALT\\Policies\\Polkit\\'
|
||||||
__registry_locks_branch = 'Software\\BaseALT\\Policies\\PolkitLocks\\'
|
__registry_locks_branch = 'Software\\BaseALT\\Policies\\PolkitLocks\\'
|
||||||
@@ -53,7 +53,7 @@ class polkit_applier(applier_frontend):
|
|||||||
template_vars_all = self.__polkit_map[self.__registry_branch][1]
|
template_vars_all = self.__polkit_map[self.__registry_branch][1]
|
||||||
template_file_all_lock = self.__polkit_map[self.__registry_locks_branch][0]
|
template_file_all_lock = self.__polkit_map[self.__registry_locks_branch][0]
|
||||||
template_vars_all_lock = self.__polkit_map[self.__registry_locks_branch][1]
|
template_vars_all_lock = self.__polkit_map[self.__registry_locks_branch][1]
|
||||||
locks = list()
|
locks = []
|
||||||
for lock in self.polkit_locks:
|
for lock in self.polkit_locks:
|
||||||
if bool(int(lock.data)):
|
if bool(int(lock.data)):
|
||||||
locks.append(lock.valuename)
|
locks.append(lock.valuename)
|
||||||
@@ -77,7 +77,7 @@ class polkit_applier(applier_frontend):
|
|||||||
self.__polkit_map[self.__registry_locks_branch][1][key] = item[1]
|
self.__polkit_map[self.__registry_locks_branch][1][key] = item[1]
|
||||||
|
|
||||||
if deny_all_win:
|
if deny_all_win:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['Deny_All_win'] = deny_all_win.data
|
logdata['Deny_All_win'] = deny_all_win.data
|
||||||
log('D69', logdata)
|
log('D69', logdata)
|
||||||
self.__polkit_map[self.__deny_all_win][1]['Deny_All'] = deny_all_win.data
|
self.__polkit_map[self.__deny_all_win][1]['Deny_All'] = deny_all_win.data
|
||||||
@@ -106,8 +106,8 @@ class polkit_applier(applier_frontend):
|
|||||||
|
|
||||||
class polkit_applier_user(applier_frontend):
|
class polkit_applier_user(applier_frontend):
|
||||||
__module_name = 'PolkitApplierUser'
|
__module_name = 'PolkitApplierUser'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
__deny_all_win = 'Software\\Policies\\Microsoft\\Windows\\RemovableStorageDevices\\Deny_All'
|
__deny_all_win = 'Software\\Policies\\Microsoft\\Windows\\RemovableStorageDevices\\Deny_All'
|
||||||
__registry_branch = 'Software\\BaseALT\\Policies\\Polkit\\'
|
__registry_branch = 'Software\\BaseALT\\Policies\\Polkit\\'
|
||||||
__polkit_map = {
|
__polkit_map = {
|
||||||
@@ -115,15 +115,14 @@ class polkit_applier_user(applier_frontend):
|
|||||||
__registry_branch : ['48-alt_group_policy_permissions_user', {'User': ''}]
|
__registry_branch : ['48-alt_group_policy_permissions_user', {'User': ''}]
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
deny_all_win = None
|
deny_all_win = None
|
||||||
if check_windows_mapping_enabled(self.storage):
|
if check_windows_mapping_enabled(self.storage):
|
||||||
deny_all_win = storage.filter_hkcu_entries(self.sid, self.__deny_all_win).first()
|
deny_all_win = storage.filter_hkcu_entries(self.__deny_all_win).first()
|
||||||
polkit_filter = '{}%'.format(self.__registry_branch)
|
polkit_filter = '{}%'.format(self.__registry_branch)
|
||||||
self.polkit_keys = self.storage.filter_hkcu_entries(self.sid, polkit_filter)
|
self.polkit_keys = self.storage.filter_hkcu_entries(polkit_filter)
|
||||||
# Deny_All hook: initialize defaults
|
# Deny_All hook: initialize defaults
|
||||||
template_file = self.__polkit_map[self.__deny_all_win][0]
|
template_file = self.__polkit_map[self.__deny_all_win][0]
|
||||||
template_vars = self.__polkit_map[self.__deny_all_win][1]
|
template_vars = self.__polkit_map[self.__deny_all_win][1]
|
||||||
@@ -146,7 +145,7 @@ class polkit_applier_user(applier_frontend):
|
|||||||
self.__polkit_map[self.__registry_branch][1][key] = item
|
self.__polkit_map[self.__registry_branch][1][key] = item
|
||||||
|
|
||||||
if deny_all_win:
|
if deny_all_win:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['user'] = self.username
|
logdata['user'] = self.username
|
||||||
logdata['Deny_All_win'] = deny_all_win.data
|
logdata['Deny_All_win'] = deny_all_win.data
|
||||||
log('D70', logdata)
|
log('D70', logdata)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2022 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -29,15 +29,14 @@ from .applier_frontend import (
|
|||||||
|
|
||||||
class scripts_applier(applier_frontend):
|
class scripts_applier(applier_frontend):
|
||||||
__module_name = 'ScriptsApplier'
|
__module_name = 'ScriptsApplier'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
__cache_scripts = '/var/cache/gpupdate_scripts_cache/machine/'
|
__cache_scripts = '/var/cache/gpupdate_scripts_cache/machine/'
|
||||||
|
|
||||||
def __init__(self, storage, sid):
|
def __init__(self, storage):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
self.startup_scripts = self.storage.get_scripts('STARTUP')
|
||||||
self.startup_scripts = self.storage.get_scripts(self.sid, 'STARTUP')
|
self.shutdown_scripts = self.storage.get_scripts('SHUTDOWN')
|
||||||
self.shutdown_scripts = self.storage.get_scripts(self.sid, 'SHUTDOWN')
|
|
||||||
self.folder_path = Path(self.__cache_scripts)
|
self.folder_path = Path(self.__cache_scripts)
|
||||||
self.__module_enabled = check_enabled(self.storage
|
self.__module_enabled = check_enabled(self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
@@ -51,8 +50,7 @@ class scripts_applier(applier_frontend):
|
|||||||
except FileNotFoundError as exc:
|
except FileNotFoundError as exc:
|
||||||
log('D154')
|
log('D154')
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'exc': exc}
|
||||||
logdata['exc'] = exc
|
|
||||||
log('E64', logdata)
|
log('E64', logdata)
|
||||||
|
|
||||||
def filling_cache(self):
|
def filling_cache(self):
|
||||||
@@ -80,22 +78,20 @@ class scripts_applier(applier_frontend):
|
|||||||
|
|
||||||
class scripts_applier_user(applier_frontend):
|
class scripts_applier_user(applier_frontend):
|
||||||
__module_name = 'ScriptsApplierUser'
|
__module_name = 'ScriptsApplierUser'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
__cache_scripts = '/var/cache/gpupdate_scripts_cache/users/'
|
__cache_scripts = '/var/cache/gpupdate_scripts_cache/users/'
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
self.logon_scripts = self.storage.get_scripts('LOGON')
|
||||||
self.logon_scripts = self.storage.get_scripts(self.sid, 'LOGON')
|
self.logoff_scripts = self.storage.get_scripts('LOGOFF')
|
||||||
self.logoff_scripts = self.storage.get_scripts(self.sid, 'LOGOFF')
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self.folder_path = Path(self.__cache_scripts + self.username)
|
self.folder_path = Path(self.__cache_scripts + self.username)
|
||||||
self.__module_enabled = check_enabled(self.storage
|
self.__module_enabled = check_enabled(self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
, self.__module_experimental
|
, self.__module_experimental
|
||||||
)
|
)
|
||||||
self.filling_cache()
|
|
||||||
|
|
||||||
def cleaning_cache(self):
|
def cleaning_cache(self):
|
||||||
log('D161')
|
log('D161')
|
||||||
@@ -104,8 +100,7 @@ class scripts_applier_user(applier_frontend):
|
|||||||
except FileNotFoundError as exc:
|
except FileNotFoundError as exc:
|
||||||
log('D155')
|
log('D155')
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'exc': exc}
|
||||||
logdata['exc'] = exc
|
|
||||||
log('E65', logdata)
|
log('E65', logdata)
|
||||||
|
|
||||||
def filling_cache(self):
|
def filling_cache(self):
|
||||||
@@ -143,15 +138,17 @@ def install_script(storage_script_entry, script_dir, access_permissions):
|
|||||||
'''
|
'''
|
||||||
dir_cr = Path(script_dir)
|
dir_cr = Path(script_dir)
|
||||||
dir_cr.mkdir(parents=True, exist_ok=True)
|
dir_cr.mkdir(parents=True, exist_ok=True)
|
||||||
script_name = str(int(storage_script_entry.number)).zfill(5) + '_' + os.path.basename(storage_script_entry.path)
|
if storage_script_entry.number is None:
|
||||||
|
return
|
||||||
|
script_name = str(storage_script_entry.number).zfill(5) + '_' + os.path.basename(storage_script_entry.path)
|
||||||
script_file = os.path.join(script_dir, script_name)
|
script_file = os.path.join(script_dir, script_name)
|
||||||
shutil.copyfile(storage_script_entry.path, script_file)
|
shutil.copyfile(storage_script_entry.path, script_file)
|
||||||
|
|
||||||
os.chmod(script_file, int(access_permissions, base = 8))
|
os.chmod(script_file, int(access_permissions, base = 8))
|
||||||
if storage_script_entry.arg:
|
if storage_script_entry.args:
|
||||||
dir_path = script_dir + '/' + script_name + '.arg'
|
dir_path = script_dir + '/' + script_name + '.arg'
|
||||||
dir_arg = Path(dir_path)
|
dir_arg = Path(dir_path)
|
||||||
dir_arg.mkdir(parents=True, exist_ok=True)
|
dir_arg.mkdir(parents=True, exist_ok=True)
|
||||||
file_arg = open(dir_path + '/arg', 'w')
|
file_arg = open(dir_path + '/arg', 'w')
|
||||||
file_arg.write(storage_script_entry.arg)
|
file_arg.write(storage_script_entry.args)
|
||||||
file_arg.close()
|
file_arg.close()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,30 +16,31 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import logging
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from .applier_frontend import (
|
from .applier_frontend import (
|
||||||
applier_frontend
|
applier_frontend
|
||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
from gpt.shortcuts import json2sc
|
|
||||||
from util.windows import expand_windows_var
|
from util.windows import expand_windows_var
|
||||||
from util.logging import slogm, log
|
from util.logging import log
|
||||||
from util.util import (
|
from util.util import (
|
||||||
get_homedir,
|
get_homedir,
|
||||||
homedir_exists
|
homedir_exists,
|
||||||
|
string_to_literal_eval
|
||||||
)
|
)
|
||||||
|
from gpt.shortcuts import shortcut, get_ttype
|
||||||
|
|
||||||
def storage_get_shortcuts(storage, sid, username=None):
|
def storage_get_shortcuts(storage, username=None, shortcuts_machine=None):
|
||||||
'''
|
'''
|
||||||
Query storage for shortcuts' rows for specified SID.
|
Query storage for shortcuts' rows for username.
|
||||||
'''
|
'''
|
||||||
shortcut_objs = storage.get_shortcuts(sid)
|
shortcut_objs = storage.get_shortcuts()
|
||||||
shortcuts = list()
|
shortcuts = []
|
||||||
|
if username and shortcuts_machine:
|
||||||
|
shortcut_objs += shortcuts_machine
|
||||||
|
|
||||||
for sc_obj in shortcut_objs:
|
for sc in shortcut_objs:
|
||||||
sc = json2sc(sc_obj.shortcut)
|
|
||||||
if username:
|
if username:
|
||||||
sc.set_expanded_path(expand_windows_var(sc.path, username))
|
sc.set_expanded_path(expand_windows_var(sc.path, username))
|
||||||
shortcuts.append(sc)
|
shortcuts.append(sc)
|
||||||
@@ -55,9 +56,7 @@ def apply_shortcut(shortcut, username=None):
|
|||||||
dest_abspath = shortcut.dest
|
dest_abspath = shortcut.dest
|
||||||
if not dest_abspath.startswith('/') and not dest_abspath.startswith('%'):
|
if not dest_abspath.startswith('/') and not dest_abspath.startswith('%'):
|
||||||
dest_abspath = '%HOME%/' + dest_abspath
|
dest_abspath = '%HOME%/' + dest_abspath
|
||||||
logdata = dict()
|
logdata = {'shortcut': dest_abspath, 'for': username}
|
||||||
logdata['shortcut'] = dest_abspath
|
|
||||||
logdata['for'] = username
|
|
||||||
log('D105', logdata)
|
log('D105', logdata)
|
||||||
dest_abspath = expand_windows_var(dest_abspath, username).replace('\\', '/') + '.desktop'
|
dest_abspath = expand_windows_var(dest_abspath, username).replace('\\', '/') + '.desktop'
|
||||||
|
|
||||||
@@ -68,31 +67,24 @@ def apply_shortcut(shortcut, username=None):
|
|||||||
if dest_abspath.startswith(get_homedir(username)):
|
if dest_abspath.startswith(get_homedir(username)):
|
||||||
# Don't try to operate on non-existent directory
|
# Don't try to operate on non-existent directory
|
||||||
if not homedir_exists(username):
|
if not homedir_exists(username):
|
||||||
logdata = dict()
|
logdata = {'user': username, 'dest_abspath': dest_abspath}
|
||||||
logdata['user'] = username
|
|
||||||
logdata['dest_abspath'] = dest_abspath
|
|
||||||
log('W7', logdata)
|
log('W7', logdata)
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
logdata = dict()
|
logdata = {'user': username, 'bad path': dest_abspath}
|
||||||
logdata['user'] = username
|
|
||||||
logdata['bad path'] = dest_abspath
|
|
||||||
log('W8', logdata)
|
log('W8', logdata)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if '%' in dest_abspath:
|
if '%' in dest_abspath:
|
||||||
logdata = dict()
|
logdata = {'dest_abspath': dest_abspath}
|
||||||
logdata['dest_abspath'] = dest_abspath
|
|
||||||
log('E53', logdata)
|
log('E53', logdata)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not dest_abspath.startswith('/'):
|
if not dest_abspath.startswith('/'):
|
||||||
logdata = dict()
|
logdata = {'dest_abspath': dest_abspath}
|
||||||
logdata['dest_abspath'] = dest_abspath
|
|
||||||
log('E54', logdata)
|
log('E54', logdata)
|
||||||
return None
|
return None
|
||||||
logdata = dict()
|
logdata = {'file': dest_abspath}
|
||||||
logdata['file'] = dest_abspath
|
|
||||||
logdata['with_action'] = shortcut.action
|
logdata['with_action'] = shortcut.action
|
||||||
log('D106', logdata)
|
log('D106', logdata)
|
||||||
shortcut.apply_desktop(dest_abspath)
|
shortcut.apply_desktop(dest_abspath)
|
||||||
@@ -111,7 +103,7 @@ class shortcut_applier(applier_frontend):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
shortcuts = storage_get_shortcuts(self.storage, self.storage.get_info('machine_sid'))
|
shortcuts = storage_get_shortcuts(self.storage)
|
||||||
if shortcuts:
|
if shortcuts:
|
||||||
for sc in shortcuts:
|
for sc in shortcuts:
|
||||||
apply_shortcut(sc)
|
apply_shortcut(sc)
|
||||||
@@ -122,9 +114,7 @@ class shortcut_applier(applier_frontend):
|
|||||||
# /usr/local/share/applications
|
# /usr/local/share/applications
|
||||||
subprocess.check_call(['/usr/bin/update-desktop-database'])
|
subprocess.check_call(['/usr/bin/update-desktop-database'])
|
||||||
else:
|
else:
|
||||||
logdata = dict()
|
log('D100')
|
||||||
logdata['machine_sid'] = self.storage.get_info('machine_sid')
|
|
||||||
log('D100', logdata)
|
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
@@ -137,14 +127,45 @@ class shortcut_applier_user(applier_frontend):
|
|||||||
__module_name = 'ShortcutsApplierUser'
|
__module_name = 'ShortcutsApplierUser'
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
|
__REGISTRY_PATH_SHORTCATSMERGE= '/Software/BaseALT/Policies/GPUpdate/ShortcutsMerge'
|
||||||
|
__DCONF_REGISTRY_PATH_PREFERENCES_MACHINE = 'Software/BaseALT/Policies/Preferences/Machine'
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
|
|
||||||
|
def get_machine_shortcuts(self):
|
||||||
|
result = []
|
||||||
|
try:
|
||||||
|
storage_machine_dict = self.storage.get_dictionary_from_dconf_file_db()
|
||||||
|
machine_shortcuts = storage_machine_dict.get(
|
||||||
|
self.__DCONF_REGISTRY_PATH_PREFERENCES_MACHINE, dict()).get('Shortcuts')
|
||||||
|
shortcut_objs = string_to_literal_eval(machine_shortcuts)
|
||||||
|
for obj in shortcut_objs:
|
||||||
|
shortcut_machine =shortcut(
|
||||||
|
obj.get('dest'),
|
||||||
|
obj.get('path'),
|
||||||
|
obj.get('arguments'),
|
||||||
|
obj.get('name'),
|
||||||
|
obj.get('action'),
|
||||||
|
get_ttype(obj.get('target_type')))
|
||||||
|
shortcut_machine.set_usercontext(1)
|
||||||
|
result.append(shortcut_machine)
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def check_enabled_shortcuts_merge(self):
|
||||||
|
return self.storage.get_key_value(self.__REGISTRY_PATH_SHORTCATSMERGE)
|
||||||
|
|
||||||
def run(self, in_usercontext):
|
def run(self, in_usercontext):
|
||||||
shortcuts = storage_get_shortcuts(self.storage, self.sid, self.username)
|
shortcuts_machine = None
|
||||||
|
if self.check_enabled_shortcuts_merge():
|
||||||
|
shortcuts_machine = self.get_machine_shortcuts()
|
||||||
|
shortcuts = storage_get_shortcuts(self.storage, self.username, shortcuts_machine)
|
||||||
|
|
||||||
if shortcuts:
|
if shortcuts:
|
||||||
for sc in shortcuts:
|
for sc in shortcuts:
|
||||||
@@ -153,8 +174,7 @@ class shortcut_applier_user(applier_frontend):
|
|||||||
if not in_usercontext and not sc.is_usercontext():
|
if not in_usercontext and not sc.is_usercontext():
|
||||||
apply_shortcut(sc, self.username)
|
apply_shortcut(sc, self.username)
|
||||||
else:
|
else:
|
||||||
logdata = dict()
|
logdata = {'username': self.username}
|
||||||
logdata['sid'] = self.sid
|
|
||||||
log('D100', logdata)
|
log('D100', logdata)
|
||||||
|
|
||||||
def user_context_apply(self):
|
def user_context_apply(self):
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,19 +21,18 @@ from .applier_frontend import (
|
|||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
from .appliers.systemd import systemd_unit
|
from .appliers.systemd import systemd_unit
|
||||||
from util.logging import slogm, log
|
from util.logging import log
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
class systemd_applier(applier_frontend):
|
class systemd_applier(applier_frontend):
|
||||||
__module_name = 'SystemdApplier'
|
__module_name = 'SystemdApplier'
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
__registry_branch = 'Software\\BaseALT\\Policies\\SystemdUnits'
|
__registry_branch = 'Software/BaseALT/Policies/SystemdUnits'
|
||||||
|
|
||||||
def __init__(self, storage):
|
def __init__(self, storage):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.systemd_unit_settings = self.storage.filter_hklm_entries('Software\\BaseALT\\Policies\\SystemdUnits%')
|
self.systemd_unit_settings = self.storage.filter_hklm_entries(self.__registry_branch)
|
||||||
self.units = []
|
self.units = []
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
@@ -43,23 +42,18 @@ class systemd_applier(applier_frontend):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
for setting in self.systemd_unit_settings:
|
for setting in self.systemd_unit_settings:
|
||||||
valuename = setting.hive_key.rpartition('\\')[2]
|
|
||||||
try:
|
try:
|
||||||
self.units.append(systemd_unit(valuename, int(setting.data)))
|
self.units.append(systemd_unit(setting.valuename, int(setting.data)))
|
||||||
logdata = dict()
|
logdata = {'unit': format(setting.valuename)}
|
||||||
logdata['unit'] = format(valuename)
|
|
||||||
log('I4', logdata)
|
log('I4', logdata)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'unit': format(setting.valuename), 'exc': exc}
|
||||||
logdata['unit'] = format(valuename)
|
|
||||||
logdata['exc'] = exc
|
|
||||||
log('I5', logdata)
|
log('I5', logdata)
|
||||||
for unit in self.units:
|
for unit in self.units:
|
||||||
try:
|
try:
|
||||||
unit.apply()
|
unit.apply()
|
||||||
except:
|
except:
|
||||||
logdata = dict()
|
logdata = {'unit': unit.unit_name}
|
||||||
logdata['unit'] = unit.unit_name
|
|
||||||
log('E45', logdata)
|
log('E45', logdata)
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
@@ -76,9 +70,9 @@ class systemd_applier_user(applier_frontend):
|
|||||||
__module_name = 'SystemdApplierUser'
|
__module_name = 'SystemdApplierUser'
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
__registry_branch = 'Software\\BaseALT\\Policies\\SystemdUnits'
|
__registry_branch = 'Software/BaseALT/Policies/SystemdUnits'
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
|
|
||||||
def user_context_apply(self):
|
def user_context_apply(self):
|
||||||
|
|||||||
68
gpoa/frontend/thunderbird_applier.py
Normal file
68
gpoa/frontend/thunderbird_applier.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2024-2025 BaseALT Ltd.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
from .applier_frontend import (
|
||||||
|
applier_frontend
|
||||||
|
, check_enabled
|
||||||
|
)
|
||||||
|
from util.logging import log
|
||||||
|
from util.util import is_machine_name
|
||||||
|
from .firefox_applier import create_dict
|
||||||
|
|
||||||
|
class thunderbird_applier(applier_frontend):
|
||||||
|
__module_name = 'ThunderbirdApplier'
|
||||||
|
__module_experimental = False
|
||||||
|
__module_enabled = True
|
||||||
|
__registry_branch = 'Software/Policies/Mozilla/Thunderbird'
|
||||||
|
__thunderbird_policies = '/etc/thunderbird/policies'
|
||||||
|
|
||||||
|
def __init__(self, storage, username):
|
||||||
|
self.storage = storage
|
||||||
|
self.username = username
|
||||||
|
self._is_machine_name = is_machine_name(self.username)
|
||||||
|
self.policies = {}
|
||||||
|
self.policies_json = {'policies': self.policies}
|
||||||
|
self.thunderbird_keys = self.storage.filter_hklm_entries(self.__registry_branch)
|
||||||
|
self.policies_gen = {}
|
||||||
|
self.__module_enabled = check_enabled(
|
||||||
|
self.storage
|
||||||
|
, self.__module_name
|
||||||
|
, self.__module_experimental
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def machine_apply(self):
|
||||||
|
'''
|
||||||
|
Write policies.json to Thunderbird.
|
||||||
|
'''
|
||||||
|
self.policies_json = create_dict(self.thunderbird_keys, self.__registry_branch)
|
||||||
|
|
||||||
|
destfile = os.path.join(self.__thunderbird_policies, 'policies.json')
|
||||||
|
os.makedirs(self.__thunderbird_policies, exist_ok=True)
|
||||||
|
with open(destfile, 'w') as f:
|
||||||
|
json.dump(self.policies_json, f)
|
||||||
|
logdata = {'destfile': destfile}
|
||||||
|
log('D212', logdata)
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
if self.__module_enabled:
|
||||||
|
log('D213')
|
||||||
|
self.machine_apply()
|
||||||
|
else:
|
||||||
|
log('D214')
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2022 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -30,19 +30,17 @@ class yandex_browser_applier(applier_frontend):
|
|||||||
__module_name = 'YandexBrowserApplier'
|
__module_name = 'YandexBrowserApplier'
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__registry_branch = 'Software\\Policies\\YandexBrowser'
|
__registry_branch = 'Software/Policies/YandexBrowser'
|
||||||
__managed_policies_path = '/etc/opt/yandex/browser/policies/managed'
|
__managed_policies_path = '/etc/opt/yandex/browser/policies/managed'
|
||||||
__recommended_policies_path = '/etc/opt/yandex/browser/policies/recommended'
|
__recommended_policies_path = '/etc/opt/yandex/browser/policies/recommended'
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self._is_machine_name = is_machine_name(self.username)
|
self._is_machine_name = is_machine_name(self.username)
|
||||||
yandex_filter = '{}%'.format(self.__registry_branch)
|
self.yandex_keys = self.storage.filter_hklm_entries(self.__registry_branch)
|
||||||
self.yandex_keys = self.storage.filter_hklm_entries(yandex_filter)
|
|
||||||
|
|
||||||
self.policies_json = dict()
|
self.policies_json = {}
|
||||||
|
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
@@ -70,16 +68,14 @@ class yandex_browser_applier(applier_frontend):
|
|||||||
os.makedirs(self.__managed_policies_path, exist_ok=True)
|
os.makedirs(self.__managed_policies_path, exist_ok=True)
|
||||||
with open(destfile, 'w') as f:
|
with open(destfile, 'w') as f:
|
||||||
json.dump(dict_item_to_list(self.policies_json), f)
|
json.dump(dict_item_to_list(self.policies_json), f)
|
||||||
logdata = dict()
|
logdata = {'destfile': destfile}
|
||||||
logdata['destfile'] = destfile
|
|
||||||
log('D185', logdata)
|
log('D185', logdata)
|
||||||
|
|
||||||
destfilerec = os.path.join(self.__recommended_policies_path, 'policies.json')
|
destfilerec = os.path.join(self.__recommended_policies_path, 'policies.json')
|
||||||
os.makedirs(self.__recommended_policies_path, exist_ok=True)
|
os.makedirs(self.__recommended_policies_path, exist_ok=True)
|
||||||
with open(destfilerec, 'w') as f:
|
with open(destfilerec, 'w') as f:
|
||||||
json.dump(dict_item_to_list(recommended__json), f)
|
json.dump(dict_item_to_list(recommended__json), f)
|
||||||
logdata = dict()
|
logdata = {'destfilerec': destfilerec}
|
||||||
logdata['destfilerec'] = destfilerec
|
|
||||||
log('D185', logdata)
|
log('D185', logdata)
|
||||||
|
|
||||||
|
|
||||||
@@ -152,7 +148,7 @@ class yandex_browser_applier(applier_frontend):
|
|||||||
'''
|
'''
|
||||||
Parse registry path string and leave key parameters
|
Parse registry path string and leave key parameters
|
||||||
'''
|
'''
|
||||||
parts = hivekeyname.replace(self.__registry_branch, '').split('\\')
|
parts = hivekeyname.replace(self.__registry_branch, '').split('/')
|
||||||
return parts
|
return parts
|
||||||
|
|
||||||
|
|
||||||
@@ -160,7 +156,7 @@ class yandex_browser_applier(applier_frontend):
|
|||||||
'''
|
'''
|
||||||
Collect dictionaries from registry keys into a general dictionary
|
Collect dictionaries from registry keys into a general dictionary
|
||||||
'''
|
'''
|
||||||
counts = dict()
|
counts = {}
|
||||||
#getting the list of keys to read as an integer
|
#getting the list of keys to read as an integer
|
||||||
valuename_typeint = self.get_valuename_typeint()
|
valuename_typeint = self.get_valuename_typeint()
|
||||||
for it_data in yandex_keys:
|
for it_data in yandex_keys:
|
||||||
@@ -188,9 +184,7 @@ class yandex_browser_applier(applier_frontend):
|
|||||||
branch[parts[-1]] = str(it_data.data).replace('\\', '/')
|
branch[parts[-1]] = str(it_data.data).replace('\\', '/')
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'Exception': exc, 'keyname': it_data.keyname}
|
||||||
logdata['Exception'] = exc
|
|
||||||
logdata['keyname'] = it_data.keyname
|
|
||||||
log('D178', logdata)
|
log('D178', logdata)
|
||||||
try:
|
try:
|
||||||
self.policies_json = counts['']
|
self.policies_json = counts['']
|
||||||
|
|||||||
@@ -23,10 +23,11 @@ import signal
|
|||||||
import gettext
|
import gettext
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
from backend import backend_factory
|
from backend import backend_factory, save_dconf
|
||||||
from frontend.frontend_manager import frontend_manager, determine_username
|
from frontend.frontend_manager import frontend_manager, determine_username
|
||||||
from plugin import plugin_manager
|
from plugin import plugin_manager
|
||||||
from messages import message_with_code
|
from messages import message_with_code
|
||||||
|
from storage import Dconf_registry
|
||||||
|
|
||||||
from util.util import get_machine_name
|
from util.util import get_machine_name
|
||||||
from util.users import (
|
from util.users import (
|
||||||
@@ -61,6 +62,9 @@ def parse_arguments():
|
|||||||
arguments.add_argument('--list-backends',
|
arguments.add_argument('--list-backends',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='Show list of available backends')
|
help='Show list of available backends')
|
||||||
|
arguments.add_argument('--force',
|
||||||
|
action='store_true',
|
||||||
|
help='Force GPT download')
|
||||||
arguments.add_argument('--loglevel',
|
arguments.add_argument('--loglevel',
|
||||||
type=int,
|
type=int,
|
||||||
default=4,
|
default=4,
|
||||||
@@ -120,6 +124,7 @@ class gpoa_controller:
|
|||||||
print('local')
|
print('local')
|
||||||
print('samba')
|
print('samba')
|
||||||
return
|
return
|
||||||
|
Dconf_registry._force = self.__args.force
|
||||||
self.start_plugins()
|
self.start_plugins()
|
||||||
self.start_backend()
|
self.start_backend()
|
||||||
|
|
||||||
@@ -148,6 +153,7 @@ class gpoa_controller:
|
|||||||
try:
|
try:
|
||||||
back.retrieve_and_store()
|
back.retrieve_and_store()
|
||||||
# Start frontend only on successful backend finish
|
# Start frontend only on successful backend finish
|
||||||
|
save_dconf(self.username, self.is_machine, nodomain)
|
||||||
self.start_frontend()
|
self.start_frontend()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict({'message': str(exc)})
|
logdata = dict({'message': str(exc)})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
import json
|
import json
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
|
from .dynamic_attributes import DynamicAttributes
|
||||||
from util.xml import get_xml_root
|
from util.xml import get_xml_root
|
||||||
|
|
||||||
def decrypt_pass(cpassword):
|
def decrypt_pass(cpassword):
|
||||||
@@ -47,7 +47,7 @@ def decrypt_pass(cpassword):
|
|||||||
# decrypt() returns byte array which is immutable and we need to
|
# decrypt() returns byte array which is immutable and we need to
|
||||||
# strip padding, then convert UTF-16LE to UTF-8
|
# strip padding, then convert UTF-16LE to UTF-8
|
||||||
binstr = decrypter.decrypt(password)
|
binstr = decrypter.decrypt(password)
|
||||||
by = list()
|
by = []
|
||||||
for item in binstr:
|
for item in binstr:
|
||||||
if item != 16:
|
if item != 16:
|
||||||
by.append(item)
|
by.append(item)
|
||||||
@@ -57,7 +57,7 @@ def decrypt_pass(cpassword):
|
|||||||
return utf8str.decode()
|
return utf8str.decode()
|
||||||
|
|
||||||
def read_drives(drives_file):
|
def read_drives(drives_file):
|
||||||
drives = list()
|
drives = []
|
||||||
|
|
||||||
for drive in get_xml_root(drives_file):
|
for drive in get_xml_root(drives_file):
|
||||||
drive_obj = drivemap()
|
drive_obj = drivemap()
|
||||||
@@ -78,9 +78,9 @@ def read_drives(drives_file):
|
|||||||
|
|
||||||
return drives
|
return drives
|
||||||
|
|
||||||
def merge_drives(storage, sid, drive_objects, policy_name):
|
def merge_drives(storage, drive_objects, policy_name):
|
||||||
for drive in drive_objects:
|
for drive in drive_objects:
|
||||||
storage.add_drive(sid, drive, policy_name)
|
storage.add_drive(drive, policy_name)
|
||||||
|
|
||||||
def json2drive(json_str):
|
def json2drive(json_str):
|
||||||
json_obj = json.loads(json_str)
|
json_obj = json.loads(json_str)
|
||||||
@@ -93,7 +93,7 @@ def json2drive(json_str):
|
|||||||
|
|
||||||
return drive_obj
|
return drive_obj
|
||||||
|
|
||||||
class drivemap:
|
class drivemap(DynamicAttributes):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.login = None
|
self.login = None
|
||||||
self.password = None
|
self.password = None
|
||||||
@@ -141,13 +141,13 @@ class drivemap:
|
|||||||
self.useLetter = useLetter
|
self.useLetter = useLetter
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
drive = dict()
|
drive = {}
|
||||||
drive['login'] = self.login
|
drive['login'] = self.login
|
||||||
drive['password'] = self.password
|
drive['password'] = self.password
|
||||||
drive['dir'] = self.dir
|
drive['dir'] = self.dir
|
||||||
drive['path'] = self.path
|
drive['path'] = self.path
|
||||||
|
|
||||||
contents = dict()
|
contents = {}
|
||||||
contents['drive'] = drive
|
contents['drive'] = drive
|
||||||
|
|
||||||
return json.dumps(contents)
|
return json.dumps(contents)
|
||||||
|
|||||||
56
gpoa/gpt/dynamic_attributes.py
Normal file
56
gpoa/gpt/dynamic_attributes.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2024 BaseALT Ltd.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class DynamicAttributes:
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.policy_name = None
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
self.__setattr__(key, value)
|
||||||
|
|
||||||
|
def __setattr__(self, key, value):
|
||||||
|
if isinstance(value, Enum):
|
||||||
|
value = str(value)
|
||||||
|
if isinstance(value, str):
|
||||||
|
for q in ["'", "\""]:
|
||||||
|
if any(q in ch for ch in value):
|
||||||
|
value = value.replace(q, "″")
|
||||||
|
self.__dict__[key] = value
|
||||||
|
|
||||||
|
def items(self):
|
||||||
|
return self.__dict__.items()
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.__dict__.items())
|
||||||
|
|
||||||
|
def get_original_value(self, key):
|
||||||
|
value = self.__dict__.get(key)
|
||||||
|
if isinstance(value, str):
|
||||||
|
value = value.replace("″", "'")
|
||||||
|
return value
|
||||||
|
|
||||||
|
class RegistryKeyMetadata(DynamicAttributes):
|
||||||
|
def __init__(self, policy_name, type, is_list=None, mod_previous_value=None):
|
||||||
|
self.policy_name = policy_name
|
||||||
|
self.type = type
|
||||||
|
self.reloaded_with_policy_key = None
|
||||||
|
self.is_list = is_list
|
||||||
|
self.mod_previous_value = mod_previous_value
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return str(dict(self))
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,49 +17,30 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from util.xml import get_xml_root
|
from util.xml import get_xml_root
|
||||||
|
from .dynamic_attributes import DynamicAttributes
|
||||||
|
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
class FileAction(Enum):
|
|
||||||
CREATE = 'C'
|
|
||||||
REPLACE = 'R'
|
|
||||||
UPDATE = 'U'
|
|
||||||
DELETE = 'D'
|
|
||||||
|
|
||||||
|
|
||||||
def action_letter2enum(letter):
|
|
||||||
if letter in ['C', 'R', 'U', 'D']:
|
|
||||||
if letter == 'C': return FileAction.CREATE
|
|
||||||
if letter == 'R': return FileAction.REPLACE
|
|
||||||
if letter == 'U': return FileAction.UPDATE
|
|
||||||
if letter == 'D': return FileAction.DELETE
|
|
||||||
|
|
||||||
return FileAction.CREATE
|
|
||||||
|
|
||||||
def read_envvars(envvars_file):
|
def read_envvars(envvars_file):
|
||||||
variables = list()
|
variables = []
|
||||||
|
|
||||||
for var in get_xml_root(envvars_file):
|
for var in get_xml_root(envvars_file):
|
||||||
props = var.find('Properties')
|
props = var.find('Properties')
|
||||||
name = props.get('name')
|
name = props.get('name')
|
||||||
value = props.get('value')
|
value = props.get('value')
|
||||||
var_obj = envvar(name, value)
|
action = props.get('action', default='C')
|
||||||
var_obj.set_action(action_letter2enum(props.get('action', default='C')))
|
var_obj = envvar(name, value, action)
|
||||||
|
|
||||||
variables.append(var_obj)
|
variables.append(var_obj)
|
||||||
|
|
||||||
return variables
|
return variables
|
||||||
|
|
||||||
def merge_envvars(storage, sid, envvar_objects, policy_name):
|
def merge_envvars(storage, envvar_objects, policy_name):
|
||||||
for envv in envvar_objects:
|
for envv in envvar_objects:
|
||||||
storage.add_envvar(sid, envv, policy_name)
|
storage.add_envvar(envv, policy_name)
|
||||||
|
|
||||||
class envvar:
|
class envvar(DynamicAttributes):
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value, action):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.value = value
|
self.value = value
|
||||||
self.action = FileAction.CREATE
|
|
||||||
|
|
||||||
def set_action(self, action):
|
|
||||||
self.action = action
|
self.action = action
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,9 +17,10 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from util.xml import get_xml_root
|
from util.xml import get_xml_root
|
||||||
|
from .dynamic_attributes import DynamicAttributes
|
||||||
|
|
||||||
def read_files(filesxml):
|
def read_files(filesxml):
|
||||||
files = list()
|
files = []
|
||||||
|
|
||||||
for fil in get_xml_root(filesxml):
|
for fil in get_xml_root(filesxml):
|
||||||
props = fil.find('Properties')
|
props = fil.find('Properties')
|
||||||
@@ -35,11 +36,11 @@ def read_files(filesxml):
|
|||||||
|
|
||||||
return files
|
return files
|
||||||
|
|
||||||
def merge_files(storage, sid, file_objects, policy_name):
|
def merge_files(storage, file_objects, policy_name):
|
||||||
for fileobj in file_objects:
|
for fileobj in file_objects:
|
||||||
storage.add_file(sid, fileobj, policy_name)
|
storage.add_file(fileobj, policy_name)
|
||||||
|
|
||||||
class fileentry:
|
class fileentry(DynamicAttributes):
|
||||||
def __init__(self, fromPath):
|
def __init__(self, fromPath):
|
||||||
self.fromPath = fromPath
|
self.fromPath = fromPath
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,28 +17,11 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
from enum import Enum
|
from .dynamic_attributes import DynamicAttributes
|
||||||
|
|
||||||
|
|
||||||
from util.xml import get_xml_root
|
from util.xml import get_xml_root
|
||||||
|
|
||||||
|
|
||||||
class FileAction(Enum):
|
|
||||||
CREATE = 'C'
|
|
||||||
REPLACE = 'R'
|
|
||||||
UPDATE = 'U'
|
|
||||||
DELETE = 'D'
|
|
||||||
|
|
||||||
|
|
||||||
def action_letter2enum(letter):
|
|
||||||
if letter in ['C', 'R', 'U', 'D']:
|
|
||||||
if letter == 'C': return FileAction.CREATE
|
|
||||||
if letter == 'R': return FileAction.REPLACE
|
|
||||||
if letter == 'U': return FileAction.UPDATE
|
|
||||||
if letter == 'D': return FileAction.DELETE
|
|
||||||
|
|
||||||
return FileAction.CREATE
|
|
||||||
|
|
||||||
|
|
||||||
def action_enum2letter(enumitem):
|
def action_enum2letter(enumitem):
|
||||||
return enumitem.value
|
return enumitem.value
|
||||||
@@ -57,12 +40,13 @@ def folder_int2bool(val):
|
|||||||
|
|
||||||
|
|
||||||
def read_folders(folders_file):
|
def read_folders(folders_file):
|
||||||
folders = list()
|
folders = []
|
||||||
|
|
||||||
for fld in get_xml_root(folders_file):
|
for fld in get_xml_root(folders_file):
|
||||||
props = fld.find('Properties')
|
props = fld.find('Properties')
|
||||||
fld_obj = folderentry(props.get('path'))
|
path = props.get('path')
|
||||||
fld_obj.set_action(action_letter2enum(props.get('action', default='C')))
|
action = props.get('action', default='C')
|
||||||
|
fld_obj = folderentry(path, action)
|
||||||
fld_obj.set_delete_folder(folder_int2bool(props.get('deleteFolder', default=1)))
|
fld_obj.set_delete_folder(folder_int2bool(props.get('deleteFolder', default=1)))
|
||||||
fld_obj.set_delete_sub_folders(folder_int2bool(props.get('deleteSubFolders', default=1)))
|
fld_obj.set_delete_sub_folders(folder_int2bool(props.get('deleteSubFolders', default=1)))
|
||||||
fld_obj.set_delete_files(folder_int2bool(props.get('deleteFiles', default=1)))
|
fld_obj.set_delete_files(folder_int2bool(props.get('deleteFiles', default=1)))
|
||||||
@@ -70,17 +54,18 @@ def read_folders(folders_file):
|
|||||||
|
|
||||||
folders.append(fld_obj)
|
folders.append(fld_obj)
|
||||||
|
|
||||||
|
|
||||||
return folders
|
return folders
|
||||||
|
|
||||||
def merge_folders(storage, sid, folder_objects, policy_name):
|
def merge_folders(storage, folder_objects, policy_name):
|
||||||
for folder in folder_objects:
|
for folder in folder_objects:
|
||||||
storage.add_folder(sid, folder, policy_name)
|
storage.add_folder(folder, policy_name)
|
||||||
|
|
||||||
|
|
||||||
class folderentry:
|
class folderentry(DynamicAttributes):
|
||||||
def __init__(self, path):
|
def __init__(self, path, action):
|
||||||
self.path = path
|
self.path = path
|
||||||
self.action = FileAction.CREATE
|
self.action = action
|
||||||
self.delete_folder = False
|
self.delete_folder = False
|
||||||
self.delete_sub_folders = False
|
self.delete_sub_folders = False
|
||||||
self.delete_files = False
|
self.delete_files = False
|
||||||
|
|||||||
48
gpoa/gpt/gpo_dconf_mapping.py
Normal file
48
gpoa/gpt/gpo_dconf_mapping.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2024 BaseALT Ltd.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from .dynamic_attributes import DynamicAttributes
|
||||||
|
|
||||||
|
class GpoInfoDconf(DynamicAttributes):
|
||||||
|
_counter = 0
|
||||||
|
def __init__(self, gpo) -> None:
|
||||||
|
GpoInfoDconf._counter += 1
|
||||||
|
self.counter = GpoInfoDconf._counter
|
||||||
|
self.display_name = None
|
||||||
|
self.name = None
|
||||||
|
self.version = None
|
||||||
|
self.link = None
|
||||||
|
self._fill_attributes(gpo)
|
||||||
|
|
||||||
|
def _fill_attributes(self, gpo):
|
||||||
|
try:
|
||||||
|
self.display_name = gpo.display_name
|
||||||
|
except:
|
||||||
|
self.display_name = "Unknown"
|
||||||
|
try:
|
||||||
|
self.name = gpo.name
|
||||||
|
except:
|
||||||
|
self.name = "Unknown"
|
||||||
|
try:
|
||||||
|
self.version = gpo.version
|
||||||
|
except:
|
||||||
|
self.version = "Unknown"
|
||||||
|
try:
|
||||||
|
self.link = gpo.link
|
||||||
|
except:
|
||||||
|
self.link = "Unknown"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -23,6 +23,7 @@ from enum import Enum, unique
|
|||||||
from samba.gp_parse.gp_pol import GPPolParser
|
from samba.gp_parse.gp_pol import GPPolParser
|
||||||
|
|
||||||
from storage import registry_factory
|
from storage import registry_factory
|
||||||
|
from storage.dconf_registry import add_to_dict
|
||||||
|
|
||||||
from .polfile import (
|
from .polfile import (
|
||||||
read_polfile
|
read_polfile
|
||||||
@@ -109,7 +110,7 @@ def get_preftype(path_to_file):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def pref_parsers():
|
def pref_parsers():
|
||||||
parsers = dict()
|
parsers = {}
|
||||||
|
|
||||||
parsers[FileType.PREG] = read_polfile
|
parsers[FileType.PREG] = read_polfile
|
||||||
parsers[FileType.SHORTCUTS] = read_shortcuts
|
parsers[FileType.SHORTCUTS] = read_shortcuts
|
||||||
@@ -131,7 +132,7 @@ def get_parser(preference_type):
|
|||||||
return parsers[preference_type]
|
return parsers[preference_type]
|
||||||
|
|
||||||
def pref_mergers():
|
def pref_mergers():
|
||||||
mergers = dict()
|
mergers = {}
|
||||||
|
|
||||||
mergers[FileType.PREG] = merge_polfile
|
mergers[FileType.PREG] = merge_polfile
|
||||||
mergers[FileType.SHORTCUTS] = merge_shortcuts
|
mergers[FileType.SHORTCUTS] = merge_shortcuts
|
||||||
@@ -153,10 +154,13 @@ def get_merger(preference_type):
|
|||||||
return mergers[preference_type]
|
return mergers[preference_type]
|
||||||
|
|
||||||
class gpt:
|
class gpt:
|
||||||
def __init__(self, gpt_path, sid):
|
def __init__(self, gpt_path, username='Machine', gpo_info=None):
|
||||||
|
add_to_dict(gpt_path, username, gpo_info)
|
||||||
self.path = gpt_path
|
self.path = gpt_path
|
||||||
self.sid = sid
|
self.username = username
|
||||||
self.storage = registry_factory('registry')
|
self.storage = registry_factory()
|
||||||
|
self.storage._gpt_read_flag = True
|
||||||
|
self.gpo_info = gpo_info
|
||||||
self.name = ''
|
self.name = ''
|
||||||
self.guid = self.path.rpartition('/')[2]
|
self.guid = self.path.rpartition('/')[2]
|
||||||
if 'default' == self.guid:
|
if 'default' == self.guid:
|
||||||
@@ -180,18 +184,18 @@ class gpt:
|
|||||||
, 'scripts'
|
, 'scripts'
|
||||||
, 'networkshares'
|
, 'networkshares'
|
||||||
]
|
]
|
||||||
self.settings = dict()
|
self.settings = {}
|
||||||
self.settings['machine'] = dict()
|
self.settings['machine'] = {}
|
||||||
self.settings['user'] = dict()
|
self.settings['user'] = {}
|
||||||
self.settings['machine']['regpol'] = find_file(self._machine_path, 'registry.pol')
|
self.settings['machine']['regpol'] = find_file(self._machine_path, 'registry.pol')
|
||||||
self.settings['user']['regpol'] = find_file(self._user_path, 'registry.pol')
|
self.settings['user']['regpol'] = find_file(self._user_path, 'registry.pol')
|
||||||
for setting in self.settings_list:
|
for setting in self.settings_list:
|
||||||
machine_preffile = find_preffile(self._machine_path, setting)
|
machine_preffile = find_preffile(self._machine_path, setting)
|
||||||
user_preffile = find_preffile(self._user_path, setting)
|
user_preffile = find_preffile(self._user_path, setting)
|
||||||
mlogdata = dict({'setting': setting, 'prefpath': machine_preffile})
|
mlogdata = {'setting': setting, 'prefpath': machine_preffile}
|
||||||
log('D24', mlogdata)
|
log('D24', mlogdata)
|
||||||
self.settings['machine'][setting] = machine_preffile
|
self.settings['machine'][setting] = machine_preffile
|
||||||
ulogdata = dict({'setting': setting, 'prefpath': user_preffile})
|
ulogdata = {'setting': setting, 'prefpath': user_preffile}
|
||||||
log('D23', ulogdata)
|
log('D23', ulogdata)
|
||||||
self.settings['user'][setting] = user_preffile
|
self.settings['user'][setting] = user_preffile
|
||||||
|
|
||||||
@@ -212,21 +216,21 @@ class gpt:
|
|||||||
try:
|
try:
|
||||||
# Merge machine policies to registry if possible
|
# Merge machine policies to registry if possible
|
||||||
if self.settings['machine']['regpol']:
|
if self.settings['machine']['regpol']:
|
||||||
mlogdata = dict({'polfile': self.settings['machine']['regpol']})
|
mlogdata = {'polfile': self.settings['machine']['regpol']}
|
||||||
log('D34', mlogdata)
|
log('D34', mlogdata)
|
||||||
util.preg.merge_polfile(self.settings['machine']['regpol'], policy_name=self.name)
|
util.preg.merge_polfile(self.settings['machine']['regpol'], policy_name=self.name, gpo_info=self.gpo_info)
|
||||||
# Merge machine preferences to registry if possible
|
# Merge machine preferences to registry if possible
|
||||||
for preference_name, preference_path in self.settings['machine'].items():
|
for preference_name, preference_path in self.settings['machine'].items():
|
||||||
if preference_path:
|
if preference_path:
|
||||||
preference_type = get_preftype(preference_path)
|
preference_type = get_preftype(preference_path)
|
||||||
logdata = dict({'pref': preference_type.value, 'sid': self.sid})
|
logdata = {'pref': preference_type.value}
|
||||||
log('D28', logdata)
|
log('D28', logdata)
|
||||||
preference_parser = get_parser(preference_type)
|
preference_parser = get_parser(preference_type)
|
||||||
preference_merger = get_merger(preference_type)
|
preference_merger = get_merger(preference_type)
|
||||||
preference_objects = preference_parser(preference_path)
|
preference_objects = preference_parser(preference_path)
|
||||||
preference_merger(self.storage, self.sid, preference_objects, self.name)
|
preference_merger(self.storage, preference_objects, self.name)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['gpt'] = self.name
|
logdata['gpt'] = self.name
|
||||||
logdata['msg'] = str(exc)
|
logdata['msg'] = str(exc)
|
||||||
log('E28', logdata)
|
log('E28', logdata)
|
||||||
@@ -238,21 +242,24 @@ class gpt:
|
|||||||
try:
|
try:
|
||||||
# Merge user policies to registry if possible
|
# Merge user policies to registry if possible
|
||||||
if self.settings['user']['regpol']:
|
if self.settings['user']['regpol']:
|
||||||
mulogdata = dict({'polfile': self.settings['user']['regpol']})
|
mulogdata = {'polfile': self.settings['user']['regpol']}
|
||||||
log('D35', mulogdata)
|
log('D35', mulogdata)
|
||||||
util.preg.merge_polfile(self.settings['user']['regpol'], sid=self.sid, policy_name=self.name)
|
util.preg.merge_polfile(self.settings['user']['regpol'],
|
||||||
|
policy_name=self.name,
|
||||||
|
username=self.username,
|
||||||
|
gpo_info=self.gpo_info)
|
||||||
# Merge user preferences to registry if possible
|
# Merge user preferences to registry if possible
|
||||||
for preference_name, preference_path in self.settings['user'].items():
|
for preference_name, preference_path in self.settings['user'].items():
|
||||||
if preference_path:
|
if preference_path:
|
||||||
preference_type = get_preftype(preference_path)
|
preference_type = get_preftype(preference_path)
|
||||||
logdata = dict({'pref': preference_type.value, 'sid': self.sid})
|
logdata = {'pref': preference_type.value}
|
||||||
log('D29', logdata)
|
log('D29', logdata)
|
||||||
preference_parser = get_parser(preference_type)
|
preference_parser = get_parser(preference_type)
|
||||||
preference_merger = get_merger(preference_type)
|
preference_merger = get_merger(preference_type)
|
||||||
preference_objects = preference_parser(preference_path)
|
preference_objects = preference_parser(preference_path)
|
||||||
preference_merger(self.storage, self.sid, preference_objects, self.name)
|
preference_merger(self.storage, preference_objects, self.name)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['gpt'] = self.name
|
logdata['gpt'] = self.name
|
||||||
logdata['msg'] = str(exc)
|
logdata['msg'] = str(exc)
|
||||||
log('E29', logdata)
|
log('E29', logdata)
|
||||||
@@ -345,13 +352,13 @@ def lp2gpt():
|
|||||||
# Write PReg
|
# Write PReg
|
||||||
polparser.write_binary(os.path.join(destdir, 'Registry.pol'))
|
polparser.write_binary(os.path.join(destdir, 'Registry.pol'))
|
||||||
|
|
||||||
def get_local_gpt(sid):
|
def get_local_gpt():
|
||||||
'''
|
'''
|
||||||
Convert default policy to GPT and create object out of it.
|
Convert default policy to GPT and create object out of it.
|
||||||
'''
|
'''
|
||||||
log('D25')
|
log('D25')
|
||||||
lp2gpt()
|
lp2gpt()
|
||||||
local_policy = gpt(str(local_policy_cache()), sid)
|
local_policy = gpt(str(local_policy_cache()))
|
||||||
local_policy.set_name('Local Policy')
|
local_policy.set_name('Local Policy')
|
||||||
|
|
||||||
return local_policy
|
return local_policy
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2022 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,9 +17,10 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from util.xml import get_xml_root
|
from util.xml import get_xml_root
|
||||||
|
from .dynamic_attributes import DynamicAttributes
|
||||||
|
|
||||||
def read_inifiles(inifiles_file):
|
def read_inifiles(inifiles_file):
|
||||||
inifiles = list()
|
inifiles = []
|
||||||
|
|
||||||
for ini in get_xml_root(inifiles_file):
|
for ini in get_xml_root(inifiles_file):
|
||||||
prors = ini.find('Properties')
|
prors = ini.find('Properties')
|
||||||
@@ -27,17 +28,17 @@ def read_inifiles(inifiles_file):
|
|||||||
ini_obj.set_section(prors.get('section', default=None))
|
ini_obj.set_section(prors.get('section', default=None))
|
||||||
ini_obj.set_property(prors.get('property', default=None))
|
ini_obj.set_property(prors.get('property', default=None))
|
||||||
ini_obj.set_value(prors.get('value', default=None))
|
ini_obj.set_value(prors.get('value', default=None))
|
||||||
ini_obj.set_action(prors.get('action'))
|
ini_obj.set_action(prors.get('action', default='C'))
|
||||||
|
|
||||||
inifiles.append(ini_obj)
|
inifiles.append(ini_obj)
|
||||||
|
|
||||||
return inifiles
|
return inifiles
|
||||||
|
|
||||||
def merge_inifiles(storage, sid, inifile_objects, policy_name):
|
def merge_inifiles(storage, inifile_objects, policy_name):
|
||||||
for iniobj in inifile_objects:
|
for iniobj in inifile_objects:
|
||||||
storage.add_ini(sid, iniobj, policy_name)
|
storage.add_ini(iniobj, policy_name)
|
||||||
|
|
||||||
class inifile:
|
class inifile(DynamicAttributes):
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2022 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,9 +17,10 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from util.xml import get_xml_root
|
from util.xml import get_xml_root
|
||||||
|
from .dynamic_attributes import DynamicAttributes
|
||||||
|
|
||||||
def read_networkshares(networksharesxml):
|
def read_networkshares(networksharesxml):
|
||||||
networkshares = list()
|
networkshares = []
|
||||||
|
|
||||||
for share in get_xml_root(networksharesxml):
|
for share in get_xml_root(networksharesxml):
|
||||||
props = share.find('Properties')
|
props = share.find('Properties')
|
||||||
@@ -34,11 +35,11 @@ def read_networkshares(networksharesxml):
|
|||||||
|
|
||||||
return networkshares
|
return networkshares
|
||||||
|
|
||||||
def merge_networkshares(storage, sid, networkshares_objects, policy_name):
|
def merge_networkshares(storage, networkshares_objects, policy_name):
|
||||||
for networkshareobj in networkshares_objects:
|
for networkshareobj in networkshares_objects:
|
||||||
storage.add_networkshare(sid, networkshareobj, policy_name)
|
storage.add_networkshare(networkshareobj, policy_name)
|
||||||
|
|
||||||
class networkshare:
|
class networkshare(DynamicAttributes):
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -23,10 +23,7 @@ from util.preg import (
|
|||||||
def read_polfile(filename):
|
def read_polfile(filename):
|
||||||
return load_preg(filename).entries
|
return load_preg(filename).entries
|
||||||
|
|
||||||
def merge_polfile(storage, sid, policy_objects, policy_name):
|
def merge_polfile(storage, policy_objects, policy_name):
|
||||||
for entry in policy_objects:
|
pass
|
||||||
if not sid:
|
|
||||||
storage.add_hklm_entry(entry, policy_name)
|
|
||||||
else:
|
|
||||||
storage.add_hkcu_entry(entry, sid, policy_name)
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from .dynamic_attributes import DynamicAttributes
|
||||||
|
|
||||||
from util.xml import get_xml_root
|
from util.xml import get_xml_root
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ def read_printers(printers_file):
|
|||||||
'''
|
'''
|
||||||
Read printer configurations from Printer.xml
|
Read printer configurations from Printer.xml
|
||||||
'''
|
'''
|
||||||
printers = list()
|
printers = []
|
||||||
|
|
||||||
for prn in get_xml_root(printers_file):
|
for prn in get_xml_root(printers_file):
|
||||||
prn_obj = printer(prn.tag, prn.get('name'), prn.get('status'))
|
prn_obj = printer(prn.tag, prn.get('name'), prn.get('status'))
|
||||||
@@ -41,9 +42,9 @@ def read_printers(printers_file):
|
|||||||
|
|
||||||
return printers
|
return printers
|
||||||
|
|
||||||
def merge_printers(storage, sid, printer_objects, policy_name):
|
def merge_printers(storage, printer_objects, policy_name):
|
||||||
for device in printer_objects:
|
for device in printer_objects:
|
||||||
storage.add_printer(sid, device, policy_name)
|
storage.add_printer(device, policy_name)
|
||||||
|
|
||||||
def json2printer(json_str):
|
def json2printer(json_str):
|
||||||
'''
|
'''
|
||||||
@@ -60,7 +61,7 @@ def json2printer(json_str):
|
|||||||
|
|
||||||
return prn
|
return prn
|
||||||
|
|
||||||
class printer:
|
class printer(DynamicAttributes):
|
||||||
def __init__(self, ptype, name, status):
|
def __init__(self, ptype, name, status):
|
||||||
'''
|
'''
|
||||||
ptype may be one of:
|
ptype may be one of:
|
||||||
@@ -100,7 +101,7 @@ class printer:
|
|||||||
'''
|
'''
|
||||||
Return string-serialized JSON representation of the object.
|
Return string-serialized JSON representation of the object.
|
||||||
'''
|
'''
|
||||||
printer = dict()
|
printer = {}
|
||||||
printer['type'] = self.printer_type
|
printer['type'] = self.printer_type
|
||||||
printer['name'] = self.name
|
printer['name'] = self.name
|
||||||
printer['status'] = self.status
|
printer['status'] = self.status
|
||||||
@@ -112,7 +113,7 @@ class printer:
|
|||||||
|
|
||||||
# Nesting JSON object into JSON object makes it easier to add
|
# Nesting JSON object into JSON object makes it easier to add
|
||||||
# metadata if needed.
|
# metadata if needed.
|
||||||
config = dict()
|
config = {}
|
||||||
config['printer'] = printer
|
config['printer'] = printer
|
||||||
|
|
||||||
return json.dumps(config)
|
return json.dumps(config)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,15 +18,15 @@
|
|||||||
|
|
||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
|
from .dynamic_attributes import DynamicAttributes
|
||||||
|
|
||||||
def read_scripts(scripts_file):
|
def read_scripts(scripts_file):
|
||||||
scripts = Scripts_lists()
|
scripts = Scripts_lists()
|
||||||
|
|
||||||
logon_scripts = dict()
|
logon_scripts = {}
|
||||||
logoff_scripts = dict()
|
logoff_scripts = {}
|
||||||
startup_scripts = dict()
|
startup_scripts = {}
|
||||||
shutdown_scripts = dict()
|
shutdown_scripts = {}
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
config.read(scripts_file, encoding = 'utf-16')
|
config.read(scripts_file, encoding = 'utf-16')
|
||||||
@@ -61,39 +61,39 @@ def read_scripts(scripts_file):
|
|||||||
section_scripts[key_index].set_args(config[act][key])
|
section_scripts[key_index].set_args(config[act][key])
|
||||||
if logon_scripts:
|
if logon_scripts:
|
||||||
for i in sorted(logon_scripts.keys()):
|
for i in sorted(logon_scripts.keys()):
|
||||||
scripts.add_script(act_upper, logon_scripts[i])
|
scripts.add_script('LOGON', logon_scripts[i])
|
||||||
|
|
||||||
if logoff_scripts:
|
if logoff_scripts:
|
||||||
for i in sorted(logoff_scripts.keys()):
|
for i in sorted(logoff_scripts.keys()):
|
||||||
scripts.add_script(act_upper, logoff_scripts[i])
|
scripts.add_script('LOGOFF', logoff_scripts[i])
|
||||||
|
|
||||||
if startup_scripts:
|
if startup_scripts:
|
||||||
for i in sorted(startup_scripts.keys()):
|
for i in sorted(startup_scripts.keys()):
|
||||||
scripts.add_script(act_upper, startup_scripts[i])
|
scripts.add_script('STARTUP', startup_scripts[i])
|
||||||
|
|
||||||
if shutdown_scripts:
|
if shutdown_scripts:
|
||||||
for i in sorted(shutdown_scripts.keys()):
|
for i in sorted(shutdown_scripts.keys()):
|
||||||
scripts.add_script(act_upper, shutdown_scripts[i])
|
scripts.add_script('SHUTDOWN', shutdown_scripts[i])
|
||||||
|
|
||||||
|
|
||||||
return scripts
|
return scripts
|
||||||
|
|
||||||
def merge_scripts(storage, sid, scripts_objects, policy_name):
|
def merge_scripts(storage, scripts_objects, policy_name):
|
||||||
for script in scripts_objects.get_logon_scripts():
|
for script in scripts_objects.get_logon_scripts():
|
||||||
storage.add_script(sid, script, policy_name)
|
storage.add_script(script, policy_name)
|
||||||
for script in scripts_objects.get_logoff_scripts():
|
for script in scripts_objects.get_logoff_scripts():
|
||||||
storage.add_script(sid, script, policy_name)
|
storage.add_script(script, policy_name)
|
||||||
for script in scripts_objects.get_startup_scripts():
|
for script in scripts_objects.get_startup_scripts():
|
||||||
storage.add_script(sid, script, policy_name)
|
storage.add_script(script, policy_name)
|
||||||
for script in scripts_objects.get_shutdown_scripts():
|
for script in scripts_objects.get_shutdown_scripts():
|
||||||
storage.add_script(sid, script, policy_name)
|
storage.add_script(script, policy_name)
|
||||||
|
|
||||||
class Scripts_lists:
|
class Scripts_lists:
|
||||||
def __init__ (self):
|
def __init__ (self):
|
||||||
self.__logon_scripts = list()
|
self.__logon_scripts = []
|
||||||
self.__logoff_scripts = list()
|
self.__logoff_scripts = []
|
||||||
self.__startup_scripts = list()
|
self.__startup_scripts = []
|
||||||
self.__shutdown_scripts = list()
|
self.__shutdown_scripts = []
|
||||||
|
|
||||||
def get_logon_scripts(self):
|
def get_logon_scripts(self):
|
||||||
return self.__logon_scripts
|
return self.__logon_scripts
|
||||||
@@ -115,7 +115,7 @@ class Scripts_lists:
|
|||||||
self.get_shutdown_scripts().append(script)
|
self.get_shutdown_scripts().append(script)
|
||||||
|
|
||||||
|
|
||||||
class Script:
|
class Script(DynamicAttributes):
|
||||||
__logon_counter = 0
|
__logon_counter = 0
|
||||||
__logoff_counter = 0
|
__logoff_counter = 0
|
||||||
__startup_counter = 0
|
__startup_counter = 0
|
||||||
@@ -126,6 +126,7 @@ class Script:
|
|||||||
self.action = action_upper
|
self.action = action_upper
|
||||||
self.path = os.path.join(script_dir, action_upper, script_filename.upper())
|
self.path = os.path.join(script_dir, action_upper, script_filename.upper())
|
||||||
if not os.path.isfile(self.path):
|
if not os.path.isfile(self.path):
|
||||||
|
self.number = None
|
||||||
return None
|
return None
|
||||||
self.args = None
|
self.args = None
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,12 +17,13 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from util.xml import get_xml_root
|
from util.xml import get_xml_root
|
||||||
|
from .dynamic_attributes import DynamicAttributes
|
||||||
|
|
||||||
def read_services(service_file):
|
def read_services(service_file):
|
||||||
'''
|
'''
|
||||||
Read Services.xml from GPT.
|
Read Services.xml from GPT.
|
||||||
'''
|
'''
|
||||||
services = list()
|
services = []
|
||||||
|
|
||||||
for srv in get_xml_root(service_file):
|
for srv in get_xml_root(service_file):
|
||||||
srv_obj = service(srv.get('name'))
|
srv_obj = service(srv.get('name'))
|
||||||
@@ -39,11 +40,11 @@ def read_services(service_file):
|
|||||||
|
|
||||||
return services
|
return services
|
||||||
|
|
||||||
def merge_services(storage, sid, service_objects, policy_name):
|
def merge_services(storage, service_objects, policy_name):
|
||||||
for srv in service_objects:
|
for srv in service_objects:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class service:
|
class service(DynamicAttributes):
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.unit = name
|
self.unit = name
|
||||||
self.servname = None
|
self.servname = None
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,20 +18,23 @@
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import stat
|
import stat
|
||||||
import logging
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from xml.etree import ElementTree
|
|
||||||
from xdg.DesktopEntry import DesktopEntry
|
from xdg.DesktopEntry import DesktopEntry
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from util.windows import transform_windows_path
|
from util.windows import transform_windows_path
|
||||||
from util.xml import get_xml_root
|
from util.xml import get_xml_root
|
||||||
|
from util.paths import get_desktop_files_directory
|
||||||
|
from .dynamic_attributes import DynamicAttributes
|
||||||
|
|
||||||
class TargetType(Enum):
|
class TargetType(Enum):
|
||||||
FILESYSTEM = 'FILESYSTEM'
|
FILESYSTEM = 'FILESYSTEM'
|
||||||
URL = 'URL'
|
URL = 'URL'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.value
|
||||||
|
|
||||||
def get_ttype(targetstr):
|
def get_ttype(targetstr):
|
||||||
'''
|
'''
|
||||||
Validation function for targetType property
|
Validation function for targetType property
|
||||||
@@ -42,7 +45,7 @@ def get_ttype(targetstr):
|
|||||||
'''
|
'''
|
||||||
ttype = TargetType.FILESYSTEM
|
ttype = TargetType.FILESYSTEM
|
||||||
|
|
||||||
if targetstr == 'URL':
|
if targetstr == 'URL'or targetstr == TargetType.URL:
|
||||||
ttype = TargetType.URL
|
ttype = TargetType.URL
|
||||||
|
|
||||||
return ttype
|
return ttype
|
||||||
@@ -66,7 +69,7 @@ def read_shortcuts(shortcuts_file):
|
|||||||
|
|
||||||
:shortcuts_file: Location of Shortcuts.xml
|
:shortcuts_file: Location of Shortcuts.xml
|
||||||
'''
|
'''
|
||||||
shortcuts = list()
|
shortcuts = []
|
||||||
|
|
||||||
for link in get_xml_root(shortcuts_file):
|
for link in get_xml_root(shortcuts_file):
|
||||||
props = link.find('Properties')
|
props = link.find('Properties')
|
||||||
@@ -92,30 +95,27 @@ def read_shortcuts(shortcuts_file):
|
|||||||
|
|
||||||
return shortcuts
|
return shortcuts
|
||||||
|
|
||||||
def merge_shortcuts(storage, sid, shortcut_objects, policy_name):
|
def merge_shortcuts(storage, shortcut_objects, policy_name):
|
||||||
for shortcut in shortcut_objects:
|
for shortcut in shortcut_objects:
|
||||||
storage.add_shortcut(sid, shortcut, policy_name)
|
storage.add_shortcut(shortcut, policy_name)
|
||||||
|
|
||||||
def json2sc(json_str):
|
|
||||||
'''
|
|
||||||
Build shortcut out of string-serialized JSON
|
|
||||||
'''
|
|
||||||
json_obj = json.loads(json_str)
|
|
||||||
link_type = get_ttype(json_obj['type'])
|
|
||||||
|
|
||||||
sc = shortcut(json_obj['dest'], json_obj['path'], json_obj['arguments'], json_obj['name'], json_obj['action'], link_type)
|
def find_desktop_entry(binary_path):
|
||||||
sc.set_changed(json_obj['changed'])
|
desktop_dir = get_desktop_files_directory()
|
||||||
sc.set_clsid(json_obj['clsid'])
|
binary_name = ''.join(binary_path.split('/')[-1])
|
||||||
sc.set_guid(json_obj['guid'])
|
desktop_file_path = Path(f"{desktop_dir}/{binary_name}.desktop")
|
||||||
sc.set_usercontext(json_obj['is_in_user_context'])
|
|
||||||
if 'comment' in json_obj:
|
|
||||||
sc.set_comment(json_obj['comment'])
|
|
||||||
if 'icon' in json_obj:
|
|
||||||
sc.set_icon(json_obj['icon'])
|
|
||||||
|
|
||||||
return sc
|
if desktop_file_path.exists():
|
||||||
|
desktop_entry = DesktopEntry()
|
||||||
|
desktop_entry.parse(desktop_file_path)
|
||||||
|
return desktop_entry
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class shortcut(DynamicAttributes):
|
||||||
|
_ignore_fields = {"desktop_file_template", "desktop_file"}
|
||||||
|
|
||||||
class shortcut:
|
|
||||||
def __init__(self, dest, path, arguments, name=None, action=None, ttype=TargetType.FILESYSTEM):
|
def __init__(self, dest, path, arguments, name=None, action=None, ttype=TargetType.FILESYSTEM):
|
||||||
'''
|
'''
|
||||||
:param dest: Path to resulting file on file system
|
:param dest: Path to resulting file on file system
|
||||||
@@ -135,6 +135,15 @@ class shortcut:
|
|||||||
self.comment = ''
|
self.comment = ''
|
||||||
self.is_in_user_context = self.set_usercontext()
|
self.is_in_user_context = self.set_usercontext()
|
||||||
self.type = ttype
|
self.type = ttype
|
||||||
|
self.desktop_file_template = None
|
||||||
|
|
||||||
|
|
||||||
|
def items(self):
|
||||||
|
return ((k, v) for k, v in super().items() if k not in self._ignore_fields)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.items())
|
||||||
|
|
||||||
|
|
||||||
def replace_slashes(self, input_path):
|
def replace_slashes(self, input_path):
|
||||||
if input_path.startswith('%'):
|
if input_path.startswith('%'):
|
||||||
@@ -202,30 +211,6 @@ class shortcut:
|
|||||||
def is_usercontext(self):
|
def is_usercontext(self):
|
||||||
return self.is_in_user_context
|
return self.is_in_user_context
|
||||||
|
|
||||||
def to_json(self):
|
|
||||||
'''
|
|
||||||
Return shortcut's JSON for further serialization.
|
|
||||||
'''
|
|
||||||
content = dict()
|
|
||||||
content['dest'] = self.dest
|
|
||||||
content['path'] = self.path
|
|
||||||
content['name'] = self.name
|
|
||||||
content['arguments'] = self.arguments
|
|
||||||
content['clsid'] = self.clsid
|
|
||||||
content['guid'] = self.guid
|
|
||||||
content['changed'] = self.changed
|
|
||||||
content['action'] = self.action
|
|
||||||
content['is_in_user_context'] = self.is_in_user_context
|
|
||||||
content['type'] = ttype2str(self.type)
|
|
||||||
if self.icon:
|
|
||||||
content['icon'] = self.icon
|
|
||||||
if self.comment:
|
|
||||||
content['comment'] = self.comment
|
|
||||||
result = self.desktop()
|
|
||||||
result.content.update(content)
|
|
||||||
|
|
||||||
return json.dumps(result.content)
|
|
||||||
|
|
||||||
def desktop(self, dest=None):
|
def desktop(self, dest=None):
|
||||||
'''
|
'''
|
||||||
Returns desktop file object which may be written to disk.
|
Returns desktop file object which may be written to disk.
|
||||||
@@ -233,6 +218,7 @@ class shortcut:
|
|||||||
if dest:
|
if dest:
|
||||||
self.desktop_file = DesktopEntry(dest)
|
self.desktop_file = DesktopEntry(dest)
|
||||||
else:
|
else:
|
||||||
|
self.desktop_file_template = find_desktop_entry(self.path)
|
||||||
self.desktop_file = DesktopEntry()
|
self.desktop_file = DesktopEntry()
|
||||||
self.desktop_file.addGroup('Desktop Entry')
|
self.desktop_file.addGroup('Desktop Entry')
|
||||||
self.desktop_file.set('Version', '1.0')
|
self.desktop_file.set('Version', '1.0')
|
||||||
@@ -244,7 +230,7 @@ class shortcut:
|
|||||||
'''
|
'''
|
||||||
Update desktop file object from internal data.
|
Update desktop file object from internal data.
|
||||||
'''
|
'''
|
||||||
if self.type == TargetType.URL:
|
if get_ttype(self.type) == TargetType.URL:
|
||||||
self.desktop_file.set('Type', 'Link')
|
self.desktop_file.set('Type', 'Link')
|
||||||
else:
|
else:
|
||||||
self.desktop_file.set('Type', 'Application')
|
self.desktop_file.set('Type', 'Application')
|
||||||
@@ -254,15 +240,21 @@ class shortcut:
|
|||||||
desktop_path = self.path
|
desktop_path = self.path
|
||||||
if self.expanded_path:
|
if self.expanded_path:
|
||||||
desktop_path = self.expanded_path
|
desktop_path = self.expanded_path
|
||||||
if self.type == TargetType.URL:
|
if get_ttype(self.type) == TargetType.URL:
|
||||||
self.desktop_file.set('URL', desktop_path)
|
self.desktop_file.set('URL', desktop_path)
|
||||||
else:
|
else:
|
||||||
self.desktop_file.set('Terminal', 'false')
|
str2bool_lambda = (lambda boolstr: boolstr if isinstance(boolstr, bool)
|
||||||
self.desktop_file.set('Exec', '{} {}'.format(desktop_path, self.arguments))
|
else boolstr and boolstr.lower() in ['True', 'true', 'yes', '1'])
|
||||||
|
if self.desktop_file_template:
|
||||||
|
terminal_state = str2bool_lambda(self.desktop_file_template.get('Terminal'))
|
||||||
|
self.desktop_file.set('Terminal', 'true' if terminal_state else 'false')
|
||||||
|
self.desktop_file.set('Exec', '{} {}'.format(desktop_path, self.get_original_value('arguments')))
|
||||||
self.desktop_file.set('Comment', self.comment)
|
self.desktop_file.set('Comment', self.comment)
|
||||||
|
|
||||||
if self.icon:
|
if self.icon:
|
||||||
self.desktop_file.set('Icon', self.icon)
|
self.desktop_file.set('Icon', self.icon)
|
||||||
|
elif self.desktop_file_template and self.desktop_file_template.get('Icon', False):
|
||||||
|
self.desktop_file.set('Icon', self.desktop_file_template.get('Icon'))
|
||||||
|
|
||||||
def _write_desktop(self, dest, create_only=False, read_firstly=False):
|
def _write_desktop(self, dest, create_only=False, read_firstly=False):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
def read_tasks(filename):
|
def read_tasks(filename):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def merge_tasks(storage, sid, task_objects, policy_name):
|
def merge_tasks(storage, task_objects, policy_name):
|
||||||
for task in task_objects:
|
for task in task_objects:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -25,6 +25,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import pwd
|
import pwd
|
||||||
import signal
|
import signal
|
||||||
|
from storage import Dconf_registry
|
||||||
|
|
||||||
from util.users import (
|
from util.users import (
|
||||||
is_root
|
is_root
|
||||||
@@ -83,6 +84,11 @@ def parse_cli_arguments():
|
|||||||
type=int,
|
type=int,
|
||||||
default=5,
|
default=5,
|
||||||
help='Set logging verbosity level')
|
help='Set logging verbosity level')
|
||||||
|
argparser.add_argument('-f',
|
||||||
|
'--force',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='Force GPT download')
|
||||||
argparser.add_argument('-s',
|
argparser.add_argument('-s',
|
||||||
'--system',
|
'--system',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
@@ -107,14 +113,14 @@ def runner_factory(args, target):
|
|||||||
target = 'COMPUTER'
|
target = 'COMPUTER'
|
||||||
except:
|
except:
|
||||||
username = None
|
username = None
|
||||||
logdata = dict({'username': args.user})
|
logdata = {'username': args.user}
|
||||||
log('W1', logdata)
|
log('W1', logdata)
|
||||||
else:
|
else:
|
||||||
# User may only perform gpupdate for machine (None) or
|
# User may only perform gpupdate for machine (None) or
|
||||||
# itself (os.getusername()).
|
# itself (os.getusername()).
|
||||||
username = pwd.getpwuid(os.getuid()).pw_name
|
username = pwd.getpwuid(os.getuid()).pw_name
|
||||||
if args.user != username:
|
if args.user != username:
|
||||||
logdata = dict({'username': username})
|
logdata = {'username': username}
|
||||||
log('W2', logdata)
|
log('W2', logdata)
|
||||||
|
|
||||||
if args.system:
|
if args.system:
|
||||||
@@ -165,6 +171,7 @@ def main():
|
|||||||
gettext.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
gettext.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
||||||
gettext.textdomain('gpoa')
|
gettext.textdomain('gpoa')
|
||||||
set_loglevel(args.loglevel)
|
set_loglevel(args.loglevel)
|
||||||
|
Dconf_registry._force = args.force
|
||||||
gpo_appliers = runner_factory(args, process_target(args.target))
|
gpo_appliers = runner_factory(args, process_target(args.target))
|
||||||
|
|
||||||
if gpo_appliers:
|
if gpo_appliers:
|
||||||
@@ -172,7 +179,7 @@ def main():
|
|||||||
try:
|
try:
|
||||||
gpo_appliers[0].run()
|
gpo_appliers[0].run()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict({'error': str(exc)})
|
logdata = {'error': str(exc)}
|
||||||
log('E5')
|
log('E5')
|
||||||
return int(ExitCodeUpdater.FAIL_GPUPDATE_COMPUTER_NOREPLY)
|
return int(ExitCodeUpdater.FAIL_GPUPDATE_COMPUTER_NOREPLY)
|
||||||
|
|
||||||
@@ -180,7 +187,7 @@ def main():
|
|||||||
try:
|
try:
|
||||||
gpo_appliers[1].run()
|
gpo_appliers[1].run()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict({'error': str(exc)})
|
logdata = {'error': str(exc)}
|
||||||
log('E6', logdata)
|
log('E6', logdata)
|
||||||
return int(ExitCodeUpdater.FAIL_GPUPDATE_USER_NOREPLY)
|
return int(ExitCodeUpdater.FAIL_GPUPDATE_USER_NOREPLY)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,9 +19,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import argparse
|
import argparse
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from util.util import (
|
from util.util import (
|
||||||
runcmd
|
runcmd
|
||||||
@@ -32,6 +30,7 @@ from util.util import (
|
|||||||
)
|
)
|
||||||
from util.config import GPConfig
|
from util.config import GPConfig
|
||||||
from util.paths import get_custom_policy_dir
|
from util.paths import get_custom_policy_dir
|
||||||
|
from frontend.appliers.ini_file import Ini_file
|
||||||
|
|
||||||
|
|
||||||
class Runner:
|
class Runner:
|
||||||
@@ -79,7 +78,7 @@ def parse_arguments():
|
|||||||
type=str,
|
type=str,
|
||||||
nargs='?',
|
nargs='?',
|
||||||
const='backend',
|
const='backend',
|
||||||
choices=['local', 'samba'],
|
choices=['local', 'samba', 'freeipa'],
|
||||||
help='Backend (source of settings) name')
|
help='Backend (source of settings) name')
|
||||||
|
|
||||||
parser_write.add_argument('status',
|
parser_write.add_argument('status',
|
||||||
@@ -94,7 +93,7 @@ def parse_arguments():
|
|||||||
type=str,
|
type=str,
|
||||||
nargs='?',
|
nargs='?',
|
||||||
const='backend',
|
const='backend',
|
||||||
choices=['local', 'samba'],
|
choices=['local', 'samba', 'freeipa'],
|
||||||
help='Backend (source of settings) name')
|
help='Backend (source of settings) name')
|
||||||
|
|
||||||
parser_enable.add_argument('--local-policy',
|
parser_enable.add_argument('--local-policy',
|
||||||
@@ -103,7 +102,7 @@ def parse_arguments():
|
|||||||
parser_enable.add_argument('--backend',
|
parser_enable.add_argument('--backend',
|
||||||
default='samba',
|
default='samba',
|
||||||
type=str,
|
type=str,
|
||||||
choices=['local', 'samba'],
|
choices=['local', 'samba', 'freeipa'],
|
||||||
help='Backend (source of settings) name')
|
help='Backend (source of settings) name')
|
||||||
|
|
||||||
parser_update.add_argument('--local-policy',
|
parser_update.add_argument('--local-policy',
|
||||||
@@ -112,7 +111,7 @@ def parse_arguments():
|
|||||||
parser_update.add_argument('--backend',
|
parser_update.add_argument('--backend',
|
||||||
default='samba',
|
default='samba',
|
||||||
type=str,
|
type=str,
|
||||||
choices=['local', 'samba'],
|
choices=['local', 'samba', 'freeipa'],
|
||||||
help='Backend (source of settings) name')
|
help='Backend (source of settings) name')
|
||||||
|
|
||||||
|
|
||||||
@@ -223,6 +222,8 @@ def enable_gp(policy_name, backend_type):
|
|||||||
cmd_enable_gpupdate_user_timer = ['/bin/systemctl', '--global', 'enable', 'gpupdate-user.timer']
|
cmd_enable_gpupdate_user_timer = ['/bin/systemctl', '--global', 'enable', 'gpupdate-user.timer']
|
||||||
cmd_enable_gpupdate_scripts_service = ['/bin/systemctl', 'enable', 'gpupdate-scripts-run.service']
|
cmd_enable_gpupdate_scripts_service = ['/bin/systemctl', 'enable', 'gpupdate-scripts-run.service']
|
||||||
cmd_enable_gpupdate_user_scripts_service = ['/bin/systemctl', '--global', 'enable', 'gpupdate-scripts-run-user.service']
|
cmd_enable_gpupdate_user_scripts_service = ['/bin/systemctl', '--global', 'enable', 'gpupdate-scripts-run-user.service']
|
||||||
|
cmd_ipa_client_samba = ['/usr/sbin/ipa-client-samba', '--unattended']
|
||||||
|
|
||||||
|
|
||||||
config = GPConfig()
|
config = GPConfig()
|
||||||
|
|
||||||
@@ -273,6 +274,28 @@ def enable_gp(policy_name, backend_type):
|
|||||||
if not is_unit_enabled('gpupdate.timer'):
|
if not is_unit_enabled('gpupdate.timer'):
|
||||||
disable_gp()
|
disable_gp()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if backend_type == 'freeipa':
|
||||||
|
result = runcmd(cmd_ipa_client_samba)
|
||||||
|
if result[0] != 0:
|
||||||
|
if "already configured" in str(result[1]) or "already exists" in str(result[1]):
|
||||||
|
print("FreeIPA is already configured")
|
||||||
|
else:
|
||||||
|
print(str(result))
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print(str(result))
|
||||||
|
|
||||||
|
ini_obj = type("ini", (), {})()
|
||||||
|
ini_obj.path = "/etc/samba/smb.conf"
|
||||||
|
ini_obj.section = "global"
|
||||||
|
ini_obj.action = "UPDATE"
|
||||||
|
ini_obj.property = "log level"
|
||||||
|
ini_obj.value = "0"
|
||||||
|
|
||||||
|
Ini_file(ini_obj)
|
||||||
|
|
||||||
|
|
||||||
# Enable gpupdate-setup.timer for all users
|
# Enable gpupdate-setup.timer for all users
|
||||||
if not rollback_on_error(cmd_enable_gpupdate_user_timer):
|
if not rollback_on_error(cmd_enable_gpupdate_user_timer):
|
||||||
return
|
return
|
||||||
@@ -344,18 +367,19 @@ def act_default_policy():
|
|||||||
def main():
|
def main():
|
||||||
arguments = parse_arguments()
|
arguments = parse_arguments()
|
||||||
|
|
||||||
action = dict()
|
action = {
|
||||||
action['list'] = act_list
|
'list': act_list,
|
||||||
action['list-backends'] = act_list_backends
|
'list-backends': act_list_backends,
|
||||||
action['status'] = act_status
|
'status': act_status,
|
||||||
action['set-backend'] = act_set_backend
|
'set-backend': act_set_backend,
|
||||||
action['write'] = act_write
|
'write': act_write,
|
||||||
action['enable'] = act_enable
|
'enable': act_enable,
|
||||||
action['update'] = act_enable
|
'update': act_enable,
|
||||||
action['disable'] = disable_gp
|
'disable': disable_gp,
|
||||||
action['active-policy'] = act_active_policy
|
'active-policy': act_active_policy,
|
||||||
action['active-backend'] = act_active_backend
|
'active-backend': act_active_backend,
|
||||||
action['default-policy'] = act_default_policy
|
'default-policy': act_default_policy
|
||||||
|
}
|
||||||
|
|
||||||
if arguments.action == None:
|
if arguments.action == None:
|
||||||
action['status']()
|
action['status']()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -65,6 +65,10 @@ msgstr "Установка свойств для пользователя"
|
|||||||
msgid "The line in the configuration file was cleared"
|
msgid "The line in the configuration file was cleared"
|
||||||
msgstr "В конфигурационном файле была очищена строка"
|
msgstr "В конфигурационном файле была очищена строка"
|
||||||
|
|
||||||
|
msgid "Found GPT in cache"
|
||||||
|
msgstr "Найден GPT в кеше"
|
||||||
|
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
msgid "Insufficient permissions to run gpupdate"
|
msgid "Insufficient permissions to run gpupdate"
|
||||||
msgstr "Недостаточно прав для запуска gpupdate"
|
msgstr "Недостаточно прав для запуска gpupdate"
|
||||||
@@ -240,6 +244,36 @@ msgstr "Ошибка создания переменных среды"
|
|||||||
msgid "Error running kwriteconfig5 command"
|
msgid "Error running kwriteconfig5 command"
|
||||||
msgstr "Ошибка выполнения команды kwriteconfig5"
|
msgstr "Ошибка выполнения команды kwriteconfig5"
|
||||||
|
|
||||||
|
msgid "Error getting list of keys"
|
||||||
|
msgstr "Ошибка получения списка ключей"
|
||||||
|
|
||||||
|
msgid "Error getting key value"
|
||||||
|
msgstr "Ошибка при получении значения ключей"
|
||||||
|
|
||||||
|
msgid "Failed to update dconf database"
|
||||||
|
msgstr "Не удалось обновить базу данных dconf"
|
||||||
|
|
||||||
|
msgid "Exception occurred while updating dconf database"
|
||||||
|
msgstr "Возникло исключение при обновлении базы данных dconf"
|
||||||
|
|
||||||
|
msgid "Failed to retrieve data from dconf database"
|
||||||
|
msgstr "Не удалось получить данные из базы dconf"
|
||||||
|
|
||||||
|
msgid "Autofs restart failed"
|
||||||
|
msgstr "Перезапуск Autofs не удался"
|
||||||
|
|
||||||
|
msgid "Failed to update LDAP with new password data"
|
||||||
|
msgstr "Не удалось обновить LDAP новыми данными пароля"
|
||||||
|
|
||||||
|
msgid "Failed to change local user password"
|
||||||
|
msgstr "Не удалось изменить пароль локального пользователя"
|
||||||
|
|
||||||
|
msgid "Unable to initialize Freeipa backend"
|
||||||
|
msgstr "Невозможно инициализировать бэкэнд Freeipa"
|
||||||
|
|
||||||
|
msgid "FreeIPA API Error"
|
||||||
|
msgstr "Ошибка API FreeIPA"
|
||||||
|
|
||||||
# Error_end
|
# Error_end
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
@@ -615,11 +649,11 @@ msgstr "Запуск применение настроек Envvar для маш
|
|||||||
msgid "Envvar applier for machine will not be started"
|
msgid "Envvar applier for machine will not be started"
|
||||||
msgstr "Применение настроек Envvar для машины не запускается"
|
msgstr "Применение настроек Envvar для машины не запускается"
|
||||||
|
|
||||||
msgid "Running Envvar applier for user in user context"
|
msgid "Running Envvar applier for user in admin context"
|
||||||
msgstr "Запуск применение настроек Envvar для пользователя в контексте пользователя"
|
msgstr "Запуск применение настроек Envvar для пользователя в контексте администратора"
|
||||||
|
|
||||||
msgid "Envvar applier for user in user context will not be started"
|
msgid "Envvar applier for user in admin context will not be started"
|
||||||
msgstr "Применение настроек Envvar для пользователя в контексте пользователя не запускается"
|
msgstr "Применение настроек Envvar для пользователя в контексте администратора не запускается"
|
||||||
|
|
||||||
msgid "Running Package applier for machine"
|
msgid "Running Package applier for machine"
|
||||||
msgstr "Запуск установки пакетов для машины"
|
msgstr "Запуск установки пакетов для машины"
|
||||||
@@ -837,6 +871,105 @@ msgstr "Изменение конфигурационного файла"
|
|||||||
msgid "Widget command completed successfully"
|
msgid "Widget command completed successfully"
|
||||||
msgstr "Команда для виджетов выполнена успешно"
|
msgstr "Команда для виджетов выполнена успешно"
|
||||||
|
|
||||||
|
msgid "Getting a list of keys"
|
||||||
|
msgstr "Получение списка ключей"
|
||||||
|
|
||||||
|
msgid "Getting the key value"
|
||||||
|
msgstr "Получение значения ключа"
|
||||||
|
|
||||||
|
msgid "Successfully updated dconf database"
|
||||||
|
msgstr "База данных dconf успешно обновлена"
|
||||||
|
|
||||||
|
msgid "Creating a dictionary with keys and values from the dconf database"
|
||||||
|
msgstr "Формирование словаря с ключами и значениями из базы dconf"
|
||||||
|
|
||||||
|
msgid "No entry found for the specified path"
|
||||||
|
msgstr "Не найдено записей по указанному пути"
|
||||||
|
|
||||||
|
msgid "Creating an ini file with policies for dconf"
|
||||||
|
msgstr "Создание ini-файла с политиками для dconf"
|
||||||
|
|
||||||
|
msgid "GPO version was not found"
|
||||||
|
msgstr "Версия GPO не найдена"
|
||||||
|
|
||||||
|
msgid "SYSVOL entry found in cache"
|
||||||
|
msgstr "Запись SYSVOL найдена в кеше"
|
||||||
|
|
||||||
|
msgid "Wrote Thunderbird preferences to"
|
||||||
|
msgstr "Настройки Thunderbird записаны в"
|
||||||
|
|
||||||
|
msgid "Running Thunderbird applier for machine"
|
||||||
|
msgstr "Запуск применение настроек Thunderbird для машины"
|
||||||
|
|
||||||
|
msgid "Thunderbird applier for machine will not be started"
|
||||||
|
msgstr "Применение настроек Thunderbird для компьютера не запускается"
|
||||||
|
|
||||||
|
msgid "The environment file has been cleaned"
|
||||||
|
msgstr "Файл environment очищен"
|
||||||
|
|
||||||
|
msgid "Cleanup of file environment failed"
|
||||||
|
msgstr "Очистка файла environment не удалась"
|
||||||
|
|
||||||
|
msgid "Failed to get dictionary"
|
||||||
|
msgstr "Не удалось получить словарь"
|
||||||
|
|
||||||
|
msgid "LAPS applier started"
|
||||||
|
msgstr "Запущен обработчик LAPS"
|
||||||
|
|
||||||
|
msgid "LAPS applier is disabled"
|
||||||
|
msgstr "Обработчик LAPS отключен"
|
||||||
|
|
||||||
|
msgid "Rebooting system after password change"
|
||||||
|
msgstr "Перезагрузка системы после смены пароля"
|
||||||
|
|
||||||
|
msgid "Password changed"
|
||||||
|
msgstr "Пароль изменён"
|
||||||
|
|
||||||
|
msgid "Writing password changes time"
|
||||||
|
msgstr "Запись времени изменения пароля"
|
||||||
|
|
||||||
|
msgid "Requirements not met"
|
||||||
|
msgstr "Требования не выполнены"
|
||||||
|
|
||||||
|
msgid "The number of hours from the moment of the last user entrance"
|
||||||
|
msgstr "Количество часов с момента последнего входа пользователя"
|
||||||
|
|
||||||
|
msgid "The number of hours since the password has last changed"
|
||||||
|
msgstr "Количество часов с момента последнего изменения пароля"
|
||||||
|
|
||||||
|
msgid "LDAP updated with new password data"
|
||||||
|
msgstr "LDAP обновлён новыми данными пароля"
|
||||||
|
|
||||||
|
msgid "No active sessions found"
|
||||||
|
msgstr "Активные сеансы не найдены"
|
||||||
|
|
||||||
|
msgid "Process terminated"
|
||||||
|
msgstr "Процесс завершён"
|
||||||
|
|
||||||
|
msgid "Password update not needed"
|
||||||
|
msgstr "Обновление пароля не требуется"
|
||||||
|
|
||||||
|
msgid "Password successfully updated"
|
||||||
|
msgstr "Пароль успешно обновлён"
|
||||||
|
|
||||||
|
msgid "Cleaning the autofs catalog"
|
||||||
|
msgstr "Очистка каталога autofs"
|
||||||
|
|
||||||
|
msgid "No user login records found"
|
||||||
|
msgstr "Не найдены записи о входе пользователя"
|
||||||
|
|
||||||
|
msgid "Calculating time since the first user login after their password change"
|
||||||
|
msgstr "Расчет времени с момента первого входа пользователя после изменения их пароля"
|
||||||
|
|
||||||
|
msgid "No logins found after password change"
|
||||||
|
msgstr "Не найдены входы после изменения пароля"
|
||||||
|
|
||||||
|
msgid "Unknown message type, no message assigned"
|
||||||
|
msgstr "Неизвестный тип сообщения"
|
||||||
|
|
||||||
|
msgid "Failed to load cached versions"
|
||||||
|
msgstr "Не удалось загрузить кешированные версии"
|
||||||
|
|
||||||
# Debug_end
|
# Debug_end
|
||||||
|
|
||||||
# Warning
|
# Warning
|
||||||
@@ -889,8 +1022,8 @@ msgstr "Не удалось скопировать файл"
|
|||||||
msgid "Failed to create KDE settings list"
|
msgid "Failed to create KDE settings list"
|
||||||
msgstr "Не удалось создать список настроек KDE"
|
msgstr "Не удалось создать список настроек KDE"
|
||||||
|
|
||||||
msgid "Could not find application tools"
|
msgid "Could not find tools to configure KDE"
|
||||||
msgstr "Не удалось найти инструменты применения"
|
msgstr "Не удалось найти инструменты для настройки KDE"
|
||||||
|
|
||||||
msgid "Failed to open KDE settings"
|
msgid "Failed to open KDE settings"
|
||||||
msgstr "Не удалось открыть настройки KDE"
|
msgstr "Не удалось открыть настройки KDE"
|
||||||
@@ -904,12 +1037,73 @@ msgstr "Ошибка при подключении к серверу"
|
|||||||
msgid "Wallpaper configuration file not found"
|
msgid "Wallpaper configuration file not found"
|
||||||
msgstr "Конфигурационный файл для обоев не найден"
|
msgstr "Конфигурационный файл для обоев не найден"
|
||||||
|
|
||||||
msgid "The user setting was not installed, conflict with computer setting."
|
msgid "The user setting was not installed, conflict with computer setting"
|
||||||
msgstr "Пользовательская настройка не была установлена, конфликт с настройкой компьютера."
|
msgstr "Пользовательская настройка не была установлена, конфликт с настройкой компьютера"
|
||||||
|
|
||||||
msgid "Action for ini file failed"
|
msgid "Action for ini file failed"
|
||||||
msgstr "Не удалось выполнить действие для INI-файла"
|
msgstr "Не удалось выполнить действие для INI-файла"
|
||||||
|
|
||||||
|
msgid "Couldn't get the uid"
|
||||||
|
msgstr "Не удалось получить uid"
|
||||||
|
|
||||||
|
msgid "Failed to load content from remote host"
|
||||||
|
msgstr "Не удалось загрузить контент с удаленного узла"
|
||||||
|
|
||||||
|
msgid "Force mode activated"
|
||||||
|
msgstr "Режим force задействован"
|
||||||
|
|
||||||
|
msgid "Failed to change password"
|
||||||
|
msgstr "Не удалось изменить пароль"
|
||||||
|
|
||||||
|
msgid "Failed to write password modification time"
|
||||||
|
msgstr "Не удалось записать время изменения пароля"
|
||||||
|
|
||||||
|
msgid "LAPS requirements not met, module disabled"
|
||||||
|
msgstr "Требования LAPS не выполнены, модуль отключён"
|
||||||
|
|
||||||
|
msgid "Could not resolve encryption principal name. Return admin group SID"
|
||||||
|
msgstr "Не удалось определить имя шифрования. Возвращён SID группы администраторов"
|
||||||
|
|
||||||
|
msgid "Failed to get expiration time from LDAP"
|
||||||
|
msgstr "Не удалось получить время истечения срока действия из LDAP"
|
||||||
|
|
||||||
|
msgid "Failed to read password modification time from dconf"
|
||||||
|
msgstr "Не удалось прочитать время изменения пароля из dconf"
|
||||||
|
|
||||||
|
msgid "Failed to get last login time"
|
||||||
|
msgstr "Не удалось получить время последнего входа"
|
||||||
|
|
||||||
|
msgid "Failed to calculate password age"
|
||||||
|
msgstr "Не удалось вычислить возраст пароля"
|
||||||
|
|
||||||
|
msgid "Failed to terminate process"
|
||||||
|
msgstr "Не удалось завершить процесс"
|
||||||
|
|
||||||
|
msgid "The user was not found to change the password"
|
||||||
|
msgstr "Пользователь для изменения пароля не был найден"
|
||||||
|
|
||||||
|
msgid "Error while cleaning the autofs catalog"
|
||||||
|
msgstr "Ошибка при очистке каталога autofs"
|
||||||
|
|
||||||
|
msgid "Problem with timezone detection"
|
||||||
|
msgstr "Проблема с определением часового пояса"
|
||||||
|
|
||||||
|
msgid "Error executing last command"
|
||||||
|
msgstr "Ошибка выполнения команды last"
|
||||||
|
|
||||||
|
msgid "Last command not found"
|
||||||
|
msgstr "Команда last не найдена"
|
||||||
|
|
||||||
|
msgid "Error getting user login times"
|
||||||
|
msgstr "Ошибка получения времени входа пользователя"
|
||||||
|
|
||||||
|
msgid "Invalid timezone in reference datetime"
|
||||||
|
msgstr "Некорректный часовой пояс в reference datetime"
|
||||||
|
|
||||||
|
msgid "wbinfo SID lookup failed; will try as trusted domain user"
|
||||||
|
msgstr "Ошибка получения SID через wbinfo; будет предпринята попытка как для пользователя доверенного домена"
|
||||||
|
# Warning_end
|
||||||
|
|
||||||
# Fatal
|
# Fatal
|
||||||
msgid "Unable to refresh GPO list"
|
msgid "Unable to refresh GPO list"
|
||||||
msgstr "Невозможно обновить список объектов групповых политик"
|
msgstr "Невозможно обновить список объектов групповых политик"
|
||||||
@@ -923,7 +1117,5 @@ msgstr "Не удалось получить GPT для пользователя
|
|||||||
msgid "Unknown fatal code"
|
msgid "Unknown fatal code"
|
||||||
msgstr "Неизвестный код фатальной ошибки"
|
msgstr "Неизвестный код фатальной ошибки"
|
||||||
|
|
||||||
# get_message
|
|
||||||
msgid "Unknown message type, no message assigned"
|
|
||||||
msgstr "Неизвестный тип сообщения"
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
import gettext
|
import gettext
|
||||||
|
|
||||||
def info_code(code):
|
def info_code(code):
|
||||||
info_ids = dict()
|
info_ids = {}
|
||||||
info_ids[1] = 'Got GPO list for username'
|
info_ids[1] = 'Got GPO list for username'
|
||||||
info_ids[2] = 'Got GPO'
|
info_ids[2] = 'Got GPO'
|
||||||
info_ids[3] = 'Working with control'
|
info_ids[3] = 'Working with control'
|
||||||
@@ -31,11 +31,12 @@ def info_code(code):
|
|||||||
info_ids[8] = 'Chromium policy'
|
info_ids[8] = 'Chromium policy'
|
||||||
info_ids[9] = 'Set user property to'
|
info_ids[9] = 'Set user property to'
|
||||||
info_ids[10] = 'The line in the configuration file was cleared'
|
info_ids[10] = 'The line in the configuration file was cleared'
|
||||||
|
info_ids[11] = 'Found GPT in cache'
|
||||||
|
|
||||||
return info_ids.get(code, 'Unknown info code')
|
return info_ids.get(code, 'Unknown info code')
|
||||||
|
|
||||||
def error_code(code):
|
def error_code(code):
|
||||||
error_ids = dict()
|
error_ids = {}
|
||||||
error_ids[1] = 'Insufficient permissions to run gpupdate'
|
error_ids[1] = 'Insufficient permissions to run gpupdate'
|
||||||
error_ids[2] = 'gpupdate will not be started'
|
error_ids[2] = 'gpupdate will not be started'
|
||||||
error_ids[3] = 'Backend execution error'
|
error_ids[3] = 'Backend execution error'
|
||||||
@@ -103,10 +104,20 @@ def error_code(code):
|
|||||||
error_ids[66] = 'Error while executing command for widgets'
|
error_ids[66] = 'Error while executing command for widgets'
|
||||||
error_ids[67] = 'Error creating environment variables'
|
error_ids[67] = 'Error creating environment variables'
|
||||||
error_ids[68] = 'Error running kwriteconfig5 command'
|
error_ids[68] = 'Error running kwriteconfig5 command'
|
||||||
|
error_ids[69] = 'Error getting list of keys'
|
||||||
|
error_ids[70] = 'Error getting key value'
|
||||||
|
error_ids[71] = 'Failed to update dconf database'
|
||||||
|
error_ids[72] = 'Exception occurred while updating dconf database'
|
||||||
|
error_ids[73] = 'Failed to retrieve data from dconf database'
|
||||||
|
error_ids[74] = 'Autofs restart failed'
|
||||||
|
error_ids[75] = 'Failed to update LDAP with new password data'
|
||||||
|
error_ids[76] = 'Failed to change local user password'
|
||||||
|
error_ids[77] = 'Unable to initialize Freeipa backend'
|
||||||
|
error_ids[78] = 'FreeIPA API error'
|
||||||
return error_ids.get(code, 'Unknown error code')
|
return error_ids.get(code, 'Unknown error code')
|
||||||
|
|
||||||
def debug_code(code):
|
def debug_code(code):
|
||||||
debug_ids = dict()
|
debug_ids = {}
|
||||||
debug_ids[1] = 'The GPOA process was started for user'
|
debug_ids[1] = 'The GPOA process was started for user'
|
||||||
debug_ids[2] = 'Username is not specified - will use username of the current process'
|
debug_ids[2] = 'Username is not specified - will use username of the current process'
|
||||||
debug_ids[3] = 'Initializing plugin manager'
|
debug_ids[3] = 'Initializing plugin manager'
|
||||||
@@ -242,8 +253,8 @@ def debug_code(code):
|
|||||||
debug_ids[133] = 'NTP applier for machine will not be started'
|
debug_ids[133] = 'NTP applier for machine will not be started'
|
||||||
debug_ids[134] = 'Running Envvar applier for machine'
|
debug_ids[134] = 'Running Envvar applier for machine'
|
||||||
debug_ids[135] = 'Envvar applier for machine will not be started'
|
debug_ids[135] = 'Envvar applier for machine will not be started'
|
||||||
debug_ids[136] = 'Running Envvar applier for user in user context'
|
debug_ids[136] = 'Running Envvar applier for user in admin context'
|
||||||
debug_ids[137] = 'Envvar applier for user in user context will not be started'
|
debug_ids[137] = 'Envvar applier for user in admin context will not be started'
|
||||||
debug_ids[138] = 'Running Package applier for machine'
|
debug_ids[138] = 'Running Package applier for machine'
|
||||||
debug_ids[139] = 'Package applier for machine will not be started'
|
debug_ids[139] = 'Package applier for machine will not be started'
|
||||||
debug_ids[140] = 'Running Package applier for user in administrator context'
|
debug_ids[140] = 'Running Package applier for user in administrator context'
|
||||||
@@ -310,10 +321,42 @@ def debug_code(code):
|
|||||||
debug_ids[201] = 'KDE applier for user in user context will not be started'
|
debug_ids[201] = 'KDE applier for user in user context will not be started'
|
||||||
debug_ids[202] = 'Changing the configuration file'
|
debug_ids[202] = 'Changing the configuration file'
|
||||||
debug_ids[203] = 'Widget command completed successfully'
|
debug_ids[203] = 'Widget command completed successfully'
|
||||||
|
debug_ids[204] = 'Getting a list of keys'
|
||||||
|
debug_ids[205] = 'Getting the key value'
|
||||||
|
debug_ids[206] = 'Successfully updated dconf database'
|
||||||
|
debug_ids[207] = 'Creating a dictionary with keys and values from the dconf database'
|
||||||
|
debug_ids[208] = 'No entry found for the specified path'
|
||||||
|
debug_ids[209] = 'Creating an ini file with policies for dconf'
|
||||||
|
debug_ids[211] = 'SYSVOL entry found in cache'
|
||||||
|
debug_ids[212] = 'Wrote Thunderbird preferences to'
|
||||||
|
debug_ids[213] = 'Running Thunderbird applier for machine'
|
||||||
|
debug_ids[214] = 'Thunderbird applier for machine will not be started'
|
||||||
|
debug_ids[215] = 'The environment file has been cleaned'
|
||||||
|
debug_ids[216] = 'Cleanup of file environment failed'
|
||||||
|
debug_ids[217] = 'Failed to get dictionary'
|
||||||
|
debug_ids[218] = 'LAPS applier started'
|
||||||
|
debug_ids[219] = 'LAPS applier is disabled'
|
||||||
|
debug_ids[220] = 'Rebooting system after password change'
|
||||||
|
debug_ids[221] = 'Password changed'
|
||||||
|
debug_ids[222] = 'Writing password changes time'
|
||||||
|
debug_ids[223] = 'Requirements not met'
|
||||||
|
debug_ids[224] = 'The number of hours from the moment of the last user entrance'
|
||||||
|
debug_ids[225] = 'The number of hours since the password has last changed'
|
||||||
|
debug_ids[226] = 'LDAP updated with new password data'
|
||||||
|
debug_ids[227] = 'No active sessions found'
|
||||||
|
debug_ids[228] = 'Process terminated'
|
||||||
|
debug_ids[229] = 'Password update not needed'
|
||||||
|
debug_ids[230] = 'Password successfully updated'
|
||||||
|
debug_ids[231] = 'Cleaning the autofs catalog'
|
||||||
|
debug_ids[232] = 'No user login records found'
|
||||||
|
debug_ids[233] = 'Calculating time since the first user login after their password change'
|
||||||
|
debug_ids[234] = 'No logins found after password change'
|
||||||
|
debug_ids[235] = 'Failed to load cached versions'
|
||||||
|
|
||||||
return debug_ids.get(code, 'Unknown debug code')
|
return debug_ids.get(code, 'Unknown debug code')
|
||||||
|
|
||||||
def warning_code(code):
|
def warning_code(code):
|
||||||
warning_ids = dict()
|
warning_ids = {}
|
||||||
warning_ids[1] = (
|
warning_ids[1] = (
|
||||||
'Unable to perform gpupdate for non-existent user, '
|
'Unable to perform gpupdate for non-existent user, '
|
||||||
'will update machine settings'
|
'will update machine settings'
|
||||||
@@ -336,19 +379,38 @@ def warning_code(code):
|
|||||||
warning_ids[14] = 'Could not create a valid list of keys'
|
warning_ids[14] = 'Could not create a valid list of keys'
|
||||||
warning_ids[15] = 'Failed to copy file'
|
warning_ids[15] = 'Failed to copy file'
|
||||||
warning_ids[16] = 'Failed to create KDE settings list'
|
warning_ids[16] = 'Failed to create KDE settings list'
|
||||||
warning_ids[17] = 'Could not find application tools'
|
warning_ids[17] = 'Could not find tools to configure KDE'
|
||||||
warning_ids[18] = 'Failed to open KDE settings'
|
warning_ids[18] = 'Failed to open KDE settings'
|
||||||
warning_ids[19] = 'Failed to change KDE configuration file'
|
warning_ids[19] = 'Failed to change KDE configuration file'
|
||||||
warning_ids[20] = 'Error connecting to server'
|
warning_ids[20] = 'Error connecting to server'
|
||||||
warning_ids[21] = 'Wallpaper configuration file not found'
|
warning_ids[21] = 'Wallpaper configuration file not found'
|
||||||
warning_ids[22] = 'The user setting was not installed, conflict with computer setting.'
|
warning_ids[22] = 'The user setting was not installed, conflict with computer setting'
|
||||||
warning_ids[23] = 'Action for ini file failed'
|
warning_ids[23] = 'Action for ini file failed'
|
||||||
|
warning_ids[24] = 'Couldn\'t get the uid'
|
||||||
|
warning_ids[25] = 'Failed to load content from remote host'
|
||||||
|
warning_ids[26] = 'Force mode activated'
|
||||||
|
warning_ids[27] = 'Failed to change password'
|
||||||
|
warning_ids[28] = 'Failed to write password modification time'
|
||||||
|
warning_ids[29] = 'LAPS requirements not met, module disabled'
|
||||||
|
warning_ids[30] = 'Could not resolve encryption principal name. Return admin group SID'
|
||||||
|
warning_ids[31] = 'Failed to get expiration time from LDAP'
|
||||||
|
warning_ids[32] = 'Failed to read password modification time from dconf'
|
||||||
|
warning_ids[33] = 'Failed to get last login time'
|
||||||
|
warning_ids[34] = 'Failed to calculate password age'
|
||||||
|
warning_ids[35] = 'Failed to terminate process'
|
||||||
|
warning_ids[36] = 'The user was not found to change the password'
|
||||||
|
warning_ids[37] = 'Error while cleaning the autofs catalog'
|
||||||
|
warning_ids[38] = 'Problem with timezone detection'
|
||||||
|
warning_ids[39] = 'Error executing last command'
|
||||||
|
warning_ids[40] = 'Last command not found'
|
||||||
|
warning_ids[41] = 'Error getting user login times'
|
||||||
|
warning_ids[42] = 'Invalid timezone in reference datetime'
|
||||||
|
warning_ids[43] = 'wbinfo SID lookup failed; will try as trusted domain user'
|
||||||
|
|
||||||
return warning_ids.get(code, 'Unknown warning code')
|
return warning_ids.get(code, 'Unknown warning code')
|
||||||
|
|
||||||
def fatal_code(code):
|
def fatal_code(code):
|
||||||
fatal_ids = dict()
|
fatal_ids = {}
|
||||||
fatal_ids[1] = 'Unable to refresh GPO list'
|
fatal_ids[1] = 'Unable to refresh GPO list'
|
||||||
fatal_ids[2] = 'Error getting GPTs for machine'
|
fatal_ids[2] = 'Error getting GPTs for machine'
|
||||||
fatal_ids[3] = 'Error getting GPTs for user'
|
fatal_ids[3] = 'Error getting GPTs for user'
|
||||||
|
|||||||
@@ -20,15 +20,12 @@
|
|||||||
import rpm
|
import rpm
|
||||||
import subprocess
|
import subprocess
|
||||||
from gpoa.storage import registry_factory
|
from gpoa.storage import registry_factory
|
||||||
|
from util.util import get_uid_by_username, string_to_literal_eval
|
||||||
import logging
|
import logging
|
||||||
from util.logging import log
|
from util.logging import log
|
||||||
import argparse
|
import argparse
|
||||||
import gettext
|
import gettext
|
||||||
import locale
|
import locale
|
||||||
from messages import message_with_code
|
|
||||||
from util.arguments import (
|
|
||||||
set_loglevel
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def is_rpm_installed(rpm_name):
|
def is_rpm_installed(rpm_name):
|
||||||
@@ -44,58 +41,54 @@ def is_rpm_installed(rpm_name):
|
|||||||
|
|
||||||
class Pkcon_applier:
|
class Pkcon_applier:
|
||||||
|
|
||||||
def __init__(self, sid = None):
|
def __init__(self, user = None):
|
||||||
self.__install_key_name = 'Install'
|
install_key_name = 'Install'
|
||||||
self.__remove_key_name = 'Remove'
|
remove_key_name = 'Remove'
|
||||||
self.__hkcu_branch = 'Software\\BaseALT\\Policies\\Packages'
|
hklm_branch = 'Software/BaseALT/Policies/Packages'
|
||||||
self.__hklm_branch = 'Software\\BaseALT\\Policies\\Packages'
|
|
||||||
self.__install_command = ['/usr/bin/pkcon', '-y', 'install']
|
self.__install_command = ['/usr/bin/pkcon', '-y', 'install']
|
||||||
self.__remove_command = ['/usr/bin/pkcon', '-y', 'remove']
|
self.__remove_command = ['/usr/bin/pkcon', '-y', 'remove']
|
||||||
self.__reinstall_command = ['/usr/bin/pkcon', '-y', 'reinstall']
|
self.__reinstall_command = ['/usr/bin/pkcon', '-y', 'reinstall']
|
||||||
self.install_packages = set()
|
self.install_packages = set()
|
||||||
self.remove_packages = set()
|
self.remove_packages = set()
|
||||||
self.storage = registry_factory('registry')
|
self.storage = registry_factory()
|
||||||
if sid:
|
if user:
|
||||||
install_branch_user = '{}\\{}%'.format(self.__hkcu_branch, self.__install_key_name)
|
uid = get_uid_by_username(user)
|
||||||
remove_branch_user = '{}\\{}%'.format(self.__hkcu_branch, self.__remove_key_name)
|
dict_dconf_db = self.storage.get_dictionary_from_dconf_file_db(uid)
|
||||||
self.install_packages_setting = self.storage.filter_hkcu_entries(sid, install_branch_user)
|
|
||||||
self.remove_packages_setting = self.storage.filter_hkcu_entries(sid, remove_branch_user)
|
|
||||||
else:
|
else:
|
||||||
install_branch = '{}\\{}%'.format(self.__hklm_branch, self.__install_key_name)
|
dict_dconf_db = self.storage.get_dictionary_from_dconf_file_db()
|
||||||
remove_branch = '{}\\{}%'.format(self.__hklm_branch, self.__remove_key_name)
|
dict_packages = dict_dconf_db.get(hklm_branch,{})
|
||||||
self.install_packages_setting = self.storage.filter_hklm_entries(install_branch)
|
self.install_packages_setting = string_to_literal_eval(dict_packages.get(install_key_name,[]))
|
||||||
self.remove_packages_setting = self.storage.filter_hklm_entries(remove_branch)
|
self.remove_packages_setting = string_to_literal_eval(dict_packages.get(remove_key_name,[]))
|
||||||
|
|
||||||
for package in self.install_packages_setting:
|
for package in self.install_packages_setting:
|
||||||
if not is_rpm_installed(package.data):
|
package = package.strip()
|
||||||
self.install_packages.add(package.data)
|
if not is_rpm_installed(package):
|
||||||
|
self.install_packages.add(package)
|
||||||
for package in self.remove_packages_setting:
|
for package in self.remove_packages_setting:
|
||||||
if package.data in self.install_packages:
|
package = package.strip()
|
||||||
self.install_packages.remove(package.data)
|
if package in self.install_packages:
|
||||||
if is_rpm_installed(package.data):
|
self.install_packages.remove(package)
|
||||||
self.remove_packages.add(package.data)
|
if is_rpm_installed(package):
|
||||||
|
self.remove_packages.add(package)
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
log('D142')
|
log('D142')
|
||||||
self.update()
|
self.update()
|
||||||
for package in self.remove_packages:
|
for package in self.remove_packages:
|
||||||
|
logdata = {'name': package}
|
||||||
try:
|
try:
|
||||||
logdata = dict()
|
|
||||||
logdata['name'] = package
|
|
||||||
log('D149', logdata)
|
log('D149', logdata)
|
||||||
self.remove_pkg(package)
|
self.remove_pkg(package)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
|
||||||
logdata['exc'] = exc
|
logdata['exc'] = exc
|
||||||
log('E58', logdata)
|
log('E58', logdata)
|
||||||
|
|
||||||
for package in self.install_packages:
|
for package in self.install_packages:
|
||||||
|
logdata = {'name': package}
|
||||||
try:
|
try:
|
||||||
logdata = dict()
|
|
||||||
logdata['name'] = package
|
|
||||||
log('D148', logdata)
|
log('D148', logdata)
|
||||||
self.install_pkg(package)
|
self.install_pkg(package)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
|
||||||
logdata['exc'] = exc
|
logdata['exc'] = exc
|
||||||
log('E57', logdata)
|
log('E57', logdata)
|
||||||
|
|
||||||
@@ -109,7 +102,7 @@ class Pkcon_applier:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def remove_pkg(self, package_name):
|
def remove_pkg(self, package_name):
|
||||||
fullcmd = self.__remove_command
|
fullcmd = list(self.__remove_command)
|
||||||
fullcmd.append(package_name)
|
fullcmd.append(package_name)
|
||||||
return subprocess.check_output(fullcmd)
|
return subprocess.check_output(fullcmd)
|
||||||
|
|
||||||
@@ -120,15 +113,14 @@ class Pkcon_applier:
|
|||||||
try:
|
try:
|
||||||
res = subprocess.check_output(['/usr/bin/apt-get', 'update'], encoding='utf-8')
|
res = subprocess.check_output(['/usr/bin/apt-get', 'update'], encoding='utf-8')
|
||||||
msg = str(res).split('\n')
|
msg = str(res).split('\n')
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
for mslog in msg:
|
for mslog in msg:
|
||||||
ms = str(mslog).split(' ')
|
ms = str(mslog).split(' ')
|
||||||
if ms:
|
if ms:
|
||||||
logdata = {ms[0]: ms[1:-1]}
|
logdata = {ms[0]: ms[1:-1]}
|
||||||
log('D143', logdata)
|
log('D143', logdata)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'msg': exc}
|
||||||
logdata['msg'] = exc
|
|
||||||
log('E56',logdata)
|
log('E56',logdata)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@@ -137,13 +129,13 @@ if __name__ == '__main__':
|
|||||||
gettext.textdomain('gpoa')
|
gettext.textdomain('gpoa')
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
parser = argparse.ArgumentParser(description='Package applier')
|
parser = argparse.ArgumentParser(description='Package applier')
|
||||||
parser.add_argument('--sid', type = str, help = 'sid', nargs = '?', default = None)
|
parser.add_argument('--user', type = str, help = 'user', nargs = '?', default = None)
|
||||||
parser.add_argument('--loglevel', type = int, help = 'loglevel', nargs = '?', default = 30)
|
parser.add_argument('--loglevel', type = int, help = 'loglevel', nargs = '?', default = 30)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
logger.setLevel(args.loglevel)
|
logger.setLevel(args.loglevel)
|
||||||
if args.sid:
|
if args.user:
|
||||||
applier = Pkcon_applier(args.sid)
|
applier = Pkcon_applier(args.user)
|
||||||
else:
|
else:
|
||||||
applier = Pkcon_applier()
|
applier = Pkcon_applier()
|
||||||
applier.apply()
|
applier.apply()
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from messages import message_with_code
|
|||||||
|
|
||||||
class plugin_manager:
|
class plugin_manager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.plugins = dict()
|
self.plugins = {}
|
||||||
logging.debug(slogm(message_with_code('D3')))
|
logging.debug(slogm(message_with_code('D3')))
|
||||||
try:
|
try:
|
||||||
self.plugins['adp'] = adp()
|
self.plugins['adp'] = adp()
|
||||||
@@ -35,6 +35,6 @@ class plugin_manager:
|
|||||||
logging.warning(slogm(str(exc)))
|
logging.warning(slogm(str(exc)))
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.plugins.get('adp', plugin('adp')).run()
|
#self.plugins.get('adp', plugin('adp')).run()
|
||||||
self.plugins.get('roles', plugin('roles')).run()
|
self.plugins.get('roles', plugin('roles')).run()
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import subprocess
|
|||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import psutil
|
||||||
|
import time
|
||||||
|
|
||||||
class Scripts_runner:
|
class Scripts_runner:
|
||||||
'''
|
'''
|
||||||
@@ -31,7 +33,7 @@ class Scripts_runner:
|
|||||||
self.dir_scripts_machine = '/var/cache/gpupdate_scripts_cache/machine/'
|
self.dir_scripts_machine = '/var/cache/gpupdate_scripts_cache/machine/'
|
||||||
self.dir_scripts_users = '/var/cache/gpupdate_scripts_cache/users/'
|
self.dir_scripts_users = '/var/cache/gpupdate_scripts_cache/users/'
|
||||||
self.user_name = user_name
|
self.user_name = user_name
|
||||||
self.list_with_all_commands = list()
|
self.list_with_all_commands = []
|
||||||
stack_dir = None
|
stack_dir = None
|
||||||
if work_mode and work_mode.upper() == 'MACHINE':
|
if work_mode and work_mode.upper() == 'MACHINE':
|
||||||
stack_dir = self.machine_runner_fill()
|
stack_dir = self.machine_runner_fill()
|
||||||
@@ -57,7 +59,7 @@ class Scripts_runner:
|
|||||||
return self.get_stack_dir(self.dir_scripts_machine)
|
return self.get_stack_dir(self.dir_scripts_machine)
|
||||||
|
|
||||||
def get_stack_dir(self, path_dir):
|
def get_stack_dir(self, path_dir):
|
||||||
stack_dir = list()
|
stack_dir = []
|
||||||
try:
|
try:
|
||||||
dir_script = Path(path_dir)
|
dir_script = Path(path_dir)
|
||||||
for it_dir in dir_script.iterdir():
|
for it_dir in dir_script.iterdir():
|
||||||
@@ -70,7 +72,7 @@ class Scripts_runner:
|
|||||||
def find_action(self, stack_dir):
|
def find_action(self, stack_dir):
|
||||||
if not stack_dir:
|
if not stack_dir:
|
||||||
return
|
return
|
||||||
list_tmp = list()
|
list_tmp = []
|
||||||
while stack_dir:
|
while stack_dir:
|
||||||
path_turn = stack_dir.pop()
|
path_turn = stack_dir.pop()
|
||||||
basename = os.path.basename(path_turn)
|
basename = os.path.basename(path_turn)
|
||||||
@@ -92,7 +94,7 @@ class Scripts_runner:
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print('Argument read for {}: {}'.format(self.list_with_all_commands.pop(), exc))
|
print('Argument read for {}: {}'.format(self.list_with_all_commands.pop(), exc))
|
||||||
else:
|
else:
|
||||||
cmd = list()
|
cmd = []
|
||||||
cmd.append(file_in_task_dir)
|
cmd.append(file_in_task_dir)
|
||||||
self.list_with_all_commands.append(cmd)
|
self.list_with_all_commands.append(cmd)
|
||||||
|
|
||||||
@@ -104,12 +106,39 @@ class Scripts_runner:
|
|||||||
|
|
||||||
def run_cmd_subprocess(self, cmd):
|
def run_cmd_subprocess(self, cmd):
|
||||||
try:
|
try:
|
||||||
subprocess.Popen(cmd)
|
subprocess.run(cmd)
|
||||||
return 'Script run: {}'.format(cmd)
|
return 'Script run: {}'.format(cmd)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
return exc
|
return exc
|
||||||
|
|
||||||
|
def find_process_by_name_and_script(name, script_path):
|
||||||
|
|
||||||
|
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
|
||||||
|
try:
|
||||||
|
# Check if the process name matches and the script path is in the command line arguments
|
||||||
|
if proc.info['name'] == name and script_path in proc.info['cmdline']:
|
||||||
|
return proc
|
||||||
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||||
|
continue
|
||||||
|
return None
|
||||||
|
|
||||||
|
def wait_for_process(name, script_path, check_interval=1):
|
||||||
|
|
||||||
|
process = find_process_by_name_and_script(name, script_path)
|
||||||
|
if not process:
|
||||||
|
print(f"Process with name {name} and script path {script_path} not found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Loop to wait for the process to finish
|
||||||
|
while process.is_running():
|
||||||
|
print(f"Waiting for process {name} with PID {process.pid} to finish...")
|
||||||
|
time.sleep(check_interval)
|
||||||
|
print(f"Process {name} with PID {process.pid} has finished.")
|
||||||
|
return
|
||||||
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||||
|
print(f"Process {name} with PID {process.pid} is no longer accessible.")
|
||||||
|
return
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser(description='Scripts runner')
|
parser = argparse.ArgumentParser(description='Scripts runner')
|
||||||
@@ -117,6 +146,9 @@ if __name__ == '__main__':
|
|||||||
parser.add_argument('--user', type = str, help = 'User name ', nargs = '?', default = None)
|
parser.add_argument('--user', type = str, help = 'User name ', nargs = '?', default = None)
|
||||||
parser.add_argument('--action', type = str, help = 'MACHINE : [STARTUP or SHUTDOWN], USER : [LOGON or LOGOFF]', nargs = '?', default = None)
|
parser.add_argument('--action', type = str, help = 'MACHINE : [STARTUP or SHUTDOWN], USER : [LOGON or LOGOFF]', nargs = '?', default = None)
|
||||||
|
|
||||||
|
process_name = "python3"
|
||||||
|
script_path = "/usr/sbin/gpoa"
|
||||||
|
wait_for_process(process_name, script_path)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
try:
|
try:
|
||||||
Scripts_runner(args.mode, args.user, args.action)
|
Scripts_runner(args.mode, args.user, args.action)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2023 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,12 +16,19 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from .sqlite_registry import sqlite_registry
|
|
||||||
from .sqlite_cache import sqlite_cache
|
|
||||||
|
|
||||||
def cache_factory(cache_name):
|
from storage.dconf_registry import Dconf_registry
|
||||||
return sqlite_cache(cache_name)
|
|
||||||
|
|
||||||
def registry_factory(registry_name='registry', registry_dir=None):
|
def registry_factory(registry_name='', envprofile=None , username=None):
|
||||||
return sqlite_registry(registry_name, registry_dir)
|
if username:
|
||||||
|
Dconf_registry._username = username
|
||||||
|
else:
|
||||||
|
Dconf_registry._envprofile = 'system'
|
||||||
|
if envprofile:
|
||||||
|
Dconf_registry._envprofile = envprofile
|
||||||
|
|
||||||
|
if registry_name == 'dconf':
|
||||||
|
return Dconf_registry()
|
||||||
|
else:
|
||||||
|
return Dconf_registry
|
||||||
|
|
||||||
|
|||||||
860
gpoa/storage/dconf_registry.py
Normal file
860
gpoa/storage/dconf_registry.py
Normal file
@@ -0,0 +1,860 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from util.util import (string_to_literal_eval,
|
||||||
|
try_dict_to_literal_eval,
|
||||||
|
touch_file, get_uid_by_username,
|
||||||
|
add_prefix_to_keys,
|
||||||
|
remove_keys_with_prefix,
|
||||||
|
clean_data)
|
||||||
|
from util.paths import get_dconf_config_path
|
||||||
|
from util.logging import log
|
||||||
|
import re
|
||||||
|
from collections import OrderedDict
|
||||||
|
import itertools
|
||||||
|
from gpt.dynamic_attributes import RegistryKeyMetadata
|
||||||
|
import gi
|
||||||
|
gi.require_version("Gvdb", "1.0")
|
||||||
|
gi.require_version("GLib", "2.0")
|
||||||
|
from gi.repository import Gvdb, GLib
|
||||||
|
|
||||||
|
|
||||||
|
class PregDconf():
|
||||||
|
def __init__(self, keyname, valuename, type_preg, data):
|
||||||
|
self.keyname = keyname
|
||||||
|
self.valuename = valuename
|
||||||
|
self.hive_key = '{}/{}'.format(self.keyname, self.valuename)
|
||||||
|
self.type = type_preg
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
|
||||||
|
class gplist(list):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def first(self):
|
||||||
|
if self:
|
||||||
|
return self[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
return len(self)
|
||||||
|
|
||||||
|
class Dconf_registry():
|
||||||
|
'''
|
||||||
|
A class variable that represents a global registry dictionary shared among instances of the class
|
||||||
|
'''
|
||||||
|
_GpoPriority = 'Software/BaseALT/Policies/GpoPriority'
|
||||||
|
_gpo_name = set()
|
||||||
|
global_registry_dict = {_GpoPriority:{}}
|
||||||
|
previous_global_registry_dict = {}
|
||||||
|
__template_file = '/usr/share/dconf/user_mandatory.template'
|
||||||
|
_policies_path = 'Software/'
|
||||||
|
_policies_win_path = 'SOFTWARE/'
|
||||||
|
_gpt_read_flag = False
|
||||||
|
_force = False
|
||||||
|
__dconf_dict_flag = False
|
||||||
|
__dconf_dict = {}
|
||||||
|
_dconf_db = {}
|
||||||
|
_dict_gpo_name_version_cache = {}
|
||||||
|
_username = None
|
||||||
|
_uid = None
|
||||||
|
_envprofile = None
|
||||||
|
_path_bin_system = "/etc/dconf/db/policy"
|
||||||
|
|
||||||
|
list_keys = []
|
||||||
|
_info = {}
|
||||||
|
_counter_gpt = itertools.count(0)
|
||||||
|
|
||||||
|
shortcuts = []
|
||||||
|
folders = []
|
||||||
|
files = []
|
||||||
|
drives = []
|
||||||
|
scheduledtasks = []
|
||||||
|
environmentvariables = []
|
||||||
|
inifiles = []
|
||||||
|
services = []
|
||||||
|
printers = []
|
||||||
|
scripts = []
|
||||||
|
networkshares = []
|
||||||
|
|
||||||
|
_true_strings = {
|
||||||
|
"True",
|
||||||
|
"true",
|
||||||
|
"TRUE",
|
||||||
|
"yes",
|
||||||
|
"Yes",
|
||||||
|
"enabled",
|
||||||
|
"enable",
|
||||||
|
"Enabled",
|
||||||
|
"Enable",
|
||||||
|
'1'
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_info(cls, key , data):
|
||||||
|
cls._info[key] = data
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_info(cls, key):
|
||||||
|
return cls._info.setdefault(key, None)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_next_number():
|
||||||
|
return next(Dconf_registry._counter_gpt)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_matching_keys(path):
|
||||||
|
if path[0] != '/':
|
||||||
|
path = '/' + path
|
||||||
|
logdata = {}
|
||||||
|
envprofile = get_dconf_envprofile()
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen(['dconf', 'list', path],
|
||||||
|
env=envprofile, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
logdata['path'] = path
|
||||||
|
log('D204', logdata)
|
||||||
|
output, error = process.communicate()
|
||||||
|
if not output and not error:
|
||||||
|
return
|
||||||
|
if not error:
|
||||||
|
keys = output.strip().split('\n')
|
||||||
|
for key in keys:
|
||||||
|
Dconf_registry.get_matching_keys(f'{path}{key}')
|
||||||
|
else:
|
||||||
|
Dconf_registry.list_keys.append(path)
|
||||||
|
return Dconf_registry.list_keys
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('E69', logdata)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_key_values(keys):
|
||||||
|
key_values = {}
|
||||||
|
for key in keys:
|
||||||
|
key_values[key] = Dconf_registry.get_key_value(key)
|
||||||
|
return key_values
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_key_value(key):
|
||||||
|
logdata = {}
|
||||||
|
envprofile = get_dconf_envprofile()
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen(['dconf', 'read', key],
|
||||||
|
env=envprofile, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
logdata['key'] = key
|
||||||
|
output, error = process.communicate()
|
||||||
|
|
||||||
|
if not error:
|
||||||
|
return string_to_literal_eval(string_to_literal_eval(output))
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('E70', logdata)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def dconf_update(uid=None):
|
||||||
|
logdata = {}
|
||||||
|
path_dconf_config = get_dconf_config_path(uid)
|
||||||
|
db_file = path_dconf_config[:-3]
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen(['dconf', 'compile', db_file, path_dconf_config],
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
output, error = process.communicate()
|
||||||
|
|
||||||
|
if error:
|
||||||
|
logdata['error'] = error
|
||||||
|
log('E71', logdata)
|
||||||
|
else:
|
||||||
|
logdata['outpupt'] = output
|
||||||
|
log('D206', logdata)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('E72', logdata)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def check_profile_template(cls):
|
||||||
|
if Path(cls.__template_file).exists():
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_dict_to_previous(cls):
|
||||||
|
dict_clean_previous = remove_keys_with_prefix(cls._dconf_db)
|
||||||
|
dict_with_previous = add_prefix_to_keys(dict_clean_previous)
|
||||||
|
cls.global_registry_dict.update(dict_with_previous)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def apply_template(cls, uid):
|
||||||
|
logdata = {}
|
||||||
|
if uid and cls.check_profile_template():
|
||||||
|
with open(cls.__template_file, "r") as f:
|
||||||
|
template = f.read()
|
||||||
|
# Replace the "{uid}" placeholder with the actual UID value
|
||||||
|
content = template.replace("{{uid}}", str(uid))
|
||||||
|
|
||||||
|
elif uid:
|
||||||
|
content = f"user-db:user\n" \
|
||||||
|
f"system-db:distr\n" \
|
||||||
|
f"system-db:policy\n" \
|
||||||
|
f"system-db:policy{uid}\n" \
|
||||||
|
f"system-db:local\n" \
|
||||||
|
f"system-db:default\n" \
|
||||||
|
f"system-db:local\n" \
|
||||||
|
f"system-db:policy{uid}\n" \
|
||||||
|
f"system-db:policy\n" \
|
||||||
|
f"system-db:distr\n"
|
||||||
|
else:
|
||||||
|
logdata['uid'] = uid
|
||||||
|
log('W24', logdata)
|
||||||
|
return
|
||||||
|
|
||||||
|
user_mandatory = f'/run/dconf/user/{uid}'
|
||||||
|
touch_file(user_mandatory)
|
||||||
|
|
||||||
|
with open(user_mandatory, "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_policies_from_dconf(cls):
|
||||||
|
return cls.get_dictionary_from_dconf(cls._policies_path, cls._policies_win_path)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_dictionary_from_dconf(self, *startswith_list):
|
||||||
|
output_dict = {}
|
||||||
|
for startswith in startswith_list:
|
||||||
|
dconf_dict = self.get_key_values(self.get_matching_keys(startswith))
|
||||||
|
for key, value in dconf_dict.items():
|
||||||
|
keys_tmp = key.split('/')
|
||||||
|
update_dict(output_dict.setdefault('/'.join(keys_tmp[:-1])[1:], {}), {keys_tmp[-1]: str(value)})
|
||||||
|
|
||||||
|
log('D207')
|
||||||
|
return output_dict
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_dictionary_from_dconf_file_db(self, uid=None, path_bin=None, save_dconf_db=False):
|
||||||
|
logdata = {}
|
||||||
|
error_skip = None
|
||||||
|
if path_bin:
|
||||||
|
error_skip = True
|
||||||
|
elif not uid:
|
||||||
|
path_bin = self._path_bin_system
|
||||||
|
else:
|
||||||
|
path_bin = self._path_bin_system + str(uid)
|
||||||
|
output_dict = {}
|
||||||
|
try:
|
||||||
|
if (GLib.file_get_contents(path_bin)[0]):
|
||||||
|
bytes1 = GLib.Bytes.new(GLib.file_get_contents(path_bin)[1])
|
||||||
|
table = Gvdb.Table.new_from_bytes(bytes1, True)
|
||||||
|
|
||||||
|
name_list = Gvdb.Table.get_names(table)
|
||||||
|
for name in name_list:
|
||||||
|
value = Gvdb.Table.get_value(table, name)
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
list_path = name.split('/')
|
||||||
|
if value.is_of_type(GLib.VariantType('s')):
|
||||||
|
part = output_dict.setdefault('/'.join(list_path[1:-1]), {})
|
||||||
|
part[list_path[-1]] = value.get_string()
|
||||||
|
elif value.is_of_type(GLib.VariantType('i')):
|
||||||
|
part = output_dict.setdefault('/'.join(list_path[1:-1]), {})
|
||||||
|
part[list_path[-1]] = value.get_int32()
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['exc'] = exc
|
||||||
|
logdata['path_bin'] = path_bin
|
||||||
|
if not error_skip:
|
||||||
|
log('E73', logdata)
|
||||||
|
else:
|
||||||
|
log('D217', logdata)
|
||||||
|
if save_dconf_db:
|
||||||
|
Dconf_registry._dconf_db = output_dict
|
||||||
|
return output_dict
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filter_entries(cls, startswith, registry_dict = None):
|
||||||
|
if not registry_dict:
|
||||||
|
registry_dict = cls.global_registry_dict
|
||||||
|
if startswith[-1] == '%':
|
||||||
|
startswith = startswith[:-1]
|
||||||
|
if startswith[-1] == '/' or startswith[-1] == '\\':
|
||||||
|
startswith = startswith[:-1]
|
||||||
|
return filter_dict_keys(startswith, flatten_dictionary(registry_dict))
|
||||||
|
return filter_dict_keys(startswith, flatten_dictionary(registry_dict))
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filter_hklm_entries(cls, startswith):
|
||||||
|
pregs = cls.filter_entries(startswith)
|
||||||
|
list_entiers = list()
|
||||||
|
for keyname, value in pregs.items():
|
||||||
|
if isinstance(value, dict):
|
||||||
|
for valuename, data in value.items():
|
||||||
|
list_entiers.append(PregDconf(
|
||||||
|
keyname, convert_string_dconf(valuename), find_preg_type(data), data))
|
||||||
|
elif isinstance(value, list):
|
||||||
|
for data in value:
|
||||||
|
list_entiers.append(PregDconf(
|
||||||
|
keyname, data, find_preg_type(data), data))
|
||||||
|
else:
|
||||||
|
list_entiers.append(PregDconf(
|
||||||
|
'/'.join(keyname.split('/')[:-1]), convert_string_dconf(keyname.split('/')[-1]), find_preg_type(value), value))
|
||||||
|
|
||||||
|
|
||||||
|
return gplist(list_entiers)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filter_hkcu_entries(cls, startswith):
|
||||||
|
return cls.filter_hklm_entries(startswith)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_storage(cls,dictionary = None):
|
||||||
|
if dictionary:
|
||||||
|
result = dictionary
|
||||||
|
elif Dconf_registry._gpt_read_flag:
|
||||||
|
result = Dconf_registry.global_registry_dict
|
||||||
|
else:
|
||||||
|
if Dconf_registry.__dconf_dict_flag:
|
||||||
|
result = Dconf_registry.__dconf_dict
|
||||||
|
else:
|
||||||
|
Dconf_registry.__dconf_dict = Dconf_registry.get_policies_from_dconf()
|
||||||
|
result = Dconf_registry.__dconf_dict
|
||||||
|
Dconf_registry.__dconf_dict_flag = True
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filling_storage_from_dconf(cls):
|
||||||
|
Dconf_registry.global_registry_dict = Dconf_registry.get_storage()
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_entry(cls, path, dictionary = None, preg = True):
|
||||||
|
logdata = {}
|
||||||
|
result = Dconf_registry.get_storage(dictionary)
|
||||||
|
|
||||||
|
keys = path.split("\\") if "\\" in path else path.split("/")
|
||||||
|
key = '/'.join(keys[:-1]) if keys[0] else '/'.join(keys[:-1])[1:]
|
||||||
|
|
||||||
|
if isinstance(result, dict) and key in result.keys():
|
||||||
|
data = result.get(key).get(keys[-1])
|
||||||
|
return PregDconf(
|
||||||
|
key, convert_string_dconf(keys[-1]), find_preg_type(data), data) if preg else data
|
||||||
|
else:
|
||||||
|
logdata['path'] = path
|
||||||
|
log('D208', logdata)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def check_enable_key(cls ,key):
|
||||||
|
data = cls.get_entry(key, preg = False)
|
||||||
|
if data:
|
||||||
|
if isinstance(data, str):
|
||||||
|
return True if data in cls._true_strings else False
|
||||||
|
elif isinstance(data, int):
|
||||||
|
return bool(data)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_hkcu_entry(cls, hive_key, dictionary = None):
|
||||||
|
return cls.get_hklm_entry(hive_key, dictionary)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_hklm_entry(cls, hive_key, dictionary = None):
|
||||||
|
return cls.get_entry(hive_key, dictionary)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_shortcut(cls, sc_obj, policy_name):
|
||||||
|
sc_obj.policy_name = policy_name
|
||||||
|
cls.shortcuts.append(sc_obj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_printer(cls, pobj, policy_name):
|
||||||
|
pobj.policy_name = policy_name
|
||||||
|
cls.printers.append(pobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_drive(cls, dobj, policy_name):
|
||||||
|
dobj.policy_name = policy_name
|
||||||
|
cls.drives.append(dobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_folder(cls, fobj, policy_name):
|
||||||
|
fobj.policy_name = policy_name
|
||||||
|
cls.folders.append(fobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_envvar(self, evobj, policy_name):
|
||||||
|
evobj.policy_name = policy_name
|
||||||
|
self.environmentvariables.append(evobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_script(cls, scrobj, policy_name):
|
||||||
|
scrobj.policy_name = policy_name
|
||||||
|
cls.scripts.append(scrobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_file(cls, fileobj, policy_name):
|
||||||
|
fileobj.policy_name = policy_name
|
||||||
|
cls.files.append(fileobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_ini(cls, iniobj, policy_name):
|
||||||
|
iniobj.policy_name = policy_name
|
||||||
|
cls.inifiles.append(iniobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_networkshare(cls, networkshareobj, policy_name):
|
||||||
|
networkshareobj.policy_name = policy_name
|
||||||
|
cls.networkshares.append(networkshareobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_shortcuts(cls):
|
||||||
|
return cls.shortcuts
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_printers(cls):
|
||||||
|
return cls.printers
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_drives(cls):
|
||||||
|
return cls.drives
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_folders(cls):
|
||||||
|
return cls.folders
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_envvars(cls):
|
||||||
|
return cls.environmentvariables
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_scripts(cls, action):
|
||||||
|
action_scripts = list()
|
||||||
|
for part in cls.scripts:
|
||||||
|
if action == 'LOGON' and part.action == 'LOGON':
|
||||||
|
action_scripts.append(part)
|
||||||
|
elif action == 'LOGOFF' and part.action == 'LOGOFF':
|
||||||
|
action_scripts.append(part)
|
||||||
|
elif action == 'STARTUP' and part.action == 'STARTUP':
|
||||||
|
action_scripts.append(part)
|
||||||
|
elif action == 'SHUTDOWN' and part.action == 'SHUTDOWN':
|
||||||
|
action_scripts.append(part)
|
||||||
|
return action_scripts
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_files(cls):
|
||||||
|
return cls.files
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_networkshare(cls):
|
||||||
|
return cls.networkshares
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_ini(cls):
|
||||||
|
return cls.inifiles
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def wipe_user(cls):
|
||||||
|
cls.wipe_hklm()
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def wipe_hklm(cls):
|
||||||
|
cls.global_registry_dict = dict({cls._GpoPriority:{}})
|
||||||
|
|
||||||
|
|
||||||
|
def filter_dict_keys(starting_string, input_dict):
|
||||||
|
result = dict()
|
||||||
|
for key in input_dict:
|
||||||
|
key_list = remove_empty_values(re.split(r'\\|/', key))
|
||||||
|
start_list = remove_empty_values(re.split(r'\\|/', starting_string))
|
||||||
|
if key_list[:len(start_list)] == start_list:
|
||||||
|
result[key] = input_dict.get(key)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def find_preg_type(argument):
|
||||||
|
if isinstance(argument, int):
|
||||||
|
return 4
|
||||||
|
else:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
def update_dict(dict1, dict2, save_key=None):
|
||||||
|
'''
|
||||||
|
Updates dict1 with the key-value pairs from dict2
|
||||||
|
'''
|
||||||
|
for key, value in dict2.items():
|
||||||
|
if key in dict1:
|
||||||
|
# If both values are dictionaries, recursively call the update_dict function
|
||||||
|
if isinstance(dict1[key], dict) and isinstance(value, dict):
|
||||||
|
save_key = key
|
||||||
|
update_dict(dict1[key], value, save_key)
|
||||||
|
# If the value in dict1 is a list, extend it with unique values from value
|
||||||
|
elif isinstance(dict1[key], list):
|
||||||
|
dict1[key].extend(set(value) - set(dict1[key]))
|
||||||
|
else:
|
||||||
|
# If the value in dict1 is not a dictionary or the value in dict2 is not a dictionary,
|
||||||
|
# replace the value in dict1 with the value from dict2
|
||||||
|
if save_key and save_key.startswith('Source'):
|
||||||
|
value.reloaded_with_policy_key = [dict1[key].policy_name]
|
||||||
|
if dict1[key].reloaded_with_policy_key:
|
||||||
|
value.reloaded_with_policy_key += dict1[key].reloaded_with_policy_key
|
||||||
|
dict1[key] = value
|
||||||
|
else:
|
||||||
|
dict1[key] = value
|
||||||
|
else:
|
||||||
|
# If the key does not exist in dict1, add the key-value pair from dict2 to dict1
|
||||||
|
dict1[key] = value
|
||||||
|
|
||||||
|
|
||||||
|
def add_to_dict(string, username, gpo_info):
|
||||||
|
if gpo_info:
|
||||||
|
counter = gpo_info.counter
|
||||||
|
display_name = gpo_info.display_name
|
||||||
|
name = gpo_info.name
|
||||||
|
version = gpo_info.version
|
||||||
|
else:
|
||||||
|
counter = 0
|
||||||
|
display_name = 'Local Policy'
|
||||||
|
name = None
|
||||||
|
version = None
|
||||||
|
|
||||||
|
if username is None or username == 'Machine':
|
||||||
|
machine= '{}/Machine/{}'.format(Dconf_registry._GpoPriority, counter)
|
||||||
|
dictionary = Dconf_registry.global_registry_dict.setdefault(machine, dict())
|
||||||
|
else:
|
||||||
|
if name in Dconf_registry._gpo_name:
|
||||||
|
return
|
||||||
|
user = '{}/User/{}'.format(Dconf_registry._GpoPriority, counter)
|
||||||
|
dictionary = Dconf_registry.global_registry_dict.setdefault(user, dict())
|
||||||
|
Dconf_registry._gpo_name.add(name)
|
||||||
|
|
||||||
|
dictionary['display_name'] = display_name
|
||||||
|
dictionary['name'] = name
|
||||||
|
dictionary['version'] = str(version)
|
||||||
|
dictionary['correct_path'] = string
|
||||||
|
|
||||||
|
def get_mod_previous_value(key_source, key_valuename):
|
||||||
|
previous_sourc = try_dict_to_literal_eval(Dconf_registry._dconf_db
|
||||||
|
.get(key_source, {})
|
||||||
|
.get(key_valuename, {}))
|
||||||
|
return previous_sourc.get('mod_previous_value') if previous_sourc else None
|
||||||
|
|
||||||
|
def get_previous_value(key_source, key_valuename):
|
||||||
|
previous = key_source.replace('Source', 'Previous')
|
||||||
|
return (Dconf_registry._dconf_db
|
||||||
|
.get(previous, {})
|
||||||
|
.get(key_valuename, None))
|
||||||
|
|
||||||
|
def load_preg_dconf(pregfile, pathfile, policy_name, username, gpo_info):
|
||||||
|
'''
|
||||||
|
Loads the configuration from preg registry into a dictionary
|
||||||
|
'''
|
||||||
|
# Prefix for storing key data
|
||||||
|
source_pre = "Source"
|
||||||
|
dd = dict()
|
||||||
|
for i in pregfile.entries:
|
||||||
|
# Skip this entry if the valuename starts with '**del'
|
||||||
|
if i.valuename.lower().startswith('**del'):
|
||||||
|
continue
|
||||||
|
valuename = convert_string_dconf(i.valuename)
|
||||||
|
data = check_data(i.data, i.type)
|
||||||
|
if i.valuename != i.data and i.valuename:
|
||||||
|
key_registry_source = f"{source_pre}/{i.keyname}".replace('\\', '/')
|
||||||
|
key_registry = f"{i.keyname}".replace('\\', '/')
|
||||||
|
key_valuename = valuename.replace('\\', '/')
|
||||||
|
if i.keyname.replace('\\', '/') in dd:
|
||||||
|
# If the key exists in dd, update its value with the new key-value pair
|
||||||
|
dd[i.keyname.replace('\\', '/')].update({key_valuename:data})
|
||||||
|
mod_previous_value = get_mod_previous_value(key_registry_source, key_valuename)
|
||||||
|
previous_value = get_previous_value(key_registry, key_valuename)
|
||||||
|
if previous_value != data:
|
||||||
|
(dd[key_registry_source]
|
||||||
|
.update({key_valuename:RegistryKeyMetadata(policy_name, i.type, mod_previous_value=previous_value)}))
|
||||||
|
else:
|
||||||
|
(dd[key_registry_source]
|
||||||
|
.update({key_valuename:RegistryKeyMetadata(policy_name, i.type, mod_previous_value=mod_previous_value)}))
|
||||||
|
else:
|
||||||
|
# If the key does not exist in dd, create a new key-value pair
|
||||||
|
dd[i.keyname.replace('\\', '/')] = {key_valuename:data}
|
||||||
|
mod_previous_value = get_mod_previous_value(key_registry_source, key_valuename)
|
||||||
|
previous_value = get_previous_value(key_registry, key_valuename)
|
||||||
|
if previous_value != data:
|
||||||
|
dd[key_registry_source] = {key_valuename:RegistryKeyMetadata(policy_name, i.type, mod_previous_value=previous_value)}
|
||||||
|
else:
|
||||||
|
dd[key_registry_source] = {key_valuename:RegistryKeyMetadata(policy_name, i.type, mod_previous_value=mod_previous_value)}
|
||||||
|
|
||||||
|
elif not i.valuename:
|
||||||
|
keyname_tmp = i.keyname.replace('\\', '/').split('/')
|
||||||
|
keyname = '/'.join(keyname_tmp[:-1])
|
||||||
|
mod_previous_value = get_mod_previous_value(f"{source_pre}/{keyname}", keyname_tmp[-1])
|
||||||
|
previous_value = get_previous_value(f"{keyname}", keyname_tmp[-1])
|
||||||
|
if keyname in dd:
|
||||||
|
# If the key exists in dd, update its value with the new key-value pair
|
||||||
|
dd[keyname].update({keyname_tmp[-1]:data})
|
||||||
|
if previous_value != data:
|
||||||
|
dd[f"{source_pre}/{keyname}"].update({keyname_tmp[-1]:RegistryKeyMetadata(policy_name, i.type, mod_previous_value=previous_value)})
|
||||||
|
else:
|
||||||
|
dd[f"{source_pre}/{keyname}"].update({keyname_tmp[-1]:RegistryKeyMetadata(policy_name, i.type, mod_previous_value=mod_previous_value)})
|
||||||
|
else:
|
||||||
|
# If the key does not exist in dd, create a new key-value pair
|
||||||
|
dd[keyname] = {keyname_tmp[-1]:data}
|
||||||
|
if previous_value != data:
|
||||||
|
dd[f"{source_pre}/{keyname}"] = {keyname_tmp[-1]:RegistryKeyMetadata(policy_name, i.type, mod_previous_value=previous_value)}
|
||||||
|
else:
|
||||||
|
dd[f"{source_pre}/{keyname}"] = {keyname_tmp[-1]:RegistryKeyMetadata(policy_name, i.type, mod_previous_value=mod_previous_value)}
|
||||||
|
|
||||||
|
else:
|
||||||
|
# If the value name is the same as the data,
|
||||||
|
# split the keyname and add the data to the appropriate location in dd.
|
||||||
|
all_list_key = i.keyname.split('\\')
|
||||||
|
key_d ='/'.join(all_list_key[:-1])
|
||||||
|
dd_target = dd.setdefault(key_d,{})
|
||||||
|
key_source = f"Source/{key_d}"
|
||||||
|
dd_target_source = dd.setdefault(key_source, {})
|
||||||
|
data_list = dd_target.setdefault(all_list_key[-1], []).append(data)
|
||||||
|
mod_previous_value = get_mod_previous_value(key_source, all_list_key[-1])
|
||||||
|
previous_value = get_previous_value(key_d, all_list_key[-1])
|
||||||
|
if previous_value != str(data_list):
|
||||||
|
dd_target_source[all_list_key[-1]] = RegistryKeyMetadata(policy_name, i.type, is_list=True, mod_previous_value=previous_value)
|
||||||
|
else:
|
||||||
|
dd_target_source[all_list_key[-1]] = RegistryKeyMetadata(policy_name, i.type, is_list=True, mod_previous_value=mod_previous_value)
|
||||||
|
|
||||||
|
# Update the global registry dictionary with the contents of dd
|
||||||
|
update_dict(Dconf_registry.global_registry_dict, dd)
|
||||||
|
|
||||||
|
|
||||||
|
def create_dconf_ini_file(filename, data, uid=None, nodomain=None):
|
||||||
|
'''
|
||||||
|
Create an ini-file based on a dictionary of dictionaries.
|
||||||
|
Args:
|
||||||
|
data (dict): The dictionary of dictionaries containing the data for the ini-file.
|
||||||
|
filename (str): The filename to save the ini-file.
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
Raises:
|
||||||
|
None
|
||||||
|
'''
|
||||||
|
with open(filename, 'a' if nodomain else 'w') as file:
|
||||||
|
for section, section_data in data.items():
|
||||||
|
file.write(f'[{section}]\n')
|
||||||
|
for key, value in section_data.items():
|
||||||
|
if isinstance(value, int):
|
||||||
|
file.write(f'{key} = {value}\n')
|
||||||
|
else:
|
||||||
|
file.write(f'{key} = "{value}"\n')
|
||||||
|
file.write('\n')
|
||||||
|
logdata = {'path': filename}
|
||||||
|
log('D209', logdata)
|
||||||
|
create_dconf_file_locks(filename, data)
|
||||||
|
Dconf_registry.dconf_update(uid)
|
||||||
|
|
||||||
|
|
||||||
|
def create_dconf_file_locks(filename_ini, data):
|
||||||
|
"""
|
||||||
|
Creates a dconf lock file based on the provided filename and data.
|
||||||
|
|
||||||
|
:param filename_ini: Path to the ini file (str)
|
||||||
|
:param data: Dictionary containing configuration data
|
||||||
|
"""
|
||||||
|
# Extract the path parts up to the directory of the ini file
|
||||||
|
tmp_lock = filename_ini.split('/')[:-1]
|
||||||
|
|
||||||
|
# Construct the path to the lock file
|
||||||
|
file_lock = '/'.join(tmp_lock + ['locks', tmp_lock[-1][:-1] + 'pol'])
|
||||||
|
|
||||||
|
# Create an empty lock file
|
||||||
|
touch_file(file_lock)
|
||||||
|
|
||||||
|
# Open the lock file for writing
|
||||||
|
with open(file_lock, 'w') as file:
|
||||||
|
# Iterate over all lock keys obtained from the data
|
||||||
|
for key_lock in get_keys_dconf_locks(data):
|
||||||
|
# Remove the "lock/" prefix from the key and split into parts
|
||||||
|
key = key_lock.split('/')[1:]
|
||||||
|
# Write the cleaned key to the lock file
|
||||||
|
file.write(f'{key}\n')
|
||||||
|
|
||||||
|
def get_keys_dconf_locks(data):
|
||||||
|
"""
|
||||||
|
Extracts keys from the provided data that start with "Locks/"
|
||||||
|
and have a value of 1.
|
||||||
|
|
||||||
|
:param data: Dictionary containing configuration data
|
||||||
|
:return: List of lock keys (str) without the "Locks/" prefix
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
# Flatten the nested dictionary into a single-level dictionary
|
||||||
|
flatten_data = flatten_dictionary(data)
|
||||||
|
|
||||||
|
# Iterate through all keys in the flattened dictionary
|
||||||
|
for key in flatten_data:
|
||||||
|
# Check if the key starts with "Locks/" and its value is 1
|
||||||
|
if key.startswith('Locks/') and flatten_data[key] == 1:
|
||||||
|
# Remove the "Locks" prefix and append to the result
|
||||||
|
result.append(key.removeprefix('Locks'))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def check_data(data, t_data):
|
||||||
|
if isinstance(data, bytes):
|
||||||
|
if t_data == 7:
|
||||||
|
return clean_data(data.decode('utf-16').replace('\x00',''))
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
elif t_data == 4:
|
||||||
|
return data
|
||||||
|
return clean_data(data)
|
||||||
|
|
||||||
|
def convert_string_dconf(input_string):
|
||||||
|
macros = {
|
||||||
|
'#': '%sharp%',
|
||||||
|
';': '%semicolon%',
|
||||||
|
'//': '%doubleslash%',
|
||||||
|
'/': '%oneslash%'
|
||||||
|
}
|
||||||
|
output_string = input_string
|
||||||
|
for key, value in macros.items():
|
||||||
|
if key in input_string:
|
||||||
|
output_string = input_string.replace(key, value)
|
||||||
|
elif value in input_string:
|
||||||
|
output_string = input_string.replace(value, key)
|
||||||
|
|
||||||
|
return output_string
|
||||||
|
|
||||||
|
def remove_empty_values(input_list):
|
||||||
|
return list(filter(None, input_list))
|
||||||
|
|
||||||
|
def flatten_dictionary(input_dict, result=None, current_key=''):
|
||||||
|
if result is None:
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for key, value in input_dict.items():
|
||||||
|
new_key = f"{current_key}/{key}" if current_key else key
|
||||||
|
|
||||||
|
if isinstance(value, dict):
|
||||||
|
flatten_dictionary(value, result, new_key)
|
||||||
|
else:
|
||||||
|
result[new_key] = value
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_dconf_envprofile():
|
||||||
|
dconf_envprofile = {'default': {'DCONF_PROFILE': 'default'},
|
||||||
|
'local': {'DCONF_PROFILE': 'local'},
|
||||||
|
'system': {'DCONF_PROFILE': 'system'}
|
||||||
|
}
|
||||||
|
|
||||||
|
if Dconf_registry._envprofile:
|
||||||
|
return dconf_envprofile.get(Dconf_registry._envprofile, dconf_envprofile['system'])
|
||||||
|
|
||||||
|
if not Dconf_registry._username:
|
||||||
|
return dconf_envprofile['system']
|
||||||
|
|
||||||
|
profile = '/run/dconf/user/{}'.format(get_uid_by_username(Dconf_registry._username))
|
||||||
|
return {'DCONF_PROFILE': profile}
|
||||||
|
|
||||||
|
|
||||||
|
def convert_elements_to_list_dicts(elements):
|
||||||
|
return list(map(lambda x: dict(x), elements))
|
||||||
|
|
||||||
|
def remove_duplicate_dicts_in_list(list_dict):
|
||||||
|
return convert_elements_to_list_dicts(list(OrderedDict((tuple(sorted(d.items())), d) for d in list_dict).values()))
|
||||||
|
|
||||||
|
def add_preferences_to_global_registry_dict(username, is_machine):
|
||||||
|
if is_machine:
|
||||||
|
prefix = 'Software/BaseALT/Policies/Preferences/Machine'
|
||||||
|
else:
|
||||||
|
prefix = f'Software/BaseALT/Policies/Preferences/{username}'
|
||||||
|
|
||||||
|
preferences_global = [('Shortcuts',remove_duplicate_dicts_in_list(Dconf_registry.shortcuts)),
|
||||||
|
('Folders',remove_duplicate_dicts_in_list(Dconf_registry.folders)),
|
||||||
|
('Files',remove_duplicate_dicts_in_list(Dconf_registry.files)),
|
||||||
|
('Drives',remove_duplicate_dicts_in_list(Dconf_registry.drives)),
|
||||||
|
('Scheduledtasks',remove_duplicate_dicts_in_list(Dconf_registry.scheduledtasks)),
|
||||||
|
('Environmentvariables',remove_duplicate_dicts_in_list(Dconf_registry.environmentvariables)),
|
||||||
|
('Inifiles',remove_duplicate_dicts_in_list(Dconf_registry.inifiles)),
|
||||||
|
('Services',remove_duplicate_dicts_in_list(Dconf_registry.services)),
|
||||||
|
('Printers',remove_duplicate_dicts_in_list(Dconf_registry.printers)),
|
||||||
|
('Scripts',remove_duplicate_dicts_in_list(Dconf_registry.scripts)),
|
||||||
|
('Networkshares',remove_duplicate_dicts_in_list(Dconf_registry.networkshares))]
|
||||||
|
|
||||||
|
preferences_global_dict = dict()
|
||||||
|
preferences_global_dict[prefix] = dict()
|
||||||
|
|
||||||
|
for key, val in preferences_global:
|
||||||
|
preferences_global_dict[prefix].update({key:clean_data(str(val))})
|
||||||
|
|
||||||
|
update_dict(Dconf_registry.global_registry_dict, preferences_global_dict)
|
||||||
|
|
||||||
|
def extract_display_name_version(data, username):
|
||||||
|
policy_force = data.get('Software/BaseALT/Policies/GPUpdate', {}).get('Force', False)
|
||||||
|
if Dconf_registry._force or policy_force:
|
||||||
|
logdata = {'username': username}
|
||||||
|
log('W26', logdata)
|
||||||
|
return {}
|
||||||
|
result = {}
|
||||||
|
tmp = {}
|
||||||
|
if isinstance(data, dict):
|
||||||
|
for key in data.keys():
|
||||||
|
if key.startswith(Dconf_registry._GpoPriority+'/'):
|
||||||
|
tmp[key] = data[key]
|
||||||
|
for value in tmp.values():
|
||||||
|
if isinstance(value, dict) and value.get('version', 'None')!='None' and value.get('display_name'):
|
||||||
|
result[value['display_name']] = {'version': value['version'], 'correct_path': value['correct_path']}
|
||||||
|
Dconf_registry._dict_gpo_name_version_cache = result
|
||||||
|
return result
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2021 BaseALT Ltd. <org@basealt.ru>
|
# Copyright (C) 2021-2025 BaseALT Ltd. <org@basealt.ru>
|
||||||
# Copyright (C) 2021 Igor Chudov <nir@nir.org.ru>
|
# Copyright (C) 2021 Igor Chudov <nir@nir.org.ru>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
@@ -27,21 +27,22 @@ import smbc
|
|||||||
from util.logging import log
|
from util.logging import log
|
||||||
from util.paths import file_cache_dir, file_cache_path_home, UNCPath
|
from util.paths import file_cache_dir, file_cache_path_home, UNCPath
|
||||||
from util.exceptions import NotUNCPathError
|
from util.exceptions import NotUNCPathError
|
||||||
|
from util.util import get_machine_name
|
||||||
|
|
||||||
class fs_file_cache:
|
class fs_file_cache:
|
||||||
__read_blocksize = 4096
|
__read_blocksize = 4096
|
||||||
|
|
||||||
def __init__(self, cache_name, username = None):
|
def __init__(self, cache_name, username = None):
|
||||||
self.cache_name = cache_name
|
self.cache_name = cache_name
|
||||||
if username:
|
self.username = username
|
||||||
|
if username and username != get_machine_name():
|
||||||
try:
|
try:
|
||||||
self.storage_uri = file_cache_path_home(username)
|
self.storage_uri = file_cache_path_home(username)
|
||||||
except:
|
except:
|
||||||
self.storage_uri = file_cache_dir()
|
self.storage_uri = file_cache_dir()
|
||||||
else:
|
else:
|
||||||
self.storage_uri = file_cache_dir()
|
self.storage_uri = file_cache_dir()
|
||||||
logdata = dict({'cache_file': self.storage_uri})
|
logdata = {'cache_file': self.storage_uri}
|
||||||
log('D20', logdata)
|
log('D20', logdata)
|
||||||
self.samba_context = smbc.Context(use_kerberos=1)
|
self.samba_context = smbc.Context(use_kerberos=1)
|
||||||
#, debug=10)
|
#, debug=10)
|
||||||
@@ -57,8 +58,11 @@ class fs_file_cache:
|
|||||||
file_path))
|
file_path))
|
||||||
else:
|
else:
|
||||||
destdir = destfile.parent
|
destdir = destfile.parent
|
||||||
|
except NotUNCPathError:
|
||||||
|
return None
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict({'exception': str(exc)})
|
logdata = {'exception': str(exc)}
|
||||||
log('D144', logdata)
|
log('D144', logdata)
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
@@ -82,7 +86,9 @@ class fs_file_cache:
|
|||||||
df.close()
|
df.close()
|
||||||
os.rename(tmpfile, destfile)
|
os.rename(tmpfile, destfile)
|
||||||
os.chmod(destfile, 0o644)
|
os.chmod(destfile, 0o644)
|
||||||
except:
|
except Exception as exc:
|
||||||
|
logdata = {'exception': str(exc)}
|
||||||
|
log('W25', logdata)
|
||||||
tmppath = Path(tmpfile)
|
tmppath = Path(tmpfile)
|
||||||
if tmppath.exists():
|
if tmppath.exists():
|
||||||
tmppath.unlink()
|
tmppath.unlink()
|
||||||
@@ -97,14 +103,16 @@ class fs_file_cache:
|
|||||||
uri_path.get_domain(),
|
uri_path.get_domain(),
|
||||||
uri_path.get_path()))
|
uri_path.get_path()))
|
||||||
except NotUNCPathError as exc:
|
except NotUNCPathError as exc:
|
||||||
logdata = dict({'path': str(exc)})
|
logdata = {'path': str(exc)}
|
||||||
log('D62', logdata)
|
log('D62', logdata)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict({'exception': str(exc)})
|
logdata = {'exception': str(exc)}
|
||||||
log('E36', logdata)
|
log('E36', logdata)
|
||||||
raise exc
|
raise exc
|
||||||
|
if Path(destfile).exists():
|
||||||
return str(destfile)
|
return str(destfile)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def get_ls_smbdir(self, uri):
|
def get_ls_smbdir(self, uri):
|
||||||
type_file_smb = 8
|
type_file_smb = 8
|
||||||
@@ -117,6 +125,6 @@ class fs_file_cache:
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
if Path(uri).exists():
|
if Path(uri).exists():
|
||||||
return None
|
return None
|
||||||
logdata = dict({'exception': str(exc)})
|
logdata = {'exception': str(exc)}
|
||||||
log('W12', logdata)
|
log('W12', logdata)
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -1,309 +0,0 @@
|
|||||||
#
|
|
||||||
# GPOA - GPO Applier for Linux
|
|
||||||
#
|
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
class samba_preg(object):
|
|
||||||
'''
|
|
||||||
Object mapping representing HKLM entry (registry key without SID)
|
|
||||||
'''
|
|
||||||
def __init__(self, preg_obj, policy_name):
|
|
||||||
self.policy_name = policy_name
|
|
||||||
self.keyname = preg_obj.keyname
|
|
||||||
self.valuename = preg_obj.valuename
|
|
||||||
self.hive_key = '{}\\{}'.format(self.keyname, self.valuename)
|
|
||||||
self.type = preg_obj.type
|
|
||||||
self.data = preg_obj.data
|
|
||||||
|
|
||||||
def update_fields(self):
|
|
||||||
fields = dict()
|
|
||||||
fields['policy_name'] = self.policy_name
|
|
||||||
fields['type'] = self.type
|
|
||||||
fields['data'] = self.data
|
|
||||||
|
|
||||||
return fields
|
|
||||||
|
|
||||||
class samba_hkcu_preg(object):
|
|
||||||
'''
|
|
||||||
Object mapping representing HKCU entry (registry key with SID)
|
|
||||||
'''
|
|
||||||
def __init__(self, sid, preg_obj, policy_name):
|
|
||||||
self.sid = sid
|
|
||||||
self.policy_name = policy_name
|
|
||||||
self.keyname = preg_obj.keyname
|
|
||||||
self.valuename = preg_obj.valuename
|
|
||||||
self.hive_key = '{}\\{}'.format(self.keyname, self.valuename)
|
|
||||||
self.type = preg_obj.type
|
|
||||||
self.data = preg_obj.data
|
|
||||||
|
|
||||||
def update_fields(self):
|
|
||||||
fields = dict()
|
|
||||||
fields['policy_name'] = self.policy_name
|
|
||||||
fields['type'] = self.type
|
|
||||||
fields['data'] = self.data
|
|
||||||
|
|
||||||
return fields
|
|
||||||
|
|
||||||
class ad_shortcut(object):
|
|
||||||
'''
|
|
||||||
Object mapping representing Windows shortcut.
|
|
||||||
'''
|
|
||||||
def __init__(self, sid, sc, policy_name):
|
|
||||||
self.sid = sid
|
|
||||||
self.policy_name = policy_name
|
|
||||||
self.path = sc.dest
|
|
||||||
self.shortcut = sc.to_json()
|
|
||||||
|
|
||||||
def update_fields(self):
|
|
||||||
fields = dict()
|
|
||||||
fields['policy_name'] = self.policy_name
|
|
||||||
fields['path'] = self.path
|
|
||||||
fields['shortcut'] = self.shortcut
|
|
||||||
|
|
||||||
return fields
|
|
||||||
|
|
||||||
class info_entry(object):
|
|
||||||
def __init__(self, name, value):
|
|
||||||
self.name = name
|
|
||||||
self.value = value
|
|
||||||
|
|
||||||
def update_fields(self):
|
|
||||||
fields = dict()
|
|
||||||
fields['value'] = self.value
|
|
||||||
|
|
||||||
return fields
|
|
||||||
|
|
||||||
class printer_entry(object):
|
|
||||||
'''
|
|
||||||
Object mapping representing Windows printer of some type.
|
|
||||||
'''
|
|
||||||
def __init__(self, sid, pobj, policy_name):
|
|
||||||
self.sid = sid
|
|
||||||
self.policy_name = policy_name
|
|
||||||
self.name = pobj.name
|
|
||||||
self.printer = pobj.to_json()
|
|
||||||
|
|
||||||
def update_fields(self):
|
|
||||||
fields = dict()
|
|
||||||
fields['policy_name'] = self.policy_name
|
|
||||||
fields['name'] = self.name
|
|
||||||
fields['printer'] = self.printer.to_json()
|
|
||||||
|
|
||||||
return fields
|
|
||||||
|
|
||||||
class drive_entry(object):
|
|
||||||
'''
|
|
||||||
Object mapping representing Samba share bound to drive letter
|
|
||||||
'''
|
|
||||||
def __init__(self, sid, dobj, policy_name):
|
|
||||||
self.sid = sid
|
|
||||||
self.policy_name = policy_name
|
|
||||||
self.login = dobj.login
|
|
||||||
self.password = dobj.password
|
|
||||||
self.dir = dobj.dir
|
|
||||||
self.path = dobj.path
|
|
||||||
self.action = dobj.action
|
|
||||||
self.thisDrive = dobj.thisDrive
|
|
||||||
self.allDrives = dobj.allDrives
|
|
||||||
self.label = dobj.label
|
|
||||||
self.persistent = dobj.persistent
|
|
||||||
self.useLetter = dobj.useLetter
|
|
||||||
|
|
||||||
|
|
||||||
def update_fields(self):
|
|
||||||
fields = dict()
|
|
||||||
fields['policy_name'] = self.policy_name
|
|
||||||
fields['login'] = self.login
|
|
||||||
fields['password'] = self.password
|
|
||||||
fields['dir'] = self.dir
|
|
||||||
fields['path'] = self.path
|
|
||||||
fields['action'] = self.action
|
|
||||||
fields['thisDrive'] = self.thisDrive
|
|
||||||
fields['allDrives'] = self.allDrives
|
|
||||||
fields['label'] = self.label
|
|
||||||
fields['persistent'] = self.persistent
|
|
||||||
fields['useLetter'] = self.useLetter
|
|
||||||
|
|
||||||
return fields
|
|
||||||
|
|
||||||
class folder_entry(object):
|
|
||||||
'''
|
|
||||||
Object mapping representing file system directory
|
|
||||||
'''
|
|
||||||
def __init__(self, sid, fobj, policy_name):
|
|
||||||
self.sid = sid
|
|
||||||
self.policy_name = policy_name
|
|
||||||
self.path = fobj.path
|
|
||||||
self.action = fobj.action.value
|
|
||||||
self.delete_folder = str(fobj.delete_folder)
|
|
||||||
self.delete_sub_folders = str(fobj.delete_sub_folders)
|
|
||||||
self.delete_files = str(fobj.delete_files)
|
|
||||||
self.hidden_folder = str(fobj.hidden_folder)
|
|
||||||
|
|
||||||
def update_fields(self):
|
|
||||||
'''
|
|
||||||
Return list of fields to update
|
|
||||||
'''
|
|
||||||
fields = dict()
|
|
||||||
fields['policy_name'] = self.policy_name
|
|
||||||
fields['action'] = self.action
|
|
||||||
fields['delete_folder'] = self.delete_folder
|
|
||||||
fields['delete_sub_folders'] = self.delete_sub_folders
|
|
||||||
fields['delete_files'] = self.delete_files
|
|
||||||
fields['hidden_folder'] = self.hidden_folder
|
|
||||||
|
|
||||||
|
|
||||||
return fields
|
|
||||||
|
|
||||||
class envvar_entry(object):
|
|
||||||
'''
|
|
||||||
Object mapping representing environment variables
|
|
||||||
'''
|
|
||||||
def __init__(self, sid, evobj, policy_name):
|
|
||||||
self.sid = sid
|
|
||||||
self.policy_name = policy_name
|
|
||||||
self.name = evobj.name
|
|
||||||
self.value = evobj.value
|
|
||||||
self.action = evobj.action.value
|
|
||||||
|
|
||||||
def update_fields(self):
|
|
||||||
'''
|
|
||||||
Return list of fields to update
|
|
||||||
'''
|
|
||||||
fields = dict()
|
|
||||||
fields['policy_name'] = self.policy_name
|
|
||||||
fields['action'] = self.action
|
|
||||||
fields['value'] = self.value
|
|
||||||
|
|
||||||
return fields
|
|
||||||
|
|
||||||
class script_entry(object):
|
|
||||||
'''
|
|
||||||
Object mapping representing scripts.ini
|
|
||||||
'''
|
|
||||||
def __init__(self, sid, scrobj, policy_name):
|
|
||||||
self.sid = sid
|
|
||||||
self.policy_name = policy_name
|
|
||||||
self.action = scrobj.action
|
|
||||||
self.number = scrobj.number
|
|
||||||
self.path = scrobj.path
|
|
||||||
self.arg = scrobj.args
|
|
||||||
|
|
||||||
def update_fields(self):
|
|
||||||
'''
|
|
||||||
Return list of fields to update
|
|
||||||
'''
|
|
||||||
fields = dict()
|
|
||||||
fields['policy_name'] = self.policy_name
|
|
||||||
fields['action'] = self.action
|
|
||||||
fields['number'] = self.number
|
|
||||||
fields['path'] = self.path
|
|
||||||
fields['arg'] = self.arg
|
|
||||||
|
|
||||||
return fields
|
|
||||||
|
|
||||||
class file_entry(object):
|
|
||||||
'''
|
|
||||||
Object mapping representing FILES.XML
|
|
||||||
'''
|
|
||||||
def __init__(self, sid, fileobj, policy_name):
|
|
||||||
self.sid = sid
|
|
||||||
self.policy_name = policy_name
|
|
||||||
self.action = fileobj.action
|
|
||||||
self.fromPath = fileobj.fromPath
|
|
||||||
self.targetPath = fileobj.targetPath
|
|
||||||
self.readOnly = fileobj.readOnly
|
|
||||||
self.archive = fileobj.archive
|
|
||||||
self.hidden = fileobj.hidden
|
|
||||||
self.suppress = fileobj.suppress
|
|
||||||
self.executable = fileobj.executable
|
|
||||||
|
|
||||||
def update_fields(self):
|
|
||||||
'''
|
|
||||||
Return list of fields to update
|
|
||||||
'''
|
|
||||||
fields = dict()
|
|
||||||
fields['policy_name'] = self.policy_name
|
|
||||||
fields['action'] = self.action
|
|
||||||
fields['fromPath'] = self.fromPath
|
|
||||||
fields['targetPath'] = self.targetPath
|
|
||||||
fields['readOnly'] = self.readOnly
|
|
||||||
fields['archive'] = self.archive
|
|
||||||
fields['hidden'] = self.hidden
|
|
||||||
fields['suppress'] = self.suppress
|
|
||||||
fields['executable'] = self.executable
|
|
||||||
|
|
||||||
return fields
|
|
||||||
|
|
||||||
class ini_entry(object):
|
|
||||||
'''
|
|
||||||
Object mapping representing INIFILES.XML
|
|
||||||
'''
|
|
||||||
def __init__(self, sid, iniobj, policy_name):
|
|
||||||
self.sid = sid
|
|
||||||
self.policy_name = policy_name
|
|
||||||
self.action = iniobj.action
|
|
||||||
self.path = iniobj.path
|
|
||||||
self.section = iniobj.section
|
|
||||||
self.property = iniobj.property
|
|
||||||
self.value = iniobj.value
|
|
||||||
|
|
||||||
|
|
||||||
def update_fields(self):
|
|
||||||
'''
|
|
||||||
Return list of fields to update
|
|
||||||
'''
|
|
||||||
fields = dict()
|
|
||||||
fields['policy_name'] = self.policy_name
|
|
||||||
fields['action'] = self.action
|
|
||||||
fields['path'] = self.path
|
|
||||||
fields['section'] = self.section
|
|
||||||
fields['property'] = self.property
|
|
||||||
fields['value'] = self.value
|
|
||||||
|
|
||||||
return fields
|
|
||||||
|
|
||||||
class networkshare_entry(object):
|
|
||||||
'''
|
|
||||||
Object mapping representing NETWORKSHARES.XML
|
|
||||||
'''
|
|
||||||
def __init__(self, sid, networkshareobj, policy_name):
|
|
||||||
self.sid = sid
|
|
||||||
self.policy_name = policy_name
|
|
||||||
self.name = networkshareobj.name
|
|
||||||
self.action = networkshareobj.action
|
|
||||||
self.path = networkshareobj.path
|
|
||||||
self.allRegular = networkshareobj.allRegular
|
|
||||||
self.comment = networkshareobj.comment
|
|
||||||
self.limitUsers = networkshareobj.limitUsers
|
|
||||||
self.abe = networkshareobj.abe
|
|
||||||
|
|
||||||
|
|
||||||
def update_fields(self):
|
|
||||||
'''
|
|
||||||
Return list of fields to update
|
|
||||||
'''
|
|
||||||
fields = dict()
|
|
||||||
fields['policy_name'] = self.policy_name
|
|
||||||
fields['name'] = self.name
|
|
||||||
fields['action'] = self.action
|
|
||||||
fields['path'] = self.path
|
|
||||||
fields['allRegular'] = self.allRegular
|
|
||||||
fields['comment'] = self.comment
|
|
||||||
fields['limitUsers'] = self.limitUsers
|
|
||||||
fields['abe'] = self.abe
|
|
||||||
|
|
||||||
return fields
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
#
|
|
||||||
# GPOA - GPO Applier for Linux
|
|
||||||
#
|
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from .cache import cache
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from sqlalchemy import (
|
|
||||||
create_engine,
|
|
||||||
Table,
|
|
||||||
Column,
|
|
||||||
Integer,
|
|
||||||
String,
|
|
||||||
)
|
|
||||||
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
from .sqlite_registry_compat import sqlite_registry_compat
|
|
||||||
|
|
||||||
from util.logging import log
|
|
||||||
from util.paths import cache_dir
|
|
||||||
|
|
||||||
def mapping_factory(mapper_suffix):
|
|
||||||
exec(
|
|
||||||
'''
|
|
||||||
class mapped_id_{}(object):
|
|
||||||
def __init__(self, str_id, value):
|
|
||||||
self.str_id = str_id
|
|
||||||
self.value = str(value)
|
|
||||||
'''.format(mapper_suffix)
|
|
||||||
)
|
|
||||||
return eval('mapped_id_{}'.format(mapper_suffix))
|
|
||||||
|
|
||||||
class sqlite_cache(cache):
|
|
||||||
def __init__(self, cache_name):
|
|
||||||
self.cache_name = cache_name
|
|
||||||
self.mapper_obj = mapping_factory(self.cache_name)
|
|
||||||
self.storage_uri = os.path.join('sqlite:///{}/{}.sqlite'.format(cache_dir(), self.cache_name))
|
|
||||||
logdata = dict({'cache_file': self.storage_uri})
|
|
||||||
log('D20', logdata)
|
|
||||||
self.db_cnt = create_engine(self.storage_uri, echo=False)
|
|
||||||
self.__compat = sqlite_registry_compat(self.db_cnt)
|
|
||||||
self.__metadata = self.__compat.metadata()
|
|
||||||
self.cache_table = Table(
|
|
||||||
self.cache_name,
|
|
||||||
self.__metadata,
|
|
||||||
Column('id', Integer, primary_key=True),
|
|
||||||
Column('str_id', String(65536), unique=True),
|
|
||||||
Column('value', String)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.__metadata.create_all(self.db_cnt)
|
|
||||||
Session = sessionmaker(bind=self.db_cnt)
|
|
||||||
self.db_session = Session()
|
|
||||||
mapper_reg = self.__compat
|
|
||||||
mapper_reg.map_imperatively(self.mapper_obj, self.cache_table)
|
|
||||||
|
|
||||||
def store(self, str_id, value):
|
|
||||||
obj = self.mapper_obj(str_id, value)
|
|
||||||
self._upsert(obj)
|
|
||||||
|
|
||||||
def get(self, obj_id):
|
|
||||||
result = self.db_session.query(self.mapper_obj).filter(self.mapper_obj.str_id == obj_id).first()
|
|
||||||
return result
|
|
||||||
|
|
||||||
def get_default(self, obj_id, default_value):
|
|
||||||
result = self.get(obj_id)
|
|
||||||
if result == None:
|
|
||||||
logdata = dict()
|
|
||||||
logdata['object'] = obj_id
|
|
||||||
log('D43', logdata)
|
|
||||||
self.store(obj_id, default_value)
|
|
||||||
return str(default_value)
|
|
||||||
return result.value
|
|
||||||
|
|
||||||
def _upsert(self, obj):
|
|
||||||
try:
|
|
||||||
self.db_session.add(obj)
|
|
||||||
self.db_session.commit()
|
|
||||||
except Exception as exc:
|
|
||||||
self.db_session.rollback()
|
|
||||||
logdata = dict()
|
|
||||||
logdata['msg'] = str(exc)
|
|
||||||
log('D44', logdata)
|
|
||||||
self.db_session.query(self.mapper_obj).filter(self.mapper_obj.str_id == obj.str_id).update({ 'value': obj.value })
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
@@ -1,621 +0,0 @@
|
|||||||
#
|
|
||||||
# GPOA - GPO Applier for Linux
|
|
||||||
#
|
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from sqlalchemy import (
|
|
||||||
create_engine,
|
|
||||||
Table,
|
|
||||||
Column,
|
|
||||||
Integer,
|
|
||||||
String,
|
|
||||||
UniqueConstraint
|
|
||||||
)
|
|
||||||
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
from .sqlite_registry_compat import sqlite_registry_compat
|
|
||||||
|
|
||||||
from util.logging import log
|
|
||||||
from util.paths import cache_dir
|
|
||||||
from .registry import registry
|
|
||||||
from .record_types import (
|
|
||||||
samba_preg
|
|
||||||
, samba_hkcu_preg
|
|
||||||
, ad_shortcut
|
|
||||||
, info_entry
|
|
||||||
, printer_entry
|
|
||||||
, drive_entry
|
|
||||||
, folder_entry
|
|
||||||
, envvar_entry
|
|
||||||
, script_entry
|
|
||||||
, file_entry
|
|
||||||
, ini_entry
|
|
||||||
, networkshare_entry
|
|
||||||
)
|
|
||||||
|
|
||||||
class sqlite_registry(registry):
|
|
||||||
def __init__(self, db_name, registry_cache_dir=None):
|
|
||||||
self.db_name = db_name
|
|
||||||
cdir = registry_cache_dir
|
|
||||||
if cdir == None:
|
|
||||||
cdir = cache_dir()
|
|
||||||
self.db_path = os.path.join('sqlite:///{}/{}.sqlite'.format(cdir, self.db_name))
|
|
||||||
self.db_cnt = create_engine(self.db_path, echo=False)
|
|
||||||
self.__compat = sqlite_registry_compat(self.db_cnt)
|
|
||||||
self.__metadata = self.__compat.metadata()
|
|
||||||
self.__info = Table(
|
|
||||||
'info',
|
|
||||||
self.__metadata,
|
|
||||||
Column('id', Integer, primary_key=True),
|
|
||||||
Column('name', String(65536), unique=True),
|
|
||||||
Column('value', String(65536))
|
|
||||||
)
|
|
||||||
self.__hklm = Table(
|
|
||||||
'HKLM'
|
|
||||||
, self.__metadata
|
|
||||||
, Column('id', Integer, primary_key=True)
|
|
||||||
, Column('hive_key', String(65536, collation='NOCASE'),
|
|
||||||
unique=True)
|
|
||||||
, Column('keyname', String(collation='NOCASE'))
|
|
||||||
, Column('valuename', String(collation='NOCASE'))
|
|
||||||
, Column('policy_name', String)
|
|
||||||
, Column('type', Integer)
|
|
||||||
, Column('data', String)
|
|
||||||
)
|
|
||||||
self.__hkcu = Table(
|
|
||||||
'HKCU'
|
|
||||||
, self.__metadata
|
|
||||||
, Column('id', Integer, primary_key=True)
|
|
||||||
, Column('sid', String)
|
|
||||||
, Column('hive_key', String(65536, collation='NOCASE'))
|
|
||||||
, Column('keyname', String(collation='NOCASE'))
|
|
||||||
, Column('valuename', String(collation='NOCASE'))
|
|
||||||
, Column('policy_name', String)
|
|
||||||
, Column('type', Integer)
|
|
||||||
, Column('data', String)
|
|
||||||
, UniqueConstraint('sid', 'hive_key')
|
|
||||||
)
|
|
||||||
self.__shortcuts = Table(
|
|
||||||
'Shortcuts'
|
|
||||||
, self.__metadata
|
|
||||||
, Column('id', Integer, primary_key=True)
|
|
||||||
, Column('sid', String)
|
|
||||||
, Column('path', String)
|
|
||||||
, Column('policy_name', String)
|
|
||||||
, Column('shortcut', String)
|
|
||||||
, UniqueConstraint('sid', 'path')
|
|
||||||
)
|
|
||||||
self.__printers = Table(
|
|
||||||
'Printers'
|
|
||||||
, self.__metadata
|
|
||||||
, Column('id', Integer, primary_key=True)
|
|
||||||
, Column('sid', String)
|
|
||||||
, Column('name', String)
|
|
||||||
, Column('policy_name', String)
|
|
||||||
, Column('printer', String)
|
|
||||||
, UniqueConstraint('sid', 'name')
|
|
||||||
)
|
|
||||||
self.__drives = Table(
|
|
||||||
'Drives'
|
|
||||||
, self.__metadata
|
|
||||||
, Column('id', Integer, primary_key=True)
|
|
||||||
, Column('sid', String)
|
|
||||||
, Column('login', String)
|
|
||||||
, Column('password', String)
|
|
||||||
, Column('dir', String)
|
|
||||||
, Column('policy_name', String)
|
|
||||||
, Column('path', String)
|
|
||||||
, Column('action', String)
|
|
||||||
, Column('thisDrive', String)
|
|
||||||
, Column('allDrives', String)
|
|
||||||
, Column('label', String)
|
|
||||||
, Column('persistent', String)
|
|
||||||
, Column('useLetter', String)
|
|
||||||
, UniqueConstraint('sid', 'dir')
|
|
||||||
)
|
|
||||||
self.__folders = Table(
|
|
||||||
'Folders'
|
|
||||||
, self.__metadata
|
|
||||||
, Column('id', Integer, primary_key=True)
|
|
||||||
, Column('sid', String)
|
|
||||||
, Column('path', String)
|
|
||||||
, Column('policy_name', String)
|
|
||||||
, Column('action', String)
|
|
||||||
, Column('delete_folder', String)
|
|
||||||
, Column('delete_sub_folders', String)
|
|
||||||
, Column('delete_files', String)
|
|
||||||
, Column('hidden_folder', String)
|
|
||||||
, UniqueConstraint('sid', 'path')
|
|
||||||
)
|
|
||||||
self.__envvars = Table(
|
|
||||||
'Envvars'
|
|
||||||
, self.__metadata
|
|
||||||
, Column('id', Integer, primary_key=True)
|
|
||||||
, Column('sid', String)
|
|
||||||
, Column('name', String)
|
|
||||||
, Column('policy_name', String)
|
|
||||||
, Column('action', String)
|
|
||||||
, Column('value', String)
|
|
||||||
, UniqueConstraint('sid', 'name')
|
|
||||||
)
|
|
||||||
self.__scripts = Table(
|
|
||||||
'Scripts'
|
|
||||||
, self.__metadata
|
|
||||||
, Column('id', Integer, primary_key=True)
|
|
||||||
, Column('sid', String)
|
|
||||||
, Column('policy_name', String)
|
|
||||||
, Column('number', String)
|
|
||||||
, Column('action', String)
|
|
||||||
, Column('path', String)
|
|
||||||
, Column('arg', String)
|
|
||||||
, UniqueConstraint('sid', 'path', 'arg')
|
|
||||||
)
|
|
||||||
self.__files = Table(
|
|
||||||
'Files'
|
|
||||||
, self.__metadata
|
|
||||||
, Column('id', Integer, primary_key=True)
|
|
||||||
, Column('sid', String)
|
|
||||||
, Column('policy_name', String)
|
|
||||||
, Column('action', String)
|
|
||||||
, Column('fromPath', String)
|
|
||||||
, Column('targetPath', String)
|
|
||||||
, Column('readOnly', String)
|
|
||||||
, Column('archive', String)
|
|
||||||
, Column('hidden', String)
|
|
||||||
, Column('suppress', String)
|
|
||||||
, Column('executable', String)
|
|
||||||
, UniqueConstraint('sid', 'policy_name', 'targetPath', 'fromPath')
|
|
||||||
)
|
|
||||||
self.__ini = Table(
|
|
||||||
'Ini'
|
|
||||||
, self.__metadata
|
|
||||||
, Column('id', Integer, primary_key=True)
|
|
||||||
, Column('sid', String)
|
|
||||||
, Column('policy_name', String)
|
|
||||||
, Column('action', String)
|
|
||||||
, Column('path', String)
|
|
||||||
, Column('section', String)
|
|
||||||
, Column('property', String)
|
|
||||||
, Column('value', String)
|
|
||||||
, UniqueConstraint('sid', 'action', 'path', 'section', 'property', 'value')
|
|
||||||
)
|
|
||||||
self.__networkshare = Table(
|
|
||||||
'Networkshare'
|
|
||||||
, self.__metadata
|
|
||||||
, Column('id', Integer, primary_key=True)
|
|
||||||
, Column('sid', String)
|
|
||||||
, Column('policy_name', String)
|
|
||||||
, Column('name', String)
|
|
||||||
, Column('action', String)
|
|
||||||
, Column('path', String)
|
|
||||||
, Column('allRegular', String)
|
|
||||||
, Column('comment', String)
|
|
||||||
, Column('limitUsers', String)
|
|
||||||
, Column('abe', String)
|
|
||||||
, UniqueConstraint('sid', 'name', 'path')
|
|
||||||
)
|
|
||||||
|
|
||||||
self.__metadata.create_all(self.db_cnt)
|
|
||||||
Session = sessionmaker(bind=self.db_cnt)
|
|
||||||
self.db_session = Session()
|
|
||||||
mapper_reg = self.__compat
|
|
||||||
try:
|
|
||||||
mapper_reg.map_imperatively(info_entry, self.__info)
|
|
||||||
mapper_reg.map_imperatively(samba_preg, self.__hklm)
|
|
||||||
mapper_reg.map_imperatively(samba_hkcu_preg, self.__hkcu)
|
|
||||||
mapper_reg.map_imperatively(ad_shortcut, self.__shortcuts)
|
|
||||||
mapper_reg.map_imperatively(printer_entry, self.__printers)
|
|
||||||
mapper_reg.map_imperatively(drive_entry, self.__drives)
|
|
||||||
mapper_reg.map_imperatively(folder_entry, self.__folders)
|
|
||||||
mapper_reg.map_imperatively(envvar_entry, self.__envvars)
|
|
||||||
mapper_reg.map_imperatively(script_entry, self.__scripts)
|
|
||||||
mapper_reg.map_imperatively(file_entry, self.__files)
|
|
||||||
mapper_reg.map_imperatively(ini_entry, self.__ini)
|
|
||||||
mapper_reg.map_imperatively(networkshare_entry, self.__networkshare)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
#logging.error('Error creating mapper')
|
|
||||||
|
|
||||||
def _add(self, row):
|
|
||||||
try:
|
|
||||||
self.db_session.add(row)
|
|
||||||
self.db_session.commit()
|
|
||||||
except Exception as exc:
|
|
||||||
self.db_session.rollback()
|
|
||||||
raise exc
|
|
||||||
|
|
||||||
def _info_upsert(self, row):
|
|
||||||
try:
|
|
||||||
self._add(row)
|
|
||||||
except:
|
|
||||||
(self
|
|
||||||
.db_session.query(info_entry)
|
|
||||||
.filter(info_entry.name == row.name)
|
|
||||||
.update(row.update_fields()))
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
def _hklm_upsert(self, row):
|
|
||||||
try:
|
|
||||||
self._add(row)
|
|
||||||
except:
|
|
||||||
(self
|
|
||||||
.db_session
|
|
||||||
.query(samba_preg)
|
|
||||||
.filter(samba_preg.hive_key == row.hive_key)
|
|
||||||
.update(row.update_fields()))
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
def _hkcu_upsert(self, row):
|
|
||||||
try:
|
|
||||||
self._add(row)
|
|
||||||
except Exception as exc:
|
|
||||||
(self
|
|
||||||
.db_session
|
|
||||||
.query(samba_hkcu_preg)
|
|
||||||
.filter(samba_hkcu_preg.sid == row.sid)
|
|
||||||
.filter(samba_hkcu_preg.hive_key == row.hive_key)
|
|
||||||
.update(row.update_fields()))
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
def _shortcut_upsert(self, row):
|
|
||||||
try:
|
|
||||||
self._add(row)
|
|
||||||
except:
|
|
||||||
(self
|
|
||||||
.db_session
|
|
||||||
.query(ad_shortcut)
|
|
||||||
.filter(ad_shortcut.sid == row.sid)
|
|
||||||
.filter(ad_shortcut.path == row.path)
|
|
||||||
.update(row.update_fields()))
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
def _printer_upsert(self, row):
|
|
||||||
try:
|
|
||||||
self._add(row)
|
|
||||||
except:
|
|
||||||
(self
|
|
||||||
.db_session
|
|
||||||
.query(printer_entry)
|
|
||||||
.filter(printer_entry.sid == row.sid)
|
|
||||||
.filter(printer_entry.name == row.name)
|
|
||||||
.update(row.update_fields()))
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
def _drive_upsert(self, row):
|
|
||||||
try:
|
|
||||||
self._add(row)
|
|
||||||
except:
|
|
||||||
(self
|
|
||||||
.db_session
|
|
||||||
.query(drive_entry)
|
|
||||||
.filter(drive_entry.sid == row.sid)
|
|
||||||
.filter(drive_entry.dir == row.dir)
|
|
||||||
.update(row.update_fields()))
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
def set_info(self, name, value):
|
|
||||||
ientry = info_entry(name, value)
|
|
||||||
logdata = dict()
|
|
||||||
logdata['varname'] = name
|
|
||||||
logdata['value'] = value
|
|
||||||
log('D19', logdata)
|
|
||||||
self._info_upsert(ientry)
|
|
||||||
|
|
||||||
def _delete_hklm_keyname(self, keyname):
|
|
||||||
'''
|
|
||||||
Delete PReg hive_key from HKEY_LOCAL_MACHINE
|
|
||||||
'''
|
|
||||||
logdata = dict({'keyname': keyname})
|
|
||||||
try:
|
|
||||||
(self
|
|
||||||
.db_session
|
|
||||||
.query(samba_preg)
|
|
||||||
.filter(samba_preg.keyname == keyname)
|
|
||||||
.delete(synchronize_session=False))
|
|
||||||
self.db_session.commit()
|
|
||||||
log('D65', logdata)
|
|
||||||
except Exception as exc:
|
|
||||||
log('D63', logdata)
|
|
||||||
|
|
||||||
def add_hklm_entry(self, preg_entry, policy_name):
|
|
||||||
'''
|
|
||||||
Write PReg entry to HKEY_LOCAL_MACHINE
|
|
||||||
'''
|
|
||||||
pentry = samba_preg(preg_entry, policy_name)
|
|
||||||
if not pentry.valuename.startswith('**'):
|
|
||||||
self._hklm_upsert(pentry)
|
|
||||||
else:
|
|
||||||
logdata = dict({'key': pentry.hive_key})
|
|
||||||
if pentry.valuename.lower() == '**delvals.':
|
|
||||||
self._delete_hklm_keyname(pentry.keyname)
|
|
||||||
else:
|
|
||||||
log('D27', logdata)
|
|
||||||
|
|
||||||
def _delete_hkcu_keyname(self, keyname, sid):
|
|
||||||
'''
|
|
||||||
Delete PReg hive_key from HKEY_CURRENT_USER
|
|
||||||
'''
|
|
||||||
logdata = dict({'sid': sid, 'keyname': keyname})
|
|
||||||
try:
|
|
||||||
(self
|
|
||||||
.db_session
|
|
||||||
.query(samba_hkcu_preg)
|
|
||||||
.filter(samba_hkcu_preg.sid == sid)
|
|
||||||
.filter(samba_hkcu_preg.keyname == keyname)
|
|
||||||
.delete(synchronize_session=False))
|
|
||||||
self.db_session.commit()
|
|
||||||
log('D66', logdata)
|
|
||||||
except:
|
|
||||||
log('D64', logdata)
|
|
||||||
|
|
||||||
def add_hkcu_entry(self, preg_entry, sid, policy_name):
|
|
||||||
'''
|
|
||||||
Write PReg entry to HKEY_CURRENT_USER
|
|
||||||
'''
|
|
||||||
hkcu_pentry = samba_hkcu_preg(sid, preg_entry, policy_name)
|
|
||||||
logdata = dict({'sid': sid, 'policy': policy_name, 'key': hkcu_pentry.hive_key})
|
|
||||||
if not hkcu_pentry.valuename.startswith('**'):
|
|
||||||
log('D26', logdata)
|
|
||||||
self._hkcu_upsert(hkcu_pentry)
|
|
||||||
else:
|
|
||||||
if hkcu_pentry.valuename.lower() == '**delvals.':
|
|
||||||
self._delete_hkcu_keyname(hkcu_pentry.keyname, sid)
|
|
||||||
else:
|
|
||||||
log('D51', logdata)
|
|
||||||
|
|
||||||
def add_shortcut(self, sid, sc_obj, policy_name):
|
|
||||||
'''
|
|
||||||
Store shortcut information in the database
|
|
||||||
'''
|
|
||||||
sc_entry = ad_shortcut(sid, sc_obj, policy_name)
|
|
||||||
logdata = dict()
|
|
||||||
logdata['link'] = sc_entry.path
|
|
||||||
logdata['sid'] = sid
|
|
||||||
log('D41', logdata)
|
|
||||||
self._shortcut_upsert(sc_entry)
|
|
||||||
|
|
||||||
def add_printer(self, sid, pobj, policy_name):
|
|
||||||
'''
|
|
||||||
Store printer configuration in the database
|
|
||||||
'''
|
|
||||||
prn_entry = printer_entry(sid, pobj, policy_name)
|
|
||||||
logdata = dict()
|
|
||||||
logdata['printer'] = prn_entry.name
|
|
||||||
logdata['sid'] = sid
|
|
||||||
log('D40', logdata)
|
|
||||||
self._printer_upsert(prn_entry)
|
|
||||||
|
|
||||||
def add_drive(self, sid, dobj, policy_name):
|
|
||||||
drv_entry = drive_entry(sid, dobj, policy_name)
|
|
||||||
logdata = dict()
|
|
||||||
logdata['uri'] = drv_entry.path
|
|
||||||
logdata['sid'] = sid
|
|
||||||
log('D39', logdata)
|
|
||||||
self._drive_upsert(drv_entry)
|
|
||||||
|
|
||||||
def add_folder(self, sid, fobj, policy_name):
|
|
||||||
fld_entry = folder_entry(sid, fobj, policy_name)
|
|
||||||
logdata = dict()
|
|
||||||
logdata['folder'] = fld_entry.path
|
|
||||||
logdata['sid'] = sid
|
|
||||||
log('D42', logdata)
|
|
||||||
try:
|
|
||||||
self._add(fld_entry)
|
|
||||||
except Exception as exc:
|
|
||||||
(self
|
|
||||||
._filter_sid_obj(folder_entry, sid)
|
|
||||||
.filter(folder_entry.path == fld_entry.path)
|
|
||||||
.update(fld_entry.update_fields()))
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
def add_envvar(self, sid, evobj, policy_name):
|
|
||||||
ev_entry = envvar_entry(sid, evobj, policy_name)
|
|
||||||
logdata = dict()
|
|
||||||
logdata['envvar'] = ev_entry.name
|
|
||||||
logdata['sid'] = sid
|
|
||||||
log('D53', logdata)
|
|
||||||
try:
|
|
||||||
self._add(ev_entry)
|
|
||||||
except Exception as exc:
|
|
||||||
(self
|
|
||||||
._filter_sid_obj(envvar_entry, sid)
|
|
||||||
.filter(envvar_entry.name == ev_entry.name)
|
|
||||||
.update(ev_entry.update_fields()))
|
|
||||||
self.db_session.commit()
|
|
||||||
def add_script(self, sid, scrobj, policy_name):
|
|
||||||
scr_entry = script_entry(sid, scrobj, policy_name)
|
|
||||||
logdata = dict()
|
|
||||||
logdata['script path'] = scrobj.path
|
|
||||||
logdata['sid'] = sid
|
|
||||||
log('D153', logdata)
|
|
||||||
try:
|
|
||||||
self._add(scr_entry)
|
|
||||||
except Exception as exc:
|
|
||||||
(self
|
|
||||||
._filter_sid_obj(script_entry, sid)
|
|
||||||
.filter(script_entry.path == scr_entry.path)
|
|
||||||
.update(scr_entry.update_fields()))
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
def add_file(self, sid, fileobj, policy_name):
|
|
||||||
f_entry = file_entry(sid, fileobj, policy_name)
|
|
||||||
logdata = dict()
|
|
||||||
logdata['targetPath'] = f_entry.targetPath
|
|
||||||
logdata['fromPath'] = f_entry.fromPath
|
|
||||||
log('D162', logdata)
|
|
||||||
try:
|
|
||||||
self._add(f_entry)
|
|
||||||
except Exception as exc:
|
|
||||||
(self
|
|
||||||
._filter_sid_obj(file_entry, sid)
|
|
||||||
.filter(file_entry.targetPath == f_entry.targetPath)
|
|
||||||
.update(f_entry.update_fields()))
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
|
|
||||||
def add_ini(self, sid, iniobj, policy_name):
|
|
||||||
inientry = ini_entry(sid, iniobj, policy_name)
|
|
||||||
logdata = dict()
|
|
||||||
logdata['path'] = inientry.path
|
|
||||||
logdata['action'] = inientry.action
|
|
||||||
log('D177', logdata)
|
|
||||||
try:
|
|
||||||
self._add(inientry)
|
|
||||||
except Exception as exc:
|
|
||||||
(self
|
|
||||||
._filter_sid_obj(ini_entry, sid)
|
|
||||||
.filter(ini_entry.path == inientry.path)
|
|
||||||
.update(inientry.update_fields()))
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
def add_networkshare(self, sid, networkshareobj, policy_name):
|
|
||||||
networkshareentry = networkshare_entry(sid, networkshareobj, policy_name)
|
|
||||||
logdata = dict()
|
|
||||||
logdata['name'] = networkshareentry.name
|
|
||||||
logdata['path'] = networkshareentry.path
|
|
||||||
logdata['action'] = networkshareentry.action
|
|
||||||
log('D186', logdata)
|
|
||||||
try:
|
|
||||||
self._add(networkshareentry)
|
|
||||||
except Exception as exc:
|
|
||||||
(self
|
|
||||||
._filter_sid_obj(networkshare_entry, sid)
|
|
||||||
.filter(networkshare_entry.path == networkshareentry.path)
|
|
||||||
.update(networkshareentry.update_fields()))
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
|
|
||||||
def _filter_sid_obj(self, row_object, sid):
|
|
||||||
res = (self
|
|
||||||
.db_session
|
|
||||||
.query(row_object)
|
|
||||||
.filter(row_object.sid == sid))
|
|
||||||
return res
|
|
||||||
|
|
||||||
def _filter_sid_list(self, row_object, sid):
|
|
||||||
res = (self
|
|
||||||
.db_session
|
|
||||||
.query(row_object)
|
|
||||||
.filter(row_object.sid == sid)
|
|
||||||
.order_by(row_object.id)
|
|
||||||
.all())
|
|
||||||
return res
|
|
||||||
|
|
||||||
def get_shortcuts(self, sid):
|
|
||||||
return self._filter_sid_list(ad_shortcut, sid)
|
|
||||||
|
|
||||||
def get_printers(self, sid):
|
|
||||||
return self._filter_sid_list(printer_entry, sid)
|
|
||||||
|
|
||||||
def get_drives(self, sid):
|
|
||||||
return self._filter_sid_list(drive_entry, sid)
|
|
||||||
|
|
||||||
def get_folders(self, sid):
|
|
||||||
return self._filter_sid_list(folder_entry, sid)
|
|
||||||
|
|
||||||
def get_envvars(self, sid):
|
|
||||||
return self._filter_sid_list(envvar_entry, sid)
|
|
||||||
|
|
||||||
def _filter_scripts_list(self, row_object, sid, action):
|
|
||||||
res = (self
|
|
||||||
.db_session
|
|
||||||
.query(row_object)
|
|
||||||
.filter(row_object.sid == sid)
|
|
||||||
.filter(row_object.action == action)
|
|
||||||
.order_by(row_object.id)
|
|
||||||
.all())
|
|
||||||
return res
|
|
||||||
|
|
||||||
def get_scripts(self, sid, action):
|
|
||||||
return self._filter_scripts_list(script_entry, sid, action)
|
|
||||||
|
|
||||||
def get_files(self, sid):
|
|
||||||
return self._filter_sid_list(file_entry, sid)
|
|
||||||
|
|
||||||
def get_networkshare(self, sid):
|
|
||||||
return self._filter_sid_list(networkshare_entry, sid)
|
|
||||||
|
|
||||||
def get_ini(self, sid):
|
|
||||||
return self._filter_sid_list(ini_entry, sid)
|
|
||||||
|
|
||||||
def get_hkcu_entry(self, sid, hive_key):
|
|
||||||
res = (self
|
|
||||||
.db_session
|
|
||||||
.query(samba_hkcu_preg)
|
|
||||||
.filter(samba_hkcu_preg.sid == sid)
|
|
||||||
.filter(samba_hkcu_preg.hive_key == hive_key)
|
|
||||||
.first())
|
|
||||||
# Try to get the value from machine SID as a default if no option is set.
|
|
||||||
if not res:
|
|
||||||
machine_sid = self.get_info('machine_sid')
|
|
||||||
res = self.db_session.query(samba_hkcu_preg).filter(samba_hkcu_preg.sid == machine_sid).filter(samba_hkcu_preg.hive_key == hive_key).first()
|
|
||||||
return res
|
|
||||||
|
|
||||||
def filter_hkcu_entries(self, sid, startswith):
|
|
||||||
res = (self
|
|
||||||
.db_session
|
|
||||||
.query(samba_hkcu_preg)
|
|
||||||
.filter(samba_hkcu_preg.sid == sid)
|
|
||||||
.filter(samba_hkcu_preg.hive_key.like(startswith)))
|
|
||||||
return res
|
|
||||||
|
|
||||||
def get_info(self, name):
|
|
||||||
res = (self
|
|
||||||
.db_session
|
|
||||||
.query(info_entry)
|
|
||||||
.filter(info_entry.name == name)
|
|
||||||
.first())
|
|
||||||
return res.value
|
|
||||||
|
|
||||||
def get_hklm_entry(self, hive_key):
|
|
||||||
res = (self
|
|
||||||
.db_session
|
|
||||||
.query(samba_preg)
|
|
||||||
.filter(samba_preg.hive_key == hive_key)
|
|
||||||
.first())
|
|
||||||
return res
|
|
||||||
|
|
||||||
def filter_hklm_entries(self, startswith):
|
|
||||||
res = (self
|
|
||||||
.db_session
|
|
||||||
.query(samba_preg)
|
|
||||||
.filter(samba_preg.hive_key.like(startswith)))
|
|
||||||
return res
|
|
||||||
|
|
||||||
def wipe_user(self, sid):
|
|
||||||
self._wipe_sid(samba_hkcu_preg, sid)
|
|
||||||
self._wipe_sid(ad_shortcut, sid)
|
|
||||||
self._wipe_sid(printer_entry, sid)
|
|
||||||
self._wipe_sid(drive_entry, sid)
|
|
||||||
self._wipe_sid(script_entry, sid)
|
|
||||||
self._wipe_sid(file_entry, sid)
|
|
||||||
self._wipe_sid(ini_entry, sid)
|
|
||||||
self._wipe_sid(networkshare_entry, sid)
|
|
||||||
|
|
||||||
def _wipe_sid(self, row_object, sid):
|
|
||||||
(self
|
|
||||||
.db_session
|
|
||||||
.query(row_object)
|
|
||||||
.filter(row_object.sid == sid)
|
|
||||||
.delete())
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
def wipe_hklm(self):
|
|
||||||
self.db_session.query(samba_preg).delete()
|
|
||||||
self.db_session.commit()
|
|
||||||
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
#
|
|
||||||
# GPOA - GPO Applier for Linux
|
|
||||||
#
|
|
||||||
# Copyright (C) 2024 BaseALT Ltd.
|
|
||||||
# Copyright (C) 2024 Evgeny SInelnikov <sin@altlinux.org>.
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
__compat__ = False
|
|
||||||
|
|
||||||
from sqlalchemy import MetaData
|
|
||||||
|
|
||||||
try:
|
|
||||||
from sqlalchemy.orm import registry
|
|
||||||
except:
|
|
||||||
from sqlalchemy.orm import mapper
|
|
||||||
__compat__ = True
|
|
||||||
|
|
||||||
class sqlite_registry_compat:
|
|
||||||
def __init__(self, db_cnt):
|
|
||||||
if not __compat__:
|
|
||||||
self.__registry = registry()
|
|
||||||
self.__metadata = MetaData()
|
|
||||||
else:
|
|
||||||
self.__metadata = MetaData(db_cnt)
|
|
||||||
|
|
||||||
def metadata(self):
|
|
||||||
return self.__metadata
|
|
||||||
|
|
||||||
def map_imperatively(self, obj, table):
|
|
||||||
if __compat__:
|
|
||||||
mapper(obj, table)
|
|
||||||
else:
|
|
||||||
self.__registry.map_imperatively(obj, table)
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#}
|
#}
|
||||||
|
|
||||||
{% if Deny_All == '1' %}
|
{% if Deny_All == 1 %}
|
||||||
polkit.addRule(function (action, subject) {
|
polkit.addRule(function (action, subject) {
|
||||||
if ((action.id == "org.freedesktop.udisks2.filesystem-mount" ||
|
if ((action.id == "org.freedesktop.udisks2.filesystem-mount" ||
|
||||||
action.id == "org.freedesktop.udisks2.filesystem-mount-system" ||
|
action.id == "org.freedesktop.udisks2.filesystem-mount-system" ||
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#}
|
#}
|
||||||
|
|
||||||
{% if Deny_All == '1' %}
|
{% if Deny_All == 1 %}
|
||||||
polkit.addRule(function (action, subject) {
|
polkit.addRule(function (action, subject) {
|
||||||
if (action.id == "org.freedesktop.udisks2.filesystem-mount" ||
|
if (action.id == "org.freedesktop.udisks2.filesystem-mount" ||
|
||||||
action.id == "org.freedesktop.udisks2.filesystem-mount-system" ||
|
action.id == "org.freedesktop.udisks2.filesystem-mount-system" ||
|
||||||
|
|||||||
@@ -16,5 +16,5 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#}
|
#}
|
||||||
{{ home_dir }}/{{mntTarget}} {{ mount_file }} -t 120 --browse
|
{{ home_dir }}/{{mntTarget}} {{ mount_file }} -t {{timeout}} --browse
|
||||||
|
|
||||||
|
|||||||
@@ -16,5 +16,5 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#}
|
#}
|
||||||
{{ home_dir }}/.{{mntTarget}} {{ mount_file }} -t 120
|
{{ home_dir }}/.{{mntTarget}} {{ mount_file }} -t {{timeout}}
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
{%- for drv in drives %}
|
{%- for drv in drives %}
|
||||||
{% if (drv.thisDrive != 'HIDE') %}
|
{% if (drv.thisDrive != 'HIDE') %}
|
||||||
{% if drv.label %}
|
{% if drv.label %}
|
||||||
"{{ drv.label }}" -fstype=cifs,cruid=$USER,sec=krb5,noperm,cifsacl :{{ drv.path }}
|
"{{ drv.label }}" -fstype=cifs,cruid=$USER,sec=krb5,noperm{% if drv.username %}{% else %},multiuser{% endif %}{% if drv.cifsacl %},cifsacl{% endif %} :{{ drv.path }}
|
||||||
{% else %}
|
{% else %}
|
||||||
"{{ drv.dir }}" -fstype=cifs,cruid=$USER,sec=krb5,noperm,cifsacl :{{ drv.path }}
|
"{{ drv.dir }}" -fstype=cifs,cruid=$USER,sec=krb5,noperm{% if drv.username %}{% else %},multiuser{% endif %}{% if drv.cifsacl %},cifsacl{% endif %} :{{ drv.path }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -19,9 +19,9 @@
|
|||||||
{%- for drv in drives %}
|
{%- for drv in drives %}
|
||||||
{% if (drv.thisDrive == 'HIDE') %}
|
{% if (drv.thisDrive == 'HIDE') %}
|
||||||
{% if drv.label %}
|
{% if drv.label %}
|
||||||
"{{ drv.label }}" -fstype=cifs,cruid=$USER,sec=krb5,noperm,cifsacl :{{ drv.path }}
|
"{{ drv.label }}" -fstype=cifs,cruid=$USER,sec=krb5,noperm{% if drv.username %}{% else %},multiuser{% endif %}{% if drv.cifsacl %},cifsacl{% endif %} :{{ drv.path }}
|
||||||
{% else %}
|
{% else %}
|
||||||
"{{ drv.dir }}" -fstype=cifs,cruid=$USER,sec=krb5,noperm,cifsacl :{{ drv.path }}
|
"{{ drv.dir }}" -fstype=cifs,cruid=$USER,sec=krb5,noperm{% if drv.username %}{% else %},multiuser{% endif %}{% if drv.cifsacl %},cifsacl{% endif %} :{{ drv.path }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2024 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,10 +18,9 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import logging.handlers
|
import logging.handlers
|
||||||
from enum import IntEnum
|
from enum import IntEnum, Enum
|
||||||
|
|
||||||
from messages import message_with_code
|
from .logging import log
|
||||||
from .logging import slogm
|
|
||||||
|
|
||||||
|
|
||||||
def set_loglevel(loglevel_num=None):
|
def set_loglevel(loglevel_num=None):
|
||||||
@@ -69,8 +68,8 @@ def process_target(target_name=None):
|
|||||||
if target_name:
|
if target_name:
|
||||||
target = target_name
|
target = target_name
|
||||||
|
|
||||||
logdata = dict({'target': target})
|
logdata = {'target': target}
|
||||||
logging.debug(slogm(message_with_code('D10'), logdata))
|
log('D10', logdata)
|
||||||
|
|
||||||
return target.upper()
|
return target.upper()
|
||||||
|
|
||||||
@@ -84,3 +83,20 @@ class ExitCodeUpdater(IntEnum):
|
|||||||
FAIL_GPUPDATE_USER_NOREPLY = 3
|
FAIL_GPUPDATE_USER_NOREPLY = 3
|
||||||
EXIT_SIGINT = 130
|
EXIT_SIGINT = 130
|
||||||
|
|
||||||
|
class FileAction(Enum):
|
||||||
|
CREATE = 'C'
|
||||||
|
REPLACE = 'R'
|
||||||
|
UPDATE = 'U'
|
||||||
|
DELETE = 'D'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
def action_letter2enum(letter):
|
||||||
|
if letter in ['C', 'R', 'U', 'D']:
|
||||||
|
if letter == 'C': return FileAction.CREATE
|
||||||
|
if letter == 'R': return FileAction.REPLACE
|
||||||
|
if letter == 'U': return FileAction.UPDATE
|
||||||
|
if letter == 'D': return FileAction.DELETE
|
||||||
|
|
||||||
|
return FileAction.CREATE
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2024 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,6 +20,7 @@ import dbus
|
|||||||
|
|
||||||
from .logging import log
|
from .logging import log
|
||||||
from .users import is_root
|
from .users import is_root
|
||||||
|
from storage import Dconf_registry
|
||||||
|
|
||||||
|
|
||||||
class dbus_runner:
|
class dbus_runner:
|
||||||
@@ -70,8 +71,9 @@ class dbus_runner:
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if self.username:
|
if self.username:
|
||||||
logdata = dict({'username': self.username})
|
logdata = {'username': self.username}
|
||||||
log('D6', logdata)
|
log('D6', logdata)
|
||||||
|
gpupdate = 'gpupdate' if not Dconf_registry._force else 'gpupdate_force'
|
||||||
if is_root():
|
if is_root():
|
||||||
# oddjobd-gpupdate's ACL allows access to this method
|
# oddjobd-gpupdate's ACL allows access to this method
|
||||||
# only for superuser. This method is called via PAM
|
# only for superuser. This method is called via PAM
|
||||||
@@ -86,8 +88,7 @@ class dbus_runner:
|
|||||||
timeout=self._synchronous_timeout)
|
timeout=self._synchronous_timeout)
|
||||||
print_dbus_result(result)
|
print_dbus_result(result)
|
||||||
except dbus.exceptions.DBusException as exc:
|
except dbus.exceptions.DBusException as exc:
|
||||||
logdata = dict()
|
logdata = {'username': self.username}
|
||||||
logdata['username'] = self.username
|
|
||||||
log('E23', logdata)
|
log('E23', logdata)
|
||||||
raise exc
|
raise exc
|
||||||
else:
|
else:
|
||||||
@@ -95,22 +96,23 @@ class dbus_runner:
|
|||||||
result = self.system_bus.call_blocking(self.bus_name,
|
result = self.system_bus.call_blocking(self.bus_name,
|
||||||
self._object_path,
|
self._object_path,
|
||||||
self.interface_name,
|
self.interface_name,
|
||||||
'gpupdate',
|
gpupdate,
|
||||||
None,
|
None,
|
||||||
[],
|
[],
|
||||||
timeout=self._synchronous_timeout)
|
timeout=self._synchronous_timeout)
|
||||||
print_dbus_result(result)
|
print_dbus_result(result)
|
||||||
except dbus.exceptions.DBusException as exc:
|
except dbus.exceptions.DBusException as exc:
|
||||||
logdata = dict({'error': str(exc)})
|
logdata = {'error': str(exc)}
|
||||||
log('E21', logdata)
|
log('E21', logdata)
|
||||||
raise exc
|
raise exc
|
||||||
else:
|
else:
|
||||||
log('D11')
|
log('D11')
|
||||||
|
gpupdate_computer = 'gpupdate_computer' if not Dconf_registry._force else 'gpupdate_computer_force'
|
||||||
try:
|
try:
|
||||||
result = self.system_bus.call_blocking(self.bus_name,
|
result = self.system_bus.call_blocking(self.bus_name,
|
||||||
self._object_path,
|
self._object_path,
|
||||||
self.interface_name,
|
self.interface_name,
|
||||||
'gpupdate_computer',
|
gpupdate_computer,
|
||||||
None,
|
None,
|
||||||
# The following positional parameter is called "args".
|
# The following positional parameter is called "args".
|
||||||
# There is no official documentation for it.
|
# There is no official documentation for it.
|
||||||
@@ -118,8 +120,7 @@ class dbus_runner:
|
|||||||
timeout=self._synchronous_timeout)
|
timeout=self._synchronous_timeout)
|
||||||
print_dbus_result(result)
|
print_dbus_result(result)
|
||||||
except dbus.exceptions.DBusException as exc:
|
except dbus.exceptions.DBusException as exc:
|
||||||
print(exc)
|
logdata = {'error': str(exc)}
|
||||||
logdata = dict({'error': str(exc)})
|
|
||||||
log('E22', logdata)
|
log('E22', logdata)
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
@@ -192,7 +193,7 @@ def print_dbus_result(result):
|
|||||||
'''
|
'''
|
||||||
exitcode = result[0]
|
exitcode = result[0]
|
||||||
message = result[1:]
|
message = result[1:]
|
||||||
logdata = dict({'retcode': exitcode})
|
logdata = {'retcode': exitcode}
|
||||||
log('D12', logdata)
|
log('D12', logdata)
|
||||||
|
|
||||||
for line in message:
|
for line in message:
|
||||||
@@ -206,7 +207,7 @@ class dbus_session:
|
|||||||
self.session_dbus = self.session_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
|
self.session_dbus = self.session_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
|
||||||
self.session_iface = dbus.Interface(self.session_dbus, 'org.freedesktop.DBus')
|
self.session_iface = dbus.Interface(self.session_dbus, 'org.freedesktop.DBus')
|
||||||
except dbus.exceptions.DBusException as exc:
|
except dbus.exceptions.DBusException as exc:
|
||||||
logdata = dict({'error': str(exc)})
|
logdata = {'error': str(exc)}
|
||||||
log('E31', logdata)
|
log('E31', logdata)
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
@@ -217,7 +218,7 @@ class dbus_session:
|
|||||||
log('D57', {"pid": pid})
|
log('D57', {"pid": pid})
|
||||||
except dbus.exceptions.DBusException as exc:
|
except dbus.exceptions.DBusException as exc:
|
||||||
if exc.get_dbus_name() != 'org.freedesktop.DBus.Error.NameHasNoOwner':
|
if exc.get_dbus_name() != 'org.freedesktop.DBus.Error.NameHasNoOwner':
|
||||||
logdata = dict({'error': str(exc)})
|
logdata = {'error': str(exc)}
|
||||||
log('E32', logdata)
|
log('E32', logdata)
|
||||||
raise exc
|
raise exc
|
||||||
log('D58', {'connection': connection})
|
log('D58', {'connection': connection})
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ def geterr():
|
|||||||
'''
|
'''
|
||||||
etype, evalue, etrace = sys.exc_info()
|
etype, evalue, etrace = sys.exc_info()
|
||||||
|
|
||||||
traceinfo = dict({
|
traceinfo = {
|
||||||
'file': etrace.tb_frame.f_code.co_filename
|
'file': etrace.tb_frame.f_code.co_filename
|
||||||
, 'line': etrace.tb_lineno
|
, 'line': etrace.tb_lineno
|
||||||
, 'name': etrace.tb_frame.f_code.co_name
|
, 'name': etrace.tb_frame.f_code.co_name
|
||||||
, 'type': etype.__name__
|
, 'type': etype.__name__
|
||||||
, 'message': evalue
|
, 'message': evalue
|
||||||
})
|
}
|
||||||
|
|
||||||
del(etype, evalue, etrace)
|
del(etype, evalue, etrace)
|
||||||
|
|
||||||
@@ -46,3 +46,10 @@ class NotUNCPathError(Exception):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.path
|
return self.path
|
||||||
|
|
||||||
|
class GetGPOListFail(Exception):
|
||||||
|
def __init__(self, exc):
|
||||||
|
self.exc = exc
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.exc
|
||||||
|
|
||||||
|
|||||||
68
gpoa/util/ipa.py
Normal file
68
gpoa/util/ipa.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2020 BaseALT Ltd.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import configparser
|
||||||
|
import os
|
||||||
|
from ipalib import api
|
||||||
|
|
||||||
|
class ipaopts:
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize the class and load the FreeIPA config file."""
|
||||||
|
self.config_file = "/etc/ipa/default.conf"
|
||||||
|
self.config = configparser.ConfigParser()
|
||||||
|
|
||||||
|
if not os.path.exists(self.config_file):
|
||||||
|
raise FileNotFoundError(f"Config file for Freeipa{self.config_file} not found.")
|
||||||
|
|
||||||
|
self.config.read(self.config_file)
|
||||||
|
|
||||||
|
def get_realm(self):
|
||||||
|
"""Return the Kerberos realm from the config."""
|
||||||
|
try:
|
||||||
|
return self.config.get('global', 'realm')
|
||||||
|
except (configparser.NoSectionError, configparser.NoOptionError):
|
||||||
|
raise ValueError("Realm not found in config file.")
|
||||||
|
|
||||||
|
def get_domain(self):
|
||||||
|
"""Return the domain from the config."""
|
||||||
|
try:
|
||||||
|
return self.config.get('global', 'domain')
|
||||||
|
except (configparser.NoSectionError, configparser.NoOptionError):
|
||||||
|
raise ValueError("Domain not found in config file.")
|
||||||
|
|
||||||
|
def get_server(self):
|
||||||
|
"""
|
||||||
|
Return the FreeIPA PDC Emulator server from API.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
result = api.Command.gpmaster_show_pdc()
|
||||||
|
pdc_server = result['result']['pdc_emulator']
|
||||||
|
return pdc_server
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_machine_name(self):
|
||||||
|
"""Return the host from the config."""
|
||||||
|
try:
|
||||||
|
return self.config.get('global', 'host')
|
||||||
|
except (configparser.NoSectionError, configparser.NoOptionError):
|
||||||
|
raise ValueError("Host not found in config file.")
|
||||||
|
|
||||||
|
def get_cache_dir(self):
|
||||||
|
"""Return the cache directory path."""
|
||||||
|
return "/var/cache/freeipa/gpo_cache"
|
||||||
102
gpoa/util/ipacreds.py
Normal file
102
gpoa/util/ipacreds.py
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
|
||||||
|
import smbc
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from ipalib import api
|
||||||
|
from pathlib import Path
|
||||||
|
from storage.dconf_registry import Dconf_registry, extract_display_name_version
|
||||||
|
from util.util import get_uid_by_username
|
||||||
|
from .ipa import ipaopts
|
||||||
|
from util.logging import log
|
||||||
|
|
||||||
|
class ipacreds(ipaopts):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.smb_context = smbc.Context(use_kerberos=True)
|
||||||
|
self.gpo_list = []
|
||||||
|
|
||||||
|
def update_gpos(self, username):
|
||||||
|
gpos = []
|
||||||
|
try:
|
||||||
|
if not api.isdone('bootstrap'):
|
||||||
|
api.bootstrap(context='cli')
|
||||||
|
if not api.isdone('finalize'):
|
||||||
|
api.finalize()
|
||||||
|
api.Backend.rpcclient.connect()
|
||||||
|
try:
|
||||||
|
server = self.get_server()
|
||||||
|
is_machine = (username == self.get_machine_name())
|
||||||
|
if is_machine:
|
||||||
|
result = api.Command.chain_resolve_for_host(username)
|
||||||
|
else:
|
||||||
|
result = api.Command.chain_resolve_for_user(username)
|
||||||
|
policies_list = result["result"]
|
||||||
|
try:
|
||||||
|
if is_machine:
|
||||||
|
dconf_dict = Dconf_registry.get_dictionary_from_dconf_file_db(save_dconf_db=True)
|
||||||
|
else:
|
||||||
|
uid = get_uid_by_username(username)
|
||||||
|
dconf_dict = Dconf_registry.get_dictionary_from_dconf_file_db(uid, save_dconf_db=True)
|
||||||
|
dict_gpo_name_version = extract_display_name_version(dconf_dict, username)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'exc': str(exc)}
|
||||||
|
log('D235', logdata)
|
||||||
|
dict_gpo_name_version = {}
|
||||||
|
|
||||||
|
for policy in policies_list:
|
||||||
|
class SimpleGPO:
|
||||||
|
def __init__(self, policy_data):
|
||||||
|
self.display_name = policy_data.get('name', 'Unknown')
|
||||||
|
self.file_sys_path = policy_data.get('file_system_path', '')
|
||||||
|
self.version = int(policy_data.get('version', 0))
|
||||||
|
self.flags = int(policy_data.get('flags', 0))
|
||||||
|
self.link = policy_data.get('link', 'Unknown')
|
||||||
|
guid_match = re.search(r'\{[^}]+\}', self.file_sys_path)
|
||||||
|
self.name = guid_match.group(0) if guid_match else f"policy_{id(self)}"
|
||||||
|
|
||||||
|
gpo = SimpleGPO(policy)
|
||||||
|
if (gpo.display_name in dict_gpo_name_version.keys() and
|
||||||
|
dict_gpo_name_version.get(gpo.display_name, {}).get('version') == str(gpo.version)):
|
||||||
|
|
||||||
|
cached_path = dict_gpo_name_version.get(gpo.display_name, {}).get('correct_path')
|
||||||
|
if cached_path and Path(cached_path).exists():
|
||||||
|
gpo.file_sys_path = cached_path
|
||||||
|
ldata = {'gpo_name': gpo.display_name, 'gpo_uuid': gpo.name, 'file_sys_path_cache': True}
|
||||||
|
else:
|
||||||
|
ldata = {'gpo_name': gpo.display_name, 'gpo_uuid': gpo.name, 'file_sys_path': gpo.file_sys_path}
|
||||||
|
else:
|
||||||
|
ldata = {'gpo_name': gpo.display_name, 'gpo_uuid': gpo.name, 'file_sys_path': gpo.file_sys_path}
|
||||||
|
gpos.append(gpo)
|
||||||
|
finally:
|
||||||
|
api.Backend.rpcclient.disconnect()
|
||||||
|
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'exc': str(exc)}
|
||||||
|
log('E78', logdata)
|
||||||
|
return gpos, server
|
||||||
|
|
||||||
|
def get_domain(self):
|
||||||
|
return super().get_domain()
|
||||||
|
|
||||||
|
def get_server(self):
|
||||||
|
return super().get_server()
|
||||||
|
|
||||||
|
def get_cache_dir(self):
|
||||||
|
return super().get_cache_dir()
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,20 +22,31 @@ import subprocess
|
|||||||
from .util import get_machine_name
|
from .util import get_machine_name
|
||||||
from .logging import log
|
from .logging import log
|
||||||
from .samba import smbopts
|
from .samba import smbopts
|
||||||
|
from .ipa import ipaopts
|
||||||
|
|
||||||
|
|
||||||
def machine_kinit(cache_name=None):
|
def machine_kinit(cache_name=None, backend_type=None):
|
||||||
'''
|
'''
|
||||||
Perform kinit with machine credentials
|
Perform kinit with machine credentials
|
||||||
'''
|
'''
|
||||||
opts = smbopts()
|
if backend_type == 'freeipa':
|
||||||
host = get_machine_name()
|
keytab_path = '/etc/samba/samba.keytab'
|
||||||
realm = opts.get_realm()
|
opts = ipaopts()
|
||||||
with_realm = '{}@{}'.format(host, realm)
|
host = "cifs/" + opts.get_machine_name()
|
||||||
os.environ['KRB5CCNAME'] = 'FILE:{}'.format(cache_name)
|
realm = opts.get_realm()
|
||||||
kinit_cmd = ['kinit', '-k', with_realm]
|
with_realm = '{}@{}'.format(host, realm)
|
||||||
|
kinit_cmd = ['kinit', '-kt', keytab_path, with_realm]
|
||||||
|
else:
|
||||||
|
opts = smbopts()
|
||||||
|
host = get_machine_name()
|
||||||
|
realm = opts.get_realm()
|
||||||
|
with_realm = '{}@{}'.format(host, realm)
|
||||||
|
kinit_cmd = ['kinit', '-k', with_realm]
|
||||||
|
|
||||||
if cache_name:
|
if cache_name:
|
||||||
|
os.environ['KRB5CCNAME'] = 'FILE:{}'.format(cache_name)
|
||||||
kinit_cmd.extend(['-c', cache_name])
|
kinit_cmd.extend(['-c', cache_name])
|
||||||
|
|
||||||
proc = subprocess.Popen(kinit_cmd)
|
proc = subprocess.Popen(kinit_cmd)
|
||||||
proc.wait()
|
proc.wait()
|
||||||
|
|
||||||
@@ -80,12 +91,10 @@ def check_krb_ticket():
|
|||||||
subprocess.check_call(['klist', '-s'])
|
subprocess.check_call(['klist', '-s'])
|
||||||
output = subprocess.check_output('klist', stderr=subprocess.STDOUT).decode()
|
output = subprocess.check_output('klist', stderr=subprocess.STDOUT).decode()
|
||||||
result = True
|
result = True
|
||||||
logdata = dict()
|
logdata = {'output': output}
|
||||||
logdata['output'] = output
|
|
||||||
log('D17', logdata)
|
log('D17', logdata)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {'krb-exc': exc}
|
||||||
logdata['krb-exc'] = exc
|
|
||||||
log('E14', logdata)
|
log('E14', logdata)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -40,15 +40,15 @@ class slogm(object):
|
|||||||
'''
|
'''
|
||||||
Structured log message class
|
Structured log message class
|
||||||
'''
|
'''
|
||||||
def __init__(self, message, kwargs=dict()):
|
def __init__(self, message, kwargs={}):
|
||||||
self.message = message
|
self.message = message
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
if not self.kwargs:
|
if not self.kwargs:
|
||||||
self.kwargs = dict()
|
self.kwargs = {}
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
now = str(datetime.datetime.now().isoformat(sep=' ', timespec='milliseconds'))
|
now = str(datetime.datetime.now().isoformat(sep=' ', timespec='milliseconds'))
|
||||||
args = dict()
|
args = {}
|
||||||
args.update(self.kwargs)
|
args.update(self.kwargs)
|
||||||
result = '{}|{}|{}'.format(now, self.message, args)
|
result = '{}|{}|{}'.format(now, self.message, args)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2021 BaseALT Ltd. <org@basealt.ru>
|
# Copyright (C) 2019-2024 BaseALT Ltd. <org@basealt.ru>
|
||||||
# Copyright (C) 2019-2021 Igor Chudov <nir@nir.org.ru>
|
# Copyright (C) 2019-2021 Igor Chudov <nir@nir.org.ru>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
@@ -93,6 +93,22 @@ def local_policy_cache():
|
|||||||
|
|
||||||
return lpcache
|
return lpcache
|
||||||
|
|
||||||
|
|
||||||
|
def get_dconf_config_path(uid = None):
|
||||||
|
if uid:
|
||||||
|
return f'/etc/dconf/db/policy{uid}.d/'
|
||||||
|
else:
|
||||||
|
return '/etc/dconf/db/policy.d/'
|
||||||
|
|
||||||
|
def get_dconf_config_file(uid = None):
|
||||||
|
if uid:
|
||||||
|
return f'/etc/dconf/db/policy{uid}.d/policy{uid}.ini'
|
||||||
|
else:
|
||||||
|
return '/etc/dconf/db/policy.d/policy.ini'
|
||||||
|
|
||||||
|
def get_desktop_files_directory():
|
||||||
|
return '/usr/share/applications'
|
||||||
|
|
||||||
class UNCPath:
|
class UNCPath:
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
self.path = path
|
self.path = path
|
||||||
@@ -107,7 +123,7 @@ class UNCPath:
|
|||||||
def get_uri(self):
|
def get_uri(self):
|
||||||
path = self.path
|
path = self.path
|
||||||
if self.type == 'unc':
|
if self.type == 'unc':
|
||||||
path = self.path.replace('\\', '/')
|
path = self.path.replace('\\\\', '/')
|
||||||
path = path.replace('//', 'smb://')
|
path = path.replace('//', 'smb://')
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2024 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
|
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
from storage import registry_factory
|
from storage.dconf_registry import load_preg_dconf
|
||||||
|
|
||||||
from samba.gp_parse.gp_pol import GPPolParser
|
from samba.gp_parse.gp_pol import GPPolParser
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ def load_xml_preg(xml_path):
|
|||||||
'''
|
'''
|
||||||
Parse XML/PReg file and return its preg object
|
Parse XML/PReg file and return its preg object
|
||||||
'''
|
'''
|
||||||
logdata = dict({'polfile': xml_path})
|
logdata = {'polfile': xml_path}
|
||||||
log('D36', logdata)
|
log('D36', logdata)
|
||||||
gpparser = GPPolParser()
|
gpparser = GPPolParser()
|
||||||
xml_root = ElementTree.parse(xml_path).getroot()
|
xml_root = ElementTree.parse(xml_path).getroot()
|
||||||
@@ -53,14 +53,14 @@ def load_pol_preg(polfile):
|
|||||||
'''
|
'''
|
||||||
Parse PReg file and return its preg object
|
Parse PReg file and return its preg object
|
||||||
'''
|
'''
|
||||||
logdata = dict({'polfile': polfile})
|
logdata = {'polfile': polfile}
|
||||||
log('D31', logdata)
|
log('D31', logdata)
|
||||||
gpparser = GPPolParser()
|
gpparser = GPPolParser()
|
||||||
data = None
|
data = None
|
||||||
|
|
||||||
with open(polfile, 'rb') as f:
|
with open(polfile, 'rb') as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
logdata = dict({'polfile': polfile, 'length': len(data)})
|
logdata = {'polfile': polfile, 'length': len(data)}
|
||||||
log('D33', logdata)
|
log('D33', logdata)
|
||||||
gpparser.parse(data)
|
gpparser.parse(data)
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ def load_pol_preg(polfile):
|
|||||||
|
|
||||||
def preg_keymap(preg):
|
def preg_keymap(preg):
|
||||||
pregfile = load_preg(preg)
|
pregfile = load_preg(preg)
|
||||||
keymap = dict()
|
keymap = {}
|
||||||
|
|
||||||
for entry in pregfile.entries:
|
for entry in pregfile.entries:
|
||||||
hive_key = '{}\\{}'.format(entry.keyname, entry.valuename)
|
hive_key = '{}\\{}'.format(entry.keyname, entry.valuename)
|
||||||
@@ -80,16 +80,15 @@ def preg_keymap(preg):
|
|||||||
return keymap
|
return keymap
|
||||||
|
|
||||||
|
|
||||||
def merge_polfile(preg, sid=None, reg_name='registry', reg_path=None, policy_name='Unknown'):
|
def merge_polfile(preg, sid=None, reg_name='registry', reg_path=None, policy_name='Unknown', username='Machine', gpo_info=None):
|
||||||
pregfile = load_preg(preg)
|
pregfile = load_preg(preg)
|
||||||
logdata = dict({'pregfile': preg})
|
if sid is None and username == 'Machine':
|
||||||
|
load_preg_dconf(pregfile, preg, policy_name, None, gpo_info)
|
||||||
|
else:
|
||||||
|
load_preg_dconf(pregfile, preg, policy_name, username, gpo_info)
|
||||||
|
logdata = {'pregfile': preg}
|
||||||
log('D32', logdata)
|
log('D32', logdata)
|
||||||
storage = registry_factory(reg_name, reg_path)
|
|
||||||
for entry in pregfile.entries:
|
|
||||||
if not sid:
|
|
||||||
storage.add_hklm_entry(entry, policy_name)
|
|
||||||
else:
|
|
||||||
storage.add_hkcu_entry(entry, sid, policy_name)
|
|
||||||
|
|
||||||
|
|
||||||
class entry:
|
class entry:
|
||||||
@@ -98,16 +97,12 @@ class entry:
|
|||||||
self.valuename = e_valuename
|
self.valuename = e_valuename
|
||||||
self.type = e_type
|
self.type = e_type
|
||||||
self.data = e_data
|
self.data = e_data
|
||||||
logdata = dict()
|
logdata = {'keyname': self.keyname, 'valuename': self.valuename, 'type': self.type, 'data': self.data}
|
||||||
logdata['keyname'] = self.keyname
|
|
||||||
logdata['valuename'] = self.valuename
|
|
||||||
logdata['type'] = self.type
|
|
||||||
logdata['data'] = self.data
|
|
||||||
log('D22', logdata)
|
log('D22', logdata)
|
||||||
|
|
||||||
class pentries:
|
class pentries:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.entries = list()
|
self.entries = []
|
||||||
|
|
||||||
|
|
||||||
def preg2entries(preg_obj):
|
def preg2entries(preg_obj):
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -28,7 +28,7 @@ def get_roles(role_dir):
|
|||||||
'''
|
'''
|
||||||
Return list of directories in /etc/role named after role plus '.d'
|
Return list of directories in /etc/role named after role plus '.d'
|
||||||
'''
|
'''
|
||||||
directories = list()
|
directories = []
|
||||||
try:
|
try:
|
||||||
for item in role_dir.iterdir():
|
for item in role_dir.iterdir():
|
||||||
if item.is_dir():
|
if item.is_dir():
|
||||||
@@ -45,7 +45,7 @@ def read_groups(role_file_path):
|
|||||||
'''
|
'''
|
||||||
Read list of whitespace-separated groups from file
|
Read list of whitespace-separated groups from file
|
||||||
'''
|
'''
|
||||||
groups = list()
|
groups = []
|
||||||
|
|
||||||
with open(role_file_path, 'r') as role_file:
|
with open(role_file_path, 'r') as role_file:
|
||||||
lines = role_file.readlines()
|
lines = role_file.readlines()
|
||||||
@@ -54,7 +54,7 @@ def read_groups(role_file_path):
|
|||||||
print(linegroups)
|
print(linegroups)
|
||||||
groups.extend(linegroups)
|
groups.extend(linegroups)
|
||||||
|
|
||||||
return set(groups)
|
return {*groups}
|
||||||
|
|
||||||
|
|
||||||
def get_rolegroups(roledir):
|
def get_rolegroups(roledir):
|
||||||
@@ -63,16 +63,16 @@ def get_rolegroups(roledir):
|
|||||||
'''
|
'''
|
||||||
roledir_path = pathlib.Path(roledir)
|
roledir_path = pathlib.Path(roledir)
|
||||||
|
|
||||||
group_files = list()
|
group_files = []
|
||||||
for item in roledir_path.iterdir():
|
for item in roledir_path.iterdir():
|
||||||
if item.is_file():
|
if item.is_file():
|
||||||
group_files.append(item)
|
group_files.append(item)
|
||||||
|
|
||||||
groups = list()
|
groups = []
|
||||||
for item in group_files:
|
for item in group_files:
|
||||||
groups.extend(read_groups(item))
|
groups.extend(read_groups(item))
|
||||||
|
|
||||||
return set(groups)
|
return {*groups}
|
||||||
|
|
||||||
def create_role(role_name, privilege_list):
|
def create_role(role_name, privilege_list):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2025 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -116,7 +116,7 @@ def install_rpms(rpm_names):
|
|||||||
'''
|
'''
|
||||||
Install set of RPMs sequentially
|
Install set of RPMs sequentially
|
||||||
'''
|
'''
|
||||||
result = list()
|
result = []
|
||||||
|
|
||||||
for package in rpm_names:
|
for package in rpm_names:
|
||||||
result.append(install_rpm(package))
|
result.append(install_rpm(package))
|
||||||
@@ -127,7 +127,7 @@ def remove_rpms(rpm_names):
|
|||||||
'''
|
'''
|
||||||
Remove set of RPMs requentially
|
Remove set of RPMs requentially
|
||||||
'''
|
'''
|
||||||
result = list()
|
result = []
|
||||||
|
|
||||||
for package in rpm_names:
|
for package in rpm_names:
|
||||||
result.append(remove_rpm(package))
|
result.append(remove_rpm(package))
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2024 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,9 +20,9 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
import pwd
|
import pwd
|
||||||
import logging
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import pysss_nss_idmap
|
import pysss_nss_idmap
|
||||||
|
from storage.dconf_registry import Dconf_registry
|
||||||
|
|
||||||
from .logging import log
|
from .logging import log
|
||||||
|
|
||||||
@@ -39,10 +39,19 @@ def wbinfo_getsid(domain, user):
|
|||||||
|
|
||||||
# This part works only on DC
|
# This part works only on DC
|
||||||
wbinfo_cmd = ['wbinfo', '-n', username]
|
wbinfo_cmd = ['wbinfo', '-n', username]
|
||||||
output = subprocess.check_output(wbinfo_cmd)
|
try:
|
||||||
sid = output.split()[0].decode('utf-8')
|
output = subprocess.check_output(wbinfo_cmd, stderr=subprocess.STDOUT)
|
||||||
|
Dconf_registry.set_info('trust', False)
|
||||||
return sid
|
return output.split()[0].decode('utf-8')
|
||||||
|
except:
|
||||||
|
log('W43')
|
||||||
|
try:
|
||||||
|
wbinfo_cmd[-1] = user
|
||||||
|
output = subprocess.check_output(wbinfo_cmd)
|
||||||
|
Dconf_registry.set_info('trust', True)
|
||||||
|
except Exception as exc:
|
||||||
|
raise exc
|
||||||
|
return output.split()[0].decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
def get_local_sid_prefix():
|
def get_local_sid_prefix():
|
||||||
@@ -66,10 +75,10 @@ def get_sid(domain, username, is_machine = False):
|
|||||||
try:
|
try:
|
||||||
sid = wbinfo_getsid(domain, username)
|
sid = wbinfo_getsid(domain, username)
|
||||||
except:
|
except:
|
||||||
logdata = dict({'sid': sid})
|
logdata = {'sid': sid}
|
||||||
log('E16', logdata)
|
log('E16', logdata)
|
||||||
|
|
||||||
logdata = dict({'sid': sid})
|
logdata = {'sid': sid}
|
||||||
log('D21', logdata)
|
log('D21', logdata)
|
||||||
|
|
||||||
return sid
|
return sid
|
||||||
@@ -204,7 +213,7 @@ def is_sid(sid):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def sid2descr(sid):
|
def sid2descr(sid):
|
||||||
sids = dict()
|
sids = {}
|
||||||
sids['S-1-0'] = 'Null Authority'
|
sids['S-1-0'] = 'Null Authority'
|
||||||
sids['S-1-0-0'] = 'Nobody'
|
sids['S-1-0-0'] = 'Nobody'
|
||||||
sids['S-1-1'] = 'World Authority'
|
sids['S-1-1'] = 'World Authority'
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ def set_privileges(username, uid, gid, groups, home):
|
|||||||
|
|
||||||
os.chdir(home)
|
os.chdir(home)
|
||||||
|
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['uid'] = uid
|
logdata['uid'] = uid
|
||||||
logdata['gid'] = gid
|
logdata['gid'] = gid
|
||||||
logdata['username'] = username
|
logdata['username'] = username
|
||||||
@@ -123,12 +123,12 @@ def with_privileges(username, func):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['msg'] = str(exc)
|
logdata['msg'] = str(exc)
|
||||||
log('E33', logdata)
|
log('E33', logdata)
|
||||||
result = 1;
|
result = 1;
|
||||||
finally:
|
finally:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['dbus_pid'] = dbus_pid
|
logdata['dbus_pid'] = dbus_pid
|
||||||
logdata['dconf_pid'] = dconf_pid
|
logdata['dconf_pid'] = dconf_pid
|
||||||
log('D56', logdata)
|
log('D56', logdata)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2021 BaseALT Ltd.
|
# Copyright (C) 2019-2024 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,8 +19,6 @@
|
|||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
|
|
||||||
from .logging import log
|
|
||||||
|
|
||||||
|
|
||||||
def is_root():
|
def is_root():
|
||||||
'''
|
'''
|
||||||
@@ -47,7 +45,6 @@ def username_match_uid(username):
|
|||||||
'''
|
'''
|
||||||
Check the passed username matches current process UID.
|
Check the passed username matches current process UID.
|
||||||
'''
|
'''
|
||||||
uid = os.getuid()
|
|
||||||
process_username = get_process_user()
|
process_username = get_process_user()
|
||||||
|
|
||||||
if process_username == username:
|
if process_username == username:
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ def get_backends():
|
|||||||
'''
|
'''
|
||||||
Get the list of backends supported by GPOA
|
Get the list of backends supported by GPOA
|
||||||
'''
|
'''
|
||||||
return ['local', 'samba']
|
return ['local', 'samba', 'freeipa']
|
||||||
|
|
||||||
def get_default_policy_name():
|
def get_default_policy_name():
|
||||||
'''
|
'''
|
||||||
@@ -138,7 +138,7 @@ def get_policy_entries(directory):
|
|||||||
'''
|
'''
|
||||||
Get list of directories representing "Local Policy" templates.
|
Get list of directories representing "Local Policy" templates.
|
||||||
'''
|
'''
|
||||||
filtered_entries = list()
|
filtered_entries = []
|
||||||
if os.path.isdir(directory):
|
if os.path.isdir(directory):
|
||||||
entries = [os.path.join(directory, entry) for entry in os.listdir(directory)]
|
entries = [os.path.join(directory, entry) for entry in os.listdir(directory)]
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ def get_policy_variants():
|
|||||||
system_policies = get_policy_entries(policy_dir)
|
system_policies = get_policy_entries(policy_dir)
|
||||||
user_policies = get_policy_entries(etc_policy_dir)
|
user_policies = get_policy_entries(etc_policy_dir)
|
||||||
|
|
||||||
general_listing = list()
|
general_listing = []
|
||||||
general_listing.extend(system_policies)
|
general_listing.extend(system_policies)
|
||||||
general_listing.extend(user_policies)
|
general_listing.extend(user_policies)
|
||||||
|
|
||||||
@@ -174,3 +174,83 @@ def string_to_literal_eval(string):
|
|||||||
except:
|
except:
|
||||||
literaleval = string
|
literaleval = string
|
||||||
return literaleval
|
return literaleval
|
||||||
|
|
||||||
|
def try_dict_to_literal_eval(string):
|
||||||
|
try:
|
||||||
|
literaleval = ast.literal_eval(string)
|
||||||
|
if isinstance(literaleval ,dict):
|
||||||
|
return literaleval
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def touch_file(filename):
|
||||||
|
path = Path(filename)
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.touch()
|
||||||
|
|
||||||
|
def get_uid_by_username(username):
|
||||||
|
try:
|
||||||
|
user_info = pwd.getpwnam(username)
|
||||||
|
return user_info.pw_uid
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def add_prefix_to_keys(dictionary: dict, prefix: str='Previous/') -> dict:
|
||||||
|
"""
|
||||||
|
Adds a prefix to each key in the dictionary.
|
||||||
|
Args: Input dictionary whose keys need to be modified
|
||||||
|
prefix string to be added to each key. Defaults to 'Previous/'
|
||||||
|
Returns: New dictionary with modified keys having the specified prefix
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
for key, value in dictionary.items():
|
||||||
|
new_key = f'{prefix}{key}'
|
||||||
|
if isinstance(value, dict):
|
||||||
|
result[new_key] = {deep_key:clean_data(val) if isinstance(val, str) else val for deep_key, val in value.items()}
|
||||||
|
else:
|
||||||
|
result[new_key] = value
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def remove_keys_with_prefix(dictionary: dict, prefix: tuple=('Previous/', 'Source/')) -> dict:
|
||||||
|
"""
|
||||||
|
Removes all keys that start with the specified prefix from the dictionary.
|
||||||
|
By default, removes keys starting with 'Previous/' and 'Source/' prefix.
|
||||||
|
"""
|
||||||
|
return {key: value for key, value in dictionary.items() if not key.startswith(prefix)}
|
||||||
|
|
||||||
|
def remove_prefix_from_keys(dictionary: dict, prefix: str) -> dict:
|
||||||
|
"""
|
||||||
|
Removes the specified prefix from the keys of the dictionary.
|
||||||
|
If a key starts with the prefix, it is removed.
|
||||||
|
"""
|
||||||
|
return {key[len(prefix):] if key.startswith(prefix) else key: value for key, value in dictionary.items()}
|
||||||
|
|
||||||
|
|
||||||
|
def get_trans_table():
|
||||||
|
return str.maketrans({
|
||||||
|
'\n': '',
|
||||||
|
'\r': '',
|
||||||
|
'"': "'",
|
||||||
|
'\\': '\\\\'
|
||||||
|
})
|
||||||
|
|
||||||
|
def clean_data(data):
|
||||||
|
try:
|
||||||
|
cleaned_string = data.translate(get_trans_table())
|
||||||
|
return cleaned_string
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def check_local_user_exists(username):
|
||||||
|
"""
|
||||||
|
Checks if a local user with the given username exists on a Linux system.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Try to get user information from the password database
|
||||||
|
pwd.getpwnam(username)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|||||||
@@ -18,36 +18,52 @@
|
|||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
from pathlib import Path
|
||||||
from samba import getopt as options
|
from samba.credentials import Credentials
|
||||||
from samba import NTSTATUSError
|
from samba import NTSTATUSError
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from samba.gpclass import get_dc_hostname, check_refresh_gpo_list
|
from samba.gpclass import get_dc_hostname, check_refresh_gpo_list
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from samba.gp.gpclass import get_dc_hostname, check_refresh_gpo_list
|
from samba.gp.gpclass import get_dc_hostname, check_refresh_gpo_list, get_gpo_list
|
||||||
|
|
||||||
from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
|
from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
|
||||||
|
from storage.dconf_registry import Dconf_registry, extract_display_name_version
|
||||||
import samba.gpo
|
import samba.gpo
|
||||||
|
|
||||||
from storage import cache_factory
|
|
||||||
from messages import message_with_code
|
|
||||||
from .xdg import (
|
from .xdg import (
|
||||||
xdg_get_desktop
|
xdg_get_desktop
|
||||||
)
|
)
|
||||||
from .util import get_homedir
|
from .util import get_homedir, get_uid_by_username
|
||||||
|
from .exceptions import GetGPOListFail
|
||||||
from .logging import log
|
from .logging import log
|
||||||
from .samba import smbopts
|
from .samba import smbopts
|
||||||
from gpoa.storage import registry_factory
|
from gpoa.storage import registry_factory
|
||||||
|
from samba.samdb import SamDB
|
||||||
|
from samba.auth import system_session
|
||||||
|
import optparse
|
||||||
|
import ldb
|
||||||
|
import ipaddress
|
||||||
|
import netifaces
|
||||||
|
import random
|
||||||
|
|
||||||
class smbcreds (smbopts):
|
class smbcreds (smbopts):
|
||||||
|
|
||||||
def __init__(self, dc_fqdn=None):
|
def __init__(self, dc_fqdn=None):
|
||||||
smbopts.__init__(self, 'GPO Applier')
|
smbopts.__init__(self, 'GPO Applier')
|
||||||
self.credopts = options.CredentialsOptions(self.parser)
|
|
||||||
self.creds = self.credopts.get_credentials(self.lp, fallback_machine=True)
|
self.creds = Credentials()
|
||||||
|
self.creds.guess(self.lp)
|
||||||
|
self.creds.set_machine_account()
|
||||||
|
|
||||||
self.set_dc(dc_fqdn)
|
self.set_dc(dc_fqdn)
|
||||||
|
self.sDomain = SiteDomainScanner(self.creds, self.lp, self.selected_dc)
|
||||||
|
self.dc_site_servers = self.sDomain.select_site_servers()
|
||||||
|
self.all_servers = self.sDomain.select_all_servers()
|
||||||
|
[self.all_servers.remove(element)
|
||||||
|
for element in self.dc_site_servers
|
||||||
|
if element in self.all_servers]
|
||||||
|
self.pdc_emulator_server = self.sDomain.select_pdc_emulator_server()
|
||||||
|
|
||||||
def get_dc(self):
|
def get_dc(self):
|
||||||
return self.selected_dc
|
return self.selected_dc
|
||||||
@@ -60,7 +76,7 @@ class smbcreds (smbopts):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if dc_fqdn is not None:
|
if dc_fqdn is not None:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['user_dc'] = dc_fqdn
|
logdata['user_dc'] = dc_fqdn
|
||||||
log('D38', logdata)
|
log('D38', logdata)
|
||||||
|
|
||||||
@@ -68,7 +84,7 @@ class smbcreds (smbopts):
|
|||||||
else:
|
else:
|
||||||
self.selected_dc = get_dc_hostname(self.creds, self.lp)
|
self.selected_dc = get_dc_hostname(self.creds, self.lp)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['msg'] = str(exc)
|
logdata['msg'] = str(exc)
|
||||||
log('E10', logdata)
|
log('E10', logdata)
|
||||||
raise exc
|
raise exc
|
||||||
@@ -83,7 +99,7 @@ class smbcreds (smbopts):
|
|||||||
# Look and python/samba/netcmd/domain.py for more examples
|
# Look and python/samba/netcmd/domain.py for more examples
|
||||||
res = netcmd_get_domain_infos_via_cldap(self.lp, None, self.selected_dc)
|
res = netcmd_get_domain_infos_via_cldap(self.lp, None, self.selected_dc)
|
||||||
dns_domainname = res.dns_domain
|
dns_domainname = res.dns_domain
|
||||||
logdata = dict({'domain': dns_domainname})
|
logdata = {'domain': dns_domainname}
|
||||||
log('D18', logdata)
|
log('D18', logdata)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
log('E15')
|
log('E15')
|
||||||
@@ -96,36 +112,68 @@ class smbcreds (smbopts):
|
|||||||
Get GPO list for the specified username for the specified DC
|
Get GPO list for the specified username for the specified DC
|
||||||
hostname
|
hostname
|
||||||
'''
|
'''
|
||||||
gpos = list()
|
gpos = []
|
||||||
|
if Dconf_registry.get_info('machine_name') == username:
|
||||||
|
dconf_dict = Dconf_registry.get_dictionary_from_dconf_file_db(save_dconf_db=True)
|
||||||
|
self.is_machine = True
|
||||||
|
else:
|
||||||
|
dconf_dict = Dconf_registry.get_dictionary_from_dconf_file_db(get_uid_by_username(username), save_dconf_db=True)
|
||||||
|
self.is_machine = False
|
||||||
|
if not self.is_machine and Dconf_registry.get_info('trust'):
|
||||||
|
# TODO: Always returning an empty list here.
|
||||||
|
# Need to implement fetching policies from the trusted domain.
|
||||||
|
return []
|
||||||
|
|
||||||
|
dict_gpo_name_version = extract_display_name_version(dconf_dict, username)
|
||||||
try:
|
try:
|
||||||
log('D48')
|
log('D48')
|
||||||
ads = samba.gpo.ADS_STRUCT(self.selected_dc, self.lp, self.creds)
|
ads = samba.gpo.ADS_STRUCT(self.selected_dc, self.lp, self.creds)
|
||||||
if ads.connect():
|
if ads.connect():
|
||||||
log('D47')
|
log('D47')
|
||||||
gpos = ads.get_gpo_list(username)
|
gpos = ads.get_gpo_list(username)
|
||||||
logdata = dict({'username': username})
|
logdata = {'username': username}
|
||||||
log('I1', logdata)
|
log('I1', logdata)
|
||||||
for gpo in gpos:
|
for gpo in gpos:
|
||||||
# These setters are taken from libgpo/pygpo.c
|
# These setters are taken from libgpo/pygpo.c
|
||||||
# print(gpo.ds_path) # LDAP entry
|
# print(gpo.ds_path) # LDAP entry
|
||||||
ldata = dict({'gpo_name': gpo.display_name, 'gpo_uuid': gpo.name, 'file_sys_path': gpo.file_sys_path})
|
if gpo.display_name in dict_gpo_name_version.keys() and dict_gpo_name_version.get(gpo.display_name, {}).get('version') == str(getattr(gpo, 'version', None)):
|
||||||
|
if Path(dict_gpo_name_version.get(gpo.display_name, {}).get('correct_path')).exists():
|
||||||
|
gpo.file_sys_path = ''
|
||||||
|
ldata = {'gpo_name': gpo.display_name, 'gpo_uuid': gpo.name, 'file_sys_path_cache': True}
|
||||||
|
log('I11', ldata)
|
||||||
|
continue
|
||||||
|
ldata = {'gpo_name': gpo.display_name, 'gpo_uuid': gpo.name, 'file_sys_path': gpo.file_sys_path}
|
||||||
log('I2', ldata)
|
log('I2', ldata)
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict({'username': username, 'dc': self.selected_dc})
|
if self.selected_dc != self.pdc_emulator_server:
|
||||||
|
raise GetGPOListFail(exc)
|
||||||
|
logdata = {'username': username, 'dc': self.selected_dc, 'exc': exc}
|
||||||
log('E17', logdata)
|
log('E17', logdata)
|
||||||
|
|
||||||
return gpos
|
return gpos
|
||||||
|
|
||||||
def update_gpos(self, username):
|
def update_gpos(self, username):
|
||||||
gpos = self.get_gpos(username)
|
|
||||||
|
|
||||||
list_selected_dc = set()
|
list_selected_dc = set()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if self.dc_site_servers:
|
||||||
|
self.selected_dc = self.dc_site_servers.pop()
|
||||||
|
|
||||||
|
self.all_servers = [dc for dc in self.all_servers if dc != self.selected_dc]
|
||||||
list_selected_dc.add(self.selected_dc)
|
list_selected_dc.add(self.selected_dc)
|
||||||
|
|
||||||
|
try:
|
||||||
|
gpos = self.get_gpos(username)
|
||||||
|
|
||||||
|
except GetGPOListFail:
|
||||||
|
self.selected_dc = self.pdc_emulator_server
|
||||||
|
gpos = self.get_gpos(username)
|
||||||
|
|
||||||
while list_selected_dc:
|
while list_selected_dc:
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['username'] = username
|
logdata['username'] = username
|
||||||
logdata['dc'] = self.selected_dc
|
logdata['dc'] = self.selected_dc
|
||||||
try:
|
try:
|
||||||
@@ -136,27 +184,141 @@ class smbcreds (smbopts):
|
|||||||
except NTSTATUSError as smb_exc:
|
except NTSTATUSError as smb_exc:
|
||||||
logdata['smb_exc'] = str(smb_exc)
|
logdata['smb_exc'] = str(smb_exc)
|
||||||
if not check_scroll_enabled():
|
if not check_scroll_enabled():
|
||||||
log('F1', logdata)
|
if self.pdc_emulator_server and self.selected_dc != self.pdc_emulator_server:
|
||||||
raise smb_exc
|
self.selected_dc = self.pdc_emulator_server
|
||||||
self.selected_dc = get_dc_hostname(self.creds, self.lp)
|
logdata['action'] = 'Selected pdc'
|
||||||
if self.selected_dc not in list_selected_dc:
|
logdata['pdc'] = self.selected_dc
|
||||||
logdata['action'] = 'Search another dc'
|
log('W11', logdata)
|
||||||
log('W11', logdata)
|
else:
|
||||||
list_selected_dc.add(self.selected_dc)
|
log('F1', logdata)
|
||||||
|
raise smb_exc
|
||||||
else:
|
else:
|
||||||
log('F1', logdata)
|
if self.dc_site_servers:
|
||||||
raise smb_exc
|
self.selected_dc = self.dc_site_servers.pop()
|
||||||
|
elif self.all_servers:
|
||||||
|
self.selected_dc = self.all_servers.pop()
|
||||||
|
else:
|
||||||
|
self.selected_dc = self.pdc_emulator_server
|
||||||
|
|
||||||
|
|
||||||
|
if self.selected_dc not in list_selected_dc:
|
||||||
|
logdata['action'] = 'Search another dc'
|
||||||
|
logdata['another_dc'] = self.selected_dc
|
||||||
|
log('W11', logdata)
|
||||||
|
list_selected_dc.add(self.selected_dc)
|
||||||
|
else:
|
||||||
|
log('F1', logdata)
|
||||||
|
raise smb_exc
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata['exc'] = str(exc)
|
logdata['exc'] = str(exc)
|
||||||
log('F1', logdata)
|
log('F1', logdata)
|
||||||
raise exc
|
raise exc
|
||||||
return gpos
|
return gpos
|
||||||
|
|
||||||
|
|
||||||
|
class SiteDomainScanner:
|
||||||
|
def __init__(self, smbcreds, lp, dc):
|
||||||
|
self.samdb = SamDB(url='ldap://{}'.format(dc), session_info=system_session(), credentials=smbcreds, lp=lp)
|
||||||
|
Dconf_registry.set_info('samdb', self.samdb)
|
||||||
|
self.pdc_emulator = self._search_pdc_emulator()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_ldb_single_message_attr(ldb_message, attr_name, encoding='utf8'):
|
||||||
|
if attr_name in ldb_message:
|
||||||
|
return ldb_message[attr_name][0].decode(encoding)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_ldb_single_result_attr(ldb_result, attr_name, encoding='utf8'):
|
||||||
|
if len(ldb_result) == 1 and attr_name in ldb_result[0]:
|
||||||
|
return ldb_result[0][attr_name][0].decode(encoding)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _get_server_hostname(self, ds_service_name):
|
||||||
|
ds_service_name_dn = ldb.Dn(self.samdb, ds_service_name)
|
||||||
|
server_dn = ds_service_name_dn.parent()
|
||||||
|
res = self.samdb.search(server_dn, scope=ldb.SCOPE_BASE)
|
||||||
|
return self._get_ldb_single_result_attr(res, 'dNSHostName')
|
||||||
|
|
||||||
|
def _search_pdc_emulator(self):
|
||||||
|
res = self.samdb.search(self.samdb.domain_dn(), scope=ldb.SCOPE_BASE)
|
||||||
|
pdc_settings_object = self._get_ldb_single_result_attr(res, 'fSMORoleOwner')
|
||||||
|
return self._get_server_hostname(pdc_settings_object)
|
||||||
|
|
||||||
|
def get_ip_addresses(self):
|
||||||
|
interface_list = netifaces.interfaces()
|
||||||
|
addresses = []
|
||||||
|
for iface in interface_list:
|
||||||
|
address_entry = netifaces.ifaddresses(iface)
|
||||||
|
if netifaces.AF_INET in address_entry:
|
||||||
|
addresses.extend(ipaddress.ip_address(ipv4_address_entry['addr']) for ipv4_address_entry in address_entry[netifaces.AF_INET])
|
||||||
|
if netifaces.AF_INET6 in address_entry:
|
||||||
|
addresses.extend(ipaddress.ip_address(ipv6_address_entry['addr']) for ipv6_address_entry in address_entry[netifaces.AF_INET6])
|
||||||
|
return addresses
|
||||||
|
|
||||||
|
def get_ad_subnets_sites(self):
|
||||||
|
subnet_dn = ldb.Dn(self.samdb, "CN=Subnets,CN=Sites")
|
||||||
|
config_dn = self.samdb.get_config_basedn()
|
||||||
|
subnet_dn.add_base(config_dn)
|
||||||
|
res = self.samdb.search(subnet_dn, ldb.SCOPE_ONELEVEL, expression='objectClass=subnet', attrs=['cn', 'siteObject'])
|
||||||
|
subnets = {ipaddress.ip_network(self._get_ldb_single_message_attr(msg, 'cn')): self._get_ldb_single_message_attr(msg, 'siteObject') for msg in res}
|
||||||
|
return subnets
|
||||||
|
|
||||||
|
def get_ad_site_servers(self, site):
|
||||||
|
servers_dn = ldb.Dn(self.samdb, "CN=Servers")
|
||||||
|
site_dn = ldb.Dn(self.samdb, site)
|
||||||
|
servers_dn.add_base(site_dn)
|
||||||
|
res = self.samdb.search(servers_dn, ldb.SCOPE_ONELEVEL, expression='objectClass=server', attrs=['dNSHostName'])
|
||||||
|
servers = [self._get_ldb_single_message_attr(msg, 'dNSHostName') for msg in res]
|
||||||
|
random.shuffle(servers)
|
||||||
|
return servers
|
||||||
|
|
||||||
|
def get_ad_all_servers(self):
|
||||||
|
sites_dn = ldb.Dn(self.samdb, "CN=Sites")
|
||||||
|
config_dn = self.samdb.get_config_basedn()
|
||||||
|
sites_dn.add_base(config_dn)
|
||||||
|
res = self.samdb.search(sites_dn, ldb.SCOPE_SUBTREE, expression='objectClass=server', attrs=['dNSHostName'])
|
||||||
|
servers = [self._get_ldb_single_message_attr(msg, 'dNSHostName') for msg in res]
|
||||||
|
random.shuffle(servers)
|
||||||
|
return servers
|
||||||
|
|
||||||
|
def check_ip_in_subnets(self, ip_addresses, subnets_sites):
|
||||||
|
return next((subnets_sites[subnet] for subnet in subnets_sites.keys()
|
||||||
|
if any(ip_address in subnet for ip_address in ip_addresses)), None)
|
||||||
|
|
||||||
|
def select_site_servers(self):
|
||||||
|
try:
|
||||||
|
ip_addresses = self.get_ip_addresses()
|
||||||
|
subnets_sites = self.get_ad_subnets_sites()
|
||||||
|
|
||||||
|
our_site = self.check_ip_in_subnets(ip_addresses, subnets_sites)
|
||||||
|
|
||||||
|
servers = []
|
||||||
|
if our_site:
|
||||||
|
servers = self.get_ad_site_servers(our_site)
|
||||||
|
random.shuffle(servers)
|
||||||
|
return servers
|
||||||
|
except Exception as e:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def select_all_servers(self):
|
||||||
|
try:
|
||||||
|
servers = self.get_ad_all_servers()
|
||||||
|
random.shuffle(servers)
|
||||||
|
return servers
|
||||||
|
except Exception as e:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def select_pdc_emulator_server(self):
|
||||||
|
return self.pdc_emulator
|
||||||
|
|
||||||
def expand_windows_var(text, username=None):
|
def expand_windows_var(text, username=None):
|
||||||
'''
|
'''
|
||||||
Scan the line for percent-encoded variables and expand them.
|
Scan the line for percent-encoded variables and expand them.
|
||||||
'''
|
'''
|
||||||
variables = dict()
|
variables = {}
|
||||||
variables['HOME'] = '/etc/skel'
|
variables['HOME'] = '/etc/skel'
|
||||||
variables['HOMEPATH'] = '/etc/skel'
|
variables['HOMEPATH'] = '/etc/skel'
|
||||||
variables['HOMEDRIVE'] = '/'
|
variables['HOMEDRIVE'] = '/'
|
||||||
@@ -194,9 +356,9 @@ def transform_windows_path(text):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def check_scroll_enabled():
|
def check_scroll_enabled():
|
||||||
storage = registry_factory('registry')
|
storage = registry_factory()
|
||||||
enable_scroll = 'Software\\BaseALT\\Policies\\GPUpdate\\ScrollSysvolDC'
|
enable_scroll = '/Software/BaseALT/Policies/GPUpdate/ScrollSysvolDC'
|
||||||
if storage.get_hklm_entry(enable_scroll):
|
if storage.get_key_value(enable_scroll):
|
||||||
data = storage.get_hklm_entry(enable_scroll).data
|
data = storage.get_hklm_entry(enable_scroll).data
|
||||||
return bool(int(data))
|
return bool(int(data))
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ def xdg_get_desktop(username, homedir = None):
|
|||||||
homedir = get_homedir(username)
|
homedir = get_homedir(username)
|
||||||
if not homedir:
|
if not homedir:
|
||||||
msgtext = message_with_code('E18')
|
msgtext = message_with_code('E18')
|
||||||
logdata = dict()
|
logdata = {}
|
||||||
logdata['username'] = username
|
|
||||||
log('E18', logdata)
|
log('E18', logdata)
|
||||||
raise Exception(msgtext)
|
raise Exception(msgtext)
|
||||||
|
|
||||||
|
|||||||
156
gpupdate.spec
156
gpupdate.spec
@@ -8,9 +8,13 @@
|
|||||||
%add_python3_req_skip gpt.gpt
|
%add_python3_req_skip gpt.gpt
|
||||||
%add_python3_req_skip gpt.printers
|
%add_python3_req_skip gpt.printers
|
||||||
%add_python3_req_skip gpt.shortcuts
|
%add_python3_req_skip gpt.shortcuts
|
||||||
|
%add_python3_req_skip gpt.gpo_dconf_mapping
|
||||||
|
%add_python3_req_skip gpt.dynamic_attributes
|
||||||
%add_python3_req_skip messages
|
%add_python3_req_skip messages
|
||||||
|
%add_python3_req_skip plugin
|
||||||
%add_python3_req_skip storage
|
%add_python3_req_skip storage
|
||||||
%add_python3_req_skip storage.fs_file_cache
|
%add_python3_req_skip storage.fs_file_cache
|
||||||
|
%add_python3_req_skip storage.dconf_registry
|
||||||
%add_python3_req_skip util
|
%add_python3_req_skip util
|
||||||
%add_python3_req_skip util.arguments
|
%add_python3_req_skip util.arguments
|
||||||
%add_python3_req_skip util.config
|
%add_python3_req_skip util.config
|
||||||
@@ -30,9 +34,11 @@
|
|||||||
%add_python3_req_skip util.windows
|
%add_python3_req_skip util.windows
|
||||||
%add_python3_req_skip util.xml
|
%add_python3_req_skip util.xml
|
||||||
%add_python3_req_skip util.gpoa_ini_parsing
|
%add_python3_req_skip util.gpoa_ini_parsing
|
||||||
|
%add_python3_req_skip util.ipacreds
|
||||||
|
%add_python3_req_skip frontend.appliers.ini_file
|
||||||
|
|
||||||
Name: gpupdate
|
Name: gpupdate
|
||||||
Version: 0.9.13.7
|
Version: 0.13.4
|
||||||
Release: alt1
|
Release: alt1
|
||||||
|
|
||||||
Summary: GPT applier
|
Summary: GPT applier
|
||||||
@@ -48,11 +54,18 @@ BuildRequires: gettext-tools
|
|||||||
Requires: python3-module-rpm
|
Requires: python3-module-rpm
|
||||||
Requires: python3-module-dbus
|
Requires: python3-module-dbus
|
||||||
Requires: python3-module-configobj
|
Requires: python3-module-configobj
|
||||||
Requires: oddjob-%name >= 0.2.0
|
Requires: python3-module-gssapi
|
||||||
|
Requires: python3-module-krb5
|
||||||
|
Requires: oddjob-%name >= 0.2.3
|
||||||
Requires: libnss-role >= 0.5.0
|
Requires: libnss-role >= 0.5.0
|
||||||
Requires: local-policy >= 0.4.9
|
Requires: local-policy >= 0.4.9
|
||||||
Requires: pam-config >= 1.9.0
|
Requires: pam-config >= 1.9.0
|
||||||
Requires: autofs
|
Requires: autofs
|
||||||
|
Requires: dconf-profile
|
||||||
|
Requires: packagekit
|
||||||
|
Requires: dconf
|
||||||
|
Requires: libgvdb-gir
|
||||||
|
Requires: freeipa-client-samba
|
||||||
# This is needed by shortcuts_applier
|
# This is needed by shortcuts_applier
|
||||||
Requires: desktop-file-utils
|
Requires: desktop-file-utils
|
||||||
# This is needed for smb file cache support
|
# This is needed for smb file cache support
|
||||||
@@ -119,6 +132,9 @@ install -Dm0644 dist/%name-remote-policy %buildroot%_sysconfdir/pam.d/%name-remo
|
|||||||
install -Dm0644 dist/%name.ini %buildroot%_sysconfdir/%name/%name.ini
|
install -Dm0644 dist/%name.ini %buildroot%_sysconfdir/%name/%name.ini
|
||||||
install -Dm0644 doc/gpoa.1 %buildroot/%_man1dir/gpoa.1
|
install -Dm0644 doc/gpoa.1 %buildroot/%_man1dir/gpoa.1
|
||||||
install -Dm0644 doc/gpupdate.1 %buildroot/%_man1dir/gpupdate.1
|
install -Dm0644 doc/gpupdate.1 %buildroot/%_man1dir/gpupdate.1
|
||||||
|
install -Dm0644 completions/gpoa %buildroot/%_datadir/bash-completion/completions/gpoa
|
||||||
|
install -Dm0644 completions/gpupdate %buildroot/%_datadir/bash-completion/completions/gpupdate
|
||||||
|
install -Dm0644 completions/gpupdate-setup %buildroot/%_datadir/bash-completion/completions/gpupdate-setup
|
||||||
|
|
||||||
for i in gpupdate-localusers \
|
for i in gpupdate-localusers \
|
||||||
gpupdate-group-users \
|
gpupdate-group-users \
|
||||||
@@ -165,9 +181,12 @@ fi
|
|||||||
%_unitdir/%name.timer
|
%_unitdir/%name.timer
|
||||||
%_man1dir/gpoa.1.*
|
%_man1dir/gpoa.1.*
|
||||||
%_man1dir/gpupdate.1.*
|
%_man1dir/gpupdate.1.*
|
||||||
/usr/lib/systemd/user/%name-user.service
|
%_datadir/bash-completion/completions/gpoa
|
||||||
/usr/lib/systemd/user/%name-user.timer
|
%_datadir/bash-completion/completions/gpupdate
|
||||||
/usr/lib/systemd/user/%name-scripts-run-user.service
|
%_datadir/bash-completion/completions/gpupdate-setup
|
||||||
|
%_user_unitdir/%name-user.service
|
||||||
|
%_user_unitdir/%name-user.timer
|
||||||
|
%_user_unitdir/%name-scripts-run-user.service
|
||||||
%dir %_sysconfdir/%name
|
%dir %_sysconfdir/%name
|
||||||
%_sysconfdir/control.d/facilities/*
|
%_sysconfdir/control.d/facilities/*
|
||||||
%config(noreplace) %_sysconfdir/%name/environment
|
%config(noreplace) %_sysconfdir/%name/environment
|
||||||
@@ -183,6 +202,133 @@ fi
|
|||||||
%exclude %python3_sitelibdir/gpoa/test
|
%exclude %python3_sitelibdir/gpoa/test
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Mon Aug 25 2025 Valery Sinelnikov <greh@altlinux.org> 0.13.4-alt1
|
||||||
|
- Added:
|
||||||
|
Production-ready modules: CUPS, file management, INI config (default),
|
||||||
|
package management, script modules
|
||||||
|
Fallback SID lookup for trusted domain users
|
||||||
|
Missing log translations (laps: timezone, login)
|
||||||
|
Added ownership handling for files within user home directory
|
||||||
|
- Changed:
|
||||||
|
Refactored to use literals ({}, []) instead of constructors
|
||||||
|
Final optimization passes and minor cleanups
|
||||||
|
Updated copyright year
|
||||||
|
Adjusted login time search and messages
|
||||||
|
- Fixed:
|
||||||
|
Skipped policy retrieval for trusted users to avoid GPO errors
|
||||||
|
Corrected login time tracking in laps
|
||||||
|
Fixed typos
|
||||||
|
Prevented subprocess errors from printing to console
|
||||||
|
Adjusted call order (save_dconf - start_frontend)
|
||||||
|
- Removed:
|
||||||
|
Legacy sid variable propagation and related helpers
|
||||||
|
|
||||||
|
* Sat Jul 26 2025 Evgeny Sinelnikov <sin@altlinux.org> 0.13.3-alt1
|
||||||
|
- Fixed machine account credentials initialization (closes: 55324)
|
||||||
|
|
||||||
|
* Thu Apr 03 2025 Valery Sinelnikov <greh@altlinux.org> 0.13.2-alt1
|
||||||
|
- Fixed: Check directory existence before cleanup to avoid errors(closes:53703)
|
||||||
|
|
||||||
|
* Fri Mar 14 2025 Valery Sinelnikov <greh@altlinux.org> 0.13.1-alt1
|
||||||
|
- Refined registry key handling: LAPS enablement and user presence check
|
||||||
|
|
||||||
|
* Thu Mar 06 2025 Valery Sinelnikov <greh@altlinux.org> 0.13.0-alt1
|
||||||
|
- Implemented Local Administrator Password Solution (LAPS) functionality,
|
||||||
|
including support for Group Policy Object (GPO) keys to
|
||||||
|
configure LAPS settings
|
||||||
|
- Added support for disabling cifsacl in autofs mounts (closes:52333)
|
||||||
|
- Implemented the ability to merge computer and user GPO shortcuts
|
||||||
|
- Added access restrictions to network directories of other users
|
||||||
|
- Added cleaning functionality for the autofs configuration catalog
|
||||||
|
- Added ability to configure KDE 6 files
|
||||||
|
|
||||||
|
* Tue Jan 14 2025 Valery Sinelnikov <greh@altlinux.org> 0.12.2-alt1
|
||||||
|
- Fixed interpretation of boolean values (closes:52683)
|
||||||
|
|
||||||
|
* Fri Jan 10 2025 Valery Sinelnikov <greh@altlinux.org> 0.12.1-alt1
|
||||||
|
- Fixed checking the path for existence (closes:52597)
|
||||||
|
|
||||||
|
* Tue Dec 10 2024 Valery Sinelnikov <greh@altlinux.org> 0.12.0-alt1
|
||||||
|
- Special thanks to Andrey Belgorodtsev (andrey@net55.su)
|
||||||
|
for valuable pre-release testing and feedback
|
||||||
|
- Added applier thunderbird
|
||||||
|
- Added environment file cleaning (closes: 51016)
|
||||||
|
- Added the ability to set the name of the directory to automount
|
||||||
|
- Added the ability to remove the prefix from a sylink
|
||||||
|
to the catalog in automount
|
||||||
|
- Added the ability to set the timeout in automount
|
||||||
|
- Added messages using the force mode
|
||||||
|
- Improved KDE update logic
|
||||||
|
- Added preservation of previous keys
|
||||||
|
|
||||||
|
* Fri Oct 11 2024 Valery Sinelnikov <greh@altlinux.org> 0.11.4-alt1
|
||||||
|
- Added skip plugin (closes: 51631)
|
||||||
|
- Fixed getting the network path (closes:51606)
|
||||||
|
- The _appliers sequence has been changed,
|
||||||
|
package_applier has been moved to the end
|
||||||
|
|
||||||
|
* Fri Sep 06 2024 Valery Sinelnikov <greh@altlinux.org> 0.11.3-alt1
|
||||||
|
- Optimized string cleaning using str.translate()
|
||||||
|
|
||||||
|
* Wed Sep 04 2024 Valery Sinelnikov <greh@altlinux.org> 0.11.2-alt1
|
||||||
|
- Fixed data type handling in kde_applier
|
||||||
|
- Removing legacy unused code
|
||||||
|
- Added saving policy data without polfile
|
||||||
|
- Added escaping of special characters in data (closes: 51201)
|
||||||
|
|
||||||
|
* Tue Aug 27 2024 Valery Sinelnikov <greh@altlinux.org> 0.11.1-alt1
|
||||||
|
- Fixed setting links in shortcuts (closes: 51275)
|
||||||
|
|
||||||
|
* Fri Aug 09 2024 Valery Sinelnikov <greh@altlinux.org> 0.11.0-alt1
|
||||||
|
- Added saving preferences in dconf
|
||||||
|
- Added versioning support for gpt
|
||||||
|
- Added the ability to force gpt download
|
||||||
|
- Added completions for --force
|
||||||
|
- Added new exceptions for Chromium 126
|
||||||
|
- Added information to the man pages
|
||||||
|
- Fixed handling of incorrect valuename
|
||||||
|
|
||||||
|
* Mon Jul 08 2024 Valery Sinelnikov <greh@altlinux.org> 0.10.6-alt1
|
||||||
|
- Fixed firefox_applier errors
|
||||||
|
|
||||||
|
* Fri Jun 28 2024 Valery Sinelnikov <greh@altlinux.org> 0.10.5-alt1
|
||||||
|
- Correction of missing entries with a upper case
|
||||||
|
- Fixed string processing in date (closes: 50782)
|
||||||
|
- Fixed getting correct data for the user for pkcon_runner
|
||||||
|
|
||||||
|
* Thu Jun 27 2024 Valery Sinelnikov <greh@altlinux.org> 0.10.4-alt1
|
||||||
|
- Fixed the definition of the module activation check (closes: 50755)
|
||||||
|
- Fixed sorting of scripts (closes: 50756)
|
||||||
|
- Fixed reading key values from dconf
|
||||||
|
- Changed the method for getting the list of packages for pkcon_runner
|
||||||
|
|
||||||
|
* Wed Jun 19 2024 Valery Sinelnikov <greh@altlinux.org> 0.10.3-alt1
|
||||||
|
- Added autocompletion for gpoa, gpupdate, gpupdate-setup
|
||||||
|
- Added correct work with json data in keys for the Firefox browser
|
||||||
|
- Polkit_appliers changed to non-experimental
|
||||||
|
- Fixed bug of not clearing kde applier settings (closes: 50336)
|
||||||
|
- Fixed registry key reading (closes: 50553)
|
||||||
|
- Added waiting for data generation for scripts (closes: 50667)
|
||||||
|
|
||||||
|
* Fri Jun 07 2024 Valery Sinelnikov <greh@altlinux.org> 0.10.2-alt1
|
||||||
|
- Added some fixes to dconf_registry and scripts
|
||||||
|
- Fixed windows registry key reading for loopback
|
||||||
|
|
||||||
|
* Tue Jun 04 2024 Valery Sinelnikov <greh@altlinux.org> 0.10.1-alt1
|
||||||
|
- Added handling of unexpected data types when writing to dconf
|
||||||
|
|
||||||
|
* Mon May 13 2024 Valery Sinelnikov <greh@altlinux.org> 0.10.0-alt1
|
||||||
|
- A method for storing registry keys obtained from GPOs (Group Policy Objects)
|
||||||
|
has undergone significant repairs. We have switched from using SQLite
|
||||||
|
to using Dconf to improve data storage efficiency
|
||||||
|
|
||||||
|
* Wed Mar 13 2024 Valery Sinelnikov <greh@altlinux.org> 0.9.13.9-alt1
|
||||||
|
- Fixed premature removal of double slash
|
||||||
|
|
||||||
|
* Thu Feb 22 2024 Valery Sinelnikov <greh@altlinux.org> 0.9.13.8-alt1
|
||||||
|
- Added search for dc on the site
|
||||||
|
- Added compatibility support for the oldest versions of SQLAlchemy
|
||||||
|
|
||||||
* Mon Feb 05 2024 Valery Sinelnikov <greh@altlinux.org> 0.9.13.7-alt1
|
* Mon Feb 05 2024 Valery Sinelnikov <greh@altlinux.org> 0.9.13.7-alt1
|
||||||
- Editing the cache size in the Yandex browser has returned (closes: 44621)
|
- Editing the cache size in the Yandex browser has returned (closes: 44621)
|
||||||
- Removed unnecessary calls to subprocess
|
- Removed unnecessary calls to subprocess
|
||||||
|
|||||||
Reference in New Issue
Block a user