Skip to content

Commit eb5047b

Browse files
Ricardofelipao-mxmatin
authored
New Passwords (#127)
* new_passwords2 * Map error codes to login exceptions * Version * Tests and cassettes * Bump version * New cassettes and resources * Missing test * user_credentials * Update version * All tests working * Readme * Fix * Version * more generalized * clarifications * correct version of cuenca-validations * cuenca-validations requirement should be 0.7.* * ERROR_CODES Co-authored-by: Felipe López <flh.1989@gmail.com> Co-authored-by: Matin Tamizi <matin@users.noreply.github.com>
1 parent 34e7721 commit eb5047b

21 files changed

+1209
-5
lines changed

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,40 @@ old_id = cuenca.session.auth[0]
113113
cuenca.session.configure(new.id, new.secret)
114114
cuenca.ApiKey.deactivate(old_id, 60) # revoke prior API key in an hour
115115
```
116+
117+
## Login
118+
119+
120+
Create a new password
121+
```python
122+
cuenca.UserCredential.create(password='1234567890')
123+
```
124+
125+
To update your password
126+
```python
127+
cuenca.UserCredential.update(password='1234567890')
128+
```
129+
130+
To reset password
131+
```python
132+
cuenca.UserCredential.update(password=None)
133+
```
134+
135+
Login in and out
136+
```python
137+
cuenca.UserLogin.create(password='1234567890')
138+
... # authenticated operation
139+
cuenca.UserLogin.logout()
140+
```
141+
142+
Create login token for biometrics
143+
```python
144+
# Must be logged in
145+
cuenca.UserLogin.create(password='1234567890')
146+
token = cuenca.LoginToken.create()
147+
cuenca.UserLogin.logout()
148+
149+
# Then you can use the token which lasts for 7 days
150+
cuenca.configure(login_token=token)
151+
... # authenticated operation
152+
```

cuenca/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
'CardTransaction',
99
'Commission',
1010
'Deposit',
11+
'LoginToken',
1112
'ServiceProvider',
1213
'Statement',
1314
'Transfer',
15+
'UserCredential',
16+
'UserLogin',
1417
'WhatsappTransfer',
1518
'configure',
1619
]
@@ -26,9 +29,12 @@
2629
CardTransaction,
2730
Commission,
2831
Deposit,
32+
LoginToken,
2933
ServiceProvider,
3034
Statement,
3135
Transfer,
36+
UserCredential,
37+
UserLogin,
3238
WhatsappTransfer,
3339
)
3440
from .version import __version__

cuenca/http/client.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from urllib.parse import urljoin
55

66
import requests
7+
from cuenca_validations.errors import ERROR_CODES
78
from cuenca_validations.typing import (
89
ClientRequestParams,
910
DictStrAny,
@@ -17,8 +18,6 @@
1718

1819
API_HOST = 'api.cuenca.com'
1920
SANDBOX_HOST = 'sandbox.cuenca.com'
20-
AWS_DEFAULT_REGION = 'us-east-1'
21-
AWS_SERVICE = 'execute-api'
2221

2322

2423
class Session:
@@ -52,6 +51,7 @@ def configure(
5251
api_secret: Optional[str] = None,
5352
use_jwt: Optional[bool] = False,
5453
sandbox: Optional[bool] = None,
54+
login_token: Optional[str] = None,
5555
):
5656
"""
5757
This allows us to instantiate the http client when importing the
@@ -72,6 +72,9 @@ def configure(
7272
if use_jwt:
7373
self.jwt_token = Jwt.create(self)
7474

75+
if login_token:
76+
self.session.headers['X-Cuenca-LoginToken'] = login_token
77+
7578
def get(
7679
self, endpoint: str, params: ClientRequestParams = None
7780
) -> DictStrAny:
@@ -116,6 +119,9 @@ def request(
116119
def _check_response(response: Response):
117120
if response.ok:
118121
return
122+
json = response.json()
123+
if 'code' in json:
124+
raise ERROR_CODES[json['code']](json['error'])
119125
raise CuencaResponseException(
120126
json=response.json(),
121127
status_code=response.status_code,

cuenca/resources/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
'CardTransaction',
88
'Commission',
99
'Deposit',
10+
'LoginToken',
1011
'ServiceProvider',
1112
'Statement',
1213
'Transfer',
14+
'UserLogin',
1315
'WhatsappTransfer',
1416
]
1517

@@ -21,10 +23,13 @@
2123
from .cards import Card
2224
from .commissions import Commission
2325
from .deposits import Deposit
26+
from .login_tokens import LoginToken
2427
from .resources import RESOURCES
2528
from .service_providers import ServiceProvider
2629
from .statements import Statement
2730
from .transfers import Transfer
31+
from .user_credentials import UserCredential
32+
from .user_logins import UserLogin
2833
from .whatsapp_transfers import WhatsappTransfer
2934

3035
# avoid circular imports
@@ -37,9 +42,12 @@
3742
CardTransaction,
3843
Commission,
3944
Deposit,
45+
LoginToken,
4046
ServiceProvider,
4147
Statement,
4248
Transfer,
49+
UserCredential,
50+
UserLogin,
4351
WhatsappTransfer,
4452
]
4553
for resource_cls in resource_classes:

cuenca/resources/login_tokens.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import ClassVar, cast
2+
3+
from pydantic.dataclasses import dataclass
4+
5+
from ..http import Session, session as global_session
6+
from .base import Creatable
7+
8+
9+
@dataclass
10+
class LoginToken(Creatable):
11+
_resource: ClassVar = 'login_tokens'
12+
13+
@classmethod
14+
def create(cls, session: Session = global_session) -> 'LoginToken':
15+
"""
16+
Use this method to create a token that will last for 7 days
17+
Make sure to store this token in a safe place
18+
:return: Token that you can use in cuenca.configure
19+
"""
20+
return cast('LoginToken', cls._create(session=session))
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import datetime as dt
2+
from typing import ClassVar, Optional, cast
3+
4+
from cuenca_validations.types.requests import (
5+
UserCredentialRequest,
6+
UserCredentialUpdateRequest,
7+
)
8+
from pydantic.dataclasses import dataclass
9+
10+
from ..http import Session, session as global_session
11+
from .base import Creatable, Updateable
12+
13+
14+
@dataclass
15+
class UserCredential(Creatable, Updateable):
16+
_resource: ClassVar = 'user_credentials'
17+
18+
is_active: bool
19+
created_at: dt.datetime
20+
21+
@classmethod
22+
def create(
23+
cls, password: str, *, session: Session = global_session
24+
) -> 'UserCredential':
25+
req = UserCredentialRequest(password=password)
26+
return cast(
27+
'UserCredential', cls._create(**req.dict(), session=session)
28+
)
29+
30+
@classmethod
31+
def update(
32+
cls,
33+
user_id: str = 'me',
34+
is_active: Optional[bool] = None,
35+
password: Optional[str] = None,
36+
*,
37+
session: Session = global_session,
38+
) -> 'UserCredential':
39+
req = UserCredentialUpdateRequest(
40+
is_active=is_active,
41+
password=password,
42+
)
43+
return cast(
44+
'UserCredential',
45+
cls._update(id=user_id, **req.dict(), session=session),
46+
)

cuenca/resources/user_logins.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import datetime as dt
2+
from dataclasses import dataclass
3+
from typing import ClassVar, Optional, cast
4+
5+
from cuenca_validations.types.requests import UserCredentialRequest
6+
7+
from ..http import Session, session as global_session
8+
from .base import Creatable
9+
10+
11+
@dataclass
12+
class UserLogin(Creatable):
13+
_resource: ClassVar = 'user_logins'
14+
15+
last_login_at: Optional[dt.datetime]
16+
success: bool
17+
18+
@classmethod
19+
def create(
20+
cls, password: str, *, session: Session = global_session
21+
) -> 'UserLogin':
22+
req = UserCredentialRequest(password=password)
23+
login = cast('UserLogin', cls._create(session=session, **req.dict()))
24+
if login.success:
25+
session.session.headers['X-Cuenca-LoginId'] = login.id
26+
return login
27+
28+
@classmethod
29+
def logout(
30+
cls, user_id: str = 'me', *, session: Session = global_session
31+
) -> None:
32+
# Using user_id vs user_login_id to avoid needing to store
33+
# user_login_id or perform a query to fetch it
34+
session.delete(f'{cls._resource}/{user_id}', dict())
35+
session.session.headers.pop('X-Cuenca-LoginId', None)

cuenca/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
__version__ = '0.6.2'
1+
__version__ = '0.6.3'
22
CLIENT_VERSION = __version__
33
API_VERSION = '2020-03-19'

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
requests==2.25.1
2-
cuenca-validations==0.7.5
2+
cuenca-validations==0.7.8
33
dataclasses>=0.7;python_version<"3.7"

0 commit comments

Comments
 (0)