ga_verify 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+ _SELF=File.readlink(__FILE__) rescue __FILE__
3
+ _ROOT=File.expand_path(File.dirname(_SELF) + '/../lib')
4
+ $LOAD_PATH.push _ROOT
5
+
6
+ require 'ga_verify/paths'
7
+ options = {
8
+ :socket => GAVerify::Paths.default_socket,
9
+ }
10
+
11
+ require 'optparse'
12
+
13
+ optparse = OptionParser.new do |opts|
14
+ opts.banner += " USER TOKEN"
15
+ opts.on(
16
+ '-s', '--socket PATH',
17
+ "Default: #{options[:socket]}"
18
+ ) do |path|
19
+ options[:socket] = path
20
+ end
21
+ end
22
+ optparse.parse!
23
+
24
+ if ARGV.size != 2
25
+ STDERR.write optparse.help
26
+ exit 1
27
+ end
28
+ user, token = ARGV[0], ARGV[1].to_i
29
+
30
+ require 'ga_verify/client'
31
+ result = GAVerify::Client.new(options).check_user(user, token)
32
+ puts GAVerify::Result::VALUE_MAP[result]
33
+ exit result
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ _SELF=File.readlink(__FILE__) rescue __FILE__
3
+ _ROOT=File.expand_path(File.dirname(_SELF) + '/../lib')
4
+ $LOAD_PATH.push _ROOT
5
+
6
+ require 'ga_verify/paths'
7
+ options = {
8
+ :socket => GAVerify::Paths.default_socket,
9
+ }
10
+
11
+ require 'optparse'
12
+
13
+ optparse = OptionParser.new do |opts|
14
+ opts.on(
15
+ '-s', '--socket PATH',
16
+ "Default: #{options[:socket]}"
17
+ ) do |path|
18
+ options[:socket] = path
19
+ end
20
+ end
21
+ optparse.parse!
22
+
23
+ require 'ga_verify/server'
24
+ GAVerify::Server.new(options[:socket]).serve!
@@ -0,0 +1,10 @@
1
+ #
2
+ # Autogenerated by Thrift
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ require 'ga-verify_types'
8
+
9
+ module GAVerify
10
+ end
@@ -0,0 +1,18 @@
1
+ #
2
+ # Autogenerated by Thrift
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+
8
+ module GAVerify
9
+ module Result
10
+ SUCCESS = 0
11
+ BAD_TOKEN = 1
12
+ BAD_USER = 2
13
+ NO_GOOGLE_AUTH = 3
14
+ VALUE_MAP = {0 => "SUCCESS", 1 => "BAD_TOKEN", 2 => "BAD_USER", 3 => "NO_GOOGLE_AUTH"}
15
+ VALID_VALUES = Set.new([SUCCESS, BAD_TOKEN, BAD_USER, NO_GOOGLE_AUTH]).freeze
16
+ end
17
+
18
+ end
@@ -0,0 +1,85 @@
1
+ #
2
+ # Autogenerated by Thrift
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ require 'thrift'
8
+ require 'ga-verify_types'
9
+
10
+ module GAVerify
11
+ module Verifier
12
+ class Client
13
+ include ::Thrift::Client
14
+
15
+ def check_user(name, token)
16
+ send_check_user(name, token)
17
+ return recv_check_user()
18
+ end
19
+
20
+ def send_check_user(name, token)
21
+ send_message('check_user', Check_user_args, :name => name, :token => token)
22
+ end
23
+
24
+ def recv_check_user()
25
+ result = receive_message(Check_user_result)
26
+ return result.success unless result.success.nil?
27
+ raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'check_user failed: unknown result')
28
+ end
29
+
30
+ end
31
+
32
+ class Processor
33
+ include ::Thrift::Processor
34
+
35
+ def process_check_user(seqid, iprot, oprot)
36
+ args = read_args(iprot, Check_user_args)
37
+ result = Check_user_result.new()
38
+ result.success = @handler.check_user(args.name, args.token)
39
+ write_result(result, oprot, 'check_user', seqid)
40
+ end
41
+
42
+ end
43
+
44
+ # HELPER FUNCTIONS AND STRUCTURES
45
+
46
+ class Check_user_args
47
+ include ::Thrift::Struct, ::Thrift::Struct_Union
48
+ NAME = 1
49
+ TOKEN = 2
50
+
51
+ FIELDS = {
52
+ NAME => {:type => ::Thrift::Types::STRING, :name => 'name'},
53
+ TOKEN => {:type => ::Thrift::Types::I32, :name => 'token'}
54
+ }
55
+
56
+ def struct_fields; FIELDS; end
57
+
58
+ def validate
59
+ end
60
+
61
+ ::Thrift::Struct.generate_accessors self
62
+ end
63
+
64
+ class Check_user_result
65
+ include ::Thrift::Struct, ::Thrift::Struct_Union
66
+ SUCCESS = 0
67
+
68
+ FIELDS = {
69
+ SUCCESS => {:type => ::Thrift::Types::I32, :name => 'success', :enum_class => GAVerify::Result}
70
+ }
71
+
72
+ def struct_fields; FIELDS; end
73
+
74
+ def validate
75
+ unless @success.nil? || GAVerify::Result::VALID_VALUES.include?(@success)
76
+ raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field success!')
77
+ end
78
+ end
79
+
80
+ ::Thrift::Struct.generate_accessors self
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -0,0 +1,11 @@
1
+ namespace rb GAVerify
2
+ enum Result {
3
+ SUCCESS = 0,
4
+ BAD_TOKEN = 1,
5
+ BAD_USER = 2,
6
+ NO_GOOGLE_AUTH = 3
7
+ }
8
+
9
+ service Verifier {
10
+ Result check_user(1:string name, 2:i32 token);
11
+ }
@@ -0,0 +1 @@
1
+ require 'ga_verify/client'
@@ -0,0 +1,21 @@
1
+ require 'ga_verify/thrift'
2
+ require 'ga_verify/paths'
3
+
4
+ require 'thrift'
5
+
6
+ module GAVerify
7
+ class Client
8
+ def initialize options={}
9
+ options[:socket] ||= GAVerify::Paths.default_socket
10
+ socket = Thrift::UNIXSocket.new(options[:socket])
11
+ transport = Thrift::FramedTransport.new(socket)
12
+ protocol = Thrift::BinaryProtocol.new(transport)
13
+ @client = GAVerify::Verifier::Client.new(protocol)
14
+ transport.open
15
+ end
16
+
17
+ def method_missing *args
18
+ @client.send *args
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,48 @@
1
+ require 'ga_verify/thrift'
2
+ require 'ga_verify/paths'
3
+
4
+ require 'rotp'
5
+
6
+ module GAVerify
7
+ class Handler
8
+ def initialize
9
+ @last_seen = Hash.new(0)
10
+ @used_tokens = Hash.new{Hash.new(0)}
11
+ end
12
+
13
+ def check_user user, token
14
+ unless user =~ /^[a-z]+$/
15
+ return GAVerify::Result::BAD_USER
16
+ end
17
+ unless File.exists? "/home/#{user}"
18
+ return GAVerify::Result::BAD_USER
19
+ end
20
+ path = GAVerify::Paths.config_path(user)
21
+ unless File.exists? path
22
+ return GAVerify::Result::NO_GOOGLE_AUTH
23
+ end
24
+ now = Time.now.to_i
25
+ # Max one login every 15s
26
+ if @last_seen[user] >= now - 15
27
+ return GAVerify::Result::BAD_TOKEN
28
+ end
29
+ @last_seen[user] = now
30
+
31
+ secret = File.open(path, 'r').first.strip
32
+ totp = ROTP::TOTP.new(secret)
33
+
34
+ # Allow +- 1 token
35
+ times = [now - 30, now, now + 30]
36
+ if times.any?{|time| totp.verify(token, time)}
37
+ # disallow token re-use within 10 minutes
38
+ if @used_tokens[user][token] < now - 600
39
+ @used_tokens[user][token] = now
40
+ # Cleanup
41
+ @used_tokens[user].reject!{|k,v| used_tokens[user][token] < now - 600}
42
+ return GAVerify::Result::SUCCESS
43
+ end
44
+ end
45
+ return GAVerify::Result::BAD_TOKEN
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,11 @@
1
+ module GAVerify
2
+ module Paths
3
+ def self.default_socket
4
+ '/var/run/ga_verifyd.sock'
5
+ end
6
+
7
+ def self.config_path user
8
+ "/home/#{user}/.google_authenticator"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ require 'ga_verify/handler'
2
+
3
+ require 'thrift'
4
+
5
+ module GAVerify
6
+ class Server < GAVerify::Handler
7
+ def initialize socket_path
8
+ handler = GAVerify::Handler.new
9
+ processor = GAVerify::Verifier::Processor.new handler
10
+ transport = Thrift::FramedTransportFactory.new
11
+ socket = Thrift::UNIXServerSocket.new(socket_path)
12
+ @server = Thrift::NonblockingServer.new processor, socket, transport
13
+ end
14
+
15
+ def serve!
16
+ @server.serve
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ module GAVerify
2
+ THRIFT_PATH=File.expand_path(File.join(File.dirname(__FILE__), '/../../gen-rb'))
3
+ end
4
+
5
+ $LOAD_PATH.push(GAVerify::THRIFT_PATH)
6
+ require 'verifier'
7
+ require 'ga-verify_constants'
8
+ require 'ga-verify_types'
9
+ $LOAD_PATH.pop
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ga_verify
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Fred Emmott
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-08-01 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rotp
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 1.3.0
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: thrift
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: 0.6.0
35
+ type: :runtime
36
+ version_requirements: *id002
37
+ description: Provides a unix socket for validating tokens
38
+ email:
39
+ - mail@fredemmott.co.uk
40
+ executables:
41
+ - ga-verify
42
+ - ga-verifyd
43
+ extensions: []
44
+
45
+ extra_rdoc_files: []
46
+
47
+ files:
48
+ - bin/ga-verifyd
49
+ - bin/ga-verify
50
+ - lib/ga_verify.rb
51
+ - lib/ga_verify/paths.rb
52
+ - lib/ga_verify/thrift.rb
53
+ - lib/ga_verify/client.rb
54
+ - lib/ga_verify/handler.rb
55
+ - lib/ga_verify/server.rb
56
+ - gen-rb/verifier.rb
57
+ - gen-rb/ga-verify_constants.rb
58
+ - gen-rb/ga-verify_types.rb
59
+ - if/ga-verify.thrift
60
+ homepage: https://github.com/fredemmott/ga-verify
61
+ licenses: []
62
+
63
+ post_install_message:
64
+ rdoc_options: []
65
+
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: "0"
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ requirements: []
81
+
82
+ rubyforge_project:
83
+ rubygems_version: 1.7.2
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Thrift client and server for validating google authenticator tokens
87
+ test_files: []
88
+