Compare commits

..

346 Commits

Author SHA1 Message Date
Adolfo Gómez García
1417a66b21 Small fixes on OpenNebula complains 2021-07-02 12:45:16 +02:00
Adolfo Gómez García
9ba4234313 Removed NX 2021-07-02 11:06:23 +02:00
Adolfo Gómez García
e85e4c4e54 Merge remote-tracking branch 'origin/v3.0' 2021-07-02 11:02:11 +02:00
Adolfo Gómez García
4be9e9ea69 Added more info to UDSClient and small typo fixed 2021-07-01 22:04:24 +02:00
Adolfo Gómez García
e38cd73f30 Added user agent 2021-07-01 21:46:00 +02:00
Adolfo Gómez García
43b785eb73 Added more info on ticket error 2021-07-01 21:42:25 +02:00
Adolfo Gómez García
9c4a4ed35c Fixed RDP for MacOS 2021-07-01 21:38:21 +02:00
Adolfo Gómez García
02737c0e8d Fixed guacamole auth url 2021-06-29 16:57:01 +02:00
Adolfo Gómez García
8bbd897cd0 Added ticket check 2021-06-29 16:26:10 +02:00
Adolfo Gómez García
c98933b6ed Fixed guacamole urls && small pam fix 2021-06-29 14:48:04 +02:00
Adolfo Gómez García
6e0292e76e Fixed guacamole new url && added 0000... as admin alias for auths 2021-06-29 13:02:09 +02:00
Adolfo Gómez García
8e6fced2ac Added second-log to login in case invalid username/password is detected 2021-06-29 11:33:58 +02:00
Adolfo Gómez García
c5a02686c4 added "auth_id" as alias for "authId" on login
Changed "uds_auth" to "uds_token"
2021-06-28 19:02:44 +02:00
Adolfo Gómez García
bddb9355c8 Adding tunnelers tokens for increased security 2021-06-28 15:36:52 +02:00
Adolfo Gómez García
25736f61b8 Removed requests "tests" and updated tunnel to use an authId 2021-06-28 13:12:49 +02:00
Adolfo Gómez García
2ee4a7bcaa Several fixes:
* Added tunnel & guacaomle "fake" authid for next security
* Fixed proxy detection & use
2021-06-28 13:05:48 +02:00
Adolfo Gómez García
0da916b57c Updated middleware 2021-06-28 11:02:47 +02:00
Adolfo Gómez García
03012dbaa7 Upgraded to Angular v12. This will be the last release supporting IE11 2021-06-27 22:06:16 +02:00
Adolfo Gómez García
4ae95e1930 Upgraded to Angular 12. Dropped support for admin for IE11 2021-06-27 21:57:15 +02:00
Adolfo Gómez García
906901753e Added "purge" to delete vm from UDS on proxmox 2021-06-25 13:51:24 +02:00
Adolfo Gómez García
109783a430 Fixed texts for client 2021-06-25 13:26:56 +02:00
Adolfo Gómez García
655a6447ba Improved Proxmox query efficiency for machines in a pookl 2021-06-25 13:21:09 +02:00
Adolfo Gómez García
52ac406853 Test migration removal 2021-06-24 12:35:47 +02:00
Adolfo Gómez García
55f9820f37 Fixed migration problem with user & group, when changing from unique_together to constrains on Meta model 2021-06-24 12:27:43 +02:00
Adolfo Gómez García
856d645652 merged 0041 and 0042 migrations 2021-06-24 12:02:41 +02:00
Adolfo Gómez García
fd789581ed Kept by now user and group unique_toguether 2021-06-24 12:02:23 +02:00
Adolfo Gómez García
a67ba2972b Updated translations 2021-06-24 11:25:49 +02:00
Adolfo Gómez García
acd1dd4702 No needed anymore here. It's already on client 2021-06-24 08:21:30 +02:00
Adolfo Gómez García
b914980793 Removing unique_together and index_together for newer Index and UniqueConstrains 2021-06-23 20:58:00 +02:00
Adolfo Gómez García
eab51248cd Fixed service launcher 2021-06-23 20:04:48 +02:00
Adolfo Gómez García
2834120b35 Fixing up status checking 2021-06-23 17:53:13 +02:00
Adolfo Gómez García
83a407d350 Added, for 3.5, check certificate as "no" for backwards compatibility (will be yes by default on 4.0 release) 2021-06-23 16:21:02 +02:00
Adolfo Gómez García
4f45caa2e9 :Merge branch 'master' of github.com:dkmstr/openuds 2021-06-23 16:17:31 +02:00
Adolfo Gómez García
011145e911 Upgraded tunnel.py to last real one 2021-06-23 16:17:23 +02:00
Adolfo Gómez
046f5836f7 Fixed logging 2021-06-23 15:59:15 +02:00
Adolfo Gómez García
4424f2a497 Fixing up client 2021-06-23 15:58:37 +02:00
Adolfo Gómez García
97841d655b more info on subprocesses 2021-06-23 15:14:12 +02:00
Adolfo Gómez García
f20a5a33b0 Fixed debian package dependency && update client tools to add some more debug info 2021-06-23 15:01:34 +02:00
Adolfo Gómez García
d1fb59ab77 Some minor cosmetic changes for UDSClient 2021-06-22 17:12:31 +02:00
Adolfo Gómez García
174d836f45 Merge remote-tracking branch 'origin/v3.0' 2021-06-22 12:11:11 +02:00
Adolfo Gómez García
a070f6878b Removed all 2.7 client due to "portables" clients being available 2021-06-22 11:35:50 +02:00
Adolfo Gómez García
d51b22096e Added check debug logging with a file 2021-06-21 18:00:57 +02:00
Adolfo Gómez García
9e0fbca339 Adding early stage unlinks... 2021-06-21 17:56:16 +02:00
Adolfo Gómez García
3e67ef2f6b Adding early stage unlinks...[F 2021-06-21 17:50:12 +02:00
Adolfo Gómez García
e7fe802b1d fixed bug on wait for tasks 2021-06-21 17:39:35 +02:00
Adolfo Gómez García
6f90a7ce83 fixed bug on wait for tasks 2021-06-21 17:15:51 +02:00
Adolfo Gómez García
25fec929a9 adding some debug to client 2021-06-21 17:11:32 +02:00
Adolfo Gómez García
1abe95c492 adding some debug to client 2021-06-21 17:04:57 +02:00
Adolfo Gómez García
d438fcf298 Updating client to allow wait for subprocesses also 2021-06-21 16:54:34 +02:00
Adolfo Gómez García
539e96d264 Added service initialization exceptio catch-and-retry 2021-06-21 15:28:24 +02:00
Adolfo Gómez García
d30a3a5e4c added tunnel rest url as no redirect url 2021-06-21 15:27:32 +02:00
Adolfo Gómez García
a302541df5 Fixed macos transports to search xfreerdp in path instead of using /usr/local/bin and added homebrew paths to default system ones 2021-06-21 11:21:46 +02:00
Adolfo Gómez García
9cdab65845 Fixed path fixing :) 2021-06-21 11:05:38 +02:00
Adolfo Gómez García
b9c55437ad fixed messages for MAC os 2021-06-21 10:58:37 +02:00
Adolfo Gómez García
0b0c72e65b Added /opt/homebrew/bin as path in mac os x for newer brew installs 2021-06-21 10:53:09 +02:00
Adolfo Gómez García
aef8c637ec added python-3-certifi as dependency for packages (now we do not use QtNetwork anymore for problems in M1) 2021-06-19 17:05:12 +02:00
Adolfo Gómez García
4ed3cbc787 Working on M1 with rosseta, so removed check (that does not recognizes m1 when running app with rosseta) 2021-06-19 17:01:39 +02:00
Adolfo Gómez García
60f69be354 More checks 2021-06-19 15:57:53 +02:00
Adolfo Gómez García
8e815c3316 some more fixes 2021-06-19 15:38:19 +02:00
Adolfo Gómez García
9180d04aaf Fix for show errors 2021-06-19 15:26:05 +02:00
Adolfo Gómez García
6e60a66ae9 Simplifying for M1 2021-06-19 15:16:49 +02:00
Adolfo Gómez García
58cfa779d1 Refactoring UDS Client to allow more possibilities 2021-06-19 14:45:51 +02:00
Adolfo Gómez García
eed4bc5fb7 Updating new UDSClient 2021-06-19 13:29:43 +02:00
Adolfo Gómez García
3ed3f03d25 Changed UDSClient to remove QApp Network access and used urllib instead 2021-06-19 12:41:51 +02:00
Adolfo Gómez García
21f811d995 Added cleanup of intermediary building folders 2021-06-18 10:50:08 +02:00
Adolfo Gómez García
985746139b Fixed Makefile && install script for different platforms 2021-06-17 18:32:22 +02:00
Adolfo Gómez García
9e4a9cc2fd * Added deadline "disabling" on osmanagers, so if we dont want to close sessions for expired users, we can do it 2021-06-17 14:21:40 +02:00
Adolfo Gómez García
c1d5e4b130 src/ 2021-06-17 13:19:47 +02:00
Adolfo Gómez García
21143ab7f2 Merge branch 'master' of github.com:dkmstr/openuds 2021-06-17 12:09:06 +02:00
Adolfo Gómez García
b62dfad922 Added i686 to appimage portable 2021-06-17 12:08:55 +02:00
Adolfo Gómez García
0b3bcbc63d Fixed UDSClientDir to /tmp (BTW, appimage-builder does not work on btrfs with compressed files due to squashfs) 2021-06-17 11:14:05 +02:00
Adolfo Gómez
b4a1e6a903 Merge branch 'master' of https://github.com/dkmstr/openuds 2021-06-16 18:31:13 +02:00
Adolfo Gómez
df9cb4eb6a Updated sysprep checking from info to debug 2021-06-16 18:31:06 +02:00
Adolfo Gómez García
7f4e7e3309 Merge branch 'master' of github.com:dkmstr/openuds 2021-06-16 18:28:17 +02:00
Adolfo Gómez García
741787f95b Fixed building of portables uds clients with freerdp + x2go client 2021-06-16 18:28:10 +02:00
Adolfo Gómez
7b0ad08685 Merge branch 'master' of https://github.com/dkmstr/openuds 2021-06-16 14:34:57 +02:00
Adolfo Gómez
25b663e069 Adding 'windows installations' checks before running uds service 2021-06-16 14:34:52 +02:00
Adolfo Gómez García
1bb258d9dc Fixed WOL for multi manual assigment 2021-06-15 16:22:47 +02:00
Adolfo Gómez García
d5f29bd20f Added ignore certificate for getting server stats on localhost 2021-06-15 13:32:22 +02:00
Adolfo Gómez García
69fe9e0d38 * User preferences is deprecated (to be removed)
* NX is disabled in code (to be removed on 4.0)
* Increeased URL size for "URL" transport to 256 (was 64 only)
2021-06-15 11:49:16 +02:00
Adolfo Gómez García
41c94913f8 added full clients appimage 2021-06-11 12:02:26 +02:00
Adolfo Gómez García
0dce270a9e Created portable udsclient using appimage. Now also for raspberry pi 2021-06-10 17:25:23 +02:00
Adolfo Gómez García
594d431af7 Created portable udsclient using appimage 2021-06-10 17:02:11 +02:00
Adolfo Gómez García
71242eba10 moving appimage creator to linux folder 2021-06-10 16:17:21 +02:00
Adolfo Gómez García
2dded06dc5 Added app image creation recipe for UDSClient + freerdp 2021-06-10 12:15:45 +02:00
Adolfo Gómez García
38490e184e Updated criptography part to be compatible with older releases of crpytography package 2021-06-09 17:47:49 +02:00
Adolfo Gómez García
9e462478fc Removed six dependency for client-py3 and related 2021-06-09 12:39:06 +02:00
Adolfo Gómez García
44d8b2b754 Several upgrades for newer distributions:
* Removed pycrypto dependencies for UDSClient
* added "-platform xcb" for uds executables for Actor & client3
2021-06-09 12:12:31 +02:00
Adolfo Gómez García
5884cde35c a couple of comments 2021-06-08 20:58:55 +02:00
Adolfo Gómez García
3b18597d8e Removed PyCrypto code and translated to cryptography code 2021-06-08 19:08:41 +02:00
Adolfo Gómez García
f6ddc7eef1 Added ID to machine info on service creation 2021-06-08 18:44:52 +02:00
Adolfo Gómez García
ed61fbf7b8 No, if a child dies, UDS Tunnel will regenerate a new process and try to clean the old one 2021-06-08 11:48:53 +02:00
Adolfo Gómez García
fb088ecc02 Updated UDSClient
* Reformated code
* Fixed UDS Rest API Route to /uds/rest/... instead of /rest/... (will not be compatible with 2.x anymore)
2021-06-07 21:10:57 +02:00
Adolfo Gómez García
5396d04555 Merge remote-tracking branch 'origin/v3.0' 2021-06-07 21:02:30 +02:00
Adolfo Gómez García
68b3b50acf Merge branch 'master' of github.com:dkmstr/openuds 2021-06-04 13:25:45 +02:00
Adolfo Gómez García
9d6560a56e fixed version 2021-06-04 12:07:07 +02:00
Adolfo Gómez García
25363269a6 Merge remote-tracking branch 'origin/v3.0' 2021-06-04 11:50:51 +02:00
Adolfo Gómez García
8aca8ead3d Merge remote-tracking branch 'origin/v3.0' 2021-06-03 21:02:36 +02:00
Adolfo Gómez García
f184fa778d Refactorized ldap and added "ignores" to non recognized correct values 2021-06-03 11:43:56 +02:00
Adolfo Gómez García
21f6df36b0 Refactor for all middlewares (now are all on same place..) 2021-06-01 13:17:53 +02:00
Adolfo Gómez García
394ceb9e66 Added radius authenticator for UDS 2021-06-01 12:41:58 +02:00
Adolfo Gómez García
5f8abdfa41 Merge remote-tracking branch 'origin/v3.0' 2021-05-31 11:33:13 +02:00
Adolfo Gómez García
b8af381042 Fixed autocomplete 2021-05-31 11:32:58 +02:00
Adolfo Gómez García
cb92be3c66 Simplifying several "is True" 2021-05-27 13:04:18 +02:00
Adolfo Gómez García
8fc5c759d8 Merge remote-tracking branch 'origin/v3.0' 2021-05-27 10:58:18 +02:00
Adolfo Gómez García
6c936a7dfa Fixed report definition, so filename can be changed (not an class variable anymore) and added sample xlsx report onlist of users (only sample, commmented out) 2021-05-25 19:03:55 +02:00
Adolfo Gómez García
0cc40198b2 Merge remote-tracking branch 'origin/v3.0' 2021-05-25 10:41:25 +02:00
Adolfo Gómez García
9789a2f868 Updated admin for pool charts 2021-05-21 14:42:11 +02:00
Adolfo Gómez García
e8733e74d1 Added graphs to servicePool 2021-05-21 14:17:49 +02:00
Adolfo Gómez García
c6d281580b Added soft shutdown first for ProxMox 2021-05-21 12:18:55 +02:00
Adolfo Gómez García
1050ada43b Improved internaldb password security (sha3_256) and added extra security to uds cookie 2021-05-21 11:38:41 +02:00
Adolfo Gómez García
f39bc9c5ba Added stats collector a global value for all stats 2021-05-19 12:22:24 +02:00
Adolfo Gómez García
c987915c41 Added stats collector a global value for all stats 2021-05-19 12:20:35 +02:00
Adolfo Gómez García
12b8354a8e Merge remote-tracking branch 'origin/v3.0' 2021-05-19 12:20:11 +02:00
Adolfo Gómez García
e7d1df5ba3 added "global" stats counter to optimize graphs on dashboard 2021-05-19 11:45:40 +02:00
Adolfo Gómez García
e87727b48f fixed counters types not being added for new counter type 2021-05-17 12:59:23 +02:00
Adolfo Gómez García
13ec1877de added number of services in cache for stats 2021-05-17 12:41:35 +02:00
Adolfo Gómez García
1b4060a727 Fixed pylance complains about runtime created variables 2021-05-17 12:13:06 +02:00
Adolfo Gómez García
d8e713ad51 Imported dns.reversename (not imported before) for IP Authenticator reverse resolution 2021-05-17 11:59:54 +02:00
Adolfo Gómez García
cdca39779b Fixed Complains about curio "runtime" types 2021-05-17 11:59:24 +02:00
Adolfo Gómez García
265d4f5103 added admin part for charts 2021-05-12 17:03:28 +02:00
Adolfo Gómez García
b85a702437 Added new index to stats_c and readded the graphs from uds 2.2 2021-05-12 14:40:58 +02:00
Adolfo Gómez García
e2d7fb0790 Reformated SimpleLDAP && fixed mypy complains about runtime variables
Added max session length for user and administrator, without the needing of editing the settings.py
2021-05-11 12:48:36 +02:00
Adolfo Gómez García
a573d2d55b added (at last), the check of invalid IP/hostname values on RDS servers or Phisical Machines 2021-05-10 10:47:00 +02:00
Adolfo Gómez García
de50fef63c Merge remote-tracking branch 'origin/v3.0' 2021-05-07 12:36:20 +02:00
Adolfo Gómez García
0133ddc2b5 Merge remote-tracking branch 'origin/v3.0' 2021-05-06 07:55:00 +02:00
Adolfo Gómez García
cddfd735b2 Merge remote-tracking branch 'origin/v3.0' 2021-05-04 13:07:10 +02:00
Adolfo Gómez García
3f6d12c89f Adding osDetector to UDSClient 2021-05-04 13:05:53 +02:00
Adolfo Gómez García
98293bba75 Merge remote-tracking branch 'origin/v3.0' 2021-05-04 13:05:31 +02:00
Adolfo Gómez García
07738e3dc2 Added support for detecting if UDS Client is launched correctly 2021-04-30 11:13:52 +02:00
Adolfo Gómez García
2b5543905a added "accesedByClient" property so we can check, from web, if local plugin is installed.... 2021-04-29 13:01:07 +02:00
Adolfo Gómez García
87c2ea8add added status checker to web API 2021-04-29 12:09:03 +02:00
Adolfo Gómez García
2a2a2b2ad0 small type checking fix & expresion fix 2021-04-28 10:47:35 +02:00
Adolfo Gómez García
47ef12ef6a Removed Crypto library remmanents. Old 2.2 RSA keys are no longer supported on 3.5 2021-04-28 10:42:45 +02:00
Adolfo Gómez García
451b8f6fb9 Fixes for mypy complains 2021-04-27 11:46:10 +02:00
Adolfo Gómez García
bd2b0cd171 Fixing mypy complains 2021-04-27 11:32:04 +02:00
Adolfo Gómez García
18a8c81af6 Merge remote-tracking branch 'origin/v3.0' 2021-04-26 11:56:20 +02:00
Adolfo Gómez García
09c65b2598 added proxy support for OpenStack & OpenStack legacy 2021-04-23 11:30:45 +02:00
Adolfo Gómez García
6e438bf4cb Added squashed migration from 26 to 38 2021-04-22 17:56:50 +02:00
Adolfo Gómez García
7502fe3bcc Removed squashed migrations 2021-04-22 17:43:29 +02:00
Adolfo Gómez García
e9a719a2eb Added metapools capacity of show grouped pools transports 2021-04-22 14:44:48 +02:00
Adolfo Gómez García
ce73d4e29f Updated admin fix from 3.0 2021-04-22 11:58:52 +02:00
Adolfo Gómez García
ffeaf9e22c Merge remote-tracking branch 'origin/v3.0' 2021-04-22 11:56:16 +02:00
Adolfo Gómez García
1e184a3a34 small fix on graphs (cosmetic) 2021-04-21 23:56:52 +02:00
Adolfo Gómez García
74d4349266 fixed signature of osManager publication method 2021-04-20 14:39:57 +02:00
Adolfo Gómez García
26c9f0edc8 Fixed scheduler next execution (typo make it wait DAYS instead of SECONDS!!! 2021-04-20 14:27:53 +02:00
Adolfo Gómez García
797a5df4a9 Fixed admin imgchoice 2021-04-20 13:30:13 +02:00
Adolfo Gómez García
8fbdda7b56 Merge remote-tracking branch 'origin/v3.0' 2021-04-19 12:59:44 +02:00
Adolfo Gómez García
9f04bdab05 Added custom parameters for freerdp mac clients.
distinct from linux custom parameters.
2021-04-16 11:21:21 +02:00
Adolfo Gómez García
5597af7d85 merged vnc from 3.0 2021-04-13 12:50:03 +02:00
Adolfo Gómez García
697c3e1c52 Merge remote-tracking branch 'origin/v3.0' 2021-04-13 12:47:27 +02:00
Adolfo Gómez García
ae7f867482 fixed default value from html5rdp to any instead of rdp and fixed onw parameter 2021-04-13 11:45:43 +02:00
Adolfo Gómez García
f595219405 added fix for vscode complaining about alias && merged 2021-04-08 17:47:03 +02:00
Adolfo Gómez García
d66e59bd50 small spelling typo fix 2021-04-08 10:23:54 +02:00
Adolfo Gómez García
cc12b7d5f6 Added "backport of new tunnel" to old python 2.7 client 2021-04-07 16:48:24 +02:00
Adolfo Gómez García
d67a9d6ddc fixed reference to Dani Torregrosa (sorry for the mispelling :) ) 2021-04-07 12:32:25 +02:00
Adolfo Gómez García
5ff3c58149 added Dani sugested changes for MSRDP of Microsoft on MacOsX 2021-04-07 10:36:57 +02:00
Adolfo Gómez García
9340e3c3c1 added "backup host" to xen server in case of connection failure to main server 2021-04-06 12:12:57 +02:00
Adolfo Gómez García
4357ef3be8 Merge remote-tracking branch 'origin/v3.0' 2021-04-05 13:21:46 +02:00
Adolfo Gómez García
a3905c0c6c added time to enter credentials before closing new connections though tunnel 2021-04-05 11:24:30 +02:00
Adolfo Gómez García
03fc488f33 Fixed provider of Physical machines MANDATORY flag for advanced config 2021-03-29 12:52:58 +02:00
Adolfo Gómez García
2aee4e9417 Added log if could not resolve ip on PhisicalMachines multi 2021-03-25 12:37:43 +01:00
Adolfo Gómez García
b8494f51ac More fixes for WOL on Phisical Machines 2021-03-24 17:33:39 +01:00
Adolfo Gómez García
93a12c180e More fixes for WOL on Phisical Machines 2021-03-24 17:18:34 +01:00
Adolfo Gómez García
26aa9f6db7 Enhaced "wolURL" check 2021-03-24 12:13:26 +01:00
Adolfo Gómez García
3f881b3e17 Added support for name resolutions on service_multi && fixed max page limit from 50 to 100 2021-03-24 11:54:03 +01:00
Adolfo Gómez García
0a0b4cb740 Merge remote-tracking branch 'origin/v3.0' 2021-03-23 16:42:38 +01:00
Adolfo Gómez García
5df8f640d8 Added config for WOL on UDS 2021-03-23 16:12:51 +01:00
Adolfo Gómez García
8c68da806a Added config for WOL on UDS 2021-03-23 15:38:02 +01:00
Adolfo Gómez García
b9ba304493 Added "ignore port check" if WOLAPP is configured 2021-03-23 12:25:04 +01:00
Adolfo Gómez García
52d3ffeac3 added config to actor block attacks 2021-03-12 15:10:39 +01:00
Adolfo Gómez García
868ff2817a Merge remote-tracking branch 'origin/v3.0' 2021-03-12 15:07:50 +01:00
Adolfo Gómez García
51916e0949 Merge remote-tracking branch 'origin/v3.0' 2021-03-12 11:42:02 +01:00
Adolfo Gómez García
e517281c6a Merge remote-tracking branch 'origin/v3.0' 2021-03-11 14:27:57 +01:00
Adolfo Gómez García
c90f9c40fd Merge remote-tracking branch 'origin/v3.0' 2021-03-11 14:15:01 +01:00
Adolfo Gómez García
c6213ff37c Merge remote-tracking branch 'origin/v3.0' 2021-03-10 18:13:42 +01:00
Adolfo Gómez
3908c875d3 Merge pull request #65 from glyptodon/easy-compat
Ensure guacamole-auth-uds is compatible with third-party branding extensions.
2021-03-05 23:42:55 +01:00
Michael Jumper
c28c6c7b98 Complete removal of code partially removed by commit 073ce3d. 2021-03-05 12:13:21 -08:00
Michael Jumper
fe3fd6c35b Remove UDS icon (conflicts with any third-party branding). 2021-03-05 12:11:53 -08:00
Adolfo Gómez García
a035633b58 Merge remote-tracking branch 'origin/v3.0' 2021-03-04 17:05:19 +01:00
Adolfo Gómez García
688acb0631 Added extra security control to guacamole tickets 2021-03-04 12:13:38 +01:00
Adolfo Gómez García
0bc1f72dc8 Merge remote-tracking branch 'origin/v3.0' 2021-03-03 12:31:45 +01:00
Adolfo Gómez García
5d52061041 Merge remote-tracking branch 'origin/v3.0' 2021-03-03 12:27:39 +01:00
Adolfo Gómez García
190079fddc Merge remote-tracking branch 'origin/v3.0' 2021-03-01 13:12:59 +01:00
Adolfo Gómez García
9f44e7fd25 Merge remote-tracking branch 'origin/v3.0' 2021-03-01 13:08:47 +01:00
Adolfo Gómez García
ff685119ae Merge remote-tracking branch 'origin/v3.0' 2021-03-01 13:07:33 +01:00
Adolfo Gómez García
388cb2644b fixing up service_multi 2021-03-01 13:07:31 +01:00
Adolfo Gómez García
bda4057173 Merge remote-tracking branch 'origin/v3.0' 2021-03-01 13:01:06 +01:00
Adolfo Gómez García
7aec9a116e added "post-login-as-sysadmin" possibility of run windows script 2021-03-01 12:13:28 +01:00
Adolfo Gómez García
f57fea4699 Merge remote-tracking branch 'origin/v3.0' 2021-03-01 11:47:02 +01:00
Adolfo Gómez García
d52bc68015 Merge remote-tracking branch 'origin/v3.0' 2021-03-01 11:42:14 +01:00
Adolfo Gómez García
8ab1342775 merged chango of timeouts on 3.0 2021-03-01 11:14:02 +01:00
Adolfo Gómez García
f602d641a0 Merge remote-tracking branch 'origin/v3.0' 2021-03-01 10:42:34 +01:00
Adolfo Gómez García
3e07cf53e4 added remove control characters of an unicode string 2021-02-25 13:45:37 +01:00
Adolfo Gómez García
2968bc7d41 Merge remote-tracking branch 'origin/v3.0' 2021-02-25 10:39:53 +01:00
Adolfo Gómez García
6a209c0836 Added new resolutions for RDP display 2021-02-25 10:39:12 +01:00
Adolfo Gómez García
9568a9b180 Merge remote-tracking branch 'origin/v3.0' 2021-02-24 15:18:04 +01:00
Adolfo Gómez García
91fcbe7336 Fixed "image" parameter for wake remote machine 2021-02-23 14:19:47 +01:00
Adolfo Gómez García
2fd5b40809 Fixed Phisical machines issues & updated macos screen calc algorithn 2021-02-22 14:25:27 +01:00
Adolfo Gómez García
4e161b15f4 Merge remote-tracking branch 'origin/v3.0' 2021-02-17 16:02:29 +01:00
Adolfo Gómez García
328d35a289 Merged 3.0 HTML5 RDP changes 2021-02-17 15:40:43 +01:00
Adolfo Gómez García
af52727862 Merge remote-tracking branch 'origin/v3.0' 2021-02-17 15:35:36 +01:00
Adolfo Gómez García
672897f828 fixed test redirect && fixed frequency 2021-02-16 13:17:10 +01:00
Adolfo Gómez García
073ce3df12 removed old unused code 2021-02-16 10:20:28 +01:00
Adolfo Gómez García
09125bb1fa Merge remote-tracking branch 'origin/v3.0' 2021-02-15 12:14:39 +01:00
Adolfo Gómez García
f3e7e21149 Added log in case of file access problems 2021-02-15 11:38:59 +01:00
Adolfo Gómez García
348258daf2 Merge remote-tracking branch 'origin/v3.0' 2021-02-14 22:11:27 +01:00
Adolfo Gómez García
49a6e01477 Merge remote-tracking branch 'origin/v3.0' 2021-02-11 09:43:15 +01:00
Adolfo Gómez
9f2354191c Merge pull request #64 from glyptodon/simplify-config
Replace complex "udsfile" logic with simplified "uds-base-url" property.
2021-02-11 09:34:50 +01:00
Adolfo Gómez
6804982b0b Merge pull request #63 from glyptodon/revalidate-data
Re-validate UDS data for each connection attempt.
2021-02-11 09:33:39 +01:00
Michael Jumper
857f8602b8 Replace complex "udsfile" logic with simplified "uds-base-url" property. 2021-02-10 16:58:03 -08:00
Michael Jumper
584dee9fcd Re-validate UDS data for each connection attempt. 2021-02-10 16:45:01 -08:00
Adolfo Gómez García
e7bf7b0258 added "hidden" mac address treatment to phisical machines 2021-02-10 14:43:36 +01:00
Adolfo Gómez García
46d056de5d Merge remote-tracking branch 'origin/v3.0' 2021-02-09 12:39:13 +01:00
Adolfo Gómez García
92e13c48de Added support for linux environmenv var expansion on parameters for freerdp 2021-02-08 12:53:42 +01:00
Adolfo Gómez García
d93e5dc566 Merge remote-tracking branch 'origin/v3.0' 2021-02-05 14:32:10 +01:00
Adolfo Gómez García
0b8a9444d1 upgraded user gui 2021-02-05 10:59:23 +01:00
Adolfo Gómez García
cea271a2ce Added typing to scheduler 2021-02-01 12:53:34 +01:00
Adolfo Gómez García
d2d190e8a4 Retry scheduler cleanup in case of locked db 2021-01-29 11:54:48 +01:00
Adolfo Gómez García
5b8ff497fa Improved sql for scheduler main loop 2021-01-29 11:53:38 +01:00
Adolfo Gómez García
ae6d36b86a manually imported changes to v3.0 on spool names variables 2021-01-28 14:06:03 +01:00
Adolfo Gómez García
600f50f203 Merge remote-tracking branch 'origin/v3.0' 2021-01-28 14:00:31 +01:00
Adolfo Gómez García
caf1d5d825 Adding support for custom connection userServices data for RDP 2021-01-28 13:09:43 +01:00
Adolfo Gómez García
99d3393a33 Merge remote-tracking branch 'origin/v3.0' 2021-01-26 16:07:49 +01:00
Adolfo Gómez García
1d06bd02c0 fixing tunnel on privileges dropping 2021-01-25 15:19:49 +01:00
Adolfo Gómez García
41991590ca added support for running as root and drop to an user later 2021-01-25 14:58:50 +01:00
Adolfo Gómez García
4313368f78 small fixes for tunnel 2021-01-25 12:47:00 +01:00
Adolfo Gómez García
50660d92e5 fixed tunnel to allow sending to broker connection stats on termination 2021-01-25 11:12:10 +01:00
Adolfo Gómez García
c796f5aaac fixed sample tunnel configuration 2021-01-25 08:42:50 +01:00
Adolfo Gómez García
9e88ff5daa * Added "no compression" to ssl options for tunnel
* Updated headers & reformated rest.py
2021-01-23 21:50:40 +01:00
Adolfo Gómez García
cb5a6f2430 Fixed osDetector name 2021-01-22 09:01:52 +01:00
Adolfo Gómez
0f87c022f3 Headers & comments fix 2021-01-22 09:00:59 +01:00
Adolfo Gómez García
69f1c88c3d Fixed headers 2021-01-22 07:28:17 +01:00
Adolfo Gómez García
6fc6fa0fe1 Remove pycrypto and only using cryptopraphy 2021-01-22 07:17:18 +01:00
Adolfo Gómez García
f634d4ef1a Added .env files for vscode editing 2021-01-22 07:16:53 +01:00
Adolfo Gómez García
f933181369 Merge remote-tracking branch 'origin/v3.0' 2021-01-21 11:00:45 +01:00
Adolfo Gómez
f0b6726e19 Merge branch 'master' of https://github.com/dkmstr/openuds 2021-01-20 12:42:38 +01:00
Adolfo Gómez
8424c14052 added certifi certificates to CA list for Qt on UDS Client 2021-01-20 12:42:30 +01:00
Adolfo Gómez García
97f709bf52 fixed opengnsys connector callbacks. 2021-01-20 09:00:25 +01:00
Adolfo Gómez García
c26c8d9df9 Removed OLD template (code cleanup) 2021-01-20 08:48:21 +01:00
Adolfo Gómez García
9f81d0a066 Upgraded angular version of interfaces to v11 2021-01-19 09:13:07 +01:00
Adolfo Gómez García
bb626889fb Working on next OpenGnsys service provider release 2021-01-19 08:47:05 +01:00
Adolfo Gómez García
d8fb0deef2 Working on next OpenGnsys service provider release 2021-01-19 08:46:00 +01:00
Adolfo Gómez García
743773e256 Working on new tunnel, translating client mods to new tunnel server 2021-01-18 11:24:34 +01:00
Adolfo Gómez García
4adc058e1a Updating client logic to new gen tunnel server 2021-01-18 07:51:47 +01:00
Adolfo Gómez García
f364b283e6 added tunnel check 2021-01-18 06:45:50 +01:00
Adolfo Gómez García
7e4975be99 cleanup new tunnel 2021-01-18 06:04:59 +01:00
Adolfo Gómez García
a2df121e45 fixed a couple readmes 2021-01-18 05:51:34 +01:00
Adolfo Gómez García
f402dadb0a Advanced A LOT on new tunnel server & client. First test passed 2021-01-15 11:31:39 +01:00
Adolfo Gómez García
865601b3c8 Merge remote-tracking branch 'origin/v3.0' 2021-01-14 17:43:07 +01:00
Adolfo Gómez García
0da51dda92 fixed log of tunnel 2021-01-14 10:05:58 +01:00
Adolfo Gómez García
7c9c510ca0 minor updates to texts 2021-01-14 08:23:34 +01:00
Adolfo Gómez García
7ae9df21a5 added new tunnel server client 2021-01-14 08:17:49 +01:00
Adolfo Gómez García
2fd1dc5fc9 Advancing on forwarder 2021-01-14 08:11:02 +01:00
Adolfo Gómez García
a4986d3b4d Advancing on forwarder 2021-01-14 08:10:46 +01:00
Adolfo Gómez García
40abfb6014 Advancing on forwarder 2021-01-14 08:10:28 +01:00
Adolfo Gómez García
d6a8639b18 new tunnel server ready for testing phase 2021-01-14 06:01:06 +01:00
Adolfo Gómez García
971e5984d9 Advancing on new tunneler 2021-01-13 10:04:26 +01:00
Adolfo Gómez García
e486d6708d added local uds_tunnel work to openuds 2021-01-13 04:42:59 +01:00
Adolfo Gómez
f0bd3782d7 Merge branch 'master' of https://github.com/dkmstr/openuds 2021-01-11 03:04:38 +01:00
Adolfo Gómez García
7e9dde66ac Merge remote-tracking branch 'origin/v3.0' 2021-01-11 03:02:38 +01:00
Adolfo Gómez
fa05d9425e Fixed for python 3 on Windows 2021-01-11 02:50:30 +01:00
Adolfo Gómez
75221a4842 fixed python3 version for windows 2021-01-11 01:33:32 +01:00
Adolfo Gómez García
4cc11d783a Merge remote-tracking branch 'origin/v3.0' 2020-12-18 11:10:35 +01:00
Adolfo Gómez García
e5a38a65ed updated actor toke user on re-registration 2020-12-17 14:04:19 +01:00
Adolfo Gómez García
dcdea31061 fixing minor possible issues with staff members && reformating code, adapting to type checking, etc... 2020-12-17 13:56:15 +01:00
Adolfo Gómez García
6b3d222a12 Merge remote-tracking branch 'origin/v3.0' 2020-12-15 16:42:02 +01:00
Adolfo Gómez García
8719896f62 Merge remote-tracking branch 'origin/v3.0' 2020-12-15 16:41:54 +01:00
Adolfo Gómez García
57d8b01757 Fixed rdp_file url generation for mac audio 2020-12-04 13:37:07 +01:00
Adolfo Gómez García
66fb59a13b fixed tests for service on 3.5 actor 2020-12-01 11:36:01 +01:00
Adolfo Gómez García
78372e593a refactorization 2020-11-27 15:56:17 +01:00
Adolfo Gómez García
1d7c57eb2f Merge remote-tracking branch 'origin/v3.0' 2020-11-27 14:33:11 +01:00
Adolfo Gómez García
501565c088 Reformated & minor updates for workers 2020-11-27 11:12:06 +01:00
Adolfo Gómez García
f2d55d6141 Merge remote-tracking branch 'origin/v3.0' 2020-11-26 18:43:03 +01:00
Adolfo Gómez García
e9a4da5acc Merge remote-tracking branch 'origin/v3.0' 2020-11-26 11:52:21 +01:00
Adolfo Gómez García
522a5ebfb7 Merge remote-tracking branch 'origin/v3.0' 2020-11-25 18:40:11 +01:00
Adolfo Gómez García
6868e471c5 Merge remote-tracking branch 'origin/v3.0' 2020-11-20 14:40:18 +01:00
Adolfo Gómez García
9e4922ba79 adde future recording of guacamole 2020-11-19 12:14:52 +01:00
Adolfo Gómez García
7a377b0065 adapting UDS to new tunnel 2020-11-18 12:21:58 +01:00
Adolfo Gómez García
b830b0ee0a Merge remote-tracking branch 'origin/v3.0' 2020-11-17 17:09:15 +01:00
Adolfo Gómez
2251618c69 Merge pull request #58 from glyptodon/migrate-extension
Convert "guacamole-tunnel" web application into an equivalent Apache Guacamole extension.
2020-11-17 00:28:12 +01:00
Michael Jumper
a6876de0b1 Convert "guacamole-tunnel" web application into an equivalent Apache Guacamole extension. 2020-11-16 14:18:41 -08:00
Adolfo Gómez García
58a70e368e * Small RDP fix (typo)
* Added secure ticket type
* Reformated sevice view
2020-11-16 19:45:54 +01:00
Adolfo Gómez García
50f3b79ee3 Merge remote-tracking branch 'origin/v3.0' 2020-11-16 14:45:24 +01:00
Adolfo Gómez García
4183069cec Fixed help for mac ox on freerdp 2020-11-16 13:27:20 +01:00
Adolfo Gómez García
905b1e7589 refactoring code 2020-11-16 13:10:02 +01:00
Adolfo Gómez García
024bb5e748 Updated default wait-time && fixed mac access 2020-11-16 13:09:33 +01:00
Adolfo Gómez García
e4345dfefa fixed tunnel scripts for macosx 2020-11-15 22:48:17 +01:00
Adolfo Gómez García
6c54f8e75a Fixed cache bug intruduced on encoders module removal
Improbed type checking for REST (ongoing)
Optimized queries (using prefetch) for service pools listing
2020-11-13 11:23:22 +01:00
Adolfo Gómez García
45ca92b77e Removed encoders modules 2020-11-13 09:35:18 +01:00
Adolfo Gómez García
06b0f1396f reformated storage source 2020-11-13 08:43:55 +01:00
Adolfo Gómez García
c698300096 Merge remote-tracking branch 'origin/v3.0' 2020-11-13 08:30:45 +01:00
Adolfo Gómez García
482cc4b2ae Removing "encoders" uds module 2020-11-13 08:30:27 +01:00
Adolfo Gómez García
2f67eacfb6 * Removing "encoders" module (nonsense and sometimes confusing. Helped on python 2.7, but now... :)
* Revised reports to improve type checking
2020-11-12 13:55:09 +01:00
Adolfo Gómez García
b8e7dc07c3 Refactoring models (without DB modification) to better type checking 2020-11-12 12:13:35 +01:00
Adolfo Gómez García
52f524eb61 * Fixed comms to not "annoy" with warnings
* Added generateUuid to allow pass in an object
* renamed "is_meta" to more convenient "is_owned_by_meta
* Fixed 2.x actor rest api small bug (not used that part right now anyway, but for future)
2020-11-12 11:48:42 +01:00
Adolfo Gómez García
637519a162 Upgrading models for pylance typechecking 2020-11-12 08:35:45 +01:00
Adolfo Gómez García
65b47686db updating type checking 2020-11-11 14:36:31 +01:00
Adolfo Gómez García
1ab534c3aa * Adding more type checking (pylance type checking works fin with theese new "checks")
* Removed redundant fields not used
2020-11-11 13:32:02 +01:00
Adolfo Gómez García
8cd5437429 removing unused ManyToManyField (it has already a relation class) 2020-11-11 11:24:13 +01:00
Adolfo Gómez García
a475d45b7b * Added better type checking on Handler
* Fixed exception of ticket store exception
* Added initial nitification for unamanged
2020-11-11 09:03:02 +01:00
Adolfo Gómez García
4f3792ced5 added typing to calendar actions pairs 2020-11-11 09:01:43 +01:00
Adolfo Gómez García
3e061275b4 Added some more typing checks 2020-11-11 09:00:34 +01:00
Adolfo Gómez García
22415b98cd Commented out fix for passwords. Do not apply to 3.x mechanics 2020-11-11 08:48:36 +01:00
Adolfo Gómez
302b9a85d5 Merge pull request #57 from danitorregrosa/escape-conflicting-chars-rdp-credential-redirection
fix for proper escaping of conflicting chars in rdp credential redire…
2020-11-10 14:20:59 +01:00
danitorregrosa
08038b5b90 fix for proper escaping of conflicting chars in rdp credential redirection 2020-11-10 14:05:56 +01:00
Adolfo Gómez García
6d9a6baa2a Merge remote-tracking branch 'origin/v3.0' 2020-11-10 09:20:16 +01:00
Adolfo Gómez
fb69866f89 Merge remote-tracking branch 'origin/v3.0' 2020-11-09 09:19:31 +01:00
Adolfo Gómez García
74ad50d7d8 working on allowing services to detect user login on unmanaged services 2020-11-08 19:17:29 +01:00
Adolfo Gómez García
00dc4c5a7b Changed default encrypt to AES 2020-11-07 08:20:08 +01:00
Adolfo Gómez García
a495f36c43 .gitignore update 2020-11-07 08:19:32 +01:00
Adolfo Gómez García
f69a9dbc82 Merge remote-tracking branch 'origin/v3.0' 2020-11-06 01:08:09 +01:00
Adolfo Gómez García
65879f4bd3 fixed ignorables for mac client 2020-11-03 07:29:18 +01:00
Adolfo Gómez García
a094e1ebee fixed log name being bytes instead of string 2020-11-03 05:41:32 +01:00
admin
c35dc90264 added ignorable 2020-11-03 04:59:51 +01:00
Adolfo Gómez García
1be48b99d4 making some tests with nuitka 2020-11-02 08:45:20 +01:00
Adolfo Gómez García
cab09aea9c Fixing up mac os RDP transport support 2020-10-31 07:21:01 +01:00
Adolfo Gómez García
da23222f0f Working on supporting better clients on Mac 2020-10-30 09:44:55 +01:00
Adolfo Gómez García
5d604bb629 Merge remote-tracking branch 'origin/v3.0' 2020-10-29 09:36:45 +01:00
Adolfo Gómez García
13de581b80 Improbed session clear on user logout 2020-10-29 07:49:43 +01:00
Adolfo Gómez García
76f4df5aa1 Added admin access API restriction by IP 2020-10-29 06:23:09 +01:00
Adolfo Gómez García
8634ce50c6 Fixed so, if an invalid IP is configured on trusted source, it gets logged and ignored 2020-10-29 06:22:52 +01:00
Adolfo Gómez García
758f409372 Renamed module "openStack" to more convenient "openstack", and a couple of minor fixes related to it 2020-10-28 06:55:15 +01:00
Adolfo Gómez García
edd6714cd0 removed version dependency from requirements for cryptography 2020-10-28 01:37:42 +01:00
Adolfo Gómez
a7f9880816 Merge pull request #56 from dkmstr/dependabot/pip/server/cryptography-3.2
Bump cryptography from 2.9.2 to 3.2 in /server
2020-10-28 00:49:29 +01:00
dependabot[bot]
d9bbbb35eb Bump cryptography from 2.9.2 to 3.2 in /server
Bumps [cryptography](https://github.com/pyca/cryptography) from 2.9.2 to 3.2.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/2.9.2...3.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-27 22:53:01 +00:00
Adolfo Gómez García
5bf8e74018 Merge remote-tracking branch 'origin/v3.0' 2020-10-27 11:10:01 +01:00
Adolfo Gómez García
f5b59889fc Merge remote-tracking branch 'origin/v3.0' 2020-10-27 07:57:48 +01:00
Adolfo Gómez García
6ced042153 upgraded debian build for actor and client 2020-10-23 08:34:10 +02:00
Adolfo Gómez García
e9f74d9ccc upgraded debian build for actor and client 2020-10-23 08:31:24 +02:00
Adolfo Gómez García
9815e21524 upgraded debian build 2020-10-23 08:28:02 +02:00
Adolfo Gómez García
666ae4e1d3 Added experimental support for L2 cache to Proxmox 2020-10-23 02:47:51 +02:00
Adolfo Gómez García
3e8a3efb75 Added L2 cache support for proxmox 2020-10-23 02:14:11 +02:00
Adolfo Gómez García
4094818ccc Merge remote-tracking branch 'origin/v3.0' 2020-10-22 11:14:04 +02:00
Adolfo Gómez García
43acedf7f6 small storage fix 2020-10-21 08:24:24 +02:00
Adolfo Gómez García
37f06617b8 Added dict-like storage management 2020-10-21 08:03:18 +02:00
Adolfo Gómez García
dd39bb4e64 Merge remote-tracking branch 'origin/v3.0' 2020-10-20 11:55:40 +02:00
Adolfo Gómez García
bafd3bc6b3 Added support for configurable admin pagesizes 2020-10-20 11:11:52 +02:00
Adolfo Gómez García
48e0577e9f Merge remote-tracking branch 'origin/v3.0' 2020-10-20 11:10:20 +02:00
Adolfo Gómez García
cb05113d88 Adding support for configurable page size of lists on admin 2020-10-20 09:19:10 +02:00
Adolfo Gómez García
afc4fd39ef Merge remote-tracking branch 'origin/v3.0' 2020-10-16 13:52:52 +02:00
Adolfo Gómez García
33502140cf Merge remote-tracking branch 'origin/v3.0' 2020-10-16 13:29:18 +02:00
Adolfo Gómez García
63280bf9cb Merge remote-tracking branch 'origin/v3.0' 2020-10-16 11:46:45 +02:00
Adolfo Gómez García
1d90f04245 Upgraded angualar version for user & admin 2020-10-15 04:40:32 +02:00
Adolfo Gómez García
a486e68e39 Removed old unused project files 2020-10-15 04:09:34 +02:00
Adolfo Gómez García
76e5aede37 Modified VERSION for future version release. Current stable is 3.0 2020-10-15 03:48:44 +02:00
633 changed files with 39022 additions and 49707 deletions

3
.gitignore vendored
View File

@@ -32,9 +32,6 @@
/client/administration/installer/UDSAdminInstaller/MSChart.exe
/client/administration/installer/UDSAdminInstaller/UDSAdminSetup.exe
# /guacamole-tunnel/
/guacamole-tunnel/target
# /linuxActor/
/linuxActor/udsactor_*

View File

@@ -12,5 +12,4 @@ This is an Open Source Source project, initiated by Spanish Company Virtualca
Any help provided will be welcome.
**Note: Master version is always under heavy development and it is not recommended for use, it will probably have unfixed bugs.
For use, please use the latest stable branch.**
**Note: Master version is always under heavy development and it is not recommended for use, it will probably have unfixed bugs. Please use the latest stable branch.**

View File

@@ -1 +1 @@
3.0.0
3.5.0

2
actor/.env Normal file
View File

@@ -0,0 +1,2 @@
PYTHONPATH=./src:${PYTHONPATH}

View File

@@ -1,3 +1,9 @@
udsactor (3.5.0) stable; urgency=medium
* Upgraded to 3.5.0 release
-- Adolfo Gómez García <agomez@virtualcable.es> Fri, 23 Oct 2020 8:00:00 +0200
udsactor (3.0.0) stable; urgency=medium
* Upgraded to 3.0.0 release

View File

@@ -10,7 +10,7 @@ Package: udsactor
Section: admin
Priority: optional
Architecture: all
Depends: policykit-1(>=0.100), python3-requests (>=0.8.2), python3-pyqt5 (>=4.9), python3-six(>=1.1), python3 (>=3.4), libxss1, xscreensaver, ${misc:Depends}
Depends: policykit-1(>=0.100), python3-requests (>=0.8.2), python3-pyqt5 (>=4.9), python3-six(>=1.1), python3 (>=3.6), libxss1, xscreensaver, ${misc:Depends}
Recommends: python3-prctl(>=1.1.1)
Description: Actor for Universal Desktop Services (UDS) Broker
This package provides the required components to allow managed machines to work on an environment managed by UDS Broker.
@@ -19,7 +19,7 @@ Package: udsactor-unmanaged
Section: admin
Priority: optional
Architecture: all
Depends: policykit-1(>=0.100), python3-requests (>=0.8.2), python3-pyqt5 (>=4.9), python3-six(>=1.1), python3 (>=3.4), libxss1, xscreensaver, ${misc:Depends}
Depends: policykit-1(>=0.100), python3-requests (>=0.8.2), python3-pyqt5 (>=4.9), python3-six(>=1.1), python3 (>=3.6), libxss1, xscreensaver, ${misc:Depends}
Recommends: python3-prctl(>=1.1.1)
Description: Actor for Universal Desktop Services (UDS) Broker Static Unmanaged machines
This package provides the required components to allow unmanaged machines (static, independent machines) to work on an environment managed by UDS Broker.

View File

@@ -1,3 +1,3 @@
udsactor-unmanaged_3.0.0_all.deb admin optional
udsactor_3.0.0_all.deb admin optional
udsactor_3.0.0_amd64.buildinfo admin optional
udsactor-unmanaged_3.5.0_all.deb admin optional
udsactor_3.5.0_all.deb admin optional
udsactor_3.5.0_amd64.buildinfo admin optional

View File

@@ -3,4 +3,4 @@
FOLDER=/usr/share/UDSActor
cd $FOLDER
exec python3 actor_config.py $@
exec python3 actor_config.py -platform xcb $@

View File

@@ -3,4 +3,4 @@
FOLDER=/usr/share/UDSActor
cd $FOLDER
exec python3 actor_config_unmanaged.py $@
exec python3 actor_config_unmanaged.py -platform xcb $@

View File

@@ -3,4 +3,4 @@
FOLDER=/usr/share/UDSActor
cd $FOLDER
exec python3 actor_client.py $@
exec python3 actor_client.py -platform xcb $@

View File

@@ -67,7 +67,7 @@ if __name__ == "__main__":
# Note: Signals are only checked on python code execution, so we create a timer to force call back to python
timer = QTimer(qApp)
timer.start(1000)
timer.timeout.connect(lambda *a: None)
timer.timeout.connect(lambda *a: None) # type: ignore # timeout can be connected to a callable
qApp.exec_()

View File

@@ -65,9 +65,9 @@ class UDSClientQApp(QApplication):
self._initialized = False
# This will be invoked on session close
self.commitDataRequest.connect(self.end) # Will be invoked on session close, to gracely close app
self.commitDataRequest.connect(self.end) # type: ignore # Will be invoked on session close, to gracely close app
# self.aboutToQuit.connect(self.end)
self.message.connect(self.showMessage)
self.message.connect(self.showMessage) # type: ignore # there are problems with Pylance and connects on PyQt5... :)
# Execute backgroup thread for actions
self._app = UDSActorClient(self)
@@ -94,7 +94,7 @@ class UDSClientQApp(QApplication):
self._app.join()
def showMessage(self, message: str) -> None:
QMessageBox.information(None, 'Message', message)
QMessageBox.information(None, 'Message', message) # type: ignore
def setMainWindow(self, mw: 'QMainWindow'):
self._mainWindow = mw
@@ -108,6 +108,7 @@ class UDSActorClient(threading.Thread): # pylint: disable=too-many-instance-att
_listener: client.HTTPServerThread
_loginInfo: typing.Optional['types.LoginResultInfoType']
_notified: bool
_notifiedDeadline: bool
_sessionStartTime: datetime.datetime
api: rest.UDSClientApi
@@ -115,13 +116,14 @@ class UDSActorClient(threading.Thread): # pylint: disable=too-many-instance-att
super().__init__()
self.api = rest.UDSClientApi() # Self initialized
self._qApp = qApp
self._qApp = typing.cast(UDSClientQApp, qApp)
self._running = False
self._forceLogoff = False
self._extraLogoff = ''
self._listener = client.HTTPServerThread(self)
self._loginInfo = None
self._notified = False
self._notifiedDeadline = False
# Capture stop signals..
logger.debug('Setting signals...')
@@ -139,8 +141,8 @@ class UDSActorClient(threading.Thread): # pylint: disable=too-many-instance-att
remainingTime = self._loginInfo.dead_line - (datetime.datetime.now() - self._sessionStartTime).total_seconds()
logger.debug('Remaining time: {}'.format(remainingTime))
if not self._notified and remainingTime < 300: # With five minutes, show a warning message
self._notified = True
if not self._notifiedDeadline and remainingTime < 300: # With five minutes, show a warning message
self._notifiedDeadline = True
self._showMessage('Your session will expire in less that 5 minutes. Please, save your work and disconnect.')
return
@@ -210,7 +212,7 @@ class UDSActorClient(threading.Thread): # pylint: disable=too-many-instance-att
platform.operations.loggoff()
def _showMessage(self, message: str) -> None:
self._qApp.message.emit(message)
self._qApp.message.emit(message) # type: ignore # there are problems with Pylance and connects on PyQt5... :)
def stop(self) -> None:
logger.debug('Stopping client Service')
@@ -230,13 +232,13 @@ class UDSActorClient(threading.Thread): # pylint: disable=too-many-instance-att
On windows, an RDP session with minimized screen will render "black screen"
So only when user is using RDP connection will return an "actual" screenshot
'''
pixmap: 'QPixmap' = self._qApp.primaryScreen().grabWindow(0)
pixmap: 'QPixmap' = self._qApp.primaryScreen().grabWindow(0) # type: ignore
ba = QByteArray()
buffer = QBuffer(ba)
buffer.open(QIODevice.WriteOnly)
pixmap.save(buffer, 'PNG')
buffer.close()
scrBase64 = bytes(ba.toBase64()).decode()
scrBase64 = bytes(ba.toBase64()).decode() # type: ignore # there are problems with Pylance and connects on PyQt5... :)
logger.debug('Screenshot length: %s', len(scrBase64))
return scrBase64 # 'result' of JSON will contain base64 of screen

View File

@@ -96,7 +96,7 @@ def _getInterfaces() -> typing.List[str]:
0x8912, # SIOCGIFCONF
struct.pack(str('iL'), space, names.buffer_info()[0])
))[0]
namestr = names.tobytes()
namestr = names.tostring()
# return namestr, outbytes
return [namestr[i:i + offset].split(b'\0', 1)[0].decode('utf-8') for i in range(0, outbytes, length)]
@@ -186,9 +186,9 @@ def getCurrentUser() -> str:
def getSessionType() -> str:
'''
Known values:
* Unknown -> No SESSIONNAME environment variable
* Console -> Local session
* RDP-Tcp#[0-9]+ -> RDP Session
* Unknown -> No XDG_SESSION_TYPE environment variable
* xrdp --> xrdp session
* other types
'''
return 'xrdp' if 'XRDP_SESSION' in os.environ else os.environ.get('XDG_SESSION_TYPE', 'unknown')

View File

@@ -100,3 +100,6 @@ def writeConfig(config: types.ActorConfigurationType) -> None:
def useOldJoinSystem() -> bool:
return False
def invokeScriptOnLogin() -> str:
return ''

View File

@@ -162,19 +162,19 @@ class UDSServerApi(UDSApi):
except Exception:
pass
def register( #pylint: disable=too-many-arguments, too-many-locals
self,
auth: str,
username: str,
password: str,
hostname: str,
ip: str,
mac: str,
preCommand: str,
runOnceCommand: str,
postCommand: str,
logLevel: int
) -> str:
def register( # pylint: disable=too-many-arguments, too-many-locals
self,
auth: str,
username: str,
password: str,
hostname: str,
ip: str,
mac: str,
preCommand: str,
runOnceCommand: str,
postCommand: str,
logLevel: int
) -> str:
"""
Raises an exception if could not register, or registers and returns the "authorization token"
"""
@@ -212,10 +212,10 @@ class UDSServerApi(UDSApi):
raise RESTError(result.content.decode())
def initialize(self, token: str, interfaces: typing.Iterable[types.InterfaceInfoType], actorType: typing.Optional[str]) -> types.InitializationResultType:
def initialize(self, token: str, interfaces: typing.Iterable[types.InterfaceInfoType], actor_type: typing.Optional[str]) -> types.InitializationResultType:
# Generate id list from netork cards
payload = {
'type': actorType or types.MANAGED,
'type': actor_type or types.MANAGED,
'token': token,
'version': VERSION,
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces]
@@ -281,9 +281,16 @@ class UDSServerApi(UDSApi):
password=result['password']
)
def login(self, own_token: str, username: str, sessionType: typing.Optional[str] = None) -> types.LoginResultInfoType:
if not own_token:
def login(
self,
actor_type: typing.Optional[str],
token: str,
username: str,
sessionType: str,
interfaces: typing.Iterable[types.InterfaceInfoType],
secret: typing.Optional[str]
) -> types.LoginResultInfoType:
if not token:
return types.LoginResultInfoType(
ip='0.0.0.0',
hostname=UNKNOWN,
@@ -291,9 +298,12 @@ class UDSServerApi(UDSApi):
max_idle=None
)
payload = {
'token': own_token,
'type': actor_type or types.MANAGED,
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces],
'token': token,
'username': username,
'session_type': sessionType or UNKNOWN
'session_type': sessionType,
'secret': secret or '',
}
result = self._doPost('login', payload)
return types.LoginResultInfoType(
@@ -303,12 +313,22 @@ class UDSServerApi(UDSApi):
max_idle=result['max_idle']
)
def logout(self, own_token: str, username: str) -> None:
if not own_token:
def logout(
self,
actor_type: typing.Optional[str],
token: str,
username: str,
interfaces: typing.Iterable[types.InterfaceInfoType],
secret: typing.Optional[str]
) -> None:
if not token:
return
payload = {
'token': own_token,
'username': username
'type': actor_type or types.MANAGED,
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces],
'token': token,
'username': username,
'secret': secret or ''
}
self._doPost('logout', payload)
@@ -339,7 +359,6 @@ class UDSClientApi(UDSApi):
def _apiURL(self, method: str) -> str:
return self._url + method
def post(
self,
method: str, # i.e. 'initialize', 'ready', ....

View File

@@ -273,7 +273,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
logger.debug('This host is not managed by UDS Broker (ids: {})'.format(self._interfaces))
return False
# Only removes token for managed machines
# Only removes master token for managed machines (will need it on next client execution)
master_token = None if self.isManaged() else self._cfg.master_token
self._cfg = self._cfg._replace(
master_token=master_token,
@@ -299,6 +299,9 @@ class CommonService: # pylint: disable=too-many-instance-attributes
except rest.RESTError as e: # Invalid key?
logger.error('Error validating with broker. (Invalid token?): {}'.format(e))
return False
except Exception:
logger.exception()
self.doWait(5000) # Wait a bit and retry...
return self.configureMachine()
@@ -314,7 +317,13 @@ class CommonService: # pylint: disable=too-many-instance-attributes
if self._loggedIn and self._cfg.own_token:
self._loggedIn = False
try:
self._api.logout(self._cfg.own_token, '')
self._api.logout(
self._cfg.actorType,
self._cfg.own_token,
'',
self._interfaces,
self._secret
)
except Exception as e:
logger.error('Error notifying final logout to UDS: %s', e)
@@ -405,18 +414,52 @@ class CommonService: # pylint: disable=too-many-instance-attributes
def login(self, username: str, sessionType: typing.Optional[str] = None) -> types.LoginResultInfoType:
result = types.LoginResultInfoType(ip='', hostname='', dead_line=None, max_idle=None)
self._loggedIn = True
master_token = None
secret = None
# If unmanaged, do initialization now, because we don't know before this
# Also, even if not initialized, get a "login" notification token
if not self.isManaged():
self.initialize()
master_token = self._cfg.master_token
secret = self._secret
# Own token will not be set if UDS did not assigned the initialized VM to an user
# In that case, take master token (if machine is Unamanaged version)
token = self._cfg.own_token or master_token
if token:
result = self._api.login(
self._cfg.actorType,
token,
username,
sessionType or '',
self._interfaces,
secret
)
if self._cfg.own_token:
result = self._api.login(self._cfg.own_token, username, sessionType)
script = platform.store.invokeScriptOnLogin()
if script:
script += f'{username} {sessionType or "unknown"} {self._cfg.actorType}'
self.execute(script, 'Logon')
return result
def logout(self, username: str) -> None:
self._loggedIn = False
if self._cfg.own_token:
self._api.logout(self._cfg.own_token, username)
master_token = self._cfg.master_token if self.isManaged() else None
# Own token will not be set if UDS did not assigned the initialized VM to an user
# In that case, take master token (if machine is Unamanaged version)
token = self._cfg.own_token or master_token
if token:
self._api.logout(
self._cfg.actorType,
token,
username,
self._interfaces,
self._secret
)
self.onLogout(username)

View File

@@ -39,6 +39,7 @@ import win32net
import win32event
import pythoncom
import servicemanager
import winreg as wreg
from . import operations
from . import store
@@ -197,6 +198,18 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
except Exception as e:
logger.error('Exception removing user from Remote Desktop Users: {}'.format(e))
def isInstallationRunning(self):
'''
Detect if windows is installing anything, so we can delay the execution of Service
'''
try:
key = wreg.OpenKey(wreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\State')
data, _ = wreg.QueryValueEx(key, 'ImageState')
logger.debug('State: %s', data)
return data != 'IMAGE_STATE_COMPLETE' # If ImageState is different of ImageStateComplete, there is something running on installation
except Exception: # If not found, means that no installation is running
return False
def SvcDoRun(self) -> None: # pylint: disable=too-many-statements, too-many-branches
'''
Main service loop
@@ -209,6 +222,17 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
pythoncom.CoInitialize() # pylint: disable=no-member
# Check if some install is running on windows before proceeding
while self._isAlive:
if self.isInstallationRunning():
win32event.WaitForSingleObject(self._hWaitStop, 1000) # Wait a bit, and check again
continue
break
if not self._isAlive: # Has been stopped while waiting windows installations
self.finish()
return
# Unmanaged services does not initializes "on start", but rather when user logs in (because userservice does not exists "as such" before that)
if self.isManaged():
if not self.initialize():

View File

@@ -76,9 +76,9 @@ def writeConfig(config: types.ActorConfigurationType) -> None:
except Exception:
key = wreg.CreateKeyEx(BASEKEY, PATH, 0, wreg.KEY_ALL_ACCESS)
fixRegistryPermissions(key.handle)
fixRegistryPermissions(key.handle) # type: ignore
wreg.SetValueEx(key, "", 0, wreg.REG_BINARY, pickle.dumps(config))
wreg.SetValueEx(key, "", 0, wreg.REG_BINARY, pickle.dumps(config)) # type: ignore
wreg.CloseKey(key)
@@ -94,3 +94,16 @@ def useOldJoinSystem() -> bool:
data = ''
return data == 'old'
def invokeScriptOnLogin() -> str:
try:
key = wreg.OpenKey(BASEKEY, PATH, 0, wreg.KEY_QUERY_VALUE)
try:
data, _ = wreg.QueryValueEx(key, 'logonScript')
except Exception:
data = ''
wreg.CloseKey(key)
except Exception:
data = ''
return data

2
client-py3/full/.env Normal file
View File

@@ -0,0 +1,2 @@
PYTHONPATH=./src:${PYTHONPATH}

View File

@@ -2,3 +2,7 @@
/udsclient-[0-9]*.spec
/debian/udsclient
/targz
/UDSClientDir
/UDSClient*.AppImage
/appimage*
/UDSClient.desktop

View File

@@ -46,8 +46,35 @@ endif
ifeq ($(DISTRO),rh)
endif
# chmod 0755 $(BINDIR)/udsclient
uninstall:
rm -rf $(LIBDIR)
# rm -f $(BINDIR)/udsclient
# rm -rf $(CFGDIR)
build-appimage:
ifeq ($(DISTRO),x86_64)
cat udsclient-appimage-x86_64.recipe | sed -e s/"version: 0.0.0"/"version: $(VERSION)"/g > appimage.recipe
endif
ifeq ($(DISTRO),armf)
cat udsclient-appimage-x86_64.recipe | sed -e s/"version: 0.0.0"/"version: $(VERSION)"/g | sed -e s/amd64\\\|x86_64/armhf/g > appimage.recipe
endif
ifeq ($(DISTRO),i686)
cat udsclient-appimage-x86_64.recipe | sed -e s/"version: 0.0.0"/"version: $(VERSION)"/g | sed -e s/amd64/i386/g | sed -e s/x86_64/i686/g > appimage.recipe
endif
# Ensure all working folders are "clean"
-rm -rf appimage appimage-builder-cache /tmp/UDSClientDir
appimage-builder --recipe appimage.recipe
# Now create dist and move appimage
rm -rf $(DESTDIR)
mkdir -p $(DESTDIR)
cp UDSClient-$(VERSION)-$(DISTRO).AppImage $(DESTDIR)
# Generate the .desktop fixed for new path
cat desktop/UDSClient.desktop | sed -e s/".usr.lib.UDSClient.UDSClient.py"/"\/usr\/bin\/UDSClient-$(VERSION)-$(DISTRO).AppImage"/g > $(DESTDIR)/UDSClient.desktop
# And also, generater installer
cat installer-appimage-template.sh | sed -e s/"0.0.0"/"$(VERSION)"/g | sed -e s/x86_64/$(DISTRO)/g > $(DESTDIR)/installer.sh
chmod 755 $(DESTDIR)/installer.sh
tar czvf ../udsclient3-$(DISTRO)-$(VERSION).tar.gz -C $(DESTDIR) .
# cleanup
-rm -rf appimage appimage-builder-cache /tmp/UDSClientDir

View File

@@ -12,6 +12,9 @@ cat udsclient-template.spec |
sed -e s/"version 0.0.0"/"version ${VERSION}"/g |
sed -e s/"release 1"/"release ${RELEASE}"/g > udsclient-$VERSION.spec
cat appimage-udsclient.recipe |
sed -e s/"version: 0.0.0"/"version: ${VERSION}"/g > appimage.recipe
# Now fix dependencies for opensuse
# Note: Right now, opensuse & rh seems to have same dependencies, only 1 package needed
# cat udsclient-template.spec |
@@ -32,6 +35,13 @@ done
#rm udsclient-$VERSION
# Make .tar.gz with source
make DESTDIR=targz DISTRO=targz VERSION=${VERSION} install
# And make FULL CLIENT .tar.gz for x86 and raspberry
make DESTDIR=appimage DISTRO=x86_64 VERSION=${VERSION} build-appimage
make DESTDIR=appimage DISTRO=armhf VERSION=${VERSION} build-appimage
make DESTDIR=appimage DISTRO=i686 VERSION=${VERSION} build-appimage
rpm --addsign ../*rpm

View File

@@ -1,3 +1,9 @@
udsclient3 (3.5.0) stable; urgency=medium
* Upgraded to 3.5.0 release
-- Adolfo Gómez García <agomez@virtualcable.es> Fri, 23 Oct 2020 08:12:10 +0200
udsclient3 (3.0.0) stable; urgency=medium
* Upgraded to 3.0.0 release

View File

@@ -10,6 +10,6 @@ Package: udsclient3
Section: admin
Priority: optional
Architecture: all
Depends: python3-paramiko (>=2.0.0), python3-crypto, python3-pyqt5 (>=5.0), python3-six(>=1.1), python3 (>=3.6), freerdp2-x11 | freerdp-x11, desktop-file-utils, ${misc:Depends}
Depends: python3-paramiko (>=2.0.0), python3-certifi, python3-cryptography, python3-psutil, python3-pyqt5 (>=5.0), python3 (>=3.6), freerdp2-x11 | freerdp-x11 | freerdp-nightly, desktop-file-utils, ${misc:Depends}
Description: Client connector for Universal Desktop Services (UDS) Broker
This package provides the required components to allow this machine to connect to services provided by UDS Broker.

View File

@@ -1,2 +1,2 @@
udsclient3_3.0.0_all.deb admin optional
udsclient3_3.0.0_amd64.buildinfo admin optional
udsclient3_3.5.0_all.deb admin optional
udsclient3_3.5.0_amd64.buildinfo admin optional

View File

@@ -2,7 +2,7 @@
Name=UDSClient
Comment=UDS Helper
Keywords=uds;client;vdi;
Exec=/usr/lib/UDSClient/UDSClient.py %u
Exec=/usr/lib/UDSClient/UDSClient.py %u -platform xcb
Icon=help-browser
StartupNotify=true
Terminal=false

View File

@@ -0,0 +1,15 @@
#!/bin/sh
# Check for root
if ! [ $(id -u) = 0 ]; then
echo "This script must be run as root"
exit 1
fi
echo "Installing UDSClient Portable..."
cp UDSClient-0.0.0-x86_64.AppImage /usr/bin
cp UDSClient.desktop /usr/share/applications
update-desktop-database
echo "Installation process done."

View File

@@ -0,0 +1,62 @@
version: 1
script:
# Remove any previous build
- rm -rf /tmp/UDSClientDir | true
# Make usr and icons dirs
- mkdir -p /tmp/UDSClientDir/usr/src
# Copy the python application code into the UDSClientDir
- cp ../src/UDS*.py /tmp/UDSClientDir/usr/src
- cp -r ../src/uds /tmp/UDSClientDir/usr/src
# Remove __pycache__ and .mypy if exists
- rm /tmp/UDSClientDir/usr/src/.mypy_cache -rf 2>&1 > /dev/null
- rm /tmp/UDSClientDir/usr/src/uds/.mypy_cache -rf 2>&1 > /dev/null
- rm /tmp/UDSClientDir/usr/src/__pycache__ -rf 2>&1 > /dev/null
- rm /tmp/UDSClientDir/usr/src/uds/__pycache__ -rf 2>&1 > /dev/null
AppDir:
# On /tmp, that is an ext4 filesystem. On btrfs squashfs complains with "Unrecognised xattr prefix btrfs.compression"
path: /tmp/UDSClientDir
app_info:
id: com.udsenterprise.UDSClient3
name: UDSClient
icon: utilities-terminal
version: 0.0.0
# Set the python executable as entry point
exec: usr/bin/python3
# Set the application main script path as argument. Use '$@' to forward CLI parameters
exec_args: "$APPDIR/usr/src/UDSClient.py $@"
apt:
arch: amd64
sources:
- sourceline: 'deb [arch=amd64] http://ftp.de.debian.org/debian/ bullseye main contrib non-free'
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x04EE7237B7D453EC'
include:
- python3
- python3-pkg-resources
- python3-pyqt5
- python3-paramiko
- python3-cryptography
- python3-certifi
- python3-psutil
- freerdp2-x11
- freerdp2-wayland
- x2goclient
- openssh-sftp-server
exclude: []
runtime:
env:
# Set python home
# See https://docs.python.org/3/using/cmdline.html#envvar-PYTHONHOME
PYTHONHOME: '${APPDIR}/usr'
# Path to the site-packages dir or other modules dirs
# See https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH
PYTHONPATH: '${APPDIR}/usr/lib/python3.9/site-packages'
AppImage:
update-information: None
sign-key: None
arch: x86_64

View File

@@ -11,7 +11,7 @@ Release: %{release}
Summary: Client for Universal Desktop Services (UDS) Broker
License: BSD3
Group: Applications/Productivity
Requires: python3-six python3-requests python3-paramiko python3-qt5 (python3-crypto or python3-pycrypto)
Requires: python3-paramiko python3-qt5 python3-cryptography python3-certifi python3-psutil
Vendor: Virtual Cable S.L.U.
URL: http://www.udsenterprise.com
Provides: udsclient

View File

@@ -1,4 +1,6 @@
/build
/dist
UDSClient.dmg
UDSClient.pkg
/UDSClient*.pkg
/UDSClient*.dist
/UDSClient*.build
/.eggs

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2017 Virtual Cable S.L.
# Copyright (c) 2014-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@@ -31,41 +31,45 @@
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
import sys
import os
import platform
import time
import webbrowser
import json
import base64, bz2
import threading
import typing
from PyQt5 import QtCore, QtGui, QtWidgets # @UnresolvedImport
import six
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import QSettings
from uds.rest import RestRequest
from uds.forward import forward # pylint: disable=unused-import
from uds.log import logger
from uds.rest import RestApi, RetryException, InvalidVersion, UDSException
# Just to ensure there are available on runtime
from uds.forward import forward # type: ignore
from uds.tunnel import forward as f2 # type: ignore
from uds.log import logger, DEBUG
from uds import tools
from uds import VERSION
from UDSWindow import Ui_MainWindow
# Server before this version uses "unsigned" scripts
OLD_METHOD_VERSION = '2.4.0'
class RetryException(Exception):
pass
class UDSClient(QtWidgets.QMainWindow):
ticket = None
scrambler = None
ticket: str = ''
scrambler: str = ''
withError = False
animTimer = None
anim = 0
animInverted = False
serverVersion = 'X.Y.Z' # Will be overwriten on getVersion
req = None
animTimer: typing.Optional[QtCore.QTimer] = None
anim: int = 0
animInverted: bool = False
api: RestApi
def __init__(self):
def __init__(self, api: RestApi, ticket: str, scrambler: str):
QtWidgets.QMainWindow.__init__(self)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
self.api = api
self.ticket = ticket
self.scrambler = scrambler
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint) # type: ignore
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
@@ -82,34 +86,29 @@ class UDSClient(QtWidgets.QMainWindow):
self.move(hpos, vpos)
self.animTimer = QtCore.QTimer()
self.animTimer.timeout.connect(self.updateAnim)
self.animTimer.timeout.connect(self.updateAnim) # type: ignore
# QtCore.QObject.connect(self.animTimer, QtCore.SIGNAL('timeout()'), self.updateAnim)
self.activateWindow()
self.startAnim()
def closeWindow(self):
self.close()
def processError(self, data):
if 'error' in data:
# QtWidgets.QMessageBox.critical(self, 'Request error {}'.format(data.get('retryable', '0')), data['error'], QtWidgets.QMessageBox.Ok)
if data.get('retryable', '0') == '1':
raise RetryException(data['error'])
raise Exception(data['error'])
# QtWidgets.QMessageBox.critical(self, 'Request error', rest.data['error'], QtWidgets.QMessageBox.Ok)
# self.closeWindow()
# return
def showError(self, error):
logger.error('got error: %s', error)
self.stopAnim()
self.ui.info.setText('UDS Plugin Error') # In fact, main window is hidden, so this is not visible... :)
self.ui.info.setText(
'UDS Plugin Error'
) # In fact, main window is hidden, so this is not visible... :)
self.closeWindow()
QtWidgets.QMessageBox.critical(None, 'UDS Plugin Error', '{}'.format(error), QtWidgets.QMessageBox.Ok)
QtWidgets.QMessageBox.critical(
None, # type: ignore
'UDS Plugin Error',
'{}'.format(error),
QtWidgets.QMessageBox.Ok,
)
self.withError = True
def cancelPushed(self):
@@ -125,146 +124,201 @@ class UDSClient(QtWidgets.QMainWindow):
self.ui.progressBar.setValue(self.anim)
def startAnim(self):
self.ui.progressBar.invertedAppearance = False
self.ui.progressBar.invertedAppearance = False # type: ignore
self.anim = 0
self.animInverted = False
self.ui.progressBar.setInvertedAppearance(self.animInverted)
self.animTimer.start(40)
if self.animTimer:
self.animTimer.start(40)
def stopAnim(self):
self.ui.progressBar.invertedAppearance = False
self.animTimer.stop()
self.ui.progressBar.invertedAppearance = False # type: ignore
if self.animTimer:
self.animTimer.stop()
def getVersion(self):
self.req = RestRequest('', self, self.version)
self.req.get()
def version(self, data):
try:
self.processError(data)
self.ui.info.setText('Processing...')
if data['result']['requiredVersion'] > VERSION:
QtWidgets.QMessageBox.critical(self, 'Upgrade required', 'A newer connector version is required.\nA browser will be opened to download it.', QtWidgets.QMessageBox.Ok)
webbrowser.open(data['result']['downloadUrl'])
self.closeWindow()
return
self.serverVersion = data['result']['requiredVersion']
self.getTransportData()
except RetryException as e:
self.ui.info.setText(str(e))
QtCore.QTimer.singleShot(1000, self.getVersion)
self.api.getVersion()
except InvalidVersion as e:
QtWidgets.QMessageBox.critical(
self,
'Upgrade required',
'A newer connector version is required.\nA browser will be opened to download it.',
QtWidgets.QMessageBox.Ok,
)
webbrowser.open(e.downloadUrl)
self.closeWindow()
return
except Exception as e:
self.showError(e)
self.getTransportData()
def getTransportData(self):
try:
self.req = RestRequest('/{}/{}'.format(self.ticket, self.scrambler), self, self.transportDataReceived, params={'hostname': tools.getHostName(), 'version': VERSION})
self.req.get()
except Exception as e:
logger.exception('Got exception on getTransportData')
raise e
def transportDataReceived(self, data):
logger.debug('Transport data received')
try:
self.processError(data)
params = None
if self.serverVersion <= OLD_METHOD_VERSION:
script = bz2.decompress(base64.b64decode(data['result']))
# This fixes uds 2.2 "write" string on binary streams on some transport
script = script.replace(b'stdin.write("', b'stdin.write(b"')
script = script.replace(b'version)', b'version.decode("utf-8"))')
else:
res = data['result']
# We have three elements on result:
# * Script
# * Signature
# * Script data
# We test that the Script has correct signature, and them execute it with the parameters
#script, signature, params = res['script'].decode('base64').decode('bz2'), res['signature'], json.loads(res['params'].decode('base64').decode('bz2'))
script, signature, params = bz2.decompress(base64.b64decode(res['script'])), res['signature'], json.loads(bz2.decompress(base64.b64decode(res['params'])))
if tools.verifySignature(script, signature) is False:
logger.error('Signature is invalid')
raise Exception('Invalid UDS code signature. Please, report to administrator')
script, params = self.api.getScriptAndParams(self.ticket, self.scrambler)
self.stopAnim()
if 'darwin' in sys.platform:
self.showMinimized()
QtCore.QTimer.singleShot(3000, self.endScript)
self.hide()
# QtCore.QTimer.singleShot(3000, self.endScript)
# self.hide()
self.closeWindow()
six.exec_(script.decode("utf-8"), globals(), {'parent': self, 'sp': params})
exec(script, globals(), {'parent': self, 'sp': params})
# Execute the waiting tasks...
threading.Thread(target=endScript).start()
except RetryException as e:
self.ui.info.setText(six.text_type(e) + ', retrying access...')
self.ui.info.setText(str(e) + ', retrying access...')
# Retry operation in ten seconds
QtCore.QTimer.singleShot(10000, self.getTransportData)
except Exception as e:
#logger.exception('Got exception executing script:')
if DEBUG:
logger.exception('Got exception on getTransportData')
self.showError(e)
def endScript(self):
# After running script, wait for stuff
try:
tools.waitForTasks()
except Exception:
pass
try:
tools.unlinkFiles()
except Exception:
pass
try:
tools.execBeforeExit()
except Exception:
pass
self.closeWindow()
def start(self):
'''
"""
Starts proccess by requesting version info
'''
"""
self.ui.info.setText('Initializing...')
QtCore.QTimer.singleShot(100, self.getVersion)
def done(data):
QtWidgets.QMessageBox.critical(None, 'Notice', six.text_type(data.data), QtWidgets.QMessageBox.Ok)
sys.exit(0)
def endScript():
# Wait a bit before start processing ending sequence
time.sleep(3)
try:
# Remove early stage files...
tools.unlinkFiles(early=True)
except Exception as e:
logger.debug('Unlinking files on early stage: %s', e)
# After running script, wait for stuff
try:
logger.debug('Wating for tasks to finish...')
tools.waitForTasks()
except Exception as e:
logger.debug('Watiting for tasks to finish: %s', e)
try:
logger.debug('Unlinking files')
tools.unlinkFiles(early=False)
except Exception as e:
logger.debug('Unlinking files on later stage: %s', e)
# Removing
try:
logger.debug('Executing threads before exit')
tools.execBeforeExit()
except Exception as e:
logger.debug('execBeforeExit: %s', e)
logger.debug('endScript done')
# Ask user to approve endpoint
def approveHost(hostName, parentWindow=None):
def approveHost(hostName: str):
settings = QtCore.QSettings()
settings.beginGroup('endpoints')
#approved = settings.value(hostName, False).toBool()
# approved = settings.value(hostName, False).toBool()
approved = bool(settings.value(hostName, False))
errorString = '<p>The server <b>{}</b> must be approved:</p>'.format(hostName)
errorString += '<p>Only approve UDS servers that you trust to avoid security issues.</p>'
errorString += (
'<p>Only approve UDS servers that you trust to avoid security issues.</p>'
)
if approved or QtWidgets.QMessageBox.warning(parentWindow, 'ACCESS Warning', errorString, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) == QtWidgets.QMessageBox.Yes:
settings.setValue(hostName, True)
approved = True
if not approved:
if (
QtWidgets.QMessageBox.warning(
None, # type: ignore
'ACCESS Warning',
errorString,
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, # type: ignore
)
== QtWidgets.QMessageBox.Yes
):
settings.setValue(hostName, True)
approved = True
settings.endGroup()
return approved
def sslError(hostname: str, serial):
settings = QSettings()
settings.beginGroup('ssl')
approved = settings.value(serial, False)
if (
approved
or QtWidgets.QMessageBox.warning(
None, # type: ignore
'SSL Warning',
f'Could not check SSL certificate for {hostname}.\nDo you trust this host?',
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, # type: ignore
)
== QtWidgets.QMessageBox.Yes
):
approved = True
settings.setValue(serial, True)
settings.endGroup()
return approved
# Used only if command line says so
def minimal(api: RestApi, ticket: str, scrambler: str):
try:
logger.info('Minimal Execution')
logger.debug('Getting version')
try:
api.getVersion()
except InvalidVersion as e:
QtWidgets.QMessageBox.critical(
None, # type: ignore
'Upgrade required',
'A newer connector version is required.\nA browser will be opened to download it.',
QtWidgets.QMessageBox.Ok,
)
webbrowser.open(e.downloadUrl)
return 0
logger.debug('Transport data')
script, params = api.getScriptAndParams(ticket, scrambler)
# Execute UDS transport script
exec(script, globals(), {'parent': None, 'sp': params})
# Execute the waiting task...
threading.Thread(target=endScript).start()
except RetryException as e:
QtWidgets.QMessageBox.warning(
None, # type: ignore
'Service not ready',
'{}'.format('.\n'.join(str(e).split('.')))
+ '\n\nPlease, retry again in a while.',
QtWidgets.QMessageBox.Ok,
)
except Exception as e:
# logger.exception('Got exception on getTransportData')
QtWidgets.QMessageBox.critical(
None, # type: ignore
'Error',
'{}'.format(str(e)) + '\n\nPlease, retry again in a while.',
QtWidgets.QMessageBox.Ok,
)
return 0
if __name__ == "__main__":
logger.debug('Initializing connector')
logger.debug('Initializing connector for %s(%s)', sys.platform, platform.machine())
# Initialize app
app = QtWidgets.QApplication(sys.argv)
@@ -274,17 +328,27 @@ if __name__ == "__main__":
if 'darwin' not in sys.platform:
logger.debug('Mac OS *NOT* Detected')
app.setStyle('plastique')
if six.PY3 is False:
logger.debug('Fixing threaded execution of commands')
import threading
threading._DummyThread._Thread__stop = lambda x: 42 # type: ignore # pylint: disable=protected-access
app.setStyle('plastique') # type: ignore
else:
logger.debug('Platform is Mac OS, adding homebrew possible paths')
os.environ['PATH'] += ''.join(
os.pathsep + i
for i in (
'/usr/local/bin',
'/opt/homebrew/bin',
)
)
logger.debug('Now path is %s', os.environ['PATH'])
# First parameter must be url
useMinimal = False
try:
uri = sys.argv[1]
if uri == '--minimal':
useMinimal = True
uri = sys.argv[2] # And get URI
if uri == '--test':
sys.exit(0)
@@ -293,17 +357,28 @@ if __name__ == "__main__":
raise Exception()
ssl = uri[3] == 's'
host, UDSClient.ticket, UDSClient.scrambler = uri.split('//')[1].split('/') # type: ignore
logger.debug('ssl:%s, host:%s, ticket:%s, scrambler:%s', ssl, host, UDSClient.ticket, UDSClient.scrambler)
host, ticket, scrambler = uri.split('//')[1].split('/') # type: ignore
logger.debug(
'ssl:%s, host:%s, ticket:%s, scrambler:%s',
ssl,
host,
UDSClient.ticket,
UDSClient.scrambler,
)
except Exception:
logger.debug('Detected execution without valid URI, exiting')
QtWidgets.QMessageBox.critical(None, 'Notice', 'UDS Client Version {}'.format(VERSION), QtWidgets.QMessageBox.Ok)
QtWidgets.QMessageBox.critical(
None, # type: ignore
'Notice',
'UDS Client Version {}'.format(VERSION),
QtWidgets.QMessageBox.Ok,
)
sys.exit(1)
# Setup REST api endpoint
RestRequest.restApiUrl = '{}://{}/rest/client'.format(['http', 'https'][ssl], host)
logger.debug('Setting request URL to %s', RestRequest.restApiUrl)
# RestRequest.restApiUrl = 'https://172.27.0.1/rest/client'
api = RestApi(
'{}://{}/uds/rest/client'.format(['http', 'https'][ssl], host), sslError
)
try:
logger.debug('Starting execution')
@@ -312,7 +387,7 @@ if __name__ == "__main__":
if approveHost(host) is False:
raise Exception('Host {} was not approved'.format(host))
win = UDSClient()
win = UDSClient(api, ticket, scrambler)
win.show()
win.start()
@@ -323,7 +398,9 @@ if __name__ == "__main__":
except Exception as e:
logger.exception('Got an exception executing client:')
exitVal = 128
QtWidgets.QMessageBox.critical(None, 'Error', six.text_type(e), QtWidgets.QMessageBox.Ok)
QtWidgets.QMessageBox.critical(
None, 'Error', str(e), QtWidgets.QMessageBox.Ok # type: ignore
)
logger.debug('Exiting')
sys.exit(exitVal)

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2017 Virtual Cable S.L.
# Copyright (c) 2014-2021 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@@ -31,7 +31,7 @@
'''
from __future__ import unicode_literals
VERSION = '3.0.0'
VERSION = '3.5.0'
__title__ = 'udclient'
__version__ = VERSION

View File

@@ -9,6 +9,7 @@ import random
import time
import select
import socketserver
import typing
import paramiko
@@ -36,6 +37,11 @@ class ForwardServer(socketserver.ThreadingTCPServer):
class Handler(socketserver.BaseRequestHandler):
event: threading.Event
thread: 'ForwardThread'
ssh_transport: paramiko.Transport
chain_host: str
chain_port: int
def handle(self):
self.thread.currentConnections += 1
@@ -86,6 +92,8 @@ class Handler(socketserver.BaseRequestHandler):
class ForwardThread(threading.Thread):
status = 0 # Connecting
client: typing.Optional[paramiko.SSHClient]
fs: typing.Optional[ForwardServer]
def __init__(self, server, port, username, password, localPort, redirectHost, redirectPort, waitTime, fingerPrints):
threading.Thread.__init__(self)
@@ -118,7 +126,7 @@ class ForwardThread(threading.Thread):
ft = ForwardThread(self.server, self.port, self.username, self.password, localPort, redirectHost, redirectPort, self.waitTime, self.fingerPrints)
ft.client = self.client
self.client.useCount += 1 # One more using this client
self.client.useCount += 1 # type: ignore
ft.start()
while ft.status == 0:
@@ -138,7 +146,7 @@ class ForwardThread(threading.Thread):
if self.client is None:
try:
self.client = paramiko.SSHClient()
self.client.useCount = 1 # Custom added variable, to keep track on when to close tunnel
self.client.useCount = 1 # type: ignore
self.client.load_system_host_keys()
self.client.set_missing_host_key_policy(CheckfingerPrints(self.fingerPrints))
@@ -173,11 +181,13 @@ class ForwardThread(threading.Thread):
self.timer.cancel()
self.stopEvent.set()
self.fs.shutdown()
if self.fs:
self.fs.shutdown()
if self.client is not None:
self.client.useCount -= 1
if self.client.useCount == 0:
self.client.useCount -= 1 # type: ignore
if self.client.useCount == 0: # type: ignore
self.client.close()
self.client = None # Clean up
except Exception:

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014 Virtual Cable S.L.
# Copyright (c) 2014-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@@ -33,23 +33,33 @@ from __future__ import unicode_literals
import logging
import os
import os.path
import sys
import tempfile
if sys.platform.startswith('linux'):
from os.path import expanduser # pylint: disable=ungrouped-imports
logFile = expanduser('~/udsclient.log')
LOGLEVEL = logging.INFO
DEBUG = False
# Update debug level if uds-debug-on exists
if 'linux' in sys.platform or 'darwin' in sys.platform:
logFile = os.path.expanduser('~/udsclient.log')
if os.path.isfile(os.path.expanduser('~/uds-debug-on')):
LOGLEVEL = logging.DEBUG
DEBUG = True
else:
logFile = os.path.join(tempfile.gettempdir(), b'udsclient.log')
logFile = os.path.join(tempfile.gettempdir(), 'udsclient.log')
if os.path.isfile(os.path.join(tempfile.gettempdir(), 'uds-debug-on')):
LOGLEVEL = logging.DEBUG
DEBUG = True
try:
logging.basicConfig(
filename=logFile,
filemode='a',
format='%(levelname)s %(asctime)s %(message)s',
level=logging.INFO
level=LOGLEVEL
)
except Exception:
logging.basicConfig(format='%(levelname)s %(asctime)s %(message)s', level=logging.INFO)
logging.basicConfig(format='%(levelname)s %(asctime)s %(message)s', level=LOGLEVEL)
logger = logging.getLogger('udsclient')

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# Copyright (c) 2017-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,

View File

@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# Copyright (c) 2017-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@@ -33,92 +32,206 @@
# pylint: disable=c-extension-no-member,no-name-in-module
import json
import bz2
import base64
import urllib
import urllib.parse
import urllib.request
import urllib.error
import ssl
import socket
import typing
from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PyQt5.QtCore import QObject, QUrl, QSettings
from PyQt5.QtCore import Qt
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply, QSslCertificate
from PyQt5.QtWidgets import QMessageBox
from . import osDetector
import certifi
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from . import os_detector
from . import tools
from . import VERSION
from .log import logger
# Server before this version uses "unsigned" scripts
OLD_METHOD_VERSION = '2.4.0'
# Callback for error on cert
# parameters are hostname, serial
# If returns True, ignores error
CertCallbackType = typing.Callable[[str, str], bool]
# Exceptions
class UDSException(Exception):
pass
class RetryException(UDSException):
pass
class InvalidVersion(UDSException):
downloadUrl: str
def __init__(self, downloadUrl: str) -> None:
super().__init__(downloadUrl)
self.downloadUrl = downloadUrl
class RestApi:
_restApiUrl: str # base Rest API URL
_callbackInvalidCert: typing.Optional[CertCallbackType]
_serverVersion: str
def __init__(
self,
restApiUrl,
callbackInvalidCert: typing.Optional[CertCallbackType] = None,
) -> None: # parent not used
logger.debug('Setting request URL to %s', restApiUrl)
self._restApiUrl = restApiUrl
self._callbackInvalidCert = callbackInvalidCert
self._serverVersion = ''
def get(self, url: str, params: typing.Optional[typing.Mapping[str, str]] = None) -> typing.Any:
if params:
url += '?' + '&'.join(
'{}={}'.format(k, urllib.parse.quote(str(v).encode('utf8')))
for k, v in params.items()
)
return json.loads(RestApi.getUrl(self._restApiUrl + url, self._callbackInvalidCert))
def processError(self, data: typing.Any) -> None:
if 'error' in data:
if data.get('retryable', '0') == '1':
raise RetryException(data['error'])
raise UDSException(data['error'])
def getVersion(self) -> str:
'''Gets and stores the serverVersion.
Also checks that the version is valid for us. If not,
will raise an "InvalidVersion' exception'''
class RestRequest(QObject):
downloadUrl = ''
if not self._serverVersion:
data = self.get('')
self.processError(data)
self._serverVersion = data['result']['requiredVersion']
downloadUrl = data['result']['downloadUrl']
restApiUrl = '' #
done = pyqtSignal(dict, name='done')
def __init__(self, url, parentWindow, done, params=None): # parent not used
super(RestRequest, self).__init__()
# private
self._manager = QNetworkAccessManager()
try:
if os.path.exists('/etc/ssl/certs/ca-certificates.crt'):
pass
# os.environ['REQUESTS_CA_BUNDLE'] = '/etc/ssl/certs/ca-certificates.crt'
except Exception:
pass
if params is not None:
url += '?' + '&'.join('{}={}'.format(k, urllib.parse.quote(str(v).encode('utf8'))) for k, v in params.items())
self.url = QUrl(RestRequest.restApiUrl + url)
# connect asynchronous result, when a request finishes
self._manager.finished.connect(self._finished)
self._manager.sslErrors.connect(self._sslError)
self._parentWindow = parentWindow
self.done.connect(done, Qt.QueuedConnection)
def _finished(self, reply):
'''
Handle signal 'finished'. A network request has finished.
'''
try:
if reply.error() != QNetworkReply.NoError:
raise Exception(reply.errorString())
data = bytes(reply.readAll())
data = json.loads(data)
if self._serverVersion > VERSION:
raise InvalidVersion(downloadUrl)
return self._serverVersion
except Exception as e:
data = {
'result': None,
'error': str(e)
}
raise UDSException(e)
self.done.emit(data)
def getScriptAndParams(self, ticket: str, scrambler: str) -> typing.Tuple[str, typing.Any]:
'''Gets the transport script, validates it if necesary
and returns it'''
try:
data = self.get(
'/{}/{}'.format(ticket, scrambler),
params={'hostname': tools.getHostName(), 'version': VERSION},
)
except Exception as e:
logger.exception('Got exception on getTransportData')
raise e
reply.deleteLater() # schedule for delete from main event loop
logger.debug('Transport data received')
self.processError(data)
def _sslError(self, reply, errors):
settings = QSettings()
settings.beginGroup('ssl')
cert = errors[0].certificate()
digest = str(cert.digest().toHex())
params = None
approved = settings.value(digest, False)
if self._serverVersion <= OLD_METHOD_VERSION:
script = bz2.decompress(base64.b64decode(data['result']))
# This fixes uds 2.2 "write" string on binary streams on some transport
script = script.replace(b'stdin.write("', b'stdin.write(b"')
script = script.replace(b'version)', b'version.decode("utf-8"))')
else:
res = data['result']
# We have three elements on result:
# * Script
# * Signature
# * Script data
# We test that the Script has correct signature, and them execute it with the parameters
# script, signature, params = res['script'].decode('base64').decode('bz2'), res['signature'], json.loads(res['params'].decode('base64').decode('bz2'))
script, signature, params = (
bz2.decompress(base64.b64decode(res['script'])),
res['signature'],
json.loads(bz2.decompress(base64.b64decode(res['params']))),
)
if tools.verifySignature(script, signature) is False:
logger.error('Signature is invalid')
errorString = '<p>The certificate for <b>{}</b> has the following errors:</p><ul>'.format(cert.subjectInfo(QSslCertificate.CommonName))
raise Exception(
'Invalid UDS code signature. Please, report to administrator'
)
for err in errors:
errorString += '<li>' + err.errorString() + '</li>'
return script.decode(), params
errorString += '</ul>'
# exec(script.decode("utf-8"), globals(), {'parent': self, 'sp': params})
if approved or QMessageBox.warning(self._parentWindow, 'SSL Warning', errorString, QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
settings.setValue(digest, True)
reply.ignoreSslErrors()
settings.endGroup()
@staticmethod
def _open(
url: str, certErrorCallback: typing.Optional[CertCallbackType] = None
) -> typing.Any:
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
ctx.load_verify_locations(certifi.where())
hostname = urllib.parse.urlparse(url)[1]
serial = ''
def get(self):
request = QNetworkRequest(self.url)
request.setRawHeader(b'User-Agent', osDetector.getOs().encode('utf-8') + b" - UDS Connector " + VERSION.encode('utf-8'))
self._manager.get(request)
if url.startswith('https'):
with ctx.wrap_socket(
socket.socket(socket.AF_INET, socket.SOCK_STREAM), server_hostname=hostname
) as s:
s.connect((hostname, 443))
# Get binary certificate
binCert = s.getpeercert(True)
if binCert:
cert = x509.load_der_x509_certificate(binCert, default_backend())
else:
raise Exception('Certificate not found!')
serial = hex(cert.serial_number)[2:]
response = None
ctx.check_hostname = True
ctx.verify_mode = ssl.CERT_REQUIRED
def urlopen(url: str):
# Generate the request with the headers
req = urllib.request.Request(url, headers={
'User-Agent': os_detector.getOs() + " - UDS Connector " + VERSION
})
return urllib.request.urlopen(req, context=ctx)
try:
response = urlopen(url)
except urllib.error.URLError as e:
if isinstance(e.reason, ssl.SSLCertVerificationError):
# Ask about invalid certificate
if certErrorCallback:
if certErrorCallback(hostname, serial):
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
response = urlopen(url)
else:
raise
else:
raise
return response
@staticmethod
def getUrl(
url: str, certErrorCallback: typing.Optional[CertCallbackType] = None
) -> bytes:
with RestApi._open(url, certErrorCallback) as response:
resp = response.read()
return resp

View File

@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2015 Virtual Cable S.L.
# Copyright (c) 2015-2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@@ -30,10 +29,6 @@
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
from base64 import b64decode
import tempfile
import string
import random
@@ -42,19 +37,24 @@ import socket
import stat
import sys
import time
import base64
import typing
import six
try:
import psutil
except ImportError:
psutil = None
from .log import logger
_unlinkFiles = []
_tasksToWait = []
_execBeforeExit = []
_unlinkFiles: typing.List[typing.Tuple[str, bool]] = []
_tasksToWait: typing.List[typing.Tuple[typing.Any, bool]] = []
_execBeforeExit: typing.List[typing.Callable[[], None]] = []
sys_fs_enc = sys.getfilesystemencoding() or 'mbcs'
# Public key for scripts
PUBLIC_KEY = '''-----BEGIN PUBLIC KEY-----
PUBLIC_KEY = b'''-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuNURlGjBpqbglkTTg2lh
dU5qPbg9Q+RofoDDucGfrbY0pjB9ULgWXUetUWDZhFG241tNeKw+aYFTEorK5P+g
ud7h9KfyJ6huhzln9eyDu3k+kjKUIB1PLtA3lZLZnBx7nmrHRody1u5lRaLVplsb
@@ -70,15 +70,13 @@ nVgtClKcDDlSaBsO875WDR0CAwEAAQ==
-----END PUBLIC KEY-----'''
def saveTempFile(content, filename=None):
def saveTempFile(content: str, filename: typing.Optional[str] = None) -> str:
if filename is None:
filename = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(16))
filename = ''.join(
random.choice(string.ascii_lowercase + string.digits) for _ in range(16)
)
filename = filename + '.uds'
if 'win32' in sys.platform:
logger.info('Fixing for win32')
filename = filename.encode(sys_fs_enc)
filename = os.path.join(tempfile.gettempdir(), filename)
with open(filename, 'w') as f:
@@ -88,10 +86,7 @@ def saveTempFile(content, filename=None):
return filename
def readTempFile(filename):
if 'win32' in sys.platform:
filename = filename.encode('utf-8')
def readTempFile(filename: str) -> typing.Optional[str]:
filename = os.path.join(tempfile.gettempdir(), filename)
try:
with open(filename, 'r') as f:
@@ -100,7 +95,7 @@ def readTempFile(filename):
return None
def testServer(host, port, timeOut=4):
def testServer(host: str, port: typing.Union[str, int], timeOut: int = 4) -> bool:
try:
sock = socket.create_connection((host, int(port)), timeOut)
sock.close()
@@ -109,11 +104,11 @@ def testServer(host, port, timeOut=4):
return True
def findApp(appName, extraPath=None):
if 'win32' in sys.platform and isinstance(appName, six.text_type):
appName = appName.encode(sys_fs_enc)
def findApp(
appName: str, extraPath: typing.Optional[str] = None
) -> typing.Optional[str]:
searchPath = os.environ['PATH'].split(os.pathsep)
if extraPath is not None:
if extraPath:
searchPath += list(extraPath)
for path in searchPath:
@@ -123,68 +118,89 @@ def findApp(appName, extraPath=None):
return None
def getHostName():
def getHostName() -> str:
'''
Returns current host name
In fact, it's a wrapper for socket.gethostname()
'''
hostname = socket.gethostname()
if 'win32' in sys.platform:
hostname = hostname.decode(sys_fs_enc)
hostname = six.text_type(hostname)
logger.info('Hostname: %s', hostname)
return hostname
# Queing operations (to be executed before exit)
def addFileToUnlink(filename):
def addFileToUnlink(filename: str, early: bool = False) -> None:
'''
Adds a file to the wait-and-unlink list
'''
_unlinkFiles.append(filename)
logger.debug(
'Added file %s to unlink on %s stage', filename, 'early' if early else 'later'
)
_unlinkFiles.append((filename, early))
def unlinkFiles():
def unlinkFiles(early: bool = False) -> None:
'''
Removes all wait-and-unlink files
'''
if _unlinkFiles:
time.sleep(5) # Wait 5 seconds before deleting anything
logger.debug('Unlinking files on %s stage', 'early' if early else 'later')
filesToUnlink = list(filter(lambda x: x[1] == early, _unlinkFiles))
if filesToUnlink:
logger.debug('Files to unlink: %s', filesToUnlink)
# Wait 2 seconds before deleting anything on early and 5 on later stages
time.sleep(1 + 2 * (1 + int(early)))
for f in _unlinkFiles:
for f in filesToUnlink:
try:
os.unlink(f)
except Exception:
pass
os.unlink(f[0])
except Exception as e:
logger.debug('File %s not deleted: %s', f[0], e)
def addTaskToWait(taks):
_tasksToWait.append(taks)
def addTaskToWait(task: typing.Any, includeSubprocess: bool = False) -> None:
logger.debug(
'Added task %s to wait %s', task, 'with subprocesses' if includeSubprocess else ''
)
_tasksToWait.append((task, includeSubprocess))
def waitForTasks():
for t in _tasksToWait:
def waitForTasks() -> None:
logger.debug('Started to wait %s', _tasksToWait)
for task, waitForSubp in _tasksToWait:
logger.debug('Waiting for task %s, subprocess wait: %s', task, waitForSubp)
try:
if hasattr(t, 'join'):
t.join()
elif hasattr(t, 'wait'):
t.wait()
except Exception:
pass
if hasattr(task, 'join'):
task.join()
elif hasattr(task, 'wait'):
task.wait()
# If wait for spanwed process (look for process with task pid) and we can look for them...
logger.debug('Psutil: %s, waitForSubp: %s, hasattr: %s', psutil, waitForSubp, hasattr(task, 'pid'))
if psutil and waitForSubp and hasattr(task, 'pid'):
subProcesses = list(filter(
lambda x: x.ppid() == task.pid, psutil.process_iter(attrs=('ppid',))
))
logger.debug('Waiting for subprocesses... %s, %s', task.pid, subProcesses)
for i in subProcesses:
logger.debug('Found %s', i)
i.wait()
except Exception as e:
logger.error('Waiting for tasks to finish error: %s', e)
def addExecBeforeExit(fnc):
def addExecBeforeExit(fnc: typing.Callable[[], None]) -> None:
logger.debug('Added exec before exit: %s', fnc)
_execBeforeExit.append(fnc)
def execBeforeExit():
def execBeforeExit() -> None:
logger.debug('Esecuting exec before exit: %s', _execBeforeExit)
for fnc in _execBeforeExit:
fnc.__call__()
fnc()
def verifySignature(script, signature):
def verifySignature(script: bytes, signature: bytes) -> bool:
'''
Verifies with a public key from whom the data came that it was indeed
signed by their private key
@@ -193,13 +209,20 @@ def verifySignature(script, signature):
return: Boolean. True if the signature is valid; False otherwise.
'''
# For signature checking
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import utils, padding
rsakey = RSA.importKey(PUBLIC_KEY)
signer = PKCS1_v1_5.new(rsakey)
digest = SHA256.new(script) # Script is "binary string" here
if signer.verify(digest, b64decode(signature)):
return True
return False
public_key = serialization.load_pem_public_key(
data=PUBLIC_KEY, backend=default_backend()
)
try:
public_key.verify(
base64.b64decode(signature), script, padding.PKCS1v15(), hashes.SHA256()
)
except Exception: # InvalidSignature
return False
# If no exception, the script was fine...
return True

View File

@@ -0,0 +1,284 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
import socket
import socketserver
import ssl
import threading
import time
import random
import threading
import select
import typing
import logging
import certifi
HANDSHAKE_V1 = b'\x5AMGB\xA5\x01\x00'
BUFFER_SIZE = 1024 * 16 # Max buffer length
DEBUG = True
LISTEN_ADDRESS = '0.0.0.0' if DEBUG else '127.0.0.1'
# ForwarServer states
TUNNEL_LISTENING, TUNNEL_OPENING, TUNNEL_PROCESSING, TUNNEL_ERROR = 0, 1, 2, 3
logger = logging.getLogger(__name__)
class ForwardServer(socketserver.ThreadingTCPServer):
daemon_threads = True
allow_reuse_address = True
remote: typing.Tuple[str, int]
ticket: str
stop_flag: threading.Event
can_stop: bool
timeout: int
timer: typing.Optional[threading.Timer]
check_certificate: bool
current_connections: int
status: int
def __init__(
self,
remote: typing.Tuple[str, int],
ticket: str,
timeout: int = 0,
local_port: int = 0,
check_certificate: bool = True,
) -> None:
local_port = local_port or random.randrange(33000, 53000)
super().__init__(
server_address=(LISTEN_ADDRESS, local_port), RequestHandlerClass=Handler
)
self.remote = remote
self.ticket = ticket
# Negative values for timeout, means "accept always connections"
# "but if no connection is stablished on timeout (positive)"
# "stop the listener"
self.timeout = int(time.time()) + timeout if timeout > 0 else 0
self.check_certificate = check_certificate
self.stop_flag = threading.Event() # False initial
self.current_connections = 0
self.status = TUNNEL_LISTENING
self.can_stop = False
timeout = abs(timeout) or 60
self.timer = threading.Timer(
abs(timeout), ForwardServer.__checkStarted, args=(self,)
)
self.timer.start()
def stop(self) -> None:
if not self.stop_flag.is_set():
logger.debug('Stopping servers')
self.stop_flag.set()
if self.timer:
self.timer.cancel()
self.timer = None
self.shutdown()
def connect(self) -> ssl.SSLSocket:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as rsocket:
logger.info('CONNECT to %s', self.remote)
rsocket.connect(self.remote)
context = ssl.create_default_context()
# Do not "recompress" data, use only "base protocol" compression
context.options |= ssl.OP_NO_COMPRESSION
context.load_verify_locations(certifi.where()) # Load certifi certificates
# If ignore remote certificate
if self.check_certificate is False:
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
logger.warning('Certificate checking is disabled!')
return context.wrap_socket(rsocket, server_hostname=self.remote[0])
def check(self) -> bool:
if self.status == TUNNEL_ERROR:
return False
logger.debug('Checking tunnel availability')
try:
with self.connect() as ssl_socket:
ssl_socket.sendall(HANDSHAKE_V1 + b'TEST')
resp = ssl_socket.recv(2)
if resp != b'OK':
raise Exception({'Invalid tunnelresponse: {resp}'})
logger.debug('Tunnel is available!')
return True
except Exception as e:
logger.error(
'Error connecting to tunnel server %s: %s', self.server_address, e
)
return False
@property
def stoppable(self) -> bool:
logger.debug('Is stoppable: %s', self.can_stop)
return self.can_stop or (self.timeout != 0 and int(time.time()) > self.timeout)
@staticmethod
def __checkStarted(fs: 'ForwardServer') -> None:
logger.debug('New connection limit reached')
fs.timer = None
fs.can_stop = True
if fs.current_connections <= 0:
fs.stop()
class Handler(socketserver.BaseRequestHandler):
# Override Base type
server: ForwardServer
# server: ForwardServer
def handle(self) -> None:
self.server.status = TUNNEL_OPENING
# If server processing is over time
if self.server.stoppable:
self.server.status = TUNNEL_ERROR
logger.info('Rejected timedout connection')
self.request.close() # End connection without processing it
return
self.server.current_connections += 1
# Open remote connection
try:
logger.debug('Ticket %s', self.server.ticket)
with self.server.connect() as ssl_socket:
# Send handhshake + command + ticket
ssl_socket.sendall(HANDSHAKE_V1 + b'OPEN' + self.server.ticket.encode())
# Check response is OK
data = ssl_socket.recv(2)
if data != b'OK':
data += ssl_socket.recv(128)
raise Exception(
f'Error received: {data.decode(errors="ignore")}'
) # Notify error
# All is fine, now we can tunnel data
self.process(remote=ssl_socket)
except Exception as e:
logger.error(f'Error connecting to {self.server.remote!s}: {e!s}')
self.server.status = TUNNEL_ERROR
self.server.stop()
finally:
self.server.current_connections -= 1
if self.server.current_connections <= 0 and self.server.stoppable:
self.server.stop()
# Processes data forwarding
def process(self, remote: ssl.SSLSocket):
self.server.status = TUNNEL_PROCESSING
logger.debug('Processing tunnel with ticket %s', self.server.ticket)
# Process data until stop requested or connection closed
try:
while not self.server.stop_flag.is_set():
r, _w, _x = select.select([self.request, remote], [], [], 1.0)
if self.request in r:
data = self.request.recv(BUFFER_SIZE)
if not data:
break
remote.sendall(data)
if remote in r:
data = remote.recv(BUFFER_SIZE)
if not data:
break
self.request.sendall(data)
logger.debug('Finished tunnel with ticket %s', self.server.ticket)
except Exception as e:
pass
def _run(server: ForwardServer) -> None:
logger.debug(
'Starting forwarder: %s -> %s, timeout: %d',
server.server_address,
server.remote,
server.timeout,
)
server.serve_forever()
logger.debug('Stoped forwarder %s -> %s', server.server_address, server.remote)
def forward(
remote: typing.Tuple[str, int],
ticket: str,
timeout: int = 0,
local_port: int = 0,
check_certificate=True,
) -> ForwardServer:
fs = ForwardServer(
remote=remote,
ticket=ticket,
timeout=timeout,
local_port=local_port,
check_certificate=check_certificate,
)
# Starts a new thread
threading.Thread(target=_run, args=(fs,)).start()
return fs
if __name__ == "__main__":
import sys
log = logging.getLogger()
log.setLevel(logging.DEBUG)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter(
'%(levelname)s - %(message)s'
) # Basic log format, nice for syslog
handler.setFormatter(formatter)
log.addHandler(handler)
ticket = 'mffqg7q4s61fvx0ck2pe0zke6k0c5ipb34clhbkbs4dasb4g'
fs = forward(
('172.27.0.1', 7777),
ticket,
local_port=49999,
timeout=-20,
check_certificate=False,
)

View File

@@ -1,4 +0,0 @@
/bin
/udsclient_*
/udsclient-*.tar.gz
/*.rpm

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>UDSclient</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}/src</path>
</pydev_pathproperty>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">system-2.7</pydev_property>
</pydev_project>

View File

@@ -1,4 +0,0 @@
/udsclient-opensuse-[0-9]*.spec
/udsclient-[0-9]*.spec
/debian/udsclient
/targz

View File

@@ -1,51 +0,0 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Version
# VERSION := 1.7.5
# Directories
SOURCEDIR := ../src
LIBDIR := $(DESTDIR)/usr/lib/UDSClient
BINDIR := $(DESTDIR)/usr/bin
SBINDIR = $(DESTDIR)/usr/sbin
APPSDIR := $(DESTDIR)/usr/share/applications
PYC := $(shell find $(SOURCEDIR) -name '*.py[co]')
CACHES := $(shell find $(SOURCEDIR) -name '__pycache__')
clean:
rm -rf $(PYC) $(CACHES) $(DESTDIR)
install:
rm -rf $(DESTDIR)
mkdir -p $(LIBDIR)
#mkdir -p $(BINDIR)
#mkdir -p $(SBINDIR)
mkdir -p $(APPSDIR)
mkdir $(LIBDIR)/uds
# Cleans up .pyc and cache folders
rm -f $(PYC) $(CACHES)
cp $(SOURCEDIR)/uds/*.py $(LIBDIR)/uds
cp $(SOURCEDIR)/UDS*.py $(LIBDIR)
# URL Catchers elements for gnome/kde
cp desktop/UDSClient.desktop $(APPSDIR)
chmod 755 $(LIBDIR)/UDSClient.py
ifeq ($(DISTRO),targz)
cp installer.sh $(DESTDIR)/install.sh
tar czvf ../udsclient-$(VERSION).tar.gz -C $(DESTDIR) .
endif
# chmod 0755 $(BINDIR)/udsclient
uninstall:
rm -rf $(LIBDIR)
# rm -f $(BINDIR)/udsclient
# rm -rf $(CFGDIR)

View File

@@ -1,36 +0,0 @@
#!/bin/bash
VERSION=`cat ../../../VERSION`
RELEASE=1
# Debian based
dpkg-buildpackage -b
# Now rpm based
top=`pwd`
cat udsclient-template.spec |
sed -e s/"version 0.0.0"/"version ${VERSION}"/g |
sed -e s/"release 1"/"release ${RELEASE}"/g > udsclient-$VERSION.spec
# Now fix dependencies for opensuse
cat udsclient-template.spec |
sed -e s/"version 0.0.0"/"version ${VERSION}"/g |
sed -e s/"name udsclient"/"name udsclient-opensuse"/g |
sed -e s/"PyQt4"/"python-qt4"/g |
sed -e s/"libXScrnSaver"/"libXss1"/g > udsclient-opensuse-$VERSION.spec
# Right now, udsactor-xrdp-.spec is not needed
for pkg in udsclient-$VERSION.spec udsclient-opensuse-$VERSION.spec; do
rm -rf rpm
for folder in SOURCES BUILD RPMS SPECS SRPMS; do
mkdir -p rpm/$folder
done
rpmbuild -v -bb --clean --buildroot=$top/rpm/BUILD/$pkg-root --target noarch $pkg 2>&1
done
#rm udsclient-$VERSION
make DESTDIR=targz DISTRO=targz VERSION=${VERSION} install

View File

@@ -1,3 +0,0 @@
/udsactor/
/udsactor-xrdp/
/udsactor-nx/

View File

@@ -1,47 +0,0 @@
udsclient (3.0.0) stable; urgency=medium
* Upgraded to 3.0.0 release
-- Adolfo Gómez García <agomez@virtualcable.es> Wed, 10 Jul 2019 9:24:10 +0200
udsclient (2.2.1) stable; urgency=medium
* Upgraded to 2.2.1 release
-- Adolfo Gómez García <agomez@virtualcable.es> Thu, 2 Oct 2018 12:44:12 +0200
udsclient (2.2.0) stable; urgency=medium
* Updated release
-- Adolfo Gómez García <agomez@virtualcable.es> Thu, 27 Aug 2017 14:18:18 +0200
udsclient (2.1.0) stable; urgency=medium
* Updated release
-- Adolfo Gómez García <agomez@virtualcable.es> Sun, 23 Oct 2016 21:12:23 +0200
udsclient (2.0.0) stable; urgency=medium
* Release upgrade
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 01 Mar 2016 09:33:18 +0100
udsclient (1.9.1) stable; urgency=medium
* Minor fixes & making version match UDS version
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 01 Mar 2016 03:02:37 +0100
udsclient (1.9.0) stable; urgency=medium
* Minor fixes & making version match UDS version
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 05 May 2015 07:03:47 +0200
udsclient (1.7.5) stable; urgency=medium
* Initial release.
-- Adolfo Gómez García <agomez@virtualcable.es> Fri, 10 Apr 2015 05:32:41 +0100

View File

@@ -1 +0,0 @@
9

View File

@@ -1,15 +0,0 @@
Source: udsclient
Section: admin
Priority: optional
Maintainer: Adolfo Gómez García <agomez@virtualcable.es>
Build-Depends: debhelper (>= 7), po-debconf
Standards-Version: 3.9.2
Homepage: http://www.virtualcable.es
Package: udsclient
Section: admin
Priority: optional
Architecture: all
Depends: python-paramiko (>=0.8.2), python-crypto, python-qt4 (>=4.9), python-six(>=1.1), python (>=2.7), freerdp2-x11 | freerdp-x11, desktop-file-utils, ${misc:Depends}
Description: Client connector for Universal Desktop Services (UDS) Broker
This package provides the required components to allow this machine to connect to services provided by UDS Broker.

View File

@@ -1,26 +0,0 @@
Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
Name: udsclient
Maintainer: Adolfo Gómez García
Source: http://www.udsenterprise.com/
Copyright: 2014 Virtual Cable S.L.U.
License: BSD-3-clause
License: GPL-2+
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
.
On Debian systems, the full text of the GNU General Public
License version 2 can be found in the file
`/usr/share/common-licenses/GPL-2'.

View File

@@ -1 +0,0 @@
readme.txt

View File

@@ -1,2 +0,0 @@
udsclient_3.0.0_all.deb admin optional
udsclient_3.0.0_amd64.buildinfo admin optional

View File

@@ -1,44 +0,0 @@
#!/usr/bin/make -f
# -*- makefile -*-
configure: configure-stamp
configure-stamp:
dh_testdir
touch configure-stamp
build: build-arch build-indep
build-arch: build-stamp
build-indep: build-stamp
build-stamp: configure-stamp
dh_testdir
$(MAKE)
touch $@
clean:
dh_testdir
dh_testroot
rm -f build-stamp configure-stamp
dh_clean
install: build
dh_testdir
dh_testroot
dh_prep
dh_installdirs
$(MAKE) DESTDIR=$(CURDIR)/debian/udsclient install
binary-arch: build install
# emptyness
binary-indep: build install
dh_testdir
dh_testroot
dh_installchangelogs
dh_installdocs
dh_installdebconf
dh_installinit --no-start
dh_python2=python
dh_compress
dh_link
dh_fixperms
dh_installdeb
dh_shlibdeps
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-indep
.PHONY: build clean binary-indep binary install configure

View File

@@ -1,17 +0,0 @@
dh_prep
dh_installdirs
dh_installchangelogs
dh_installdocs
dh_installdebconf
dh_installinit
dh_compress
dh_link
dh_fixperms
dh_installdeb
dh_shlibdeps
dh_gencontrol
dh_md5sums
dh_builddeb
dh_builddeb
dh_builddeb
dh_builddeb

View File

@@ -1,21 +0,0 @@
#!/bin/sh
. /usr/share/debconf/confmodule
set -e
case "$1" in
configure)
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
exit 0

View File

@@ -1,6 +0,0 @@
#!/bin/sh -e
. /usr/share/debconf/confmodule
set -e

View File

@@ -1 +0,0 @@
#! /bin/bash -e

View File

@@ -1,20 +0,0 @@
Template: udsactor/host
Type: string
Default:
Description: UDS Server address:
The actor needs the address of the server in order to communicate with it.
Provide here full address (or i) of the UDS server
Template: udsactor/secure
Type: boolean
Default: true
Description: Use secure (https) connection to communicate with UDS server?
If selected, the communication will be done using https.
If not selected, the communication will be done using http
Template: udsactor/masterKey
Type: string
Default:
Description: Master Key:
This key is available on UDS Administration interface.
Look for it under configuration, on Security tab.

View File

@@ -1,11 +0,0 @@
[Desktop Entry]
Name=UDSClient
Comment=UDS Helper
Keywords=uds;client;vdi;
Exec=/usr/lib/UDSClient/UDSClient.py %u
Icon=help-browser
StartupNotify=true
Terminal=false
Type=Application
Categories=Utility;
MimeType=x-scheme-handler/uds;x-scheme-handler/udss;

View File

@@ -1,14 +0,0 @@
#!/bin/sh
cp -r usr/lib/UDSClient /usr/lib/UDSClient
cp -r usr/share/applications /usr/lib/applications -R
update-desktop-database
echo "Installation process done."
echo "Remembar that the following packages must be installed on system:"
echo "* Python paramiko"
echo "* Python pyqt4"
echo "Theese packages (as their names), are dependent on your platform, so you must locate and install them"
echo "You can install them directly on any platform with pip, using this simple command: "
echo "pip install PyQt4 paramiko"

View File

@@ -1,3 +0,0 @@
UDSClient is the client connector needed to get acccess to services managed by UDS Broker.
Please, visit http://www.udsenterprise.com for more information

View File

@@ -1,52 +0,0 @@
%define _topdir %(echo $PWD)/rpm
%define name udsclient
%define version 0.0.0
%define release 1
%define buildroot %{_topdir}/%{name}-%{version}-%{release}-root
BuildRoot: %{buildroot}
Name: %{name}
Version: %{version}
Release: %{release}
Summary: Client for Universal Desktop Services (UDS) Broker
License: BSD3
Group: Applications/Productivity
Requires: python-six python-paramiko PyQt4
Vendor: Virtual Cable S.L.U.
URL: http://www.udsenterprise.com
Provides: udsclient
%define _rpmdir ../
%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
%install
curdir=`pwd`
cd ../..
make DESTDIR=$RPM_BUILD_ROOT DISTRO=rh install
cd $curdir
%post
/usr/bin/update-desktop-database
if [ ! -d /media ]; then mkdir /media; echo "/media created for compatibility"; fi
%clean
rm -rf $RPM_BUILD_ROOT
curdir=`pwd`
cd ../..
make DESTDIR=$RPM_BUILD_ROOT DISTRO=rh clean
cd $curdir
%postun
# And, posibly, the .pyc leaved behind on /usr/share/UDSActor
rm -rf /usr/share/UDClient > /dev/null 2>&1
/usr/bin/update-desktop-database
%description
This package provides the required components to allow connection to services offered by UDS Broker.
%files
%defattr(-,root,root)
/usr/lib/UDSClient/*
/usr/share/applications/UDSClient.desktop

View File

@@ -1,4 +0,0 @@
/build
/dist
UDSClient.dmg
UDSClient.pkg

View File

@@ -1,338 +0,0 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2017 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
# pylint: disable=c-extension-no-member
from __future__ import unicode_literals
import sys
import webbrowser
import json
from PyQt4 import QtCore, QtGui # @UnresolvedImport
import six
from uds.rest import RestRequest
from uds.forward import forward # pylint: disable=unused-import
from uds.log import logger
from uds import tools
from uds import VERSION
from UDSWindow import Ui_MainWindow
# Server before this version uses "unsigned" scripts
OLD_METHOD_VERSION = '2.4.0'
class RetryException(Exception):
pass
class UDSClient(QtGui.QMainWindow):
ticket = None
scrambler = None
withError = False
animTimer = None
anim = 0
animInverted = False
serverVersion = 'X.Y.Z' # Will be overwriten on getVersion
req = None
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.progressBar.setValue(0)
self.ui.cancelButton.clicked.connect(self.cancelPushed)
self.ui.info.setText('Initializing...')
screen = QtGui.QDesktopWidget().screenGeometry()
mysize = self.geometry()
hpos = (screen.width() - mysize.width()) / 2
vpos = (screen.height() - mysize.height() - mysize.height()) / 2
self.move(hpos, vpos)
self.animTimer = QtCore.QTimer()
QtCore.QObject.connect(self.animTimer, QtCore.SIGNAL('timeout()'), self.updateAnim)
self.activateWindow()
self.startAnim()
def closeWindow(self):
self.close()
def processError(self, data):
if 'error' in data:
# QtGui.QMessageBox.critical(self, 'Request error {}'.format(data.get('retryable', '0')), data['error'], QtGui.QMessageBox.Ok)
if data.get('retryable', '0') == '1':
raise RetryException(data['error'])
raise Exception(data['error'])
# QtGui.QMessageBox.critical(self, 'Request error', rest.data['error'], QtGui.QMessageBox.Ok)
# self.closeWindow()
# return
def showError(self, error):
logger.error('got error: %s', error)
self.stopAnim()
self.ui.info.setText('UDS Plugin Error') # In fact, main window is hidden, so this is not visible... :)
self.closeWindow()
QtGui.QMessageBox.critical(None, 'UDS Plugin Error', '{}'.format(error), QtGui.QMessageBox.Ok)
self.withError = True
def cancelPushed(self):
self.close()
@QtCore.pyqtSlot()
def updateAnim(self):
self.anim += 2
if self.anim > 99:
self.animInverted = not self.animInverted
self.ui.progressBar.setInvertedAppearance(self.animInverted)
self.anim = 0
self.ui.progressBar.setValue(self.anim)
def startAnim(self):
self.ui.progressBar.invertedAppearance = False
self.anim = 0
self.animInverted = False
self.ui.progressBar.setInvertedAppearance(self.animInverted)
self.animTimer.start(40)
def stopAnim(self):
self.ui.progressBar.invertedAppearance = False
self.animTimer.stop()
@QtCore.pyqtSlot()
def getVersion(self):
self.req = RestRequest('', self, self.version)
self.req.get()
@QtCore.pyqtSlot(dict)
def version(self, data):
try:
self.processError(data)
self.ui.info.setText('Processing...')
if data['result']['requiredVersion'] > VERSION:
QtGui.QMessageBox.critical(self, 'Upgrade required', 'A newer connector version is required.\nA browser will be opened to download it.', QtGui.QMessageBox.Ok)
webbrowser.open(data['result']['downloadUrl'])
self.closeWindow()
return
self.serverVersion = data['result']['requiredVersion']
self.getTransportData()
except RetryException as e:
self.ui.info.setText(six.text_type(e))
QtCore.QTimer.singleShot(1000, self.getVersion)
except Exception as e:
self.showError(e)
@QtCore.pyqtSlot()
def getTransportData(self):
try:
self.req = RestRequest('/{}/{}'.format(self.ticket, self.scrambler), self, self.transportDataReceived, params={'hostname': tools.getHostName(), 'version': VERSION})
self.req.get()
except Exception as e:
logger.exception('Got exception on getTransportData')
raise e
@QtCore.pyqtSlot(dict)
def transportDataReceived(self, data):
logger.debug('Transport data received')
try:
self.processError(data)
params = None
if self.serverVersion <= OLD_METHOD_VERSION:
script = data['result'].decode('base64').decode('bz2')
else:
res = data['result']
# We have three elements on result:
# * Script
# * Signature
# * Script data
# We test that the Script has correct signature, and them execute it with the parameters
script, signature, params = res['script'].decode('base64').decode('bz2'), res['signature'], json.loads(res['params'].decode('base64').decode('bz2'))
if tools.verifySignature(script, signature) is False:
logger.error('Signature is invalid')
raise Exception('Invalid UDS code signature. Please, report to administrator')
self.stopAnim()
if 'darwin' in sys.platform:
self.showMinimized()
QtCore.QTimer.singleShot(3000, self.endScript)
self.hide()
# if self.serverVersion <= OLD_METHOD_VERSION:
# errorString = '<p>The server <b>{}</b> runs an old version of UDS:</p>'.format(host)
# errorString += '<p>To avoid security issues, you must approve old UDS Version access.</p>'
#
# if QtGui.QMessageBox.warning(None, 'ACCESS Warning', errorString, QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) == QtGui.QMessageBox.No:
# raise Exception('Server not approved. Access denied.')
six.exec_(script, globals(), {'parent': self, 'sp': params})
except RetryException as e:
self.ui.info.setText(six.text_type(e) + ', retrying access...')
# Retry operation in ten seconds
QtCore.QTimer.singleShot(10000, self.getTransportData)
except Exception as e:
#logger.exception('Got exception executing script:')
self.showError(e)
def endScript(self):
# After running script, wait for stuff
try:
tools.waitForTasks()
except Exception:
pass
try:
tools.unlinkFiles()
except Exception:
pass
try:
tools.execBeforeExit()
except Exception:
pass
self.closeWindow()
def start(self):
'''
Starts proccess by requesting version info
'''
self.ui.info.setText('Initializing...')
QtCore.QTimer.singleShot(100, self.getVersion)
def done(data):
QtGui.QMessageBox.critical(None, 'Notice', six.text_type(data.data), QtGui.QMessageBox.Ok)
sys.exit(0)
# Ask user to approve endpoint
def approveHost(hostName, parentWindow=None):
settings = QtCore.QSettings()
settings.beginGroup('endpoints')
approved = settings.value(hostName, False).toBool()
errorString = '<p>The server <b>{}</b> must be approved:</p>'.format(hostName)
errorString += '<p>Only approve UDS servers that you trust to avoid security issues.</p>'
if approved or QtGui.QMessageBox.warning(parentWindow, 'ACCESS Warning', errorString, QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) == QtGui.QMessageBox.Yes:
settings.setValue(hostName, True)
approved = True
settings.endGroup()
return approved
if __name__ == "__main__":
logger.debug('Initializing connector')
# Initialize app
app = QtGui.QApplication(sys.argv)
# Set several info for settings
QtCore.QCoreApplication.setOrganizationName('Virtual Cable S.L.U.')
QtCore.QCoreApplication.setApplicationName('UDS Connector')
if 'darwin' not in sys.platform:
logger.debug('Mac OS *NOT* Detected')
app.setStyle('plastique')
if six.PY3 is False:
logger.debug('Fixing threaded execution of commands')
import threading
threading._DummyThread._Thread__stop = lambda x: 42 # type: ignore, pylint: disable=protected-access
# First parameter must be url
try:
uri = sys.argv[1]
if uri == '--test':
sys.exit(0)
logger.debug('URI: %s', uri)
if uri[:6] != 'uds://' and uri[:7] != 'udss://':
raise Exception()
ssl = uri[3] == 's'
host, UDSClient.ticket, UDSClient.scrambler = uri.split('//')[1].split('/') # type: ignore
logger.debug('ssl:%s, host:%s, ticket:%s, scrambler:%s', ssl, host, UDSClient.ticket, UDSClient.scrambler)
except Exception:
logger.debug('Detected execution without valid URI, exiting')
QtGui.QMessageBox.critical(None, 'Notice', 'UDS Client Version {}'.format(VERSION), QtGui.QMessageBox.Ok)
sys.exit(1)
# Setup REST api endpoint
RestRequest.restApiUrl = '{}://{}/rest/client'.format(['http', 'https'][ssl], host)
logger.debug('Setting request URL to %s', RestRequest.restApiUrl)
# RestRequest.restApiUrl = 'https://172.27.0.1/rest/client'
try:
logger.debug('Starting execution')
# Approbe before going on
if approveHost(host) is False:
raise Exception('Host {} was not approved'.format(host))
win = UDSClient()
win.show()
win.start()
exitVal = app.exec_()
logger.debug('Execution finished correctly')
except Exception as e:
logger.exception('Got an exception executing client:')
exitVal = 128
QtGui.QMessageBox.critical(None, 'Error', six.text_type(e), QtGui.QMessageBox.Ok)
logger.debug('Exiting')
sys.exit(exitVal)

View File

@@ -1,6 +0,0 @@
<RCC>
<qresource prefix="images">
<file alias="logo-uds-small">images/logo-uds-small.png</file>
<file alias="logo-uds-big">images/logo-uds.png</file>
</qresource>
</RCC>

File diff suppressed because it is too large Load Diff

View File

@@ -1,105 +0,0 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'UDSWindow.ui'
#
# Created: Mon Apr 27 21:41:43 2015
# by: PyQt4 UI code generator 4.11.2
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.setWindowModality(QtCore.Qt.NonModal)
MainWindow.resize(259, 185)
MainWindow.setCursor(QtGui.QCursor(QtCore.Qt.BusyCursor))
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/images/logo-uds-small")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
MainWindow.setWindowIcon(icon)
MainWindow.setWindowOpacity(1.0)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setAutoFillBackground(True)
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
self.verticalLayout_2 = QtGui.QVBoxLayout(self.centralwidget)
self.verticalLayout_2.setSpacing(4)
self.verticalLayout_2.setMargin(4)
self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
self.frame = QtGui.QFrame(self.centralwidget)
self.frame.setFrameShape(QtGui.QFrame.StyledPanel)
self.frame.setFrameShadow(QtGui.QFrame.Raised)
self.frame.setObjectName(_fromUtf8("frame"))
self.verticalLayout_3 = QtGui.QVBoxLayout(self.frame)
self.verticalLayout_3.setSpacing(4)
self.verticalLayout_3.setMargin(4)
self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3"))
self.verticalLayout = QtGui.QVBoxLayout()
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.image = QtGui.QLabel(self.frame)
self.image.setMinimumSize(QtCore.QSize(0, 24))
self.image.setAutoFillBackground(True)
self.image.setText(_fromUtf8(""))
self.image.setPixmap(QtGui.QPixmap(_fromUtf8(":/images/logo-uds-small")))
self.image.setScaledContents(False)
self.image.setAlignment(QtCore.Qt.AlignCenter)
self.image.setObjectName(_fromUtf8("image"))
self.verticalLayout.addWidget(self.image)
self.info = QtGui.QLabel(self.frame)
self.info.setMaximumSize(QtCore.QSize(16777215, 16))
self.info.setObjectName(_fromUtf8("info"))
self.verticalLayout.addWidget(self.info)
self.progressBar = QtGui.QProgressBar(self.frame)
self.progressBar.setProperty("value", 24)
self.progressBar.setTextVisible(False)
self.progressBar.setObjectName(_fromUtf8("progressBar"))
self.verticalLayout.addWidget(self.progressBar)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.cancelButton = QtGui.QPushButton(self.frame)
self.cancelButton.setDefault(True)
self.cancelButton.setFlat(False)
self.cancelButton.setObjectName(_fromUtf8("cancelButton"))
self.horizontalLayout.addWidget(self.cancelButton)
spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.verticalLayout.addLayout(self.horizontalLayout)
self.verticalLayout_3.addLayout(self.verticalLayout)
self.verticalLayout_2.addWidget(self.frame)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "UDS Connection", None))
self.info.setText(_translate("MainWindow", "TextLabel", None))
self.cancelButton.setText(_translate("MainWindow", "Cancel", None))
import UDSResources_rc
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

View File

@@ -1,160 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>259</width>
<height>185</height>
</rect>
</property>
<property name="cursor">
<cursorShape>BusyCursor</cursorShape>
</property>
<property name="windowTitle">
<string>UDS Connection</string>
</property>
<property name="windowIcon">
<iconset resource="UDSResources.qrc">
<normaloff>:/images/logo-uds-small</normaloff>:/images/logo-uds-small</iconset>
</property>
<property name="windowOpacity">
<double>1.000000000000000</double>
</property>
<widget class="QWidget" name="centralwidget">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>4</number>
</property>
<property name="margin">
<number>4</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>4</number>
</property>
<property name="margin">
<number>4</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="image">
<property name="minimumSize">
<size>
<width>0</width>
<height>24</height>
</size>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="UDSResources.qrc">:/images/logo-uds-small</pixmap>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="info">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
<property name="default">
<bool>true</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
<resources>
<include location="UDSResources.qrc"/>
</resources>
<connections/>
</ui>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

View File

@@ -1,211 +0,0 @@
# Based on forward.py from paramiko
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# https://github.com/paramiko/paramiko/blob/master/demos/forward.py
from __future__ import unicode_literals
from binascii import hexlify
import threading
import random
import time
import select
import paramiko
import six
from .log import logger
if six.PY2:
import SocketServer as socketserver # pylint: disable=import-error
else:
import socketserver
class CheckfingerPrints(paramiko.MissingHostKeyPolicy):
def __init__(self, fingerPrints):
super(CheckfingerPrints, self).__init__()
self.fingerPrints = fingerPrints
def missing_host_key(self, client, hostname, key):
if self.fingerPrints:
remotefingerPrints = hexlify(key.get_fingerprint()).decode().lower()
logger.debug('Checking keys {} against {}'.format(remotefingerPrints, self.fingerPrints))
if remotefingerPrints not in self.fingerPrints.split(','):
logger.error("Server {!r} has invalid fingerPrints. ({} vs {})".format(hostname, remotefingerPrints, self.fingerPrints))
raise paramiko.SSHException(
"Server {!r} has invalid fingerPrints".format(hostname)
)
class ForwardServer(socketserver.ThreadingTCPServer):
daemon_threads = True
allow_reuse_address = True
class Handler(socketserver.BaseRequestHandler):
def handle(self):
self.thread.currentConnections += 1
try:
chan = self.ssh_transport.open_channel('direct-tcpip',
(self.chain_host, self.chain_port),
self.request.getpeername())
except Exception as e:
logger.exception('Incoming request to %s:%d failed: %s', self.chain_host, self.chain_port, repr(e))
return
if chan is None:
logger.error('Incoming request to %s:%d was rejected by the SSH server.', self.chain_host, self.chain_port)
return
logger.debug('Connected! Tunnel open %r -> %r -> %r', self.request.getpeername(), chan.getpeername(), (self.chain_host, self.chain_port))
try:
while self.event.is_set() is False:
r, _w, _x = select.select([self.request, chan], [], [], 1) # pylint: disable=unused-variable
if self.request in r:
data = self.request.recv(1024)
if not data:
break
chan.send(data)
if chan in r:
data = chan.recv(1024)
if not data:
break
self.request.send(data)
except Exception:
pass
try:
peername = self.request.getpeername()
chan.close()
self.request.close()
logger.debug('Tunnel closed from %r', peername,)
except Exception:
pass
self.thread.currentConnections -= 1
if self.thread.stoppable is True and self.thread.currentConnections == 0:
self.thread.stop()
class ForwardThread(threading.Thread):
status = 0 # Connecting
def __init__(self, server, port, username, password, localPort, redirectHost, redirectPort, waitTime, fingerPrints):
threading.Thread.__init__(self)
self.client = None
self.fs = None
self.server = server
self.port = int(port)
self.username = username
self.password = password
self.localPort = int(localPort)
self.redirectHost = redirectHost
self.redirectPort = redirectPort
self.waitTime = waitTime
self.fingerPrints = fingerPrints
self.stopEvent = threading.Event()
self.timer = None
self.currentConnections = 0
self.stoppable = False
self.client = None
def clone(self, redirectHost, redirectPort, localPort=None):
if localPort is None:
localPort = random.randrange(33000, 52000)
ft = ForwardThread(self.server, self.port, self.username, self.password, localPort, redirectHost, redirectPort, self.waitTime, self.fingerPrints)
ft.client = self.client
self.client.useCount += 1 # One more using this client
ft.start()
while ft.status == 0:
time.sleep(0.1)
return (ft, localPort)
def _timerFnc(self):
self.timer = None
logger.debug('Timer fnc: %s', self.currentConnections)
self.stoppable = True
if self.currentConnections <= 0:
self.stop()
def run(self):
if self.client is None:
try:
self.client = paramiko.SSHClient()
self.client.useCount = 1 # Custom added variable, to keep track on when to close tunnel
# self.client.load_system_host_keys()
self.client.set_missing_host_key_policy(CheckfingerPrints(self.fingerPrints))
logger.debug('Connecting to ssh host %s:%d ...', self.server, self.port)
self.client.connect(self.server, self.port, username=self.username, password=self.password, timeout=5)
except Exception:
logger.exception('Exception connecting: ')
self.status = 2 # Error
return
class SubHandler(Handler):
chain_host = self.redirectHost
chain_port = self.redirectPort
ssh_transport = self.client.get_transport()
event = self.stopEvent
thread = self
logger.debug('Wait Time: %s', self.waitTime)
self.timer = threading.Timer(self.waitTime, self._timerFnc)
self.timer.start()
self.status = 1 # Ok, listening
self.fs = ForwardServer(('', self.localPort), SubHandler)
self.fs.serve_forever()
def stop(self):
try:
if self.timer:
self.timer.cancel()
self.stopEvent.set()
self.fs.shutdown()
if self.client is not None:
self.client.useCount -= 1
if self.client.useCount == 0:
self.client.close()
self.client = None # Clean up
except Exception:
logger.exception('Exception stopping')
def forward(server, port, username, password, redirectHost, redirectPort, localPort=None, waitTime=10, fingerPrints=None):
'''
Instantiates an ssh connection to server:port
Returns the Thread created and the local redirected port as a list: (thread, port)
'''
port, redirectPort = int(port), int(redirectPort)
if localPort is None:
localPort = random.randrange(33000, 52000)
logger.debug('Connecting to %s:%s using %s/%s redirecting to %s:%s, listening on 127.0.0.1:%s',
server, port, username, password, redirectHost, redirectPort, localPort)
ft = ForwardThread(server, port, username, password, localPort, redirectHost, redirectPort, waitTime, fingerPrints)
ft.start()
while ft.status == 0:
time.sleep(0.1)
return (ft, localPort)

View File

@@ -1,123 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
# pylint: disable=c-extension-no-member,no-name-in-module
from __future__ import unicode_literals
import json
import urllib
import six
from PyQt4.QtCore import pyqtSignal, pyqtSlot
from PyQt4.QtCore import QObject, QUrl, QSettings
from PyQt4.QtCore import Qt
from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply, QSslCertificate
from PyQt4.QtGui import QMessageBox
from . import osDetector
from . import VERSION
class RestRequest(QObject):
restApiUrl = '' #
done = pyqtSignal(dict, name='done')
def __init__(self, url, parentWindow, done, params=None): # parent not used
super(RestRequest, self).__init__()
# private
self._manager = QNetworkAccessManager()
if params is not None:
url += '?' + '&'.join('{}={}'.format(k, urllib.quote(six.text_type(v).encode('utf8'))) for k, v in params.iteritems())
self.url = QUrl(RestRequest.restApiUrl + url)
# connect asynchronous result, when a request finishes
self._manager.finished.connect(self._finished)
self._manager.sslErrors.connect(self._sslError)
self._parentWindow = parentWindow
self.done.connect(done, Qt.QueuedConnection)
# private slot, no need to declare as slot
@pyqtSlot(QNetworkReply)
def _finished(self, reply):
'''
Handle signal 'finished'. A network request has finished.
'''
try:
if reply.error() != QNetworkReply.NoError:
raise Exception(reply.errorString())
data = six.text_type(reply.readAll())
data = json.loads(data)
except Exception as e:
data = {
'result': None,
'error': six.text_type(e)
}
self.done.emit(data)
reply.deleteLater() # schedule for delete from main event loop
@pyqtSlot(QNetworkReply, list)
def _sslError(self, reply, errors):
# reply.ignoreSslErrors()
settings = QSettings()
settings.beginGroup('ssl')
cert = errors[0].certificate()
digest = six.text_type(cert.digest().toHex())
approved = settings.value(digest, False).toBool()
errorString = '<p>Please, accept the certificate for <b>{}</b></p>'.format(cert.subjectInfo(QSslCertificate.CommonName))
# for err in errors:
# errorString += '<li>' + err.errorString() + '</li>'
# errorString += '</ul>'
if approved or QMessageBox.warning(self._parentWindow, 'SSL Warning', errorString, QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
settings.setValue(digest, True)
reply.ignoreSslErrors()
settings.endGroup()
def get(self):
request = QNetworkRequest(self.url)
request.setRawHeader('User-Agent', osDetector.getOs() + " - UDS Connector " + VERSION)
self._manager.get(request)

View File

@@ -1,205 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2015 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
from base64 import b64decode
import tempfile
import string
import random
import os
import socket
import stat
import sys
import time
import six
from .log import logger
_unlinkFiles = []
_tasksToWait = []
_execBeforeExit = []
sys_fs_enc = sys.getfilesystemencoding() or 'mbcs'
# Public key for scripts
PUBLIC_KEY = '''-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuNURlGjBpqbglkTTg2lh
dU5qPbg9Q+RofoDDucGfrbY0pjB9ULgWXUetUWDZhFG241tNeKw+aYFTEorK5P+g
ud7h9KfyJ6huhzln9eyDu3k+kjKUIB1PLtA3lZLZnBx7nmrHRody1u5lRaLVplsb
FmcnptwYD+3jtJ2eK9ih935DYAkYS4vJFi2FO+npUQdYBZHPG/KwXLjP4oGOuZp0
pCTLiCXWGjqh2GWsTECby2upGS/ZNZ1r4Ymp4V2A6DZnN0C0xenHIY34FWYahbXF
ZGdr4DFBPdYde5Rb5aVKJQc/pWK0CV7LK6Krx0/PFc7OGg7ItdEuC7GSfPNV/ANt
5BEQNF5w2nUUsyN8ziOrNih+z6fWQujAAUZfpCCeV9ekbwXGhbRtdNkbAryE5vH6
eCE0iZ+cFsk72VScwLRiOhGNelMQ7mIMotNck3a0P15eaGJVE2JV0M/ag/Cnk0Lp
wI1uJQRAVqz9ZAwvF2SxM45vnrBn6TqqxbKnHCeiwstLDYG4fIhBwFxP3iMH9EqV
2+QXqdJW/wLenFjmXfxrjTRr+z9aYMIdtIkSpADIlbaJyTtuQpEdWnrlDS2b1IGd
Okbm65EebVzOxfje+8dRq9Uqwip8f/qmzFsIIsx3wPSvkKawFwb0G5h2HX5oJrk0
nVgtClKcDDlSaBsO875WDR0CAwEAAQ==
-----END PUBLIC KEY-----'''
def saveTempFile(content, filename=None):
if filename is None:
filename = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(16))
filename = filename + '.uds'
if 'win32' in sys.platform:
logger.info('Fixing for win32')
filename = filename.encode(sys_fs_enc)
filename = os.path.join(tempfile.gettempdir(), filename)
with open(filename, 'w') as f:
f.write(content)
logger.info('Returning filename')
return filename
def readTempFile(filename):
if 'win32' in sys.platform:
filename = filename.encode('utf-8')
filename = os.path.join(tempfile.gettempdir(), filename)
try:
with open(filename, 'r') as f:
return f.read()
except Exception:
return None
def testServer(host, port, timeOut=4):
try:
sock = socket.create_connection((host, int(port)), timeOut)
sock.close()
except Exception:
return False
return True
def findApp(appName, extraPath=None):
if 'win32' in sys.platform and isinstance(appName, six.text_type):
appName = appName.encode(sys_fs_enc)
searchPath = os.environ['PATH'].split(os.pathsep)
if extraPath is not None:
searchPath += list(extraPath)
for path in searchPath:
fileName = os.path.join(path, appName)
if os.path.isfile(fileName) and (os.stat(fileName).st_mode & stat.S_IXUSR) != 0:
return fileName
return None
def getHostName():
'''
Returns current host name
In fact, it's a wrapper for socket.gethostname()
'''
hostname = socket.gethostname()
if 'win32' in sys.platform:
hostname = hostname.decode(sys_fs_enc)
hostname = six.text_type(hostname)
logger.info('Hostname: %s', hostname)
return hostname
# Queing operations (to be executed before exit)
def addFileToUnlink(filename):
'''
Adds a file to the wait-and-unlink list
'''
_unlinkFiles.append(filename)
def unlinkFiles():
'''
Removes all wait-and-unlink files
'''
if _unlinkFiles:
time.sleep(5) # Wait 5 seconds before deleting anything
for f in _unlinkFiles:
try:
os.unlink(f)
except Exception:
pass
def addTaskToWait(taks):
_tasksToWait.append(taks)
def waitForTasks():
for t in _tasksToWait:
try:
if hasattr(t, 'join'):
t.join()
elif hasattr(t, 'wait'):
t.wait()
except Exception:
pass
def addExecBeforeExit(fnc):
_execBeforeExit.append(fnc)
def execBeforeExit():
for fnc in _execBeforeExit:
fnc.__call__()
def verifySignature(script, signature):
'''
Verifies with a public key from whom the data came that it was indeed
signed by their private key
param: public_key_loc Path to public key
param: signature String signature to be verified
return: Boolean. True if the signature is valid; False otherwise.
'''
# For signature checking
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
rsakey = RSA.importKey(PUBLIC_KEY)
signer = PKCS1_v1_5.new(rsakey)
digest = SHA256.new(script) # Script is "binary string" here
if signer.verify(digest, b64decode(signature)):
return True
return False

View File

@@ -1,15 +0,0 @@
#!/bin/bash
function process {
for a in *.ui; do
pyuic4 $a -o `basename $a .ui`.py -x
done
}
pyrcc4 -py3 UDSResources.qrc -o UDSResources_rc.py
# process current directory ui's
process

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>UDSClient-Thin</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}/src</path>
</pydev_pathproperty>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">system-2.7</pydev_property>
</pydev_project>

View File

@@ -1,174 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
import sys
import json
import six
from uds import ui
from uds.rest import RestRequest, RetryException
from uds.forward import forward # pylint: disable=unused-import
from uds import VERSION
from uds.log import logger # @UnresolvedImport
from uds import tools
# Server before this version uses "unsigned" scripts
OLD_METHOD_VERSION = '2.4.0'
def approveHost(hostName):
from os.path import expanduser
hostsFile = expanduser('~/.udsclient.hosts')
try:
with open(hostsFile, 'r') as f:
approvedHosts = f.read().splitlines()
except Exception:
approvedHosts = []
hostName = hostName.lower()
if hostName in approvedHosts:
return True
errorString = 'The server {} must be approved:\n'.format(hostName)
errorString += 'Only approve UDS servers that you trust to avoid security issues.'
approved = ui.question("ACCESS Warning", errorString)
if approved:
approvedHosts.append(hostName)
logger.debug('Host was approved, saving to approvedHosts file')
try:
with open(hostsFile, 'w') as f:
f.write('\n'.join(approvedHosts))
except Exception:
logger.warning('Got exception writing to %s', hostsFile)
return approved
def getWithRetry(api, url, parameters=None):
while True:
try:
return api.get(url, parameters)
except RetryException as e:
if ui.question('Service not available', 'Error {}.\nPlease, wait a minute and press "OK" to retry, or "CANCEL" to abort'.format(e)) is True:
continue
raise Exception('Cancelled by user')
if __name__ == "__main__":
logger.debug('Initializing connector')
if six.PY3 is False:
logger.debug('Fixing threaded execution of commands')
import threading
threading._DummyThread._Thread__stop = lambda x: 42 # type: ignore, pylint:disable=protected-access
# First parameter must be url
try:
uri = sys.argv[1]
if uri == '--test':
sys.exit(0)
logger.debug('URI: %s', uri)
if uri[:6] != 'uds://' and uri[:7] != 'udss://':
raise Exception()
ssl = uri[3] == 's'
host, ticket, scrambler = uri.split('//')[1].split('/')
logger.debug('ssl: %s, host:%s, ticket:%s, scrambler:%s', ssl, host, ticket, scrambler)
except Exception:
logger.debug('Detected execution without valid URI, exiting')
ui.message('UDS Client', 'UDS Client Version {}'.format(VERSION))
sys.exit(1)
rest = RestRequest(host, ssl)
logger.debug('Setting request URL to %s', rest.restApiUrl)
# Main requests part
# First, get version
try:
res = getWithRetry(rest, '')
logger.debug('Got information %s', res)
requiredVersion = res['requiredVersion']
if requiredVersion > VERSION:
ui.message("New UDS Client available", "A new uds version is needed in order to access this version of UDS.\nPlease, download and install it")
sys.exit(1)
res = getWithRetry(rest, '/{}/{}'.format(ticket, scrambler), parameters={'hostname': tools.getHostName(), 'version': VERSION})
params = None
if requiredVersion <= OLD_METHOD_VERSION:
script = res.decode('base64').decode('bz2')
else:
# We have three elements on result:
# * Script
# * Signature
# * Script data
# We test that the Script has correct signature, and them execute it with the parameters
script, signature, params = res['script'].decode('base64').decode('bz2'), res['signature'], json.loads(res['params'].decode('base64').decode('bz2'))
if tools.verifySignature(script, signature) is False:
logger.error('Signature is invalid')
raise Exception('Invalid UDS code signature. Please, report to administrator')
logger.debug('Script: %s', script)
six.exec_(script, globals(), {'parent': None, 'sp': params})
except Exception as e:
error = 'ERROR: {}'.format(e)
logger.error(error)
ui.message('Error', error)
sys.exit(2)
# Finalize
try:
tools.waitForTasks()
except Exception:
pass
try:
tools.unlinkFiles()
except Exception:
pass
try:
tools.execBeforeExit()
except Exception:
pass

View File

@@ -1 +0,0 @@
../../../full/src/uds/__init__.py

View File

@@ -1 +0,0 @@
../../../full/src/uds/forward.py

View File

@@ -1 +0,0 @@
../../../full/src/uds/log.py

View File

@@ -1 +0,0 @@
../../../full/src/uds/osDetector.py

View File

@@ -1,84 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2015 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
import requests
from . import VERSION
import json
import six
import osDetector
from .log import logger
class RetryException(Exception):
pass
class RestRequest(object):
restApiUrl = ''
def __init__(self, host, ssl=True): # parent not used
super(RestRequest, self).__init__()
self.host = host
self.ssl = ssl
self.restApiUrl = '{}://{}/rest/client'.format(['http', 'https'][ssl], host)
def get(self, url, params=None):
url = self.restApiUrl + url
if params is not None:
url += '?' + '&'.join('{}={}'.format(k, six.moves.urllib.parse.quote(six.text_type(v).encode('utf8'))) for k, v in params.iteritems()) # @UndefinedVariable
logger.debug('Requesting %s', url)
try:
r = requests.get(url, headers={'Content-type': 'application/json', 'User-Agent': osDetector.getOs() + " - UDS Connector " + VERSION}, verify=False)
except requests.exceptions.ConnectionError:
raise Exception('Error connecting to UDS Server at {}'.format(self.restApiUrl[0:-11]))
if r.ok:
logger.debug('Request was OK. %s', r.text)
data = json.loads(r.text)
if not 'error' in data:
return data['result']
# Has error
if data.get('retryable', '0') == '1':
raise RetryException(data['error'])
raise Exception(data['error'])
logger.error('Error requesting %s: %s, %s', url, r.code, r.text)
raise Exception('Error {}: {}'.format(r.code, r.text))

View File

@@ -1 +0,0 @@
../../../full/src/uds/tools.py

View File

@@ -1,58 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
import random
import os
import tempfile
import string
import webbrowser
TEMPLATE = '''<html>
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<p>{message}<P>
</body>
</html>
'''
def _htmlFilename():
return os.path.join(tempfile.gettempdir(), ''.join([random.choice(string.ascii_lowercase) for i in range(22)]) + '.html')
def message(title, message):
filename = _htmlFilename()
with open(filename, 'w') as f:
f.write(TEMPLATE.format(title=title, message=message))
webbrowser.open('file://' + filename, new=0, autoraise=False)

View File

@@ -1,49 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
import sys
import time
from uds.log import logger
counter = 0
def message(title, message):
sys.stderr.write("** {} **\n {}\n".format(title, message))
def question(title, message):
global counter
if counter > 100: # 5 minutes
return False
counter += 1
sys.stderr.write("** {} **\n{}\nReturning YES in 3 seconds. (counter is {})\n".format(title, message, counter))
time.sleep(3) # Wait 3 seconds before returning
return True

View File

@@ -1,143 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from __future__ import unicode_literals
import re
import pygtk
pygtk.require('2.0')
import gtk
import gobject
LINE_LEN = 65
class Dialog():
def __init__(self, title, message, timeout=-1, withCancel=True):
self.title = title
self.message = message
self.timeout = timeout
self.withCancel = withCancel
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_position(gtk.WIN_POS_CENTER)
# self.window.set_size_request(320, 200)
self.window.set_title(self.title)
self.create_widgets()
self.connect_signals()
self.window.show_all()
self.window.connect("destroy", self.destroy)
# Setup "auto OK" timer
if timeout != -1:
self.timerId = gobject.timeout_add(self.timeout * 1000, self.callback_timer)
else:
self.timerId = -1
self.result = False
gtk.main()
@property
def adapted_message(self):
msg = ''
for l in re.sub(r'<p[^>]*>', '', self.message).replace('</p>', '\n').split('\n'):
words = []
length = 0
for word in l.split(' '):
if length + len(word) >= LINE_LEN:
msg += ' '.join(words) + '\n'
words = []
length = 0
length += len(word) + 1
words.append(word)
msg += ' '.join(words) + '\n'
return msg
def create_widgets(self):
self.vbox = gtk.VBox(spacing=10)
self.vbox.set_size_request(490, -1)
self.messageLabel = gtk.Label()
# Fix message markup
# self.message = re.sub(r'<p[^>]*>', '<span font_weight="bold">', self.message).replace('</p>', '</span>\n' )
# Set as simple markup
self.messageLabel.set_markup('\n' + self.adapted_message + '\n')
self.messageLabel.set_alignment(xalign=0.5, yalign=1)
self.hbox = gtk.HBox(spacing=10)
self.button_ok = gtk.Button("OK")
self.hbox.pack_start(self.button_ok)
if self.withCancel:
self.button_cancel = gtk.Button("Cancel")
self.hbox.pack_start(self.button_cancel)
self.vbox.pack_start(self.messageLabel)
self.vbox.pack_start(self.hbox)
self.window.add(self.vbox)
def connect_signals(self):
self.button_ok.connect("clicked", self.callback_ok)
if self.withCancel:
self.button_cancel.connect("clicked", self.callback_cancel)
def destroy(self, widget, data=None):
self.setResult(False)
def setResult(self, val):
if self.timerId != -1:
gobject.source_remove(self.timerId)
self.timerId = -1
self.result = val
self.window.hide()
gtk.main_quit()
def callback_ok(self, widget, callback_data=None):
self.setResult(True)
def callback_cancel(self, widget, callback_data=None):
self.setResult(False)
def callback_timer(self):
self.setResult(True)
def message(title, message):
Dialog(title, message, withCancel=False)
def question(title, message):
dlg = Dialog(title, message, timeout=30, withCancel=True)
return dlg.result

View File

@@ -1,4 +0,0 @@
#!/bin/sh
cd /lib/UDSClient
exec python UDSClient.pyc $@

View File

@@ -1,11 +0,0 @@
Steps:
1.- If building from repository, full copy (recursive) the "src" folder of "client/thin" (from openuds project) inside the "udsclient" folder here (so we will have an client/thin/udsclient/src folder, with the source code of thin client). If building from the .tar.gz, simply ignore this step
2.- Copy the folder "udsclient" to /build/packages inside the thinstation build environment
3.- enter the chroot of thinstation
4.- go to the udsclient folder (/build/packages/udsclient)
5.- Execute "build.sh"
6.- Edit the file /build/build.conf, and add this line:
package udsclient
7.- Execute the build process
Ready!!!

Some files were not shown because too many files have changed in this diff Show More