singularity 0.0.7 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/singularity.rb CHANGED
@@ -1,9 +1,7 @@
1
1
  require 'rubygems'
2
- require 'redis_hash'
2
+ require 'redis'
3
3
  require 'securerandom'
4
4
 
5
- require_relative 'global_user'
6
-
7
5
  module Singularity
8
6
  def self.cattr_accessor(attribute, default = nil)
9
7
  class_variable_set("@@#{attribute}", default)
@@ -18,10 +16,10 @@ module Singularity
18
16
  EOS
19
17
  end
20
18
 
21
- #cattr_accessor :user_class, IG::User::GlobalUser
22
19
  cattr_accessor :auth_path, "http://authmaster.local:9292/api"
23
20
  cattr_accessor :master, false
24
21
  cattr_accessor :session_prefix, "rack_session"
22
+ cattr_accessor :profile_prefix, "profile"
25
23
  cattr_accessor :token_prefix, "singularity_session_token"
26
24
  cattr_accessor :warden_user_key, "warden.user.default.key"
27
25
 
@@ -80,9 +78,7 @@ module Singularity
80
78
  end
81
79
 
82
80
  def redis
83
- @@redis ||= Redis::NativeHash.redis # JUNK
81
+ @redis ||= Redis.current
84
82
  end
85
83
  end
86
84
  end
87
-
88
- require 'singularity/engine' if defined?(Rails)
@@ -19,12 +19,13 @@ module Singularity
19
19
  digest_algorithm.length
20
20
  end
21
21
 
22
- def self.generate(password, salt)
23
- OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest_algorithm).unpack('H*').first
22
+ def self.generate(password, salt, options = {})
23
+ iters = options.fetch(:iterations, iterations)
24
+ OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iters, key_length, digest_algorithm).unpack('H*').first
24
25
  end
25
26
 
26
- def matches?(password, salt)
27
- Rack::Utils.secure_compare digest, self.class.generate(password, salt)
27
+ def matches?(password, salt, options = {})
28
+ Rack::Utils.secure_compare digest, self.class.generate(password, salt, options)
28
29
  end
29
30
  end
30
- end
31
+ end
@@ -0,0 +1,66 @@
1
+ require 'redis/hash_key'
2
+ require 'forwardable'
3
+ require 'singularity/password_hash'
4
+
5
+ module Singularity
6
+ class Profile
7
+ extend Forwardable
8
+
9
+ attr_reader :prefix, :identity, :redis
10
+
11
+ def initialize(identity, options = {})
12
+ raise ArgumentError, "identity must be present" unless identity.to_s.length > 0
13
+
14
+ @prefix = options.fetch(:prefix) { Singularity.profile_prefix }
15
+ @identity = identity
16
+ @redis = options.fetch(:redis) { Redis.current }
17
+ end
18
+
19
+ def_delegators :profile, :[], :[]=
20
+ def_delegators :profile, :all, :bulk_get, :bulk_set, :each, :empty?, :fetch
21
+ def_delegators :profile, :has_key?, :include?, :incr, :incrby, :key, :key?
22
+ def_delegators :profile, :keys, :member?, :vals, :values, :clear
23
+
24
+ def password_hash
25
+ self['password_hash']
26
+ end
27
+
28
+ def password_hash=(value)
29
+ self['password_hash'] = value
30
+ end
31
+
32
+ def password_salt
33
+ self['password_salt']
34
+ end
35
+
36
+ def password_salt=(value)
37
+ self['password_salt'] = value
38
+ end
39
+
40
+ def password_iterations
41
+ iters = self['password_iterations'].to_i
42
+ iters = PasswordHash.iterations unless iters > 0
43
+
44
+ iters
45
+ end
46
+
47
+ def password_iterations=(value)
48
+ self['password_iterations'] = value
49
+ end
50
+
51
+ def password?(value)
52
+ return false unless password_hash.to_s.length > 0
53
+ return false unless password_salt.to_s.length > 0
54
+
55
+ PasswordHash.new(password_hash).matches?(value, password_salt, iterations: password_iterations)
56
+ end
57
+
58
+ private
59
+
60
+ def profile
61
+ @profile ||= Redis::HashKey.new("#{ prefix }:#{ identity }", marshal: true).tap do |hash|
62
+ hash.instance_variable_set(:@redis, redis)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,3 +1,3 @@
1
1
  module Singularity
