Skip to content

Commit 711c4e1

Browse files
author
sadnub
committed
sdd user options and serverinfo and userinfo
1 parent 014a6b6 commit 711c4e1

File tree

2 files changed

+383
-1
lines changed

2 files changed

+383
-1
lines changed

src/meshctrl/meshctrl.py

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ async def _websocket_call(self, data: dict) -> dict:
9696

9797
async for message in websocket:
9898
response = json.loads(message)
99+
99100
if "responseid" in response:
100101
if data["responseid"] == response["responseid"]:
101102
return response
@@ -108,6 +109,78 @@ def _send(self, data: dict) -> dict:
108109

109110
return asyncio.run(self._websocket_call(data))
110111

112+
def server_info(self) -> dict:
113+
"""Gets MeshCentral server info.
114+
115+
Returns:
116+
dict:
117+
Returns server info.
118+
119+
Example:
120+
{
121+
'domain': '',
122+
'name': 'mesh.example.com',
123+
'mpsname': 'mesh.example.com',
124+
'mpsport': 4433,
125+
'port': 4443,
126+
'emailcheck': True,
127+
'domainauth': False,
128+
'serverTime': 1645560067270,
129+
'features': 9607777,
130+
'features2': 16513,
131+
'languages': ['en', 'cs', 'da', 'de', 'es', 'fi', 'fr', 'hi', 'it', 'ja', 'ko', 'nl', 'nn', 'pl', 'pt-br', 'pt', 'ru', 'sv', 'tr', 'zh-chs', 'zh-cht'],
132+
'tlshash': '16D462CC0D306CFC7F242382A1606E2A57E6481B4EAAC7E5C6D91EFA306F9CABD0CD91566A8A35C3DA9580E1F51CF985',
133+
'agentCertHash': 'V7IZUeuuIWMCY8e1SIb8fKqM1RkS4fUmCbCZzi4cMMzHAi3EJPi9Y8CP5XQfz2tZ',
134+
'https': True,
135+
'redirport': 8080,
136+
'magenturl': 'mc://mesh.example.com:4443',
137+
'domainsuffix': '',
138+
'certExpire': 1652972190000
139+
}
140+
"""
141+
142+
data = {
143+
"action": "serverinfo"
144+
}
145+
146+
return self._send(data)["serverinfo"]
147+
148+
def user_info(self) -> dict:
149+
"""Gets logged on user info.
150+
151+
Returns:
152+
dict:
153+
Returns current user info
154+
155+
Example:
156+
{
157+
'_id': 'user//username',
158+
'name': 'username',
159+
'creation': 1643754241,
160+
'links': {
161+
'mesh//oAUeYE3HCqUFXWCkqwqfW@ElJ7orX6hrNv$r$RyCEsVgtUQNxYC6dLs4jlfQNTPA': {
162+
'rights': 4294967295
163+
},
164+
'mesh//$lhtFH8ZYcVEZYSqLx1O2vxqgSdzX9bjZLAbmRMz3lJ@XLulbyhqeRUPF4MbaN64': {
165+
'rights': 4294967295
166+
}
167+
},
168+
'email': 'example@example.com',
169+
'emailVerified': True,
170+
'siteadmin': 4294967295,
171+
'pastlogin': 1645505345,
172+
'access': 1645558617,
173+
'login': 1645505346
174+
}
175+
"""
176+
177+
data = {
178+
"action": "userinfo"
179+
}
180+
181+
return self._send(data)["userinfo"]
182+
183+
111184
def get_device_group_id_by_name(self, group: str) -> Optional[str]:
112185
"""Get the device group id by group name.
113186
@@ -347,6 +420,261 @@ def edit_device_group(
347420

348421
return self._send(data)
349422

423+
def get_user_id_by_name(self, username: str) -> Optional[str]:
424+
"""Get the user account id by username.
425+
426+
Args:
427+
username (str):
428+
Used to search through users.
429+
430+
Returns:
431+
str, None:
432+
Returns the user account _id if the username exists otherwise returns None.
433+
"""
434+
435+
users = self.list_users()
436+
437+
for user in users:
438+
if user["username"] == username:
439+
return user["_id"]
440+
441+
return None
442+
443+
def user_exists(
444+
self, username: Optional[str] = None, id: Optional[str] = None
445+
) -> bool:
446+
"""Check if a user account exists by username or id.
447+
448+
This method needs either user or id arguments set. If both are set then name
449+
takes precedence.
450+
451+
Args:
452+
username (str):
453+
Used to check if a device group with the same name exists.
454+
id (str):
455+
Used to check if a device group with the same id exists.
456+
457+
Returns:
458+
bool: True or False depending on if the user account exists.
459+
"""
460+
461+
if not username and not id:
462+
raise ValueError("Arguments username or id must be specified")
463+
464+
users = self.list_users()
465+
466+
for user in users:
467+
if user:
468+
if user["name"] == username:
469+
return True
470+
elif id:
471+
if user["_id"] == id:
472+
return True
473+
474+
return False
475+
476+
def list_users(self) -> list:
477+
"""List users
478+
479+
Returns:
480+
list: Mesh user accounts.
481+
"""
482+
483+
data = {
484+
"action": "users"
485+
}
486+
487+
return self._send(data)["users"]
488+
489+
def add_user(
490+
self,
491+
username: str,
492+
password: Optional[str] = None,
493+
random_pass: bool = False,
494+
domain: Optional[str] = None,
495+
email: Optional[str] = None,
496+
email_verfied: bool = False,
497+
reset_pass: bool = False,
498+
full_name: Optional[str] = None,
499+
phone: Optional[str] = None,
500+
rights: Optional[str] = None,
501+
) -> dict:
502+
"""Add User
503+
504+
This method needs a username set and password is optional only is random_pass is true. random_pass
505+
will take precedence.
506+
507+
Args:
508+
username (str):
509+
Username for the user that is used to login
510+
password (str):
511+
Password to set for the user. Not needed if random_pass is set to True
512+
random_pass (str, optional):
513+
Sets a random password for the user account.
514+
domain (str, optional):
515+
Account domain, only for cross-domain admins.
516+
email (str, optional):
517+
New account email address.
518+
email_verified (bool, optional):
519+
New account email is verified.
520+
reset_pass (bool, optional):
521+
Request password reset on next login.
522+
full_name (str, optional):
523+
Set the full name for this account.
524+
phone (str, optional):
525+
Set the account phone number.
526+
rights (str, optional):
527+
Server permissions for account. Can be none, full, or a comma separated
528+
list of these possible values:
529+
manageusers,backup,restore,update,fileaccess,locked,nonewgroups,notools,usergroups,recordings,locksettings,allevents
530+
531+
Returns:
532+
dict: Returns a confirmation that the user was added
533+
534+
Example:
535+
{
536+
'action': 'adduser',
537+
'responseid': '31424b26-9539-400d-ab41-e406aeb337b2',
538+
'result': 'ok'
539+
}
540+
"""
541+
542+
if not password and not random_pass:
543+
raise ValueError("Either password or random_pass must be set")
544+
545+
data = {
546+
"action": "adduser",
547+
"username": username,
548+
"pass": utils.gen_password() if random_pass else password,
549+
"responseid": utils.gen_response_id(),
550+
}
551+
552+
if email:
553+
data["email"] = email
554+
if email_verfied:
555+
data["emailVerified"] = True
556+
557+
if reset_pass:
558+
data["resetNextLogin"] = True
559+
560+
if domain:
561+
data["domain"] = domain
562+
563+
if phone:
564+
data["phone"] = phone
565+
566+
if full_name:
567+
data["realname"] = full_name
568+
569+
if rights:
570+
data["siteadmin"] = utils.permissions_str_to_int(rights)
571+
572+
return self._send(data)
573+
574+
def edit_user(
575+
self,
576+
username: str,
577+
domain: str = "",
578+
email: Optional[str] = None,
579+
email_verfied: bool = False,
580+
reset_pass: bool = False,
581+
full_name: Optional[str] = None,
582+
phone: Optional[str] = None,
583+
rights: Optional[str] = None,
584+
) -> dict:
585+
"""Edit User
586+
587+
This method needs a username set to identify the user to edit.
588+
589+
Args:
590+
username (str):
591+
Username for the user that is used to login
592+
domain (str, optional):
593+
Account domain, only for cross-domain admins. (defaults to '')
594+
email (str, optional):
595+
New account email address.
596+
email_verified (bool, optional):
597+
New account email is verified.
598+
reset_pass (bool, optional):
599+
Request password reset on next login.
600+
full_name (str, optional):
601+
Set the full name for this account.
602+
phone (str, optional):
603+
Set the account phone number.
604+
rights (str, optional):
605+
Server permissions for account. Can be none, full, or a comma separated
606+
list of these possible values:
607+
manageusers,backup,restore,update,fileaccess,locked,nonewgroups,notools,usergroups,recordings,locksettings,allevents
608+
609+
Returns:
610+
dict: Returns a confirmation that the user was edited
611+
612+
Example:
613+
{
614+
'action': 'edituser',
615+
'responseid': '1d508225-818d-444c-9a33-62c4ef76f652',
616+
'result': 'ok'
617+
}
618+
"""
619+
620+
data = {
621+
"action": "edituser",
622+
"userid": utils.format_user_id(username, domain),
623+
"responseid": utils.gen_response_id(),
624+
}
625+
626+
if email:
627+
data["email"] = email
628+
if email_verfied:
629+
data["emailVerified"] = True
630+
631+
if reset_pass:
632+
data["resetNextLogin"] = True
633+
634+
if domain:
635+
data["domain"] = domain
636+
637+
if phone:
638+
data["phone"] = phone
639+
640+
if full_name:
641+
data["realname"] = full_name
642+
643+
if rights:
644+
data["siteadmin"] = utils.permissions_str_to_int(rights)
645+
646+
return self._send(data)
647+
648+
def remove_user(self, username: str, domain: str = "") -> dict:
649+
"""Delete User
650+
651+
This method needs a username set to identify the user to delete.
652+
653+
Args:
654+
username (str):
655+
Username for the user that is used to login
656+
domain (str, optional)
657+
Account domain, only for cross-domain admins. (defaults to '')
658+
659+
Returns:
660+
dict: Returns a confirmation that the user was deleted.
661+
662+
Example:
663+
{
664+
'action': 'deleteuser',
665+
'responseid': '1d508225-818d-444c-9a33-62c4ef76f652',
666+
'result': 'ok'
667+
}
668+
"""
669+
670+
data = {
671+
"action": "deleteuser",
672+
"userid": utils.format_user_id(username, domain),
673+
"responseid": utils.gen_response_id(),
674+
}
675+
676+
return self._send(data)
677+
350678
# run command on an agent
351679
def run_command(self, node_id: str, command: str, runAsUser: int = 0) -> dict:
352680

0 commit comments

Comments
 (0)