Skip to content

Commit

Permalink
Merge pull request #35 from skyflowapi/fix-contentType
Browse files Browse the repository at this point in the history
add content-type default header
  • Loading branch information
yennamsanthosh-skyflow authored Feb 24, 2022
2 parents 6ec242c + 0834218 commit a073f28
Show file tree
Hide file tree
Showing 29 changed files with 361 additions and 117 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ jobs:
pip install requests pyjwt datetime aiohttp cryptography python-dotenv coverage
coverage run --source skyflow -m unittest discover
- name: coverage
run: coverage report
run: coverage report -m
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All notable changes to this project will be documented in this file.

## [1.3.0] - 2021-02-24

### Added
- Request ID in error logs and error responses for API Errors
- Caching to accessToken token
- `isValid` method for validating Service Account bearer token

## [1.2.1] - 2022-01-18

### Fixed
Expand Down
41 changes: 30 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,24 @@ The `generateBearerToken(filepath)` function takes the credentials file path for


```python
from skyflow.ServiceAccount import generateBearerToken
from skyflow.Errors import SkyflowError
from skyflow.ServiceAccount import generateBearerToken, isValid

# cache token for reuse
bearerToken = ''
tokenType = ''
def tokenProvider():
if not isValid(bearerToken):
bearerToken, tokenType = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return bearerToken, tokenType

filepath = '<YOUR_CREDENTIALS_FILE_PATH>'
accessToken, tokenType = generateBearerToken(filepath) # or generateBearerTokenFromCreds(credentials)
try:
accessToken, tokenType = tokenProvider()
print("Access Token:", accessToken)
print("Type of token:", tokenType)
except SkyflowError as e:
print(e)

print("Access Token:", accessToken)
print("Type of token:", tokenType)
```


Expand All @@ -49,12 +60,17 @@ To use this module, the skyflow client must first be initialized as follows.

```python
from skyflow.Vault import Client, Configuration
from skyflow.ServiceAccount import generateBearerToken
from skyflow.ServiceAccount import generateBearerToken, isValid

# cache for reuse
bearerToken = ''

#User defined function to provide access token to the vault apis
# User defined function to provide access token to the vault apis
def tokenProvider():
token, _ = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return token
if isValid(bearerToken):
return bearerToken
bearerToken, _ = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return bearerToken

#Initializing a Skyflow Client instance with a SkyflowConfiguration object
config = Configuration('<YOUR_VAULT_ID>', '<YOUR_VAULT_URL>', tokenProvider)
Expand Down Expand Up @@ -293,9 +309,12 @@ An example of invokeConnection:
```python
from skyflow.Vault import ConnectionConfig, Configuration, RequestMethod

bearerToken = ''
def tokenProvider():
token, _ = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return token
if isValid(bearerToken):
return bearerToken
bearerToken, _ = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return bearerToken

try:
config = Configuration('<YOUR_VAULT_ID>', '<YOUR_VAULT_URL>', tokenProvider)
Expand Down
10 changes: 6 additions & 4 deletions samples/InsertSample.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from skyflow.Errors import SkyflowError
from skyflow.ServiceAccount import generateBearerToken
from skyflow.ServiceAccount import generateBearerToken, isValid
from skyflow.Vault import Client, InsertOptions, Configuration


# cache token for reuse
bearerToken = ''
def tokenProvider():
token, _ = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return token
if not isValid(bearerToken):
bearerToken, _ = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return bearerToken

try:
config = Configuration('<YOUR_VAULT_ID>', '<YOUR_VAULT_URL>', tokenProvider)
Expand Down
11 changes: 9 additions & 2 deletions samples/SATokenSample.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
from skyflow.Errors import SkyflowError
from skyflow.ServiceAccount import generateBearerToken
from skyflow.ServiceAccount import generateBearerToken, isValid

# cache token for reuse
bearerToken = ''
tokenType = ''
def tokenProvider():
if not isValid(bearerToken):
bearerToken, tokenType = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return bearerToken, tokenType

try:
accessToken, tokenType = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
accessToken, tokenType = tokenProvider()
print("Access Token:", accessToken)
print("Type of token:", tokenType)
except SkyflowError as e:
Expand Down
10 changes: 6 additions & 4 deletions samples/detokenizeSample.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from skyflow.Errors import SkyflowError
from skyflow.ServiceAccount import generateBearerToken
from skyflow.ServiceAccount import generateBearerToken, isValid
from skyflow.Vault import Client, Configuration


# cache token for reuse
bearerToken = ''
def tokenProvider():
token, _ = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return token
if not isValid(bearerToken):
bearerToken, _ = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return bearerToken

try:
config = Configuration('<YOUR_VAULT_ID>', '<YOUR_VAULT_URL>', tokenProvider)
Expand Down
15 changes: 12 additions & 3 deletions samples/generateBearerTokenFromCredsSample.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json