2
- VERSION = "0.0.7"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ require 'singularity'
3
+ require 'singularity/profile'
4
+
5
+ describe Singularity::Profile do
6
+ let(:redis) { MockRedis.new }
7
+
8
+ subject { Singularity::Profile.new('me@example.com', redis: redis) }
9
+
10
+ it "provides #password_hash" do
11
+ expect{ subject.password_hash = 'foobar' }.to_not raise_error
12
+ expect( subject.password_hash ).to eq 'foobar'
13
+ end
14
+
15
+ it "provides #password_salt" do
16
+ expect{ subject.password_salt = 'baz' }.to_not raise_error
17
+ expect( subject.password_salt ).to eq 'baz'
18
+ end
19
+
20
+ it "provides #password_iterations" do
21
+ expect{ subject.password_iterations = 15_000 }.to_not raise_error
22
+ expect( subject.password_iterations ).to eq 15_000
23
+ end
24
+
25
+ it "password_iterations returns default iterations if zero" do
26
+ subject.password_iterations = 0
27
+ expect( subject.password_iterations ).to eq Singularity::PasswordHash.iterations
28
+ end
29
+
30
+ it "passes through [] methods to profile hash" do
31
+ subject['foo'] = 'bar'
32
+ expect( redis.hget('profile:me@example.com', 'foo') ).to eq 'bar'
33
+ end
34
+
35
+ it "provides #password?" do
36
+ subject.password_salt = SecureRandom.hex(32)
37
+ subject.password_iterations = 25_000
38
+ subject.password_hash = Singularity::PasswordHash.generate('passw0rd', subject.password_salt, iterations: 25_000)
39
+
40
+ expect( subject.password?('passw0rd') ).to be true
41
+ end
42
+ end
@@ -0,0 +1,19 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
18
+
19
+ require 'mock_redis'
metadata CHANGED
@@ -1,7 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: singularity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.1.0
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Carl Zulauf
@@ -9,11 +10,12 @@ authors:
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2013-12-07 00:00:00.000000000 Z
13
+ date: 2015-11-18 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
- name: redis-native_hash
16
+ name: redis-objects
16
17
  requirement: !ruby/object:Gem::Requirement
18
+ none: false
17
19
  requirements:
18
20
  - - ! '>='
19
21
  - !ruby/object:Gem::Version
@@ -21,108 +23,113 @@ dependencies:
21
23
  type: :runtime
22
24
  prerelease: false
23
25
  version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
24
27
  requirements:
25
28
  - - ! '>='
26
29
  - !ruby/object:Gem::Version
27
30
  version: '0'
28
31
  - !ruby/object:Gem::Dependency
29
- name: railties
32
+ name: bundler
30
33
  requirement: !ruby/object:Gem::Requirement
34
+ none: false
31
35
  requirements:
32
36
  - - ! '>='
33
37
  - !ruby/object:Gem::Version
34
- version: '3.1'
35
- type: :runtime
38
+ version: '0'
39
+ type: :development
36
40
  prerelease: false
37
41
  version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
38
43
  requirements:
39
44
  - - ! '>='
40
45
  - !ruby/object:Gem::Version
41
- version: '3.1'
46
+ version: '0'
42
47
  - !ruby/object:Gem::Dependency
43
- name: jquery-rails
48
+ name: pry
44
49
  requirement: !ruby/object:Gem::Requirement
50
+ none: false
45
51
  requirements:
46
52
  - - ! '>='
47
53
  - !ruby/object:Gem::Version
48
54
  version: '0'
49
- type: :runtime
55
+ type: :development
50
56
  prerelease: false
51
57
  version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
52
59
  requirements:
53
60
  - - ! '>='
54
61
  - !ruby/object:Gem::Version
55
62
  version: '0'
56
63
  - !ruby/object:Gem::Dependency
57
- name: bundler
64
+ name: rspec
58
65
  requirement: !ruby/object:Gem::Requirement
66
+ none: false
59
67
  requirements:
60
68
  - - ~>
61
69
  - !ruby/object:Gem::Version
62
- version: 1.0.0
70
+ version: '2.3'
63
71
  type: :development
64
72
  prerelease: false
65
73
  version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
66
75
  requirements:
67
76
  - - ~>
