oplop 0.0.1
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/.gitignore +5 -0
- data/Gemfile +4 -0
- data/README.md +8 -0
- data/Rakefile +9 -0
- data/bin/oplop +48 -0
- data/lib/oplop.rb +36 -0
- data/lib/oplop/cli.rb +42 -0
- data/lib/oplop/version.rb +3 -0
- data/oplop.gemspec +26 -0
- data/spec/oplop_spec.rb +34 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/testdata.json +51 -0
- metadata +113 -0
data/Gemfile
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
data/bin/oplop
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
oplop_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
$LOAD_PATH.unshift(oplop_dir) unless $LOAD_PATH.include?(oplop_dir)
|
4
|
+
require 'oplop'
|
5
|
+
require 'oplop/cli'
|
6
|
+
require 'highline/import'
|
7
|
+
|
8
|
+
|
9
|
+
if ARGV.first =~ /\-help|\?/ or ARGV.empty?
|
10
|
+
puts Oplop::Cli.help
|
11
|
+
exit 0
|
12
|
+
end
|
13
|
+
|
14
|
+
new_or_label = ARGV.shift
|
15
|
+
mode = :query
|
16
|
+
|
17
|
+
label = if new_or_label =~ /\-new/
|
18
|
+
mode = :new
|
19
|
+
ARGV.shift || ""
|
20
|
+
else
|
21
|
+
new_or_label
|
22
|
+
end
|
23
|
+
|
24
|
+
if label.empty?
|
25
|
+
puts "!! You need to pass in a label!"
|
26
|
+
exit 2
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
case mode
|
31
|
+
when :query
|
32
|
+
master = ask("Enter your master password: ") { |q| q.echo = "*" }
|
33
|
+
when :new
|
34
|
+
master = ask("Enter your master password: ") { |q| q.echo = "*" }
|
35
|
+
master_confirm = ask("Enter your master password (again): ") { |q| q.echo = "*" }
|
36
|
+
if (master != master_confirm)
|
37
|
+
puts "!! Your master and confirm don't match!"
|
38
|
+
exit 2
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
password = Oplop.password(:label => label, :master => master)
|
43
|
+
|
44
|
+
puts "\n Password for #{label}:\t#{password}\n\n"
|
45
|
+
|
46
|
+
if Oplop::Cli.copy_to_clipboard(password)
|
47
|
+
print "This has been copied to your clipboard.\n"
|
48
|
+
end
|
data/lib/oplop.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module Oplop
|
5
|
+
|
6
|
+
LENGTH = 8
|
7
|
+
|
8
|
+
#
|
9
|
+
# Oplop.password(:master => 'p@ssw0rd', :label => 'github')
|
10
|
+
#
|
11
|
+
def self.password(args={})
|
12
|
+
unless (args.keys.include?(:master) && args.keys.include?(:label))
|
13
|
+
raise ArgumentError.new "Master and label are required arguments."
|
14
|
+
end
|
15
|
+
|
16
|
+
master_label = "%s%s" % [ args.delete(:master), args.delete(:label) ]
|
17
|
+
|
18
|
+
raise ArgumentError.new "Unknown keys #{args.keys.join(",")}." if args.keys.any?
|
19
|
+
|
20
|
+
password = urlsafe_b64encode(master_label).encode('UTF-8')
|
21
|
+
|
22
|
+
if m = password.match(/\d+/)
|
23
|
+
password = "%s%s" % [ m[0], password ] if (m.begin(0) >= LENGTH)
|
24
|
+
else
|
25
|
+
password = "1%s" % password
|
26
|
+
end
|
27
|
+
|
28
|
+
password[0,LENGTH]
|
29
|
+
end
|
30
|
+
|
31
|
+
# See http://www.ietf.org/rfc/rfc4648.txt
|
32
|
+
def self.urlsafe_b64encode(string)
|
33
|
+
Base64.encode64(Digest::MD5.digest(string)).tr('+/', '-_').gsub("\n",'')
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/oplop/cli.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Oplop
|
2
|
+
module Cli
|
3
|
+
|
4
|
+
def self.help
|
5
|
+
help = <<-HELP
|
6
|
+
Usage: oplap [OPTION] [label]
|
7
|
+
|
8
|
+
Simple cli program to generate oplap passwords.
|
9
|
+
|
10
|
+
You will be prompted for your master password. If you are
|
11
|
+
on a Mac (or have pbcopy installed), or if you have xclip
|
12
|
+
installed, the password will be copied to your clipboard.
|
13
|
+
|
14
|
+
Options:
|
15
|
+
|
16
|
+
--help : This help message
|
17
|
+
--new : Runs in new mode, double prompts for
|
18
|
+
master password.
|
19
|
+
|
20
|
+
Examples:
|
21
|
+
|
22
|
+
Get my github password:
|
23
|
+
|
24
|
+
oplap github
|
25
|
+
|
26
|
+
Generate a new password for amazon:
|
27
|
+
|
28
|
+
oplap --new amazon
|
29
|
+
|
30
|
+
HELP
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.copy_to_clipboard(string)
|
34
|
+
if (copy_program = `which pbcopy`) && !copy_program.empty?
|
35
|
+
%x{ echo #{string} | #{copy_program} }
|
36
|
+
elsif (copy_program = `which xclip`) && !copy_program.empty?
|
37
|
+
%x{ echo #{string} | #{copy_program} }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/oplop.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "oplop/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "oplop"
|
7
|
+
s.version = Oplop::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Brian Kaney"]
|
10
|
+
s.email = ["brian@vermonster.com"]
|
11
|
+
s.homepage = "http://github.com/bkaney/oplop"
|
12
|
+
s.summary = %q{Oplop for Ruby}
|
13
|
+
s.description = %q{Provides a ruby library and command line tool for Oplop}
|
14
|
+
|
15
|
+
s.rubyforge_project = "oplop"
|
16
|
+
|
17
|
+
s.add_dependency(%q<highline>, ["~> 1.6.1"])
|
18
|
+
s.add_development_dependency(%q<ruby-debug19>)
|
19
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
|
20
|
+
s.add_development_dependency(%q<yajl-ruby>, ["~> 0.8.2"])
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
end
|
data/spec/oplop_spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Oplop do
|
4
|
+
|
5
|
+
describe ".password" do
|
6
|
+
|
7
|
+
specify "valid arguments" do
|
8
|
+
lambda{ Oplop.password(:master => "a", :label => "a") }.should_not raise_error(ArgumentError)
|
9
|
+
end
|
10
|
+
|
11
|
+
specify "invalid arguments" do
|
12
|
+
lambda{ Oplop.password }.should raise_error(ArgumentError, /Master and label are required/)
|
13
|
+
end
|
14
|
+
|
15
|
+
specify "invalid arguments" do
|
16
|
+
lambda{ Oplop.password(:master => "a", :label => "b", :foo => 'c') }.should raise_error(ArgumentError, /Unknown key/)
|
17
|
+
end
|
18
|
+
|
19
|
+
context "tests from testdata.json (see Python implementation at http://code.google.com/p/oplop/)" do
|
20
|
+
|
21
|
+
# loop around each "case" in testdata.yml
|
22
|
+
Yajl::Parser.new.parse(File.new(File.dirname(__FILE__) + "/testdata.json", 'r')).each do |testdata|
|
23
|
+
|
24
|
+
specify testdata["why"] do
|
25
|
+
Oplop.password(:master => testdata["master"], :label => testdata["label"]).should == testdata["password"]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/testdata.json
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"why": "label and master password are easy to type (i.e., short and the same)",
|
4
|
+
"label": "a",
|
5
|
+
"master": "a",
|
6
|
+
"hash": "QSS8CpM1wn8IbyS6IHpJEg==",
|
7
|
+
"password": "QSS8CpM1"
|
8
|
+
},
|
9
|
+
{
|
10
|
+
"why": "hash has a digit in first 8 characters & label differs from master password",
|
11
|
+
"label": "0",
|
12
|
+
"master": "1",
|
13
|
+
"hash": "09lEaAKkQll1XTjm0WPoIA==",
|
14
|
+
"password": "09lEaAKk"
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"why": "hash has a sequence of digits outside of first 8 chars",
|
18
|
+
"label": "0",
|
19
|
+
"master": "9",
|
20
|
+
"hash": "hhOYXsSeuPdXrmQ56Hm7Kg==",
|
21
|
+
"password": "56hhOYXs"
|
22
|
+
},
|
23
|
+
{
|
24
|
+
"why": "hash has a lone digit outside of first 8 chars",
|
25
|
+
"label": "0",
|
26
|
+
"master": "7",
|
27
|
+
"hash": "fLvECeyZDxnHjHW9HgbyFQ==",
|
28
|
+
"password": "9fLvECey"
|
29
|
+
},
|
30
|
+
{
|
31
|
+
"why": "hash has no digits",
|
32
|
+
"label": "0",
|
33
|
+
"master": "0",
|
34
|
+
"hash": "tLFHvFIoKHMfGgFr-nLAcw==",
|
35
|
+
"password": "1tLFHvFI"
|
36
|
+
},
|
37
|
+
{
|
38
|
+
"why": "master password w/ UTF-8 char",
|
39
|
+
"label": "ü",
|
40
|
+
"master": "0",
|
41
|
+
"hash": "fHFfKQlvumngcWEWq7HCgg==",
|
42
|
+
"password": "7fHFfKQl"
|
43
|
+
},
|
44
|
+
{
|
45
|
+
"why": "mixed case, length greater than 1 for label and master",
|
46
|
+
"label": "Aa",
|
47
|
+
"master": "Bb",
|
48
|
+
"hash": "pb3iGMs2S-VgWAX1kElklg==",
|
49
|
+
"password": "pb3iGMs2"
|
50
|
+
}
|
51
|
+
]
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: oplop
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brian Kaney
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-05-12 00:00:00 -04:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: highline
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 1.6.1
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ruby-debug19
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: "0"
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rspec
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 2.6.0
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: yajl-ruby
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ~>
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 0.8.2
|
58
|
+
type: :development
|
59
|
+
version_requirements: *id004
|
60
|
+
description: Provides a ruby library and command line tool for Oplop
|
61
|
+
email:
|
62
|
+
- brian@vermonster.com
|
63
|
+
executables:
|
64
|
+
- oplop
|
65
|
+
extensions: []
|
66
|
+
|
67
|
+
extra_rdoc_files: []
|
68
|
+
|
69
|
+
files:
|
70
|
+
- .gitignore
|
71
|
+
- Gemfile
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- bin/oplop
|
75
|
+
- lib/oplop.rb
|
76
|
+
- lib/oplop/cli.rb
|
77
|
+
- lib/oplop/version.rb
|
78
|
+
- oplop.gemspec
|
79
|
+
- spec/oplop_spec.rb
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
- spec/testdata.json
|
82
|
+
has_rdoc: true
|
83
|
+
homepage: http://github.com/bkaney/oplop
|
84
|
+
licenses: []
|
85
|
+
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: "0"
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: "0"
|
103
|
+
requirements: []
|
104
|
+
|
105
|
+
rubyforge_project: oplop
|
106
|
+
rubygems_version: 1.6.2
|
107
|
+
signing_key:
|
108
|
+
specification_version: 3
|
109
|
+
summary: Oplop for Ruby
|
110
|
+
test_files:
|
111
|
+
- spec/oplop_spec.rb
|
112
|
+
- spec/spec_helper.rb
|
113
|
+
- spec/testdata.json
|