Mobile Gaming: Build a Security Token Service with Object Storage Service

SitePoint Sponsors
Share

This article was originally published on Alibaba Cloud. Thank you for supporting the partners who make SitePoint possible.

Think you got a better tip for making the best use of Alibaba Cloud services? Tell us about it and go in for your chance to win a Macbook Pro (plus other cool stuff). Find out more here.

In mobile gaming, many applications require developers to segregate player resources. This includes a range of things, from saving files to processing user profile information. Using traditional methods, developers can manage this segregation, but must consider many other problems such as security, scalability, and APIs.

As cloud technologies evolve, the need for higher-level usability and features is increasing. With Object Storage Service (OSS), customers can store and manage their objects easily and efficiently. OSS provides real-time image processing service online. Some customers may want additional features such as allowing users to have limited access to a service like OSS, but with the convenience of secure, centralized management.

Security Token Service provides short-term access permission management for Alibaba Cloud accounts or RAM users. Through STS, you can issue federated users, who are managed in your local account system, with an access credential that customizes the expiration duration and access permission. Federated users can use the STS temporary access credential to directly call the Alibaba Cloud service API or to log on to the Alibaba Cloud Management Console to access authenticated resources.

In this scenario, we test the functionality of STS by using OSS.

Prerequisites

It requires the ability to adjust Resource Access Management (RAM) settings and Roles. For more information, see Roles.

The sample code is written in Python. While it is not required, a basic understanding of computer programming is an advantage. The sample code provided in this tutorial can serve as a template which can be modified to meet your specific needs. Many people are currently using the raw API so as to manage an environment, or an application. While an SDK is available in many languages, the raw API provides more flexibility.

Architecture

In this diagram, a RAM user wants to upload images to a separate folder in an OSS bucket.

The upload process is as follows:

  1. The user assumes a RAM role for Read and Write OSS Access for a specific folder in Alibaba Cloud by calling AssumeRole.
  2. STS returns a set of temporary security credentials.
  3. The user applies the temporary security credentials to access OSS. The user can then make a read or write call on the object.

We take OSS as an example here. However, STS can be used to grant temporary access to a wide range of Alibaba Cloud services. In this tutorial, we use fine-grained STS permission to limit access to a specific OSS bucket.

Implementation

Three files in the sample code are as follows:

  • sts.py
    • This is the code for assuming the role and to retrieve essential information such as accessKeyId, accessKeySecret, and securityToken.

The available functions are as follows:

  • Generate signatures to guarantee request authenticity
  • Get HTTPS requests

The example code for file “sts.py” is as follows:

from base64 import b64encode
from datetime import datetime
from Crypto.Hash import SHA, HMAC
import md5, httplib, urllib, uuid, json
##### CONFIG MANAGEMENT
accessKeyId = "<access_key_id>"
accessKeySecret = "<access_key_secret>"
##### FUNCTION MANAGEMENT
def generateSignature(accessKeySecret, stringToSign):
    hmac = HMAC.new(accessKeySecret, stringToSign, SHA)
    return b64encode(hmac.digest())
def getHttpsRequest(host, verb, path):
    conn = httplib.HTTPSConnection(host)
    conn.request(verb, path)
    return conn.getresponse()
# ###### STS MANAGEMENT
host = "sts.aliyuncs.com"
verb = "GET"
bucketName = "<bucket_name>"
folderName = "1"
policy = '{"Statement": [{"Effect": "Allow","Action": ["oss:*"],"Resource": ["acs:oss:*:*:' + bucketName + '/' + folderName + '","acs:oss:*:*:' + bucketName + '/' + folderName + '/*"]}],"Version": "1"}'
dictionaryParams = {
    "AccessKeyId": accessKeyId,
    "Action": "AssumeRole",
    "DurationSeconds": "3600",
    "Format": "JSON",
    "Policy": policy,
    "RoleArn": "acs:ram::5081099437682835:role/ramtestossreadwrite",
    "RoleSessionName": "<session_name>",
    "SignatureMethod": "HMAC-SHA1",
    "SignatureNonce": str(uuid.uuid1()),
    "SignatureVersion": "1.0",
    "Timestamp": datetime.strftime(datetime.utcnow(), "%Y-%m-%dT%H:%M:%SZ"),
    "Version": "2015-04-01"
}
stringToSign = ""
for key in sorted(dictionaryParams.iterkeys()):
    value = urllib.quote(dictionaryParams[key], safe="")
    if stringToSign != "":
        stringToSign += "&"
    stringToSign += key + "=" + value
stringToSign = verb + "&%2F&" + urllib.quote(stringToSign)
signature = generateSignature(accessKeySecret + "&", stringToSign)
dictionaryParams["Signature"] = signature
params = urllib.urlencode(dictionaryParams)
path = "/?" + params
response = getHttpsRequest(host, verb, path)
if response.status == 200:
    jsonData = json.loads(response.read())
    print "Copy paste the respective information to file ossrest.py\n"
    print "accessKeyId: " + jsonData['Credentials']['AccessKeyId']
    print "accessKeySecret: " + jsonData['Credentials']['AccessKeySecret']
    print "securityToken: " + jsonData['Credentials']['SecurityToken']
  • ossrest.py
    • This is the code to upload and delete the object.

The available functions are as follows:

  • Generate signatures
  • Generate headers
  • Make HTTP requests
  • Upload objects
  • Delete objects

The example code for the file “ossrest.py” is as follows:

from base64 import b64encode
from datetime import datetime
from Crypto.Hash import SHA, HMAC
import md5, httplib, urllib, uuid
##### MAIN CONFIG (STS)
accessKeyId = "<access_key_id>"
accessKeySecret = "<access_key_secret>"
securityToken = "<security_token>"
##### FUNCTION MANAGEMENT
def generateSignature(accessKeySecret, stringToSign):
    hmac = HMAC.new(accessKeySecret, stringToSign, SHA)
    return b64encode(hmac.digest())