68
77
  - !ruby/object:Gem::Version
69
- version: 1.0.0
78
+ version: '2.3'
70
79
  - !ruby/object:Gem::Dependency
71
- name: rspec
80
+ name: mock_redis
72
81
  requirement: !ruby/object:Gem::Requirement
82
+ none: false
73
83
  requirements:
74
- - - ~>
84
+ - - ! '>='
75
85
  - !ruby/object:Gem::Version
76
- version: 2.3.0
86
+ version: '0'
77
87
  type: :development
78
88
  prerelease: false
79
89
  version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
80
91
  requirements:
81
- - - ~>
92
+ - - ! '>='
82
93
  - !ruby/object:Gem::Version
83
- version: 2.3.0
94
+ version: '0'
84
95
  description: Singularity helps you keep users logged in across many domains
85
96
  email:
86
- - czulauf@lyconic.com
87
97
  - alassek@lyconic.com
88
98
  executables: []
89
99
  extensions: []
90
100
  extra_rdoc_files: []
91
101
  files:
92
- - .gitignore
93
- - Gemfile
94
- - Gemfile.lock
95
102
  - LICENSE
96
- - Rakefile
97
- - lib/assets/javascripts/singularity.js.erb
98
- - lib/global_user.rb
99
- - lib/singularity.rb
100
- - lib/singularity/engine.rb
101
103
  - lib/singularity/password_hash.rb
104
+ - lib/singularity/profile.rb
102
105
  - lib/singularity/version.rb
103
- - singularity.gemspec
106
+ - lib/singularity.rb
107
+ - spec/singularity/profile_spec.rb
108
+ - spec/spec_helper.rb
104
109
  homepage: https://github.com/lyconic/singularity
105
110
  licenses:
106
111
  - MIT
107
- metadata: {}
108
112
  post_install_message:
109
113
  rdoc_options: []
110
114
  require_paths:
111
115
  - lib
112
116
  required_ruby_version: !ruby/object:Gem::Requirement
117
+ none: false
113
118
  requirements:
114
119
  - - ! '>='
115
120
  - !ruby/object:Gem::Version
116
121
  version: '0'
117
122
  required_rubygems_version: !ruby/object:Gem::Requirement
123
+ none: false
118
124
  requirements:
119
125
  - - ! '>='
120
126
  - !ruby/object:Gem::Version
121
127
  version: '0'
122
128
  requirements: []
123
129
  rubyforge_project:
124
- rubygems_version: 2.1.10
130
+ rubygems_version: 1.8.23.2
125
131
  signing_key:
126
- specification_version: 4
132
+ specification_version: 3
127
133
  summary: multi-domain single sign on
128
134
  test_files: []