from skyflow.Errors import SkyflowError
from skyflow.ServiceAccount import generateBearerTokenFromCreds
from skyflow.ServiceAccount import generateBearerTokenFromCreds, isValid

'''
This sample demonstrates the usage of generateBearerTokenFromCreds
Expand All @@ -10,8 +10,11 @@
- Use generateBearerTokenFromCreds(jsonString) to get the Bearer Token
'''

# cache token for reuse
bearerToken = ''
tokenType = ''
def tokenProvider():

try:
# As an example
credentials = {
"clientID": "<YOUR_clientID>",
Expand All @@ -21,7 +24,13 @@
"privateKey": "<YOUR_PEM_privateKey>"
}
jsonString = json.dumps(credentials)
accessToken, tokenType = generateBearerTokenFromCreds(credentials=jsonString)
if not isValid(bearerToken):
bearerToken, tokenType = generateBearerTokenFromCreds(credentials=jsonString)

return bearerToken

try:
accessToken, tokenType = tokenProvider()
print("Access Token:", accessToken)
print("Type of token:", tokenType)
except SkyflowError as e:
Expand Down
9 changes: 6 additions & 3 deletions samples/getByIDSample.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from skyflow.Errors import SkyflowError
from skyflow.ServiceAccount import generateBearerToken
from skyflow.ServiceAccount import generateBearerToken, isValid
from skyflow.Vault import Client, Configuration, RedactionType


# cache token for reuse
bearerToken = ''
def tokenProvider():
token, _ = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return token
if not isValid(bearerToken):
bearerToken, _ = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return bearerToken

try:
config = Configuration('<YOUR_VAULT_ID>', '<YOUR_VAULT_URL>', tokenProvider)
Expand Down
11 changes: 7 additions & 4 deletions samples/invokeConnectionSample.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
from skyflow.Errors import SkyflowError
from skyflow.ServiceAccount import generateBearerToken
from skyflow.ServiceAccount import generateBearerToken, isValid
from skyflow.Vault import Client, Configuration, RequestMethod, ConnectionConfig

'''
This sample is for generating CVV using Skyflow Connection with a third party integration such as VISA
'''


# cache token for reuse
bearerToken = ''
def tokenProvider():
token, _ = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return token
if not isValid(bearerToken):
bearerToken, _ = generateBearerToken('<YOUR_CREDENTIALS_FILE_PATH>')
return bearerToken


try:
config = Configuration('<YOUR_VAULT_ID>', '<YOUR_VAULT_URL>', tokenProvider)
Expand Down
1 change: 1 addition & 0 deletions skyflow/Errors/_skyflowErrors.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class SkyflowErrorMessages(Enum):
JWT_INVALID_FORMAT = "Private key is not in correct format"
MISSING_ACCESS_TOKEN = "accessToken not present in response"
MISSING_TOKEN_TYPE = "tokenType not present in response"
JWT_DECODE_ERROR = "Invalid access token"

# vault
RECORDS_KEY_ERROR = "Records key is missing from payload"
Expand Down
3 changes: 2 additions & 1 deletion skyflow/ServiceAccount/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from ._token import GenerateToken
from ._token import generateBearerToken
from ._token import ResponseToken
from ._token import generateBearerTokenFromCreds
from ._token import generateBearerTokenFromCreds
from ._validity import isValid
12 changes: 7 additions & 5 deletions skyflow/ServiceAccount/_token.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from collections import namedtuple
import json
import jwt
import datetime
import requests
from warnings import warn
from collections import namedtuple
from skyflow._utils import log_info, InterfaceName, InfoMessages

from requests.models import Response

from skyflow.Errors._skyflowErrors import *