def generateHeaders(verb, canonicalizedResource = "/", canonicalizedOSSHeaders = {}, signature = {}):
    # authorization
    stringToSign = verb + "\n"
    if "content" in signature:
        stringToSign += md5.new(signature["content"]).digest()
    stringToSign += "\n"
    if "content_type" in signature:
        stringToSign += signature["content_type"]
    stringToSign += "\n"
    date = datetime.strftime(datetime.utcnow(), "%a, %d %b %Y %H:%M:%S GMT")
    stringToSign += date + "\n"
    if len(canonicalizedOSSHeaders):
        for index, value in canonicalizedOSSHeaders.items():
            stringToSign += index.lower() + ":" + value + "\n"
    stringToSign += canonicalizedResource
    signature = generateSignature(accessKeySecret, stringToSign)
    # headers
    headers = {"Date": date, "Authorization": "OSS " + accessKeyId + ":" + signature}
    headers.update(canonicalizedOSSHeaders)
    return headers
def sendHttpsRequest(host, verb, headers, path = "/", params = ""):
    conn = httplib.HTTPSConnection(host)
    conn.request(verb, path, params, headers)
    return conn.getresponse()
##### OBJECT MANAGEMENT
canonicalizedOSSHeaders = {"x-oss-acl": "public-read", "x-oss-security-token": securityToken}
bucketName = "<bucket_name>"
host = bucketName + ".oss-ap-southeast-1.aliyuncs.com"
hostMain = "oss-ap-southeast-1.aliyuncs.com"
folderName = "1"
fileName = "<filename>"
### UPLOAD OBJECT
verb = "PUT"
canonicalizedResource = "/" + bucketName + "/" + folderName + "/" + fileName
headers = generateHeaders(verb, canonicalizedResource, canonicalizedOSSHeaders)
response = sendHttpsRequest(host, verb, headers, "/" + folderName + "/" + fileName, open(fileName, "rb"))
print "Successfully uploaded " + fileName + " object to " + bucketName + "/" + folderName + " bucket/folder."
print response.status, response.reason
print response.read()
### DELETE OBJECT
verb = "DELETE"
canonicalizedResource = "/" + bucketName + "/" + folderName + "/" + fileName
headers = generateHeaders(verb, canonicalizedResource, canonicalizedOSSHeaders)
response = sendHttpsRequest(host, verb, headers, "/" + folderName + "/" + fileName)
print "Successfully deleted " + fileName + " object."
print response.status, response.reason
print response.read()
  • other_sample.py
    • This is the code for other scenarios. These samples may not be directly applicable to STS, but are provided as examples.

The available functions are as follows:

  • Create buckets
  • List buckets
  • Upload objects
  • List objects
  • Delete objects
  • Delete buckets

The example code for the file “other_sample.py” is as follows:

bucketName = "<bucket_name>"
host = bucketName + ".oss-ap-southeast-1.aliyuncs.com"
fileName = "<file_name>"
### CREATE BUCKET
verb = "PUT"
signature = {}
canonicalizedResource = "/" + bucketName + "/"
headers = generateHeaders(verb, signature, canonicalizedResource, canonicalizedOSSHeaders)
response = sendRequest(host, verb, headers)
print "Successfully created " + bucketName + " bucket."
print response.status, response.reason
print response.read()
### LIST BUCKET
host = "oss-ap-southeast-1.aliyuncs.com"
verb = "GET"
signature = {}
canonicalizedResource = "/"
headers = generateHeaders(verb, signature, canonicalizedResource, canonicalizedOSSHeaders)
response = sendRequest(host, verb, headers)
print "Successfully listed buckets."
print response.status, response.reason
print response.read()
### UPLOAD OBJECT
verb = "PUT"
signature = {}
canonicalizedResource = "/" + bucketName + "/" + fileName
headers = generateHeaders(verb, signature, canonicalizedResource, canonicalizedOSSHeaders)
response = sendRequest(host, verb, headers, "/" + fileName, open(fileName, "rb"))
print "Successfully uploaded " + fileName + " object to " + bucketName + " bucket."
print response.status, response.reason
print response.read()
### LIST OBJECT
verb = "GET"
signature = {}
canonicalizedResource = "/" + bucketName + "/"
headers = generateHeaders(verb, signature, canonicalizedResource, canonicalizedOSSHeaders)
response = sendRequest(host, verb, headers)
print "Successfully listed objects in " + bucketName + " bucket."
print response.status, response.reason
print response.read()
### DELETE OBJECT
verb = "DELETE"
signature = {}
canonicalizedResource = "/" + bucketName + "/" + fileName
headers = generateHeaders(verb, signature, canonicalizedResource, canonicalizedOSSHeaders)
response = sendRequest(host, verb, headers, "/" + fileName)
print "Successfully deleted " + fileName + " object."
print response.status, response.reason
print response.read()
### DELETE BUCKET
verb = "DELETE"
signature = {}
canonicalizedResource = "/" + bucketName + "/"
headers = generateHeaders(verb, signature, canonicalizedResource, canonicalizedOSSHeaders)
response = sendRequest(host, verb, headers)
print "Successfully deleted " + bucketName + " bucket."
print response.status, response.reason
print response.read()

The expected responses are as follows:

sts.py:

ossrest.py:

Conclusion

This example focuses on OSS, but the STS service can be used to control access to other Alibaba Cloud services as well. The use case we describe in this tutorial is gaming. Other scenarios or services which require short-term access to OSS may include:

  • Web applications
  • Mobile applications

Additional Information