135
+ has_rdoc:
checksums.yaml DELETED
@@ -1,15 +0,0 @@
1
- ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- YTZmY2IxNDdmMzIyYTg2YjM2MGMxY2MwNDgwZTI3NjcwMzZiZWMzNQ==
5
- data.tar.gz: !binary |-
6
- MzAyMWEyMjdlNWIzNGYyZGViOTU1ZTE3ZjQyOTg2ZTFkMzcwNTEwMg==
7
- SHA512:
8
- metadata.gz: !binary |-
9
- ODk1MzdmM2FiMzNhMmJjZTk1NWNiZjk4ZDhlZjY3OTIwYjljMTMyMmRlODlm
10
- ZjA0MTQxNjVjZjNhMTVkYTg2ODdlNjVkMDU0ZDc0YTNiMGI4Mjc3M2U2NTQ1
11
- ZGFiOWMxNDgzNzU0YTJlYmU2YmQxNDExN2RkYjZhYzM4MzJkOWE=
12
- data.tar.gz: !binary |-
13
- MTk0MDdmMGVkYjQ5ZmM1ZjJjYjhiNTZjN2UzZjRkODUxNDE0Y2MxODQ0N2Jh
14
- NTVmODBjN2RhNDY2NjMxMTQwMjljMmRhYzk5OTQ1ZDQyZTA1MjcyZTJkNTE3
15
- OTFjYTZkYTJmZjlkMTdjMDIxZThlN2E2YWZlZGJlMTJkYTAxODc=
data/.gitignore DELETED
@@ -1,4 +0,0 @@
1
- pkg/
2
- *.swp
3
- *.swo
4
- .DS_Store
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- gemspec
4
-
data/Gemfile.lock DELETED
@@ -1,82 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- singularity (0.0.2)
5
- jquery-rails (~> 1.0)
6
- railties (~> 3.1)
7
- redis-native_hash
8
-
9
- GEM
10
- remote: http://rubygems.org/
11
- specs:
12
- actionpack (3.1.3)
13
- activemodel (= 3.1.3)
14
- activesupport (= 3.1.3)
15
- builder (~> 3.0.0)
16
- erubis (~> 2.7.0)
17
- i18n (~> 0.6)
18
- rack (~> 1.3.5)
19
- rack-cache (~> 1.1)
20
- rack-mount (~> 0.8.2)
21
- rack-test (~> 0.6.1)
22
- sprockets (~> 2.0.3)
23
- activemodel (3.1.3)
24
- activesupport (= 3.1.3)
25
- builder (~> 3.0.0)
26
- i18n (~> 0.6)
27
- activesupport (3.1.3)
28
- multi_json (~> 1.0)
29
- builder (3.0.0)
30
- diff-lcs (1.1.3)
31
- erubis (2.7.0)
32
- hike (1.2.1)
33
- i18n (0.6.0)
34
- jquery-rails (1.0.19)
35
- railties (~> 3.0)
36
- thor (~> 0.14)
37
- json (1.6.3)
38
- multi_json (1.0.4)
39
- rack (1.3.5)
40
- rack-cache (1.1)
41
- rack (>= 0.4)
42
- rack-mount (0.8.3)
43
- rack (>= 1.0.0)
44
- rack-ssl (1.3.2)
45
- rack
46
- rack-test (0.6.1)
47
- rack (>= 1.0)
48
- railties (3.1.3)
49
- actionpack (= 3.1.3)
50
- activesupport (= 3.1.3)
51
- rack-ssl (~> 1.3.2)
52
- rake (>= 0.8.7)
53
- rdoc (~> 3.4)
54
- thor (~> 0.14.6)
55
- rake (0.9.2.2)
56
- rdoc (3.11)
57
- json (~> 1.4)
58
- redis (2.2.2)
59
- redis-native_hash (0.1.0)
60
- redis (>= 2.0.0)
61
- rspec (2.3.0)
62
- rspec-core (~> 2.3.0)
63
- rspec-expectations (~> 2.3.0)
64
- rspec-mocks (~> 2.3.0)
65
- rspec-core (2.3.1)
66
- rspec-expectations (2.3.0)
67
- diff-lcs (~> 1.1.2)
68
- rspec-mocks (2.3.0)
69
- sprockets (2.0.3)
70
- hike (~> 1.2)
71
- rack (~> 1.0)
72
- tilt (!= 1.3.0, ~> 1.1)
73
- thor (0.14.6)
74
- tilt (1.3.3)
75
-
76
- PLATFORMS
77
- ruby
78
-
79
- DEPENDENCIES
80
- bundler (~> 1.0.0)
81
- rspec (~> 2.3.0)
82
- singularity!
data/Rakefile DELETED
@@ -1,36 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'rubygems'
4
- require 'bundler'
5
- begin
6
- Bundler.setup(:default, :development)
7
- rescue Bundler::BundlerError => e
8
- $stderr.puts e.message
9
- $stderr.puts "Run `bundle install` to install missing gems"
10
- exit e.status_code
11
- end
12
- require 'rake'
13
-
14
- require 'rspec/core'
15
- require 'rspec/core/rake_task'
16
- RSpec::Core::RakeTask.new(:spec) do |spec|
17
- spec.pattern = FileList['spec/**/*_spec.rb']
18
- end
19
-
20
- RSpec::Core::RakeTask.new(:rcov) do |spec|
21
- spec.pattern = 'spec/**/*_spec.rb'
22
- spec.rcov = true
23
- end
24
-
25
- task :default => :spec
26
-
27
- require 'rake/rdoctask'
28
- Rake::RDocTask.new do |rdoc|
29
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
30
-
31
- rdoc.rdoc_dir = 'rdoc'
32
- rdoc.title = "singularity #{version}"
33
- rdoc.rdoc_files.include('README*')
34
- rdoc.rdoc_files.include('lib/**/*.rb')
35
- end
36
-
@@ -1,59 +0,0 @@
1
- var Singularity = (function () {
2
-
3
- this.master_auth = "<%= Singularity.auth_path %>";
4
- this.logged_in = false;
5
- this.session_token = '';
6
-
7
- this.extend = function ( options ) {
8
- jQuery.extend( true, this, options );
9
- }
10
-
11
- this.init = function ( command ) {
12
- if ( this.logged_in === true )
13
- if ( command === 'register' ) {
14
- this.registerMasterAuth();
15
- }
16
-
17
- if ( this.logged_in === false )
18
- if ( command === 'lookup' ) {
19
- this.checkMasterAuth();
20
- }
21
- }
22
-
23
- function login ( result ) {
24
- $(Singularity).trigger(result['logged_in'] ? 'login.success' : 'login.failure')
25
- }
26
-
27
- function register ( result ) {
28
- $(Singularity).trigger(result['logged_in'] ? 'register.success' : 'register.failure');
29
- }
30
-
31
- var xhr, timeout;
32
-
33
- this.checkMasterAuth = function () {
34
- $(this).trigger('lookup');
35
-
36
- var url = this.master_auth + '/lookup?token=' + this.session_token + '&callback=?';
37
- xhr = $.getJSON(url).then(login).fail(function () { $(Singularity).trigger('login.failure') });
38
- timeout = setTimeout(requestTimeout, 5000);
39
-
40
- return xhr;
41
- }
42
-
43
- function requestTimeout () {
44
- if ( xhr.readyState !== 4 ) xhr.abort();
45
- }
46
-
47
- this.registerMasterAuth = function () {
48
- $(this).trigger('register');
49
-
50
- var url = this.master_auth + '/register?token=' + this.session_token + '&callback=?';
51
- return xhr = $.getJSON(url).then(register).fail(function () { $(Singularity).trigger('register.failure') });
52
- }
53
-
54
- $(this).on('login.success', function () { window.location.reload(true) });
55
- $(this).on('login.success login.failure', function () { clearTimeout(timeout) });
56
-
57
- return this;
58
-
59
- }).apply({});
data/lib/global_user.rb DELETED
@@ -1,32 +0,0 @@
1
- require 'singularity/password_hash'
2
-
3
- class GlobalUser < Redis::NativeHash
4
- attr_persist :login, :password_hash
5
-
6
- def initialize(data = {})
7
- super(namespace)
8
- update(data)
9
- end
10
-
11
- def namespace; :global_user; end
12
-
13
- def salt
14
- self[:password_salt] ||= SecureRandom.hex(32)
15
- end
16
-
17
- def password=(value)
18
- self[:password_hash] = Singularity::PasswordHash.generate(value, salt)
19
- end
20
-
21
- def password?(value)
22
- return false unless value.present?
23
- return false unless self[:password_hash].present?
24
- Singularity::PasswordHash.new(self[:password_hash]).matches?(value, salt)
25
- end
26
-
27
- def save
28
- self.key = login unless login.nil?
29
- super
30
- end
31
- end
32
-
@@ -1,5 +0,0 @@
1
- require 'singularity/version'
2
-
3
- module Singularity
4
- class Engine < ::Rails::Engine; end
5
- end
data/singularity.gemspec DELETED
@@ -1,24 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/singularity/version', __FILE__)
3
-
4
- Gem::Specification.new do |s|
5
- s.name = "singularity"
6
- s.homepage = "https://github.com/lyconic/singularity"
7
- s.version = Singularity::VERSION
8
- s.platform = Gem::Platform::RUBY
9
- s.authors = ["Carl Zulauf", "Adam Lassek"]
10
- s.email = ["czulauf@lyconic.com", "alassek@lyconic.com"]
11
- s.summary = "multi-domain single sign on"
12
- s.description = "Singularity helps you keep users logged in across many domains"
13
- s.files = `git ls-files`.split("\n")
14
- s.licenses = ["MIT"]
15
- s.require_path = "lib"
16
-
17
- s.add_dependency "redis-native_hash", ">= 0"
18
- s.add_dependency "railties", ">= 3.1"
19
- s.add_dependency "jquery-rails"
20
-
21
- s.add_development_dependency "bundler", "~> 1.0.0"
22
- s.add_development_dependency "rspec", "~> 2.3.0"
23
- end
24
-