Skip to content

Commit 65b718f

Browse files
Brought profile-related functionality to API parity (#250)
## 📝 Description Brought profile-related functionality to API parity ## ✔️ How to Test Run `tox`. Ticket: TPT-1893
1 parent 18d372b commit 65b718f

11 files changed

+416
-1
lines changed

linode_api4/groups/profile.py

+178
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
from linode_api4.groups import Group
77
from linode_api4.objects import (
88
AuthorizedApp,
9+
MappedObject,
910
PersonalAccessToken,
1011
Profile,
12+
ProfileLogin,
1113
SSHKey,
14+
TrustedDevice,
1215
)
1316

1417

@@ -40,6 +43,181 @@ def __call__(self):
4043
p = Profile(self.client, result["username"], result)
4144
return p
4245

46+
def trusted_devices(self):
47+
"""
48+
Returns the Trusted Devices on your profile.
49+
50+
API Documentation: https://www.linode.com/docs/api/profile/#trusted-devices-list
51+
52+
:returns: A list of Trusted Devices for this profile.
53+
:rtype: PaginatedList of TrustedDevice
54+
"""
55+
return self.client._get_and_filter(TrustedDevice)
56+
57+
def user_preferences(self):
58+
"""
59+
View a list of user preferences tied to the OAuth client that generated the token making the request.
60+
"""
61+
62+
result = self.client.get(
63+
"{}/preferences".format(Profile.api_endpoint), model=self
64+
)
65+
66+
return MappedObject(**result)
67+
68+
def security_questions(self):
69+
"""
70+
Returns a collection of security questions and their responses, if any, for your User Profile.
71+
"""
72+
73+
result = self.client.get(
74+
"{}/security-questions".format(Profile.api_endpoint), model=self
75+
)
76+
77+
return MappedObject(**result)
78+
79+
def security_questions_answer(self, questions):
80+
"""
81+
Adds security question responses for your User. Requires exactly three unique questions.
82+
Previous responses are overwritten if answered or reset to null if unanswered.
83+
84+
Example question:
85+
{
86+
"question_id": 11,
87+
"response": "secret answer 3"
88+
}
89+
"""
90+
91+
if len(questions) != 3:
92+
raise ValueError("Exactly 3 security questions are required.")
93+
94+
params = {"security_questions": questions}
95+
96+
result = self.client.post(
97+
"{}/security-questions".format(Profile.api_endpoint),
98+
model=self,
99+
data=params,
100+
)
101+
102+
return MappedObject(**result)
103+
104+
def user_preferences_update(self, **preferences):
105+
"""
106+
Updates a user’s preferences.
107+
"""
108+
109+
result = self.client.put(
110+
"{}/preferences".format(Profile.api_endpoint),
111+
model=self,
112+
data=preferences,
113+
)
114+
115+
return MappedObject(**result)
116+
117+
def phone_number_delete(self):
118+
"""
119+
Delete the verified phone number for the User making this request.
120+
121+
API Documentation: https://api.linode.com/v4/profile/phone-number
122+
123+
:returns: Returns True if the operation was successful.
124+
:rtype: bool
125+
"""
126+
127+
resp = self.client.delete(
128+
"{}/phone-number".format(Profile.api_endpoint), model=self
129+
)
130+
131+
if "error" in resp:
132+
raise UnexpectedResponseError(
133+
"Unexpected response when deleting phone number!",
134+
json=resp,
135+
)
136+
137+
return True
138+
139+
def phone_number_verify(self, otp_code):
140+
"""
141+
Verify a phone number by confirming the one-time code received via SMS message
142+
after accessing the Phone Verification Code Send (POST /profile/phone-number) command.
143+
144+
API Documentation: https://api.linode.com/v4/profile/phone-number/verify
145+
146+
:param otp_code: The one-time code received via SMS message after accessing the Phone Verification Code Send
147+
:type otp_code: str
148+
149+
:returns: Returns True if the operation was successful.
150+
:rtype: bool
151+
"""
152+
153+
if not otp_code:
154+
raise ValueError("OTP Code required to verify phone number.")
155+
156+
params = {"otp_code": str(otp_code)}
157+
158+
resp = self.client.post(
159+
"{}/phone-number/verify".format(Profile.api_endpoint),
160+
model=self,
161+
data=params,
162+
)
163+
164+
if "error" in resp:
165+
raise UnexpectedResponseError(
166+
"Unexpected response when verifying phone number!",
167+
json=resp,
168+
)
169+
170+
return True
171+
172+
def phone_number_verification_code_send(self, iso_code, phone_number):
173+
"""
174+
Send a one-time verification code via SMS message to the submitted phone number.
175+
176+
API Documentation: https://api.linode.com/v4/profile/phone-number
177+
178+
:param iso_code: The two-letter ISO 3166 country code associated with the phone number.
179+
:type iso_code: str
180+
181+
:param phone_number: A valid phone number.
182+
:type phone_number: str
183+
184+
:returns: Returns True if the operation was successful.
185+
:rtype: bool
186+
"""
187+
188+
if not iso_code:
189+
raise ValueError("ISO Code required to send verification code.")
190+
191+
if not phone_number:
192+
raise ValueError("Phone Number required to send verification code.")
193+
194+
params = {"iso_code": iso_code, "phone_number": phone_number}
195+
196+
resp = self.client.post(
197+
"{}/phone-number".format(Profile.api_endpoint),
198+
model=self,
199+
data=params,
200+
)
201+
202+
if "error" in resp:
203+
raise UnexpectedResponseError(
204+
"Unexpected response when sending verification code!",
205+
json=resp,
206+
)
207+
208+
return True
209+
210+
def logins(self):
211+
"""
212+
Returns the logins on your profile.
213+
214+
API Documentation: https://www.linode.com/docs/api/profile/#logins-list
215+
216+
:returns: A list of logins for this profile.
217+
:rtype: PaginatedList of ProfileLogin
218+
"""
219+
return self.client._get_and_filter(ProfileLogin)
220+
43221
def tokens(self, *filters):
44222
"""
45223
Returns the Person Access Tokens active for this user.

linode_api4/objects/profile.py

+29
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ class Profile(Base):
5656
"authorized_keys": Property(mutable=True),
5757
"two_factor_auth": Property(),
5858
"restricted": Property(),
59+
"authentication_type": Property(),
60+
"authorized_keys": Property(),
61+
"verified_phone_number": Property(),
5962
}
6063

6164
def enable_tfa(self):
@@ -146,3 +149,29 @@ class SSHKey(Base):
146149
"ssh_key": Property(),
147150
"created": Property(is_datetime=True),
148151
}
152+
153+
154+
class TrustedDevice(Base):
155+
api_endpoint = "/profile/devices/{id}"
156+
157+
properties = {
158+
"id": Property(identifier=True),
159+
"created": Property(is_datetime=True),
160+
"expiry": Property(is_datetime=True),
161+
"last_authenticated": Property(is_datetime=True),
162+
"last_remote_addr": Property(),
163+
"user_agent": Property(),
164+
}
165+
166+
167+
class ProfileLogin(Base):
168+
api_endpoint = "profile/logins/{id}"
169+
170+
properties = {
171+
"id": Property(identifier=True),
172+
"datetime": Property(is_datetime=True),
173+
"ip": Property(),
174+
"restricted": Property(),
175+
"status": Property(),
176+
"username": Property(),
177+
}

test/fixtures/profile.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"authentication_type": "password",
3+
"authorized_keys": [
4+
null
5+
],
6+
"email": "example-user@gmail.com",
7+
"email_notifications": true,
8+
"ip_whitelist_enabled": false,
9+
"lish_auth_method": "keys_only",
10+
"referrals": {
11+
"code": "871be32f49c1411b14f29f618aaf0c14637fb8d3",
12+
"completed": 0,
13+
"credit": 0,
14+
"pending": 0,
15+
"total": 0,
16+
"url": "https://www.linode.com/?r=871be32f49c1411b14f29f618aaf0c14637fb8d3"
17+
},
18+
"restricted": false,
19+
"timezone": "US/Eastern",
20+
"two_factor_auth": true,
21+
"uid": 1234,
22+
"username": "exampleUser",
23+
"verified_phone_number": "+5555555555"
24+
}

test/fixtures/profile_device_123.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"created": "2018-01-01T01:01:01",
3+
"expiry": "2018-01-31T01:01:01",
4+
"id": 123,
5+
"last_authenticated": "2018-01-05T12:57:12",
6+
"last_remote_addr": "203.0.113.1",
7+
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 Vivaldi/2.1.1337.36\n"
8+
}

test/fixtures/profile_devices.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"data": [
3+
{
4+
"created": "2018-01-01T01:01:01",
5+
"expiry": "2018-01-31T01:01:01",
6+
"id": 123,
7+
"last_authenticated": "2018-01-05T12:57:12",
8+
"last_remote_addr": "203.0.113.1",
9+
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 Vivaldi/2.1.1337.36\n"
10+
}
11+
],
12+
"page": 1,
13+
"pages": 1,
14+
"results": 1
15+
}

test/fixtures/profile_logins.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"data": [
3+
{
4+
"datetime": "2018-01-01T00:01:01",
5+
"id": 123,
6+
"ip": "192.0.2.0",
7+
"restricted": true,
8+
"status": "successful",
9+
"username": "example_user"
10+
}
11+
],
12+
"page": 1,
13+
"pages": 1,
14+
"results": 1
15+
}

test/fixtures/profile_logins_123.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"datetime": "2018-01-01T00:01:01",
3+
"id": 123,
4+
"ip": "192.0.2.0",
5+
"restricted": true,
6+
"status": "successful",
7+
"username": "example_user"
8+
}
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"key1": "value1",
3+
"key2": "value2"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"security_questions": [
3+
{
4+
"id": 1,
5+
"question": "In what city were you born?",
6+
"response": "Gotham City"
7+
}
8+
]
9+
}

0 commit comments

Comments
 (0)