Super Identity
AAF Identity Enhancement (IdE) client library for Ruby applications.
Copyright 2016, Australian Access Federation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Installation
Add the super-identity
dependency to your application's Gemfile
:
gem 'super-identity'
Use Bundler to install the dependency:
bundle install
Querying IdE from your application
AAF typically uses this library along with rapid-rack or shib-rack,
although it can be used anywhere that you have access to a user's
auEduPersonSharedToken
. This example shows a simple SubjectReceiver
class for rapid-rack which is using SuperIdentity::Client
to query IdE:
class SubjectReceiver
# Include the client mixin, which provides the `identity_enhancements` and
# `entitlements` methods. We'll be using `entitlements`.
include SuperIdentity::Client
# Map attributes from the attribute claim, as normal. In particular note we
# have the 'auedupersonsharedtoken' here, which is needed when querying IdE.
def map_attributes(_env, attrs)
{
shared_token: attrs['auedupersonsharedtoken'],
display_name: attrs['displayname'],
mail: attrs['mail']
}
end
# Provision a Subject based on the attributes, and call the `assign_roles`
# method defined below.
def subject(_env, attrs)
subject = Subject.find_or_initialize_by(shared_token: attrs[:shared_token])
assign_roles(subject)
subject.update!(attrs)
end
# Query IdE, and set the admin flag on the Subject based on the presence of
# the entitlement we're using for authorization.
def assign_roles(subject)
# Here, `values` is an Array of String values which represent the
# entitlements that were returned from IdE.
values = entitlements(subject.shared_token)
# Usually you'd do something more sophisticated, but boolean access control
# works fine in a README :)
subject.admin = values.include?('urn:mace:aaf.edu.au:ide:test-only')
end
# SuperIdentity::Client requires that this method be defined on the including
# class. To access IdE, it needs the hostname and an client keypair. The
# certificate must be issued by https://certs.aaf.edu.au and granted access to
# the ide[.test].aaf.edu.au, otherwise requests will fail.
def ide_config
{
host: 'ide.test.aaf.edu.au',
cert: 'config/api-client.crt',
key: 'config/api-client.key'
}
end
end
Testing code which queries IdE
A test helper is provided which uses webmock to return a set of entitlements which are provided by the test cases.
Example setup in RSpec:
# spec/spec_helper.rb
require 'webmock/rspec'
RSpec.configure do |config|
# Include the `TestStub` mixin, which provides the `stub_ide` method for use
# by your test cases.
config.include SuperIdentity::TestStub
end
The complete test code:
# spec/lib/subject_receiver_spec.rb
RSpec.describe SubjectReceiver do
# The `cert` and `key` files provided here should actually exist in your
# project, though they shouldn't be a real keypair, since you'd typically
# commit these to source control. A self-signed certificate is recommended
# here (see below for more information).
let(:ide_config) do
{ host: 'ide.example.edu', cert: 'spec/api.crt', key: 'spec/api.key' }
end
# Override the real `ide_config` in the class under test.
before { allow(subject).to receive(:ide_config).and_return(ide_config) }
# Empty Rack env, because the code under test isn't using it.
let(:env) { {} }
# User attributes
let(:attrs) do
{
shared_token: shared_token,
display_name: 'Super Identity Test',
mail: '[email protected]'
}
end
context 'when the expected entitlement is present' do
# Define the shared_token and entitlements which will be returned from the
# stubbed web request.
let(:shared_token) { SecureRandom.urlsafe_base64(20) }
let(:entitlements) { ['urn:mace:aaf.edu.au:ide:test-only'] }
before do
# This `stub_ide` method is provided by the `SuperIdentity::TestStub`
# mixin. It takes `shared_token` and `entitlements` keyword arguments, and
# sets up the response payload in webmock.
stub_ide(shared_token: shared_token, entitlements: entitlements)
end
it 'assigns an administrator' do
# Call the `SubjectReceiver`
new_subject = subject.subject(env, attrs)
# The `admin?` predicate will be true if the expected entitlement was
# returned from IdE.
expect(new_subject).to be_admin
end
end
end
If you aren't familiar with generating certiicates, here's a suggested method of
creating the spec/api.crt
and spec/api.key
files required for this test
case.
openssl genrsa -out spec/api.key 512
openssl req -new -x509 -key spec/api.key -out spec/api.crt -subj '/CN=test_api_client/'
Contributing
Refer to GitHub Flow for help contributing to this project.