Skip to content

Commit

Permalink
Merge branch 'feature/nonce' into 'master'
Browse files Browse the repository at this point in the history
Nonce support

See merge request !1
  • Loading branch information
azul committed Jan 25, 2017
2 parents dc8dff4 + 17a3a2e commit cdf5d4b
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 24 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
<a name="0.3.0"></a>
### 0.3.0 (2017-01-24)


#### Features

* Add nonce to authentication ([97bb193](/../../commit/97bb193))

#### API Changes

Ticket version now is 4 to indicate support for nonces.

Server#ticket now takes an arguments hash instead of order arguments.

Replace:
server.ticket user, service, domain
With:
server.ticket user: user, service: service, domain: domain

This allows us to introduce nounce as an option without introducing a
fourth argument.

<a name="0.2.2"></a>
### 0.2.2 (2017-01-13)

Expand Down
15 changes: 9 additions & 6 deletions lib/rbsso/authentication.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
module RbSSO
class Authentication
VERSION = 3
VERSION = 4

class VersionMismatch < ArgumentError
def initialize(version)
super "Version mismatch! Expected: #{VERSION} Got: #{version}."
end
end

attr_reader :user, :service, :domain, :groups, :expires
attr_reader :user, :service, :domain, :groups, :nonce, :expires

def initialize(user:, service:, domain:, groups: [], ttl: 3600, expires: nil)
def initialize(user:, service:, domain:, groups: [], nonce: nil, ttl: 3600, expires: nil)
@user, @service, @domain, @groups = user, service, domain, groups
@nonce = nonce
@expires = expires || (Time.now + ttl).to_i
end

def self.parse(string)
version, user, service, domain, expires, groups = string.split '|'
version, user, service, domain, expires, nonce, groups = string.split '|'
check_version(version)
new user: user,
service: service,
domain: domain,
expires: expires.to_i,
nonce: nonce,
groups: (groups || '').split(',')
end

Expand All @@ -34,15 +36,16 @@ def to_info
end

def content
[VERSION, user, service, domain, expires.to_s, groups.join(',')]
[VERSION, user, service, domain, expires.to_s, nonce, groups.join(',')]
end

def ==(other)
user == other.user &&
service == other.service &&
domain == other.domain &&
groups == other.groups &&
expires == other.expires
expires == other.expires &&
nonce == other.nonce
end

def expired?
Expand Down
9 changes: 8 additions & 1 deletion lib/rbsso/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ def initialize(expected, was)
end
end

class NonceMismatch < RuntimeError
def initialize(expected, was)
super "Ticket nonce '#{was}' differs from session nonce '#{expected}'."
end
end

def initialize(service, key)
if !key || key !~ /[0-9a-f]{64}/i
raise ArgumentError, "key MUST be 32 bytes, hex encoded string, was: #{key}"
Expand All @@ -25,11 +31,12 @@ def initialize(service, key)
@verify_key = key
end

def open(ticket_string)
def open(ticket_string, nonce: nil)
ticket = RbSSO::Ticket.open ticket_string, verify_key
auth = RbSSO::Authentication.parse ticket.content
raise TicketExpired.new(auth.expires) if auth.expired?
raise WrongService.new(service, auth.service) if auth.service != service
raise NonceMismatch.new(nonce, auth.nonce) if auth.nonce != nonce
auth.to_info
end

Expand Down
5 changes: 3 additions & 2 deletions lib/rbsso/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ def initialize(secret)
@key = RbNaCl::SigningKey.new seed_binary
end

def ticket(user, service, domain)
def ticket(user:, service:, domain:, nonce: nil)
auth = RbSSO::Authentication.new user: user,
service: service,
domain: domain
domain: domain,
nonce: nonce
ticket = RbSSO::Ticket.sign auth, key
return ticket.to_base64
end
Expand Down
2 changes: 1 addition & 1 deletion rbsso.gemspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = 'rbsso'
s.version = '0.2.2'
s.version = '0.3.0'
s.licenses = ['MIT']
s.summary = "Ruby implementation for ai's libsso"
s.description = <<-EODESC
Expand Down
26 changes: 21 additions & 5 deletions test/rbsso/authentication_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,36 @@ def test_to_s
assert_match /^3|user|service|domain|\d*|$/, auth.to_s
end

