Repository: zalando-stups/zign

Issue:
Transparently get service tokens via "tokens" library if possible
We already have the `zign.api.get_token` function and we should consider getting service tokens transparently also when using `zign token` directly.

Discussion:

Patch:
diff --git a/zign/api.py b/zign/api.py
index d98ed9a..065afae 100644
--- a/zign/api.py
+++ b/zign/api.py
@@ -100,6 +100,11 @@ def get_named_token(scope, realm, name, user, password, url=None,
         if existing_token:
             return existing_token
 
+    if name and not realm:
+        access_token = get_service_token(name, scope)
+        if access_token:
+            return {'access_token': access_token}
+
     config = get_config()
 
     url = url or config.get('url')
@@ -153,6 +158,21 @@ def is_user_scope(scope: str):
     return scope in set(['uid', 'cn'])
 
 
+def get_service_token(name: str, scopes: list):
+    '''Get service token (tokens lib) if possible, otherwise return None'''
+    tokens.manage(name, scopes)
+    try:
+        access_token = tokens.get(name)
+    except tokens.ConfigurationError:
+        # will be thrown if configuration is missing (e.g. OAUTH2_ACCESS_TOKEN_URL)
+        access_token = None
+    except tokens.InvalidCredentialsError:
+        # will be thrown if $CREDENTIALS_DIR/*.json cannot be read
+        access_token = None
+
+    return access_token
+
+
 def get_token(name: str, scopes: list):
     '''Get an OAuth token, either from Token Service
     or directly from OAuth provider (using the Python tokens library)'''
@@ -163,14 +183,7 @@ def get_token(name: str, scopes: list):
     if token:
         return token['access_token']
 
-    tokens.manage(name, scopes)
-    try:
-        access_token = tokens.get(name)
-    except tokens.ConfigurationError:
-        access_token = None
-    except tokens.InvalidCredentialsError:
-        access_token = None
-
+    access_token = get_service_token(name, scopes)
     if access_token:
         return access_token
 


Test patch:
diff --git a/tests/test_api.py b/tests/test_api.py
index ad3cc20..e6e2f6c 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -1,4 +1,5 @@
 import pytest
+import time
 import tokens
 import zign.api
 
@@ -72,3 +73,18 @@ def test_get_token_fallback_success(monkeypatch):
     monkeypatch.setattr('zign.api.get_new_token', lambda *args, **kwargs: {'access_token': 'tt77'})
 
     assert zign.api.get_token('mytok', ['myscope']) == 'tt77'
+
+
+def test_get_named_token_existing(monkeypatch):
+    existing = {'mytok': {'access_token': 'tt77', 'creation_time': time.time() - 10, 'expires_in': 3600}}
+    monkeypatch.setattr('zign.api.get_tokens', lambda: existing)
+    tok = zign.api.get_named_token(scope=['myscope'], realm=None, name='mytok', user='myusr', password='mypw')
+    assert tok['access_token'] == 'tt77'
+
+
+def test_get_named_token_services(monkeypatch):
+    response = MagicMock(status_code=401)
+    monkeypatch.setattr('requests.get', MagicMock(return_value=response))
+    monkeypatch.setattr('tokens.get', lambda x: 'svcmytok123')
+    tok = zign.api.get_named_token(scope=['myscope'], realm=None, name='mytok', user='myusr', password='mypw')
+    assert tok['access_token'] == 'svcmytok123'