Stormpath is the first easy, secure user management and authentication service for developers. This is the Ruby SDK to ease integration of its features with any Ruby language based application.
$ gem install stormpath-sdk --pre
If you have not already done so, register as a developer on Stormpath and set up your API credentials and resources:
-
Create a Stormpath developer account and create your API Keys downloading the
apiKey.properties
file into a.stormpath
folder under your local home directory. -
Through the Stormpath Admin UI, create yourself an Application Resource. On the Create New Application screen, make sure the "Create a new directory with this application" box is checked. This will provision a Directory Resource along with your new Application Resource and link the Directory to the Application as a Login Source. This will allow users associated with that Directory Resource to authenticate and have access to that Application Resource.
It is important to note that although your developer account comes with a built-in Application Resource (called "Stormpath") - you will still need to provision a separate Application Resource.
-
Take note of the REST URL of the Application you just created. Your web application will communicate with the Stormpath API in the context of this one Application Resource (operations such as: user-creation, authentication, etc.)
-
Require the Stormpath Ruby SDK
require 'stormpath-sdk'
-
Create a client using the API key properties file
client = Stormpath::Client.new api_key_file_location: File.join(ENV['HOME'], '.stormpath', 'apiKey.properties')
-
List all your applications and directories
client.applications.each do |application| p "Application: #{application.name}" end client.directories.each do |directory| p "Directory: #{directory.name}" end
-
Get access to the specific application and directory using the URLs you acquired above.
application = client.applications.get application_url directory = client.directories.get directory_url
-
Create an account for a user on the directory.
account = directory.accounts.create({ given_name: 'John', surname: 'Smith', email: 'john.smith@example.com', username: 'johnsmith', password: '4P@$$w0rd!' })
-
Update an account
account.given_name = 'Johnathan' account.middle_name = 'A.' account.save
-
Authenticate the Account for use with an application:
auth_request = Stormpath::Authentication::UsernamePasswordRequest.new 'johnsmith', '4P@$$w0rd!' begin auth_result = application.authenticate_account auth_request account = auth_result.account rescue Stormpath::Error => e #If credentials are invalid or account doesn't exist end
-
Send a password reset request
application.send_password_reset_email 'john.smith@example.com'
-
Create a group in a directory
directory.groups.create name: 'Admins'
-
Add the account to the group
group.add_account account
-
Check for account inclusion in group by reloading the account
account = client.accounts.get account.href is_admin = account.groups.any? { |group| group.name == 'Admins' }
All Stormpath features are accessed through a
Stormpath::Client
instance, or a resource
created from one. A client needs an API key (made up of an id and a
secret) from your Stormpath developer account to manage resources
on that account. That API key can be specified any number of ways
in the hash of values passed on Client initialization:
-
The location of API key properties file:
client = Stormpath::Client.new api_key_file_location: '/some/path/to/apiKey.properties'
You can even identify the names of the properties to use as the API key id and secret. For example, suppose your properties was:
foo=APIKEYID bar=APIKEYSECRET
You could load it with the following:
client = Stormpath::Client.new api_key_file_location: '/some/path/to/apiKey.properties', api_key_id_property_name: 'foo', api_key_secret_property_name: 'bar'
-
Passing in a Stormpath::APIKey instance:
api_key = Stormpath::ApiKey.new api_id, api_secret client = Stormpath::Client.new api_key: api_key
-
By explicitly setting the API key id and secret:
client = Stormpath::Client.new api_key: { id: api_id, secret: api_secret }
-
By passing a composite application url to
Application.load
:composite_url = "http://#{api_key_id}:#{api_key_secret}@api.stormpath.com/v1/applications/#{application_id}" application = Stormpath::Resource::Application.load composite_url client = application.client
Most of the work you do with Stormpath is done through the applications and directories you have registered. You use the client to access them with their REST URL:
application = client.applications.get application_url
directory = client.directories.get directory_url
The applications
and directories
property on a
client instance are also Enumerable
allowing you to iterate
and scan for resources via that interface.
Additional resources are accounts
, groups
,
group_membership
, and the single reference to your
tenant
.
Applications and directories can be created directly off the client.
application = client.applications.create name: 'foo', description: 'bar'
directory = client.directories.create name: 'foo', description: 'bar'
Resource collections can be searched by a general query string or by attribute.
Passing a string to the search method will filter by any attribute on the collection:
client.applications.search 'foo'
To search a specific attribute or attributes, pass a hash:
client.applications.search name: 'foo', description: 'bar'
Collections can be paginated using chainable Arel-like methods. offset
is the zero-based starting index in the entire collection of the first item to return. Default is 0. limit
is the maximum number of collection items to return for a single request. Minimum value is 1. Maximum value is 100. Default is 25.
client.applications.offset(10).limit(100).each do |application|
# do something
end
Collections can be ordered. In the following example, a paginated collection is ordered.
client.applications.offset(10).limit(100).order('name asc,description desc')
A resource's children can be eager loaded by passing the entity expansion object as the second argument to a call to get
.
expansion = Stormpath::Resource::Expansion.new 'groups', 'group_memberships'
client.accounts.get account.href, expansion
limit
and offset
can be specified for each child resource by calling add_property
.
expansion = Stormpath::Resource::Expansion.new
expansion.add_property 'groups', offset: 5, limit: 10
client.accounts.get account.href, expansion
Accounts are created on a directory instance. They can be created in two ways:
-
With the
create_account
method:account = directory.create_account({ given_name: 'John', surname: 'Smith', email: 'john.smith@example.com', username: 'johnsmith', password: '4P@$$w0rd!' })
This metod can take an additional flag to indicate if the account can skip any registration workflow configured on the directory.
## Will skip workflow, if any account = directory.create_account account_props, false
-
Creating it directly on the
accounts
collection property on the directory:account = directory.accounts.create({ given_name: 'John', surname: 'Smith', email: 'john.smith@example.com', username: 'johnsmith', password: '4P@$$w0rd!' })
Both these methods can take either a Hash
of the account
properties, or a Stormpath::Account
.
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.
For example, suppose you have a Sinatra application
that is handling the email verification at the path
/users/verify
. You could use the following code:
get '/users/verify' do
token = params[:sptoken]
account = client.accounts.verify_email_token token
#proceed to update session, display account, etc
end
Authentication is accomplished by passing a username or an email and a
password to authenticate_account
of an application we've
registered on Stormpath. This will either return a
Stormpath::Authentication::AuthenticationResult
instance if
the credentials are valid, or raise a Stormpath::Error
otherwise. In the former case, you can get the account
associated with the credentials.
auth_request =
Stormpath::Authentication::UsernamePasswordRequest.new 'johnsmith', '4P@$$w0rd!'
begin
auth_result = application.authenticate_account auth_request
account = auth_result.account
rescue Stormpath::Error => e
#If credentials are invalid or account doesn't exist
end
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 'john.smith@example.com'
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.
For example, suppose you have a Sinatra application that is verifying the tokens. You use the following to carry it out:
get '/users/verify' do
token = params[:sptoken]
account = application.verify_password_reset_token token
#proceed to update session, display account, etc
end
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 Enumerable
, you can use any of
that module's methods to determine if an account belongs to a specific
group:
account.groups.any? {|group| group.name == 'administrators'}
You can create groups and assign them to accounts using the Stormpath web console, or programmatically. Groups are created on directories:
group = directory.groups.create name: 'administrators'
Group membership can be created by:
-
Explicitly creating a group membership resource with your client:
group_membership = client.group_memberships.create group: group, account: 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
You will need to reload the account or group resource after these operations to ensure they've picked up the changes.
Account and Group resources have predefined fields that are useful to many applications, but you are likely to have your own custom data that you need to associate with an account or group as well.
For this reason, both the account and group resources support a linked custom_data resource that you can use for your own needs.
Set Custom Data
account = Stormpath::Resource::Account.new({ email: "test@example.com", given_name: 'Ruby SDK', password: 'P@$$w0rd', surname: 'SDK',})
account.custom_data["rank"] = "Captain"
account.custom_data["birth_date"] = "2305-07-13"
account.custom_data["birth_place"] = "La Barre, France"
directory.create_account account
Notice how we did not call account.custom_data.save - creating the account (or updating it later via save) will automatically persist the account's customData resource. The account 'knows' that the custom data resource has been changed and it will propogate those changes automatically when you persist the account.
Groups work the same way - you can save a group and it's custom data resource will be saved as well.
Delete a specific Custom Data field
account.custom_data["birth_date"] #=> "2305-07-13"
account.custom_data.delete("birth_date")
account.custom_data.save
Delete all Custom Data
account.custom_data.delete
The functional tests of the SDK run against a Stormpath tenant. In that account, create:
- An application reserved for testing.
- A directory reserved for test accounts. Be sure to associate this directory to the test application as a login source.
- Another directory reserved for test accounts with the account verification workflow turned on. Be sure to associate this directory to the test application as a login source.
The following environment variables need will then need to be set:
STORMPATH_SDK_TEST_API_KEY_ID
- Theid
from your Stormpath API key.STORMPATH_SDK_TEST_API_KEY_SECRET
- Thesecret
from your Stormpath API key.STORMPATH_SDK_TEST_APPLICATION_URL
- The URL to the application created above.STORMPATH_SDK_TEST_DIRECTORY_URL
- The URL to the first directory created above.STORMPATH_SDK_TEST_DIRECTORY_WITH_VERIFICATION_URL
- The URL to the second directory created above.
Once properly configured, the tests can be run as the default
Rake
task:
$ rake
Or by specifying the spec
task:
$ rake spec
Or through rspec
You can make your own contributions by forking the development
branch, making your changes, and issuing pull-requests on the
development
branch.
To build and install the development branch yourself from the latest source:
$ git clone git@github.com:stormpath/stormpath-sdk-ruby.git
$ cd stormpath-sdk-ruby
$ rake gem
$ gem install pkg/stormpath-sdk-{version}.gem
+-------------+
| Application |
| |
+-------------+
+ 1
|
| +-------------+
| | LoginSource |
o- - - - - -| |
| +-------------+
|
v 0..*
+--------------+ +--------------+
| Directory | 1 1 | Account |1
| |<----------+| |+----------+
| | | | |
| | 1 0..* | |0..* |
| |+---------->| |+-----+ |
| | +--------------+ | | +-----------------+
| | | | | GroupMembership |
| | o- - o - - - - | |
| | +--------------+ | | +-----------------+
| | 1 0..* | Group |1 | |
| |+---------->| |<-----+ |
| | | | |
| | 1 1 | |0..* |
| |<----------+| |<----------+
+--------------+ +--------------+
Copyright © 2013 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.