Skip to content

Commit

Permalink
Merge pull request #59 from yandex-cloud/feature/support-iam-tokens
Browse files Browse the repository at this point in the history
Support IAM tokens as authorisation method
  • Loading branch information
MAnyKey committed Jul 18, 2022
2 parents ce33ad9 + 76ebf92 commit bdef47e
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 22 deletions.
22 changes: 11 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,29 @@
REPO_ROOT:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))

deps: ## install deps (library & development)
python -m pip install --upgrade pip
python -m pip install -r requirements-dev.txt
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements-dev.txt

deps-genproto: ## install deps (library & development)
python -m pip install --upgrade pip
python -m pip install -r requirements-genproto.txt
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements-genproto.txt

tox: ## run ALL checks for ALL available python versions
python -m tox
python3 -m tox

tox-current: ## run ALL checks ONLY for current python version
python -m tox -e `python -c 'import platform; print("py" + "".join(platform.python_version_tuple()[:2]))'`
python3 -m tox -e `python3 -c 'import platform; print("py" + "".join(platform.python_version_tuple()[:2]))'`

test: ## run tests ONLY for current python version
python -m pytest
python3 -m pytest

lint: ## run linters, formatters for current python versions
python -m flake8 yandexcloud
python -m pylint yandexcloud
python3 -m flake8 yandexcloud
python3 -m pylint yandexcloud

format:
python -m isort yandexcloud setup.py changelog.py
python -m black yandexcloud setup.py changelog.py
python3 -m isort yandexcloud setup.py changelog.py
python3 -m black yandexcloud setup.py changelog.py

test-all-versions: ## run test for multiple python versions using docker
# python 3.10 not provided in image so we skip it
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ Installation:
## Getting started

There are several options for authorization your requests - OAuth Token,
Metadata Service (if you're executing code inside VMs or Functions
running in Yandex.Cloud) and Service Account Keys
Metadata Service (if you're executing your code inside VMs or Cloud Functions
running in Yandex.Cloud), Service Account Keys, and externally created IAM tokens.

### OAuth Token

Expand Down Expand Up @@ -52,6 +52,12 @@ sa_key = {
sdk = yandexcloud.SDK(service_account_key=sa_key)
```

### IAM tokens

```python
sdk = yandexcloud.SDK(iam_token="t1.9eu...")
```

Check `examples` directory for more examples.


Expand Down
7 changes: 6 additions & 1 deletion tests/test_service_account_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def test_both_params_error(token, service_account_key):
with pytest.raises(RuntimeError) as e:
get_auth_token_requester(token=token, service_account_key=service_account_key).get_token_request()

assert str(e.value) == "Conflicting API credentials properties 'token' and 'service-account-key' are set."
assert str(e.value) == "Conflicting API credentials properties are set: ['token', 'service_account_key']."


def test_invalid_service_account_type():
Expand Down Expand Up @@ -57,3 +57,8 @@ def test_service_account_key(service_account_key):
assert parsed["iss"] == service_account_key["service_account_id"]
assert parsed["aud"] == "https://iam.api.cloud.yandex.net/iam/v1/tokens"
assert now - 60 <= int(parsed["iat"]) <= now

def test_iam_token(iam_token):
token_func = get_auth_token_requester(iam_token=iam_token).get_token
token = token_func()
assert token == iam_token
32 changes: 25 additions & 7 deletions yandexcloud/_auth_fabric.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,28 @@ def __validate_service_account_key(sa_key):
raise RuntimeError(error_message)


def get_auth_token_requester(token=None, service_account_key=None, metadata_addr=_MDS_ADDR):
if token is not None and service_account_key is not None:
raise RuntimeError("Conflicting API credentials properties 'token' and 'service-account-key' are set.")
def get_auth_token_requester(token=None, service_account_key=None, iam_token=None, metadata_addr=_MDS_ADDR):
auth_methods = [("token", token), ("service_account_key", service_account_key), ("iam_token", iam_token)]
auth_methods = [(auth_type, value) for auth_type, value in auth_methods if value is not None]

if token is not None:
return TokenAuth(token=token)
if len(auth_methods) == 0:
return MetadataAuth(metadata_addr=metadata_addr)

if len(auth_methods) > 1:
raise RuntimeError(
"Conflicting API credentials properties are set: {}.".format([auth[0] for auth in auth_methods])
)

if service_account_key is not None:
auth_name, _ = auth_methods[0]
if auth_name == "token":
return TokenAuth(token=token)
if auth_name == "service_account_key":
__validate_service_account_key(service_account_key)
return ServiceAccountAuth(service_account_key)
if auth_name == "iam_token":
return IamTokenAuth(iam_token)

return MetadataAuth(metadata_addr=metadata_addr)
raise RuntimeError("Unknown auth method: {}".format(auth_name))


class MetadataAuth:
Expand Down Expand Up @@ -106,3 +116,11 @@ def __prepare_request(self):
}

return jwt.encode(payload, self.__sa_key["private_key"], algorithm="PS256", headers=headers)


class IamTokenAuth:
def __init__(self, iam_token):
self.__iam_token = iam_token

def get_token(self):
return self.__iam_token
4 changes: 3 additions & 1 deletion yandexcloud/_channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ def __init__(self, client_user_agent=None, **kwargs):
)
self._endpoint = kwargs.get("endpoint", "api.cloud.yandex.net")
self._token_requester = get_auth_token_requester(
token=kwargs.get("token"), service_account_key=kwargs.get("service_account_key")
token=kwargs.get("token"),
service_account_key=kwargs.get("service_account_key"),
iam_token=kwargs.get("iam_token"),
)

self._unauthenticated_channel = None
Expand Down

0 comments on commit bdef47e

Please sign in to comment.