Stormpath is the first simple and secure user management and authentication service for developers like you. This Python SDK makes using Stormpath with your application a painless, and even enjoyable process.
NOTE: This library will NOT work on Google App Engine due to incompatibility with the requests package.
To get started, install the stormpath
library using
Pip.
$ pip install stormpath
If this doesn't work, you might need to install pip
on your computer. See the
Pip installation guide
for more information.
If you have not already done so, register as a developer on
Stormpath, and create (and download) an API Key pair
(this consists of an APIKeyID
and an APIKeySecret
).
When you first create your API Key pair using the Stormpath interface, you'll be
prompted to download a file named apiKey.properties
. This file contains your
API Key pair information, which you'll need below.
Next, you'll want to create a Stormpath API client. You can do this in a few ways:
from stormpath.client import Client
# 1) By using the apiKey.properties file you previously downloaded.
client = Client(api_key_file='/path/to/apiKey.properties')
# 2) By specifying your API Key pair credentials manually.
client = Client(
api_key_id = STORMPATH_API_KEY_ID,
api_key_secret = STORMPATH_API_KEY_SECRET,
)
# 3) By setting an environment variable called STORMPATH_API_KEY_FILE to the
# path of your apiKey.properties file.
client = Client()
# 4) By setting environment variables called STORMPATH_API_KEY_ID and
# STORMPATH_API_KEY_SECRET which contain your keys.
client = Client()
# 5) By defining a file in ~/.stormpath/apiKey.properties -- this file will be
# used automatically if it exists and no other credentials are specified.
client = Client()
NOTE: Once you've created a client, you can use it for the life of your application. There's no need to recreate a new client for each new request -- the client is smart, and will smartly handle SDK requests.
To view a list of all your Stormpath
Applications and
Directories, you
can easily iterate through the client.applications
and client.directories
generators show below:
for application in client.applications:
print 'Application (name):', application.name
print 'Application (href):', application.href
for directory in client.directories:
print 'Directory (name):', directory.name
print 'Directory (href):', directory.href
If you know the full
Application and
Directory href
(like the ones show in the previous example), you can easily retrieve the
application
and directory
objects directly, like so:
application = client.applications.get('https://api.stormpath.com/v1/applications/<uid>')
directory = client.directories.get('https://api.stormpath.com/v1/directories/<uid>')
Easy, right?
As you can probably guess, creating an Application is also simple business.
The example below shows you how to create an Application by itself, or, if you want, an Application and a Directory together!
The benefits to creating them at the same time is that all the association stuff is handled automatically!
# Create an Application by itself.
application = client.applications.create({
'name': 'Instagram',
'description': 'A place to post photos of your food.',
})
# Create an Application AND Directory.
application = client.applications.create({
'name': 'Instagram',
'description': 'A place to post photos of your food.',
}, create_directory=True)
Now that you've (hopefully!) already created an Application and associated Directory, we can now move on to creating a user Account.
You can create a new Account on either an Application or Directory instance:
# Create a new Account on an Application instance.
emusk = application.accounts.create({
'given_name': 'Elon',
'surname': 'Musk',
'email': '[email protected]',
'password': 'KINGofSPACE!W00',
})
# Create a new Account on a Directory instance.
pgraham = directory.accounts.create({
'given_name': 'Paul',
'surname': 'Graham',
'email': '[email protected]',
'username': 'pg',
'password': 'STARTUPSar3th3b3sT!',
})
Once you have a few Accounts created, updating them is equally simple:
pgraham.middle_name = 'Iceman'
pgraham.save()
Now that you have some user Accounts, we'll cover how you can securely check a user's credentials:
from stormpath.error import Error as StormpathError
try:
auth_attempt = application.authenticate_account('pg', 'STARTUPSar3th3b3sT!')
except StormpathError, err:
print 'Human friendly error message:', err.message
print 'Developer friendly error message:', err.developer_message
It's often very useful to be able to reset a user's account password. Doing
this is simple using the send_password_reset_email
method:
application.send_password_reset_email('[email protected]')
In Stormpath, the best way to think about roles and permissions is with Groups. Groups allow you to categorize Accounts, and build complex permissions systems.
Creating a new Group is easy:
directory.groups.create({
'name': 'Administrators',
'description': 'This group holds all Administrator accounts with *full* system access.',
})
To add an Account to a Group, just do the following:
group.add_account(pgraham)
NOTE: An Account may belong to an infinite number of Groups.
One of the newest (and most popular) of Stormpath's features is the ability to store variable data with each Account instance.
The example below stores some custom data:
pgraham.custom_data['favorite_company'] = 'Stormpath'
pgraham.custom_data['millions_invested'] = 99.999999
pgraham.custom_data['billions_acquired'] = 5
pgraham.custom_data['favorite_movie'] = 'The Lion King'
pgraham.custom_data.save()
print 'All custom data:', dict(pgraham.custom_data)
NOTE: None of the custom data entered above is actually saved to Stormpath
until the .save()
method is called.
Below you'll find information on using our Python SDK to accomplish commonly requested tasks.
Most of the work you do with Stormpath is done through the Applications and Directories you have created.
If you know what your
Application or
Directory href
is, you can fetch the instance directly:
application = client.applications.get(application_url)
directory = client.directories.get(directory_url)
The applications
and directories
properties on the client
instance allow
you to iterate through all existing
Applications and
Directories,
respectively.
for application in client.applications:
print '%s (%s)' % (application.name, application.href)
NOTE: If you have a lot of Applications, the above code snippet will take a while to run, as it will iterate through all applications.
There are, of course, other resources available to iterate through, as well!
If you're on a Client instance, you can iterate through the following objects:
-
applications
- Iterate through all Applications. -
directories
- Iterate through all Directories. -
tenant
- A single link to your current Tenant.
If you're on an Application instance, you can iterate through the following objects:
-
accounts
- Iterate through all Accounts. -
groups
- Iterate through all Groups. -
tenant
- A single link to your current Tenant.
If you're on a Directory instance, you can iterate through the following objects:
-
accounts
- Iterate through all Accounts. -
groups
- Iterate through all Groups. -
tenant
- A single link to your current Tenant.
If you're on a Group instance, you can iterate through the following objects:
When creating new Accounts in Stormpath, you have several options.
There are only 4 required fields for each new Account you create:
given_name
- The user's first name.surname
- The user's last name.email
- The user's email address.password
- The user's plain text password -- this will be hashed and securely stored when sent to Stormpath.
There are several other optional fields which can be used:
middle_name
- The user's middle name.status
- The user's status (can be one of: 'enabled', 'disabled', 'unverified').custom_data
- A dictionary of custom user data (up to 10MB, per user).username
- A username.
If you have custom Stormpath workflows configured (rules that say what passwords
are allowed, if email verification is required, etc.), you can optionally choose
to create a new user account and skip applying these workflow rules by using
the registration_workflow_enabled
flag:
# This example will skip over the normal workflow you've got configured, and
# just create the user.
account = directory.accounts.create({
'given_name': 'Michael',
'surname': 'Bay',
'middle_name': 'BOOM!',
'email': '[email protected]',
'password': 'ILOVE3xpl0si0ns!!!!!',
}, registration_workflow_enabled=False)
If the Directory
has been configured with an email verification workflow and a non-Stormpath
URL, you have to pass the verification token sent to the URL in a sptoken
query parameter back to Stormpath to complete the workflow. This is done
through the verify_email_token
on the accounts
collection.
When you authenticate users, you can provide either the username
OR email
,
and password
fields. This way you can accept registration using only email
and password, username and password, or email, username, and password.
When users are successfully authenticated, an AuthenticationResult
object will
be return, with the
Account attached.
To check for successful authentication, you should do something like the following:
from stormpath.error import Error as StormpathError
try:
account = application.authenticate_account('username_or_email',
'password').account
except StormpathError, err:
print 'Human friendly error message:', err.message
print 'Developer friendly error message:', err.developer_message
except Exception, err:
print 'Something unexpected happened:', err
A password reset workflow, if configured on the
Directory the
Account is registered
on, can be kicked off with the send_password_reset_email
method on an
Application:
application.send_password_reset_email('[email protected]')
If the workflow has been configured to verify through a non-Stormpath URL, you
can verify the token sent in the query parameter sptoken
with the
verify_password_reset_token
method on the
Application.
With the Account acquired you can then update the password:
account.password = new_password
account.save()
NOTE: Confirming a new password is left up to the web application code calling the Stormpath SDK. The SDK does not require confirmation.
Memberships of Accounts
in certain Groups can be
used as an authorization mechanism. As the groups
collection property on an
Account instance is
iterable, you can use any of that module's methods to determine if an
Account belongs to a
specific Group.
You can create Groups and assign them to Accounts using the Stormpath web console, or programmatically
Creating a Group is
easy, just call the create
method from your
Directory instance:
group = directory.groups.create({'name': 'Administrators'})
Group membership can be created by:
-
Explicitly creating a GroupMembership resource with your client:
group_memebership = client.group_memberships.create(group, account)
-
Using the
add_group
method on the Account instance:account.add_group(group)
-
Using the
add_account
method on the Group instance:group.add_account(account)
Groups and Accounts have CustomData fields that act as a dictionary:
-
Accessing custom data field:
print account.custom_data['favorite_color'] print group.custom_data['favorite_api_company']
-
Creating or updating a CustomData field:
account.custom_data['rank'] = 'Captain' account.custom_data.save() group.custom_data['affiliation'] = 'NCC-1701' group.custom_data.save()
-
Deleting a CustomData field:
del account.custom_data['rank'] del group.custom_data['affiliation']
-
Saving CustomData changes (creates, updates and deletes) to Stormpath only take place when
save()
is explicitly called.account.custom_data.save() group.custom_data.save()
OR
account.save() group.save()
The Stormpath Python SDK is well tested. Don't take our word on it though, run our test suite and see for yourself!
We currently test against Python 2.7, 3.2 and 3.3.
The simplest way is to run the test suite is to install tox. Tox automatically tests the code on multiple versions of Python by creating virtualenvs.
To get started, installed tox
:
$ pip install tox
There is a tox.ini
file in the root folder of Stormpath SDK. You can modify
it to suit your needs, then run:
$ tox
To run the test suite.
Running a single environment (Python 2.7)
$ tox -e py27
Running a single test in that environment:
$ tox -e py27 -- -k test_name_of_the_test
What is common in all tests is that our setup.py
uses
pytest to run tests and the tests themselves use
HTTPretty
with unittest
. Python mock
is also (sometimes) used, but in
Python 3.3, mock
became part of the unittest
module so you don't have to
install it if you're using Python 3.3. The tests make sure the correct module
is used.
To install those dependencies manually, there is a testdep
command that
checks the Python version and installs required packages accordingly:
$ python setup.py testdep
To run tests:
$ python setup.py test
All of the above methods use mock
and HTTPretty
and don't query Stormpath.
That makes them fast and self-reliant. If you want to run tests that don't
patch any of the methods, you have to set the following environment variables
to working Stormpath credentials:
$ export STORMPATH_API_KEY_ID=YOUR_APIKEY_ID
$ export STORMPATH_API_KEY_SECRET=YOUR_APIKEY_SECRET
To run the live tests against the Stormpath service, you can then run:
$ python setup.py livetest
WARNING: Since the tests make live changes to Stormpath data, DO NOT run these tests in a production environment!
You can make your own contributions by forking the development
branch of this
repository, making your changes, and issuing pull requests on the development
branch.
We regularly maintain our GitHub repostiory, and are quick about reviewing pull requests and accepting changes!
To build and install the development branch yourself, you can do the following:
$ git clone [email protected]:stormpath/stormpath-sdk-python.git
$ cd stormpath-sdk-python
$ python setup.py develop # If you want to install the package for development.
To generate our Sphinx documentation, you'll need to
first install sphinx
:
$ pip install sphinx
Next, you'll want to run:
$ python setup.py docs
To build the HTML documentation. You can then open your browser and navigate to
docs/_build/html/index.html
, which should open the fully built HTML
documentation!
+-------------+
| Application |
| |
+-------------+
+ 1
|
| +------------------------+
| | AccountStore |
o- - - - | |
| +------------------------+
| ^ 1..*
| |
| |
| +---------OR---------+
| | |
| | |
v 0..* 1 + + 1
+---------------------+ +--------------+
| Directory | 1 1 | Group |1
| |<----------+| |+----------+
| | | | |
| | 1 0..* | |0..* |
| |+---------->| |<-----+ |
| | +--------------+ | | +-----------------+
| | | | | GroupMembership |
| | o- - o - - - - | |
| | +--------------+ | | +-----------------+
| | 1 0..* | Account |1 | |
| |+---------->| |+-----+ |
| | | | |
| | 1 1 | |0..* |
| |<----------+| |<----------+
+---------------------+ +--------------+
Copyright © 2012, 2013, 2014 Stormpath, Inc. and contributors.
This project is licensed under the Apache 2.0 Open Source License.
For additional information, please see the full Project Documentation.