Expand All @@ -17,7 +16,7 @@ def GenerateToken(credentialsFilePath: str) -> ResponseToken:
This function has been deprecated and replaced with generateBearerToken(credentialsFilePath: str)
'''
warn('This function has been deprecated and replaced with generateBearerToken(credentialsFilePath: str)', DeprecationWarning)
generateBearerToken(credentialsFilePath)
return generateBearerToken(credentialsFilePath)

def generateBearerToken(credentialsFilePath: str) -> ResponseToken:

Expand All @@ -41,7 +40,7 @@ def generateBearerToken(credentialsFilePath: str) -> ResponseToken:

try:
credentials = json.load(credentialsFile)
except Exception as e:
except Exception:
raise SkyflowError(SkyflowErrorCodes.INVALID_INPUT, SkyflowErrorMessages.FILE_INVALID_JSON.value % (credentialsFilePath))
finally:
credentialsFile.close()
Expand Down Expand Up @@ -137,6 +136,9 @@ def sendRequestWithToken(url, token):
try:
response.raise_for_status()
except requests.exceptions.HTTPError as error:
message = error.strerror
if 'x-request-id' in response.headers:
message += ' - request id: ' + response.headers['x-request-id']
raise SkyflowError(statusCode, error.strerror)

return response
Expand All @@ -152,4 +154,4 @@ def getResponseToken(token):
except:
raise SkyflowError(SkyflowErrorCodes.SERVER_ERROR, SkyflowErrorMessages.MISSING_TOKEN_TYPE)

return ResponseToken(AccessToken=accessToken, TokenType=tokenType)
return ResponseToken(AccessToken=accessToken, TokenType=tokenType)
26 changes: 26 additions & 0 deletions skyflow/ServiceAccount/_validity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import jwt
import time
from skyflow.Errors._skyflowErrors import *
from skyflow._utils import InterfaceName, log_info, log_error, InfoMessages

def isValid(token: str):
'''
Check if stored token is not expired, if not return a new token,
if the token has expiry time before 5min of current time, call returns False
'''
interface = InterfaceName.IS_TOKEN_VALID.value
log_info(InfoMessages.IS_TOKEN_VALID_TRIGGERED, interface)

if len(token) == 0:
log_info(InfoMessages.EMPTY_ACCESS_TOKEN, interface)
return False

try:
decoded = jwt.decode(token, options={"verify_signature": False})
if time.time() < decoded['exp']:
return True
except jwt.ExpiredSignatureError:
return False
except Exception as e:
log_error(e.message, interface)
return False
21 changes: 13 additions & 8 deletions skyflow/Vault/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import asyncio
from skyflow.Errors._skyflowErrors import SkyflowError, SkyflowErrorCodes, SkyflowErrorMessages
from skyflow._utils import log_info, InfoMessages, InterfaceName
from ._token import tokenProviderWrapper
class Client:
def __init__(self, config: Configuration):

Expand All @@ -28,6 +29,7 @@ def __init__(self, config: Configuration):
self.vaultID = config.vaultID
self.vaultURL = config.vaultURL.rstrip('/')
self.tokenProvider = config.tokenProvider
self.storedToken = ''
log_info(InfoMessages.CLIENT_INITIALIZED.value, interface=interface)

def insert(self, records: dict, options: InsertOptions = InsertOptions()):
Expand All @@ -38,9 +40,9 @@ def insert(self, records: dict, options: InsertOptions = InsertOptions()):

jsonBody = getInsertRequestBody(records, options.tokens)
requestURL = self.vaultURL + "/v1/vaults/" + self.vaultID
token = self.tokenProvider()
self.storedToken = tokenProviderWrapper(self.storedToken, self.tokenProvider, interface)
headers = {
"Authorization": "Bearer " + token
"Authorization": "Bearer " + self.storedToken
}

response = requests.post(requestURL, data=jsonBody, headers=headers)
Expand All @@ -57,11 +59,14 @@ def invokeConnection(self, config: ConnectionConfig):

self._checkConfig(interface)
session = requests.Session()
token = self.tokenProvider()
self.storedToken = tokenProviderWrapper(self.storedToken, self.tokenProvider, interface)
request = createRequest(config)

if not 'X-Skyflow-Authorization' in request.headers.keys():
request.headers['X-Skyflow-Authorization'] = token
request.headers['X-Skyflow-Authorization'] = self.storedToken

if not 'Content-Type' in request.headers.keys():
request.headers['Content-Type'] = 'application/json'

response = session.send(request)
session.close()
Expand All @@ -72,9 +77,9 @@ def detokenize(self, records):
log_info(InfoMessages.DETOKENIZE_TRIGGERED.value, interface)

self._checkConfig(interface)
token = self.tokenProvider()
self.storedToken = tokenProviderWrapper(self.storedToken, self.tokenProvider, interface)
url = self.vaultURL + "/v1/vaults/" + self.vaultID + "/detokenize"
responses = asyncio.run(sendDetokenizeRequests(records, url, token))
responses = asyncio.run(sendDetokenizeRequests(records, url, self.storedToken))
result, partial = createDetokenizeResponseBody(responses)
if partial:
raise SkyflowError(SkyflowErrorCodes.PARTIAL_SUCCESS ,SkyflowErrorMessages.PARTIAL_SUCCESS, result, interface=interface)
Expand All @@ -87,9 +92,9 @@ def getById(self, records):
log_info(InfoMessages.GET_BY_ID_TRIGGERED.value, interface)

self._checkConfig(interface)
token = self.tokenProvider()
self.storedToken = tokenProviderWrapper(self.storedToken, self.tokenProvider, interface)
url = self.vaultURL + "/v1/vaults/" + self.vaultID
responses = asyncio.run(sendGetByIdRequests(records, url, token))
responses = asyncio.run(sendGetByIdRequests(records, url, self.storedToken))
result, partial = createGetByIdResponseBody(responses)
if partial:
raise SkyflowError(SkyflowErrorCodes.PARTIAL_SUCCESS ,SkyflowErrorMessages.PARTIAL_SUCCESS, result, interface=interface)
Expand Down
Loading

0 comments on commit a073f28

Please sign in to comment.