singularity 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
-