def test_to_s_with_nonce
assert_match /^3|user|service|domain|\d*|nonce|$/,
auth_with_nonce.to_s
end

def test_parse
parsed = RbSSO::Authentication.parse auth.to_s
assert_equal auth, parsed
end

def test_parse_with_nonce
parsed = RbSSO::Authentication.parse auth_with_nonce.to_s
assert_equal auth_with_nonce, parsed
end

def test_invalid_version
assert_raises RbSSO::Authentication::VersionMismatch do
RbSSO::Authentication.parse '4|other versions may contain other auth'
RbSSO::Authentication.parse '2|other versions may contain other auth'
end
end

def auth
RbSSO::Authentication.new user: 'user',
service: 'service',
domain: 'domain'
def auth(user: 'user', service: 'service', domain: 'domain', nonce: nil)
RbSSO::Authentication.new user: user,
service: service,
domain: domain,
nonce: nonce
end

def auth_with_nonce
auth nonce: 'nonce'
end

end
22 changes: 19 additions & 3 deletions test/rbsso/client_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,34 @@ def test_check_key_content
end

def test_open_ticket
auth = client.open(current_ticket_string)
assert auth
end

def test_open_ticket_with_nonce
auth = client.open(current_ticket_with_nonce_string, nonce: 'nonce')
assert auth
end

def test_reject_expired_ticket
assert_raises Client::TicketExpired do
client.open(static_ticket_string)
client.open(expired_ticket_string)
end
end

def test_reject_ticket
def test_reject_wrong_service
assert_raises Client::WrongService do
client(service: 'other_service').open(current_ticket_string)
end
end

def client(service: 'service/', key: nil)
def test_reject_missing_nonce
assert_raises Client::NonceMismatch do
client.open(current_ticket_string, nonce: 'nonce')
end
end

def client(service: 'service', key: nil)
Client.new(service, key || verify_key_hex)
end

Expand Down
7 changes: 5 additions & 2 deletions test/rbsso/integration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ class IntegrationTest < Minitest::Test
def test_server_client_flow
# server
server = RbSSO::Server.new seed
ticket = server.ticket("user", "service/", "domain")
ticket = server.ticket user: "user",
service: "service/",
domain: "domain",
nonce: "nonce"

# client
client = RbSSO::Client.new 'service/', server.verify_key
info = client.open(ticket)
info = client.open(ticket, nonce: "nonce")
assert_equal 'user', info[:name]
assert_equal 'user@domain', info[:email]
end
Expand Down
8 changes: 7 additions & 1 deletion test/rbsso/server_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ def test_check_seed

def test_ticket_content
assert_match /3|user|service|domain|14/,
Base64.urlsafe_decode64(server.ticket(user, service, domain))
Base64.urlsafe_decode64(ticket)
end

def ticket
server.ticket user: user,
service: service,
domain: domain
end

def server
Expand Down
26 changes: 23 additions & 3 deletions test/test_data.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
module TestData
def static_content
'3|user|service|domain|1483964492|'
def expired_content
"4|user|service|domain|#{Time.now.to_i - 1000}|"
end

def current_content
"3|user|service|domain|#{Time.now.to_i}|"
"4|user|service|domain|#{Time.now.to_i}|"
end

def current_content_with_nonce
"4|user|service|domain|#{Time.now.to_i}|nonce|"
end

def signing_key
Expand All @@ -28,7 +32,23 @@ def current_ticket_string
RbSSO::Ticket.sign(current_content, signing_key).to_base64
end

def expired_ticket_string
RbSSO::Ticket.sign(expired_content, signing_key).to_base64
end

def current_ticket_with_nonce_string
RbSSO::Ticket.sign(current_content_with_nonce, signing_key).to_base64
end

# This is outdated content but we continue to use it to
# test the signing and base64 encoding of the tickets.

def static_content
'3|user|service|domain|1483964492|'
end

def static_ticket_string
"loFbFifM6T_WJfe8D9Jyr80KXWxnBYNeJUoUA2PiSZi-Q_zSbFNu6gI-ujcDHTOq90GivY5nngTDz94C4zpgDjN8dXNlcnxzZXJ2aWNlfGRvbWFpbnwxNDgzOTY0NDkyfA=="
end

end

0 comments on commit cdf5d4b

Please sign in to comment.