calagoapi 1.0.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- calagoapi/__init__.py +190 -0
- calagoapi/account/__init__.py +14 -0
- calagoapi/account/account.py +292 -0
- calagoapi/account/auth.py +72 -0
- calagoapi/account/models.py +221 -0
- calagoapi/admin/__init__.py +48 -0
- calagoapi/admin/admin.py +341 -0
- calagoapi/admin/managers.py +490 -0
- calagoapi/admin/resources.py +970 -0
- calagoapi/aio/__init__.py +9 -0
- calagoapi/aio/console.py +289 -0
- calagoapi/aio/panel.py +176 -0
- calagoapi/api/__init__.py +63 -0
- calagoapi/api/account.py +195 -0
- calagoapi/api/admin.py +402 -0
- calagoapi/api/attachments.py +27 -0
- calagoapi/api/auth.py +52 -0
- calagoapi/api/backups.py +99 -0
- calagoapi/api/client.py +198 -0
- calagoapi/api/databases.py +217 -0
- calagoapi/api/files.py +210 -0
- calagoapi/api/network.py +84 -0
- calagoapi/api/public.py +13 -0
- calagoapi/api/schedules.py +79 -0
- calagoapi/api/servers.py +114 -0
- calagoapi/api/users.py +27 -0
- calagoapi/api/versions.py +29 -0
- calagoapi/backups/__init__.py +8 -0
- calagoapi/backups/backup.py +292 -0
- calagoapi/backups/manager.py +177 -0
- calagoapi/console/__init__.py +6 -0
- calagoapi/console/console.py +392 -0
- calagoapi/databases/__init__.py +12 -0
- calagoapi/databases/database.py +445 -0
- calagoapi/databases/explorer.py +159 -0
- calagoapi/databases/manager.py +122 -0
- calagoapi/files/__init__.py +9 -0
- calagoapi/files/file.py +196 -0
- calagoapi/files/manager.py +437 -0
- calagoapi/network/__init__.py +8 -0
- calagoapi/network/allocation.py +99 -0
- calagoapi/network/manager.py +132 -0
- calagoapi/nodes/__init__.py +6 -0
- calagoapi/nodes/node.py +477 -0
- calagoapi/panel.py +162 -0
- calagoapi/schedules/__init__.py +14 -0
- calagoapi/schedules/builders.py +461 -0
- calagoapi/schedules/manager.py +107 -0
- calagoapi/schedules/schedule.py +214 -0
- calagoapi/servers/__init__.py +13 -0
- calagoapi/servers/admin.py +430 -0
- calagoapi/servers/generic.py +785 -0
- calagoapi/servers/manager.py +167 -0
- calagoapi/servers/minecraft.py +207 -0
- calagoapi/tunnels/__init__.py +5 -0
- calagoapi/tunnels/tunnel.py +112 -0
- calagoapi/users/__init__.py +11 -0
- calagoapi/users/activity.py +45 -0
- calagoapi/users/subuser.py +127 -0
- calagoapi/users/user.py +267 -0
- calagoapi/utils/__init__.py +43 -0
- calagoapi/utils/convert.py +113 -0
- calagoapi/utils/exceptions.py +72 -0
- calagoapi/utils/model.py +142 -0
- calagoapi/utils/resource.py +100 -0
- calagoapi/utils/time.py +68 -0
- calagoapi-1.0.dev1.dist-info/METADATA +266 -0
- calagoapi-1.0.dev1.dist-info/RECORD +71 -0
- calagoapi-1.0.dev1.dist-info/WHEEL +5 -0
- calagoapi-1.0.dev1.dist-info/licenses/LICENSE +674 -0
- calagoapi-1.0.dev1.dist-info/top_level.txt +1 -0
calagoapi/__init__.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
from importlib.metadata import version as _pkg_version, PackageNotFoundError
|
|
2
|
+
from types import SimpleNamespace
|
|
3
|
+
|
|
4
|
+
# package version
|
|
5
|
+
try:
|
|
6
|
+
__version__ = _pkg_version('calagoapi')
|
|
7
|
+
except PackageNotFoundError:
|
|
8
|
+
__version__ = '0.0.0'
|
|
9
|
+
|
|
10
|
+
# modules
|
|
11
|
+
from .panel import Panel
|
|
12
|
+
from .aio import AsyncPanel, AsyncConsole
|
|
13
|
+
from .console import Console, ConsoleEvent
|
|
14
|
+
from .servers import GenericServer, MinecraftServer, AdminServer, ServerGroup
|
|
15
|
+
from .files import File
|
|
16
|
+
from .backups import Backup, BackupGroup
|
|
17
|
+
from .databases import Database, DatabaseInstance, InstanceDatabase, InstanceUser, DatabaseExplorer
|
|
18
|
+
from .schedules import Schedule, ScheduleStep, triggers, conditions, actions, variable
|
|
19
|
+
from .schedules import backups as backup_selectors
|
|
20
|
+
from .users import Subuser, User, Activity
|
|
21
|
+
from .network import Allocation, Subdomain
|
|
22
|
+
from .nodes import Node
|
|
23
|
+
from .account import ApiKey, SshKey, SecurityKey, Session, OAuthLink, CommandSnippet
|
|
24
|
+
from .admin import (
|
|
25
|
+
Location,
|
|
26
|
+
Nest,
|
|
27
|
+
Egg,
|
|
28
|
+
EggVariable,
|
|
29
|
+
Mount,
|
|
30
|
+
Device,
|
|
31
|
+
Role,
|
|
32
|
+
Announcement,
|
|
33
|
+
OAuthProvider,
|
|
34
|
+
DatabaseHost,
|
|
35
|
+
DatabaseAgentHost,
|
|
36
|
+
DatabaseAgentTemplate,
|
|
37
|
+
BackupConfiguration,
|
|
38
|
+
BackupPolicy,
|
|
39
|
+
EggRepository,
|
|
40
|
+
EggConfiguration,
|
|
41
|
+
SubdomainDomain,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
from .utils import (
|
|
45
|
+
Page,
|
|
46
|
+
convert,
|
|
47
|
+
formatBytes,
|
|
48
|
+
formatDuration,
|
|
49
|
+
waitForState,
|
|
50
|
+
waitForOnline,
|
|
51
|
+
waitForOffline,
|
|
52
|
+
secsUntil,
|
|
53
|
+
minsUntil,
|
|
54
|
+
CalagoAPIError,
|
|
55
|
+
BadRequestError,
|
|
56
|
+
AuthenticationError,
|
|
57
|
+
PermissionDeniedError,
|
|
58
|
+
NotFoundError,
|
|
59
|
+
ConflictError,
|
|
60
|
+
RateLimitError,
|
|
61
|
+
ServerError,
|
|
62
|
+
PanelConnectionError,
|
|
63
|
+
ConsoleError,
|
|
64
|
+
InvalidActionError,
|
|
65
|
+
WaitTimeoutError,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# namespaces
|
|
69
|
+
utils = SimpleNamespace(
|
|
70
|
+
convert = convert,
|
|
71
|
+
formatBytes = formatBytes,
|
|
72
|
+
formatDuration = formatDuration,
|
|
73
|
+
waitForState = waitForState,
|
|
74
|
+
waitForOnline = waitForOnline,
|
|
75
|
+
waitForOffline = waitForOffline,
|
|
76
|
+
secsUntil = secsUntil,
|
|
77
|
+
minsUntil = minsUntil,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
servers = SimpleNamespace(
|
|
81
|
+
Panel = Panel,
|
|
82
|
+
Generic = GenericServer,
|
|
83
|
+
Minecraft = MinecraftServer,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
schedules = SimpleNamespace(
|
|
87
|
+
triggers = triggers,
|
|
88
|
+
conditions = conditions,
|
|
89
|
+
actions = actions,
|
|
90
|
+
backups = backup_selectors,
|
|
91
|
+
variable = variable,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
__all__ = [
|
|
96
|
+
'__version__',
|
|
97
|
+
|
|
98
|
+
# core
|
|
99
|
+
'Panel',
|
|
100
|
+
'AsyncPanel',
|
|
101
|
+
'Console',
|
|
102
|
+
'AsyncConsole',
|
|
103
|
+
'ConsoleEvent',
|
|
104
|
+
'GenericServer',
|
|
105
|
+
'MinecraftServer',
|
|
106
|
+
'AdminServer',
|
|
107
|
+
'ServerGroup',
|
|
108
|
+
'Page',
|
|
109
|
+
|
|
110
|
+
# server features
|
|
111
|
+
'File',
|
|
112
|
+
'Backup',
|
|
113
|
+
'BackupGroup',
|
|
114
|
+
'Database',
|
|
115
|
+
'DatabaseInstance',
|
|
116
|
+
'InstanceDatabase',
|
|
117
|
+
'InstanceUser',
|
|
118
|
+
'DatabaseExplorer',
|
|
119
|
+
'Schedule',
|
|
120
|
+
'ScheduleStep',
|
|
121
|
+
'Subuser',
|
|
122
|
+
'Allocation',
|
|
123
|
+
'Subdomain',
|
|
124
|
+
'Activity',
|
|
125
|
+
|
|
126
|
+
# schedule builders
|
|
127
|
+
'triggers',
|
|
128
|
+
'conditions',
|
|
129
|
+
'actions',
|
|
130
|
+
'backup_selectors',
|
|
131
|
+
'variable',
|
|
132
|
+
|
|
133
|
+
# account
|
|
134
|
+
'ApiKey',
|
|
135
|
+
'SshKey',
|
|
136
|
+
'SecurityKey',
|
|
137
|
+
'Session',
|
|
138
|
+
'OAuthLink',
|
|
139
|
+
'CommandSnippet',
|
|
140
|
+
|
|
141
|
+
# admin
|
|
142
|
+
'User',
|
|
143
|
+
'Node',
|
|
144
|
+
'Location',
|
|
145
|
+
'Nest',
|
|
146
|
+
'Egg',
|
|
147
|
+
'EggVariable',
|
|
148
|
+
'Mount',
|
|
149
|
+
'Device',
|
|
150
|
+
'Role',
|
|
151
|
+
'Announcement',
|
|
152
|
+
'OAuthProvider',
|
|
153
|
+
'DatabaseHost',
|
|
154
|
+
'DatabaseAgentHost',
|
|
155
|
+
'DatabaseAgentTemplate',
|
|
156
|
+
'BackupConfiguration',
|
|
157
|
+
'BackupPolicy',
|
|
158
|
+
'EggRepository',
|
|
159
|
+
'EggConfiguration',
|
|
160
|
+
'SubdomainDomain',
|
|
161
|
+
|
|
162
|
+
# utilities
|
|
163
|
+
'convert',
|
|
164
|
+
'formatBytes',
|
|
165
|
+
'formatDuration',
|
|
166
|
+
'waitForState',
|
|
167
|
+
'waitForOnline',
|
|
168
|
+
'waitForOffline',
|
|
169
|
+
'secsUntil',
|
|
170
|
+
'minsUntil',
|
|
171
|
+
|
|
172
|
+
# exceptions
|
|
173
|
+
'CalagoAPIError',
|
|
174
|
+
'BadRequestError',
|
|
175
|
+
'AuthenticationError',
|
|
176
|
+
'PermissionDeniedError',
|
|
177
|
+
'NotFoundError',
|
|
178
|
+
'ConflictError',
|
|
179
|
+
'RateLimitError',
|
|
180
|
+
'ServerError',
|
|
181
|
+
'PanelConnectionError',
|
|
182
|
+
'ConsoleError',
|
|
183
|
+
'InvalidActionError',
|
|
184
|
+
'WaitTimeoutError',
|
|
185
|
+
|
|
186
|
+
# namespaces
|
|
187
|
+
'utils',
|
|
188
|
+
'servers',
|
|
189
|
+
'schedules',
|
|
190
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .account import Account
|
|
2
|
+
from .auth import Auth
|
|
3
|
+
from .models import ApiKey, SshKey, SecurityKey, Session, OAuthLink, CommandSnippet
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
'Account',
|
|
7
|
+
'Auth',
|
|
8
|
+
'ApiKey',
|
|
9
|
+
'SshKey',
|
|
10
|
+
'SecurityKey',
|
|
11
|
+
'Session',
|
|
12
|
+
'OAuthLink',
|
|
13
|
+
'CommandSnippet',
|
|
14
|
+
]
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from calagoapi.account.models import ApiKey, SshKey, SecurityKey, Session, OAuthLink, CommandSnippet
|
|
4
|
+
from calagoapi.users.activity import Activity
|
|
5
|
+
from calagoapi.utils.model import idOf
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Account:
|
|
9
|
+
'''
|
|
10
|
+
The account that owns the API key, available as `panel.account`.
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
print(panel.account.getUsername(), panel.account.getEmail())
|
|
14
|
+
panel.account.addSshKey('laptop', open('~/.ssh/id_ed25519.pub').read())
|
|
15
|
+
for session in panel.account.getSessions():
|
|
16
|
+
print(session.getIP(), session.getUserAgent())
|
|
17
|
+
'''
|
|
18
|
+
def __init__(self, panel):
|
|
19
|
+
self.panel = panel
|
|
20
|
+
self.api = panel.api.account
|
|
21
|
+
self.data = None
|
|
22
|
+
|
|
23
|
+
def __repr__(self):
|
|
24
|
+
return f'<Account {self.getUsername()!r}>' if self.data else '<Account>'
|
|
25
|
+
|
|
26
|
+
# profile
|
|
27
|
+
def getUser(self) -> dict:
|
|
28
|
+
'''Returns the account details (cached, see refresh()).'''
|
|
29
|
+
if self.data is None:
|
|
30
|
+
self.refresh()
|
|
31
|
+
return self.data
|
|
32
|
+
|
|
33
|
+
def refresh(self) -> dict:
|
|
34
|
+
'''Re-fetches the account details.'''
|
|
35
|
+
self.data = self.api.get()
|
|
36
|
+
return self.data
|
|
37
|
+
|
|
38
|
+
def getUUID(self) -> str:
|
|
39
|
+
'''Returns the user UUID.'''
|
|
40
|
+
return self.getUser().get('uuid')
|
|
41
|
+
|
|
42
|
+
def getUsername(self) -> str:
|
|
43
|
+
'''Returns the username.'''
|
|
44
|
+
return self.getUser().get('username')
|
|
45
|
+
|
|
46
|
+
def getEmail(self) -> str:
|
|
47
|
+
'''Returns the email address.'''
|
|
48
|
+
return self.getUser().get('email')
|
|
49
|
+
|
|
50
|
+
def getName(self) -> str:
|
|
51
|
+
'''Returns "First Last", or the username if no name is set.'''
|
|
52
|
+
user = self.getUser()
|
|
53
|
+
return ' '.join(filter(None, [user.get('name_first'), user.get('name_last')])) or user.get('username')
|
|
54
|
+
|
|
55
|
+
def getLanguage(self) -> str:
|
|
56
|
+
'''Returns the language code.'''
|
|
57
|
+
return self.getUser().get('language')
|
|
58
|
+
|
|
59
|
+
def getRole(self) -> dict:
|
|
60
|
+
'''Returns the account role, or None.'''
|
|
61
|
+
return self.getUser().get('role')
|
|
62
|
+
|
|
63
|
+
def getTwoFactorMethods(self) -> list:
|
|
64
|
+
'''Returns enabled two-factor methods: totp, security_key, email.'''
|
|
65
|
+
return self.getUser().get('two_factor_methods', [])
|
|
66
|
+
|
|
67
|
+
def isAdmin(self) -> bool:
|
|
68
|
+
'''Returns True for root administrators.'''
|
|
69
|
+
return bool(self.getUser().get('admin'))
|
|
70
|
+
|
|
71
|
+
def hasTwoFactor(self) -> bool:
|
|
72
|
+
'''Returns True if any two-factor method is enabled.'''
|
|
73
|
+
return bool(self.getTwoFactorMethods())
|
|
74
|
+
|
|
75
|
+
def isEmailVerified(self) -> bool:
|
|
76
|
+
'''Returns True if the email address is verified.'''
|
|
77
|
+
return bool(self.getUser().get('email_verified'))
|
|
78
|
+
|
|
79
|
+
def update(self, username: str = None, name_first: str = None, name_last: str = None, language: str = None) -> None:
|
|
80
|
+
'''Changes profile fields. Only the given fields change.'''
|
|
81
|
+
self.api.update(username, name_first, name_last, language)
|
|
82
|
+
self.data = None
|
|
83
|
+
|
|
84
|
+
def setUsername(self, username: str) -> None:
|
|
85
|
+
'''Changes the username.'''
|
|
86
|
+
return self.update(username=username)
|
|
87
|
+
|
|
88
|
+
def setName(self, name_first: str, name_last: str = None) -> None:
|
|
89
|
+
'''Changes the first and last name.'''
|
|
90
|
+
return self.update(name_first=name_first, name_last=name_last)
|
|
91
|
+
|
|
92
|
+
def setLanguage(self, language: str) -> None:
|
|
93
|
+
'''Changes the language, e.g. "en" or "de".'''
|
|
94
|
+
return self.update(language=language)
|
|
95
|
+
|
|
96
|
+
def changeEmail(self, email: str, password: str) -> dict:
|
|
97
|
+
'''Starts an email change. The new address must be verified.'''
|
|
98
|
+
return self.api.changeEmail(email, password)
|
|
99
|
+
|
|
100
|
+
def resendVerificationEmail(self) -> dict:
|
|
101
|
+
'''Sends the verification email again.'''
|
|
102
|
+
return self.api.resendVerification()
|
|
103
|
+
|
|
104
|
+
def changePassword(self, password: str, new_password: str) -> None:
|
|
105
|
+
'''Changes the password.'''
|
|
106
|
+
return self.api.changePassword(password, new_password)
|
|
107
|
+
|
|
108
|
+
def setPasswordLogin(self, enabled: bool, password: str) -> None:
|
|
109
|
+
'''Allows or forbids logging in with a password (for OAuth/passkey-only accounts).'''
|
|
110
|
+
return self.api.setPasswordLogin(not enabled, password)
|
|
111
|
+
|
|
112
|
+
def setAvatar(self, image, content_type: str = 'image/png') -> str:
|
|
113
|
+
'''
|
|
114
|
+
Uploads a new avatar.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
image: Local file path or image bytes.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
The avatar URL.
|
|
121
|
+
'''
|
|
122
|
+
if isinstance(image, (str, os.PathLike)):
|
|
123
|
+
with open(image, 'rb') as f:
|
|
124
|
+
image = f.read()
|
|
125
|
+
return self.api.setAvatar(image, content_type)
|
|
126
|
+
|
|
127
|
+
def removeAvatar(self) -> None:
|
|
128
|
+
'''Removes the avatar.'''
|
|
129
|
+
return self.api.deleteAvatar()
|
|
130
|
+
|
|
131
|
+
def getSettings(self) -> dict:
|
|
132
|
+
'''Returns personal UI settings.'''
|
|
133
|
+
return self.api.settings()
|
|
134
|
+
|
|
135
|
+
def updateSettings(self, settings: dict) -> None:
|
|
136
|
+
'''Saves personal UI settings.'''
|
|
137
|
+
return self.api.updateSettings(settings)
|
|
138
|
+
|
|
139
|
+
def getPermissions(self) -> dict:
|
|
140
|
+
'''
|
|
141
|
+
Returns every permission that exists, grouped into user, admin and server permissions.
|
|
142
|
+
|
|
143
|
+
Useful when creating API keys, roles or subusers.
|
|
144
|
+
'''
|
|
145
|
+
return self.api.permissions()
|
|
146
|
+
|
|
147
|
+
def getActivity(self, search: str = None, page: int = 1, per_page: int = 50):
|
|
148
|
+
'''Returns the account activity log.'''
|
|
149
|
+
return self.api.activity(search, page, per_page).map(lambda data: Activity(self.panel, data))
|
|
150
|
+
|
|
151
|
+
def logout(self) -> None:
|
|
152
|
+
'''Ends the current session.'''
|
|
153
|
+
return self.api.logout()
|
|
154
|
+
|
|
155
|
+
# two factor
|
|
156
|
+
def getTwoFactorSetup(self) -> dict:
|
|
157
|
+
'''Returns the TOTP secret and otpauth:// URL for setting up an authenticator app.'''
|
|
158
|
+
return self.api.twoFactorSetup()
|
|
159
|
+
|
|
160
|
+
def enableTwoFactor(self, code: str, password: str) -> list:
|
|
161
|
+
'''Enables TOTP two-factor. Returns recovery codes.'''
|
|
162
|
+
return self.api.enableTwoFactor(code, password)
|
|
163
|
+
|
|
164
|
+
def disableTwoFactor(self, code: str, password: str) -> None:
|
|
165
|
+
'''Disables TOTP two-factor.'''
|
|
166
|
+
return self.api.disableTwoFactor(code, password)
|
|
167
|
+
|
|
168
|
+
def enableEmailTwoFactor(self, password: str) -> list:
|
|
169
|
+
'''Enables email two-factor. Returns recovery codes.'''
|
|
170
|
+
return self.api.enableEmailTwoFactor(password)
|
|
171
|
+
|
|
172
|
+
def disableEmailTwoFactor(self, password: str) -> None:
|
|
173
|
+
'''Disables email two-factor.'''
|
|
174
|
+
return self.api.disableEmailTwoFactor(password)
|
|
175
|
+
|
|
176
|
+
# api keys
|
|
177
|
+
def getApiKeys(self, search: str = None, page: int = None, per_page: int = None):
|
|
178
|
+
'''Lists the account's API keys.'''
|
|
179
|
+
return self.api.apiKeys(search, page, per_page).map(lambda data: ApiKey(self.panel, data))
|
|
180
|
+
|
|
181
|
+
def getApiKey(self, identifier: str) -> ApiKey:
|
|
182
|
+
'''Returns an API key by its identifier (the start of the key).'''
|
|
183
|
+
return ApiKey(self.panel, self.api.apiKeyByIdentifier(identifier))
|
|
184
|
+
|
|
185
|
+
def createApiKey(self, name: str, user_permissions: list = None, server_permissions: list = None,
|
|
186
|
+
admin_permissions: list = None, allowed_ips: list = None, expires: str = None) -> ApiKey:
|
|
187
|
+
'''
|
|
188
|
+
Creates an API key.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
name: Key name.
|
|
192
|
+
user_permissions: Account permissions.
|
|
193
|
+
server_permissions: Server permissions, e.g. ['control.*', 'file.read'].
|
|
194
|
+
admin_permissions: Admin permissions.
|
|
195
|
+
allowed_ips: Restrict usage to these IPs/CIDRs.
|
|
196
|
+
expires: ISO-8601 expiry time.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
The ApiKey. Call `getKey()` to read the secret, it is shown only once.
|
|
200
|
+
'''
|
|
201
|
+
result = self.api.createApiKey(name, allowed_ips, user_permissions, admin_permissions, server_permissions, expires=expires)
|
|
202
|
+
return ApiKey(self.panel, result['api_key'], result.get('key'))
|
|
203
|
+
|
|
204
|
+
def deleteApiKey(self, api_key) -> None:
|
|
205
|
+
'''Deletes an API key.'''
|
|
206
|
+
return self.api.deleteApiKey(idOf(api_key))
|
|
207
|
+
|
|
208
|
+
# ssh keys
|
|
209
|
+
def getSshKeys(self, search: str = None, page: int = None, per_page: int = None):
|
|
210
|
+
'''Lists SSH keys.'''
|
|
211
|
+
return self.api.sshKeys(search, page, per_page).map(lambda data: SshKey(self.panel, data))
|
|
212
|
+
|
|
213
|
+
def addSshKey(self, name: str, public_key: str) -> SshKey:
|
|
214
|
+
'''Adds an SSH public key for SFTP.'''
|
|
215
|
+
return SshKey(self.panel, self.api.createSshKey(name, public_key))
|
|
216
|
+
|
|
217
|
+
def importSshKeys(self, provider: str, username: str) -> list:
|
|
218
|
+
'''
|
|
219
|
+
Imports public keys from github, gitlab or launchpad.
|
|
220
|
+
|
|
221
|
+
Example:
|
|
222
|
+
panel.account.importSshKeys('github', 'octocat')
|
|
223
|
+
'''
|
|
224
|
+
return [SshKey(self.panel, data) for data in self.api.importSshKeys(provider, username)]
|
|
225
|
+
|
|
226
|
+
def deleteSshKey(self, ssh_key) -> None:
|
|
227
|
+
'''Deletes an SSH key.'''
|
|
228
|
+
return self.api.deleteSshKey(idOf(ssh_key))
|
|
229
|
+
|
|
230
|
+
# security keys
|
|
231
|
+
def getSecurityKeys(self, search: str = None, page: int = None, per_page: int = None):
|
|
232
|
+
'''Lists security keys / passkeys.'''
|
|
233
|
+
return self.api.securityKeys(search, page, per_page).map(lambda data: SecurityKey(self.panel, data))
|
|
234
|
+
|
|
235
|
+
def startSecurityKeyRegistration(self, name: str, password: str, allow_usernameless_login: bool = None) -> dict:
|
|
236
|
+
'''
|
|
237
|
+
Starts registering a security key.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
dict with the pending `security_key` and WebAuthn `options` to pass to the authenticator.
|
|
241
|
+
Finish with `finishSecurityKeyRegistration()`.
|
|
242
|
+
'''
|
|
243
|
+
return self.api.createSecurityKey(name, password, allow_usernameless_login)
|
|
244
|
+
|
|
245
|
+
def finishSecurityKeyRegistration(self, security_key, public_key_credential: dict) -> str:
|
|
246
|
+
'''Completes a security key registration with the authenticator's response.'''
|
|
247
|
+
key_id = security_key.get('uuid') if isinstance(security_key, dict) else idOf(security_key)
|
|
248
|
+
return self.api.challengeSecurityKey(key_id, public_key_credential)
|
|
249
|
+
|
|
250
|
+
def deleteSecurityKey(self, security_key, password: str = None) -> None:
|
|
251
|
+
'''Deletes a security key.'''
|
|
252
|
+
return self.api.deleteSecurityKey(idOf(security_key), password)
|
|
253
|
+
|
|
254
|
+
# sessions
|
|
255
|
+
def getSessions(self, search: str = None, page: int = None, per_page: int = None):
|
|
256
|
+
'''Lists active browser sessions.'''
|
|
257
|
+
return self.api.sessions(search, page, per_page).map(lambda data: Session(self.panel, data))
|
|
258
|
+
|
|
259
|
+
def logoutOtherSessions(self) -> int:
|
|
260
|
+
'''Logs out every other session. Returns how many were removed.'''
|
|
261
|
+
return self.api.deleteOtherSessions()
|
|
262
|
+
|
|
263
|
+
# oauth
|
|
264
|
+
def getOAuthLinks(self, page: int = None, per_page: int = None):
|
|
265
|
+
'''Lists linked external accounts.'''
|
|
266
|
+
return self.api.oauthLinks(page, per_page).map(lambda data: OAuthLink(self.panel, data))
|
|
267
|
+
|
|
268
|
+
def getOAuthLink(self, oauth_link_id: str) -> OAuthLink:
|
|
269
|
+
'''Returns one linked external account.'''
|
|
270
|
+
return OAuthLink(self.panel, self.api.oauthLink(oauth_link_id))
|
|
271
|
+
|
|
272
|
+
def unlinkOAuth(self, oauth_link) -> None:
|
|
273
|
+
'''Removes a linked external account.'''
|
|
274
|
+
return self.api.deleteOauthLink(idOf(oauth_link))
|
|
275
|
+
|
|
276
|
+
# command snippets
|
|
277
|
+
def getCommandSnippets(self, search: str = None, page: int = None, per_page: int = None):
|
|
278
|
+
'''Lists saved command snippets.'''
|
|
279
|
+
return self.api.commandSnippets(search, page, per_page).map(lambda data: CommandSnippet(self.panel, data))
|
|
280
|
+
|
|
281
|
+
def createCommandSnippet(self, name: str, command: str, eggs: list = None) -> CommandSnippet:
|
|
282
|
+
'''
|
|
283
|
+
Saves a console command.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
eggs: Eggs (UUIDs) the snippet is shown for. All when empty.
|
|
287
|
+
'''
|
|
288
|
+
return CommandSnippet(self.panel, self.api.createCommandSnippet(name, command, [idOf(e) for e in eggs or []]))
|
|
289
|
+
|
|
290
|
+
def deleteCommandSnippet(self, snippet) -> None:
|
|
291
|
+
'''Deletes a command snippet.'''
|
|
292
|
+
return self.api.deleteCommandSnippet(idOf(snippet))
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
class Auth:
|
|
2
|
+
'''
|
|
3
|
+
Public authentication endpoints, available as `panel.auth`.
|
|
4
|
+
|
|
5
|
+
These do not need an API key. They are useful for account tooling such as
|
|
6
|
+
registration bots or password reset helpers.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
panel.auth.forgotPassword('user@example.com')
|
|
10
|
+
for provider in panel.auth.getOAuthProviders():
|
|
11
|
+
print(provider['name'], panel.auth.getOAuthLoginUrl(provider))
|
|
12
|
+
'''
|
|
13
|
+
def __init__(self, panel):
|
|
14
|
+
self.panel = panel
|
|
15
|
+
self.api = panel.api.auth
|
|
16
|
+
|
|
17
|
+
def login(self, user: str, password: str, captcha: str = None) -> dict:
|
|
18
|
+
'''
|
|
19
|
+
Logs in with a username/email and password.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
dict with `type` 'completed', or 'two_factor_required' together with
|
|
23
|
+
a `token` for `completeTwoFactor()`.
|
|
24
|
+
'''
|
|
25
|
+
return self.api.login(user, password, captcha)
|
|
26
|
+
|
|
27
|
+
def completeTwoFactor(self, code: str, confirmation_token: str, method: str = None) -> dict:
|
|
28
|
+
'''Finishes a login that requires two-factor authentication.'''
|
|
29
|
+
return self.api.checkpoint(code, confirmation_token, method)
|
|
30
|
+
|
|
31
|
+
def sendTwoFactorEmail(self, confirmation_token: str) -> None:
|
|
32
|
+
'''Sends the email two-factor code for a pending login.'''
|
|
33
|
+
return self.api.checkpointEmail(confirmation_token)
|
|
34
|
+
|
|
35
|
+
def register(self, username: str, email: str, password: str, name_first: str = None, name_last: str = None,
|
|
36
|
+
language: str = None, captcha: str = None) -> dict:
|
|
37
|
+
'''Registers a new account (if registration is enabled on the panel).'''
|
|
38
|
+
return self.api.register(username, email, password, name_first, name_last, language, captcha)
|
|
39
|
+
|
|
40
|
+
def verifyEmail(self, token: str) -> dict:
|
|
41
|
+
'''Verifies an email address with the token from the verification email.'''
|
|
42
|
+
return self.api.verifyEmail(token)
|
|
43
|
+
|
|
44
|
+
def forgotPassword(self, email: str, captcha: str = None) -> None:
|
|
45
|
+
'''Sends a password reset email.'''
|
|
46
|
+
return self.api.forgotPassword(email, captcha)
|
|
47
|
+
|
|
48
|
+
def resetPassword(self, token: str, new_password: str) -> None:
|
|
49
|
+
'''Sets a new password using the token from the reset email.'''
|
|
50
|
+
return self.api.resetPassword(token, new_password)
|
|
51
|
+
|
|
52
|
+
def getOAuthProviders(self) -> list:
|
|
53
|
+
'''Returns the OAuth providers users can log in with.'''
|
|
54
|
+
return self.api.oauthProviders()
|
|
55
|
+
|
|
56
|
+
def getOAuthLoginUrl(self, provider) -> str:
|
|
57
|
+
'''Returns the URL that starts an OAuth login with a provider (dict or UUID).'''
|
|
58
|
+
provider_id = provider.get('uuid') if isinstance(provider, dict) else provider
|
|
59
|
+
return self.api.oauthRedirectUrl(provider_id)
|
|
60
|
+
|
|
61
|
+
def getSecurityKeyOptions(self, user: str = None) -> dict:
|
|
62
|
+
'''
|
|
63
|
+
Starts a passkey login.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
user: Username for a username-first login. Omit for discoverable (usernameless) passkeys.
|
|
67
|
+
'''
|
|
68
|
+
return self.api.securityKeyOptions(user, discoverable=user is None)
|
|
69
|
+
|
|
70
|
+
def loginWithSecurityKey(self, uuid: str, public_key_credential: dict, discoverable: bool = False) -> dict:
|
|
71
|
+
'''Completes a passkey login with the authenticator's response.'''
|
|
72
|
+
return self.api.securityKeyLogin(uuid, public_key_credential, discoverable)
|