dragons_keep 0.5.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/Gemfile +29 -0
- data/LICENSE +3 -0
- data/README +3 -0
- data/Rakefile +59 -0
- data/bin/dragons_keep +17 -0
- data/bin/dragons_keep.bat +1 -0
- data/db/migrations/201005101750_create_accounts.rb +19 -0
- data/lib/dragons_keep.rb +33 -0
- data/lib/dragons_keep/account.rb +99 -0
- data/lib/dragons_keep/account_controller.rb +85 -0
- data/lib/dragons_keep/account_dialog.rb +103 -0
- data/lib/dragons_keep/generate_password_dialog.rb +35 -0
- data/lib/dragons_keep/icons/lock_add.png +0 -0
- data/lib/dragons_keep/icons/lock_add.xpm +182 -0
- data/lib/dragons_keep/icons/lock_delete.png +0 -0
- data/lib/dragons_keep/icons/lock_delete.xpm +189 -0
- data/lib/dragons_keep/icons/lock_edit.png +0 -0
- data/lib/dragons_keep/icons/lock_edit.xpm +200 -0
- data/lib/dragons_keep/icons/user_add.png +0 -0
- data/lib/dragons_keep/icons/user_delete.png +0 -0
- data/lib/dragons_keep/icons/user_edit.png +0 -0
- data/lib/dragons_keep/keeps_main.rb +238 -0
- metadata +149 -0
data/Gemfile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# ruby gems needed for Dragon's Keep
|
4
|
+
gem 'ezcrypto'
|
5
|
+
gem 'uuid'
|
6
|
+
# using datamapper instead of activerecord
|
7
|
+
# Active record Gems
|
8
|
+
#gem 'activerecord', "3.0.0.beta3"
|
9
|
+
#gem 'sqlite3-ruby'
|
10
|
+
#
|
11
|
+
# Data Mapper gems 0.10.2
|
12
|
+
gem 'dm-core', "0.10.2"
|
13
|
+
gem 'do_sqlite3', '0.10.2'
|
14
|
+
|
15
|
+
# Data Mapper gems 1.0.0
|
16
|
+
#gem 'data_mapper', "1.0.0"
|
17
|
+
#gem 'dm-sqlite-adapter', "1.0.0"
|
18
|
+
|
19
|
+
# Wxruby renames gem for 1.9.1 support
|
20
|
+
if (RUBY_PLATFORM =~ /linux$/) == nil
|
21
|
+
#Decide which gem to load based on version of ruby installed
|
22
|
+
if (RUBY_VERSION =~/^1.9/) != nil
|
23
|
+
gem 'wxruby-ruby19'
|
24
|
+
else
|
25
|
+
gem 'wxruby'
|
26
|
+
end
|
27
|
+
else
|
28
|
+
gem 'wxruby', "2.0.1"
|
29
|
+
end
|
data/LICENSE
ADDED
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#
|
2
|
+
# To change this template, choose Tools | Templates
|
3
|
+
# and open the template in the editor.
|
4
|
+
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'rake'
|
8
|
+
require 'rake/clean'
|
9
|
+
require 'rake/gempackagetask'
|
10
|
+
require 'rake/rdoctask'
|
11
|
+
require 'rake/testtask'
|
12
|
+
require 'bundler'
|
13
|
+
|
14
|
+
|
15
|
+
spec = Gem::Specification.new do |s|
|
16
|
+
s.name = 'dragons_keep'
|
17
|
+
s.version = '0.5.0'
|
18
|
+
s.has_rdoc = true
|
19
|
+
s.extra_rdoc_files = ['README', 'LICENSE']
|
20
|
+
s.summary = 'Secure Password Keeper Application'
|
21
|
+
s.description = s.summary
|
22
|
+
s.author = 'Allan Davis'
|
23
|
+
s.email = 'javaalley@gmail.com'
|
24
|
+
s.homepage = 'http://github.com/javaalley/dragons_keep'
|
25
|
+
s.executables = ['dragons_keep']
|
26
|
+
s.files = %w(LICENSE README Rakefile Gemfile) + Dir.glob("{bin,lib,spec,db}/**/*")
|
27
|
+
s.require_path = "lib"
|
28
|
+
s.bindir = "bin"
|
29
|
+
s.add_bundler_dependencies
|
30
|
+
end
|
31
|
+
|
32
|
+
Rake::GemPackageTask.new(spec) do |p|
|
33
|
+
p.gem_spec = spec
|
34
|
+
p.need_tar = true
|
35
|
+
p.need_zip = true
|
36
|
+
end
|
37
|
+
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
files =['README', 'LICENSE', 'lib/**/*.rb']
|
40
|
+
rdoc.rdoc_files.add(files)
|
41
|
+
rdoc.main = "README" # page to start on
|
42
|
+
rdoc.title = "dragons_keep Docs"
|
43
|
+
rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
|
44
|
+
rdoc.options << '--line-numbers'
|
45
|
+
end
|
46
|
+
|
47
|
+
Rake::TestTask.new do |t|
|
48
|
+
t.test_files = FileList['test/**/*.rb']
|
49
|
+
end
|
50
|
+
|
51
|
+
#desc "Migrate the database through migrations scripts"
|
52
|
+
#task :migrate => :enviroment do
|
53
|
+
# ActiveRecord::Migrator.migrate('db/migrations', ENV["VERSION"]? ENV["VERSION"].to_i : nil)
|
54
|
+
#end
|
55
|
+
#
|
56
|
+
#task :enviroment do
|
57
|
+
# ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml')))
|
58
|
+
# ActiveRecord::Base.logger = Logger.new(File.open('database.log', 'a'))
|
59
|
+
#end
|
data/bin/dragons_keep
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# To change this template, choose Tools | Templates
|
3
|
+
# and open the template in the editor.
|
4
|
+
require 'rubygems'
|
5
|
+
begin
|
6
|
+
require 'bundler'
|
7
|
+
Bundler.setup
|
8
|
+
rescue
|
9
|
+
end
|
10
|
+
|
11
|
+
# Set the path to load from the lib and main directory
|
12
|
+
path = File.expand_path(File.dirname(__FILE__))
|
13
|
+
$: << path
|
14
|
+
$: << File.join(path, "..", "lib")
|
15
|
+
|
16
|
+
load "dragons_keep.rb"
|
17
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
@"ruby.exe" "%~dpn0" %*
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# To change this template, choose Tools | Templates
|
2
|
+
# and open the template in the editor.
|
3
|
+
|
4
|
+
class CreateAccounts < ActiveRecord::Migration
|
5
|
+
def self.up
|
6
|
+
create_table :accounts do |t|
|
7
|
+
t.string :name
|
8
|
+
t.string :password
|
9
|
+
t.string :salt
|
10
|
+
t.string :url
|
11
|
+
t.string :user_name
|
12
|
+
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
end
|
16
|
+
def self.down
|
17
|
+
drop_table :accounts
|
18
|
+
end
|
19
|
+
end
|
data/lib/dragons_keep.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#! /usr/etc ruby
|
2
|
+
# To change this template, choose Tools | Templates
|
3
|
+
# and open the template in the editor.
|
4
|
+
require 'rubygems'
|
5
|
+
#require 'bundler'
|
6
|
+
#Bundler.setup
|
7
|
+
|
8
|
+
# Set the path to load from the lib and main directory
|
9
|
+
path = File.expand_path(File.dirname(__FILE__))
|
10
|
+
$LOAD_PATH.insert 0, path
|
11
|
+
#puts "LOAD_PATH"
|
12
|
+
#puts $LOAD_PATH
|
13
|
+
#$: << File.join(path, "..", "lib")
|
14
|
+
|
15
|
+
require 'wx'
|
16
|
+
require 'dragons_keep/keeps_main'
|
17
|
+
require 'ezcrypto'
|
18
|
+
require 'uuid'
|
19
|
+
|
20
|
+
module DragonsKeep
|
21
|
+
|
22
|
+
class DragonsKeepApp < Wx::App
|
23
|
+
def on_init()
|
24
|
+
self.app_name = "Dragon's Keep"
|
25
|
+
@frame = KeepsMain.new "Dragon's Keep"
|
26
|
+
@frame.center_on_screen(Wx::BOTH)
|
27
|
+
@frame.show
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
dk = DragonsKeep::DragonsKeepApp.new
|
33
|
+
dk.main_loop
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# To change this template, choose Tools | Templates
|
2
|
+
# and open the template in the editor.
|
3
|
+
require 'dm-core'
|
4
|
+
require 'ezcrypto'
|
5
|
+
module DragonsKeep
|
6
|
+
# Account class to store accounts and passwords into a keeper system
|
7
|
+
class Account
|
8
|
+
include DataMapper::Resource
|
9
|
+
#Data Mapper properties
|
10
|
+
property :id, Serial
|
11
|
+
property :name, String, :length => 100
|
12
|
+
property :password, String, :length => 100
|
13
|
+
property :salt, String, :length => 100
|
14
|
+
property :user_name, String, :length => 100
|
15
|
+
property :url, String, :length => 100
|
16
|
+
|
17
|
+
#register callbacks
|
18
|
+
before :save, :before_create
|
19
|
+
|
20
|
+
|
21
|
+
# Transient storage of unencrypted password and confirmation field
|
22
|
+
attr_accessor :unencrypted_password
|
23
|
+
attr_reader :password_confirmation
|
24
|
+
|
25
|
+
# create writer for password confirmation
|
26
|
+
def password_confirmation=(value)
|
27
|
+
@password_confirmation = value
|
28
|
+
@unencrypted = true
|
29
|
+
end
|
30
|
+
|
31
|
+
# loading data validate password filed and check status
|
32
|
+
def after_initialize
|
33
|
+
@unencrypted = self.password.nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
# Before create data set salt and encrypt password
|
37
|
+
def before_create
|
38
|
+
raise PasswordException, "Password is not Encrypted" if self.new_password? && self.unencrypted?
|
39
|
+
end
|
40
|
+
|
41
|
+
# are we re defining the account password
|
42
|
+
def new_password?()
|
43
|
+
return !(self.unencrypted_password.blank? and self.password_confirmation.blank?)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Encrypt the password
|
47
|
+
# encrypt_pass = Given password when user created the account information
|
48
|
+
def encrypt_password(encrypt_pass)
|
49
|
+
if self.unencrypted? && self.unencrypted_password == self.password_confirmation
|
50
|
+
self.create_salt
|
51
|
+
self.password = Base64.encode64(EzCrypto::Key.encrypt_with_password(encrypt_pass, self.salt, self.unencrypted_password))
|
52
|
+
@unencrypted = false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Decrypt the password
|
57
|
+
# encrypt_pass = Given password when user created the account information
|
58
|
+
def decrpyt_password(encrypt_pass)
|
59
|
+
if !(self.unencrypted?)
|
60
|
+
self.unencrypted_password = EzCrypto::Key.decrypt_with_password(encrypt_pass, self.salt, Base64.decode64( self.password))
|
61
|
+
@unencrypted = true
|
62
|
+
self.password_confirmation=""
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Is the password encrypted?
|
67
|
+
def unencrypted?
|
68
|
+
return (@unencrypted.nil?)? self.password.nil?: @unencrypted
|
69
|
+
end
|
70
|
+
|
71
|
+
# Generate a random password
|
72
|
+
# length_of_password = Length of the password to generate
|
73
|
+
# special_char = Can this password need to contain Special Characters
|
74
|
+
def generate_password( length_of_pass, special_char )
|
75
|
+
chars = []
|
76
|
+
("a".."z").each {|ele| chars << ele}
|
77
|
+
("A".."Z").each {|ele| chars << ele}
|
78
|
+
("0".."9").each {|ele| chars << ele}
|
79
|
+
if(special_char)
|
80
|
+
["@", "!", "_",].each {|ele| chars << ele}
|
81
|
+
end
|
82
|
+
newpass = ""
|
83
|
+
1.upto(length_of_pass) { |i| newpass << chars[rand(chars.size-1)] }
|
84
|
+
#self.password
|
85
|
+
self.unencrypted_password = newpass
|
86
|
+
self.password_confirmation = newpass
|
87
|
+
@unencrypted = true
|
88
|
+
end
|
89
|
+
|
90
|
+
# Create the salt
|
91
|
+
def create_salt
|
92
|
+
self.salt = UUID.generate(:compact)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Error on Passwords Encryption or validation
|
97
|
+
class PasswordException < RuntimeError
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# To change this template, choose Tools | Templates
|
2
|
+
# and open the template in the editor.
|
3
|
+
require 'dragons_keep/account'
|
4
|
+
require 'dm-core'
|
5
|
+
require 'ezcrypto'
|
6
|
+
require 'uuid'
|
7
|
+
require 'digest/sha1'
|
8
|
+
|
9
|
+
module DragonsKeep
|
10
|
+
class AccountController
|
11
|
+
attr_reader :encrypt_pass
|
12
|
+
attr_accessor :database
|
13
|
+
|
14
|
+
def encrypt_pass=(pass)
|
15
|
+
@encrypt_pass = Digest::SHA1.hexdigest(pass)
|
16
|
+
end
|
17
|
+
|
18
|
+
def establish_connection
|
19
|
+
migrate = false
|
20
|
+
if !(File.exist?(self.database))
|
21
|
+
migrate = true
|
22
|
+
end
|
23
|
+
|
24
|
+
#DataMapper::Logger.new $stdout, :debug
|
25
|
+
|
26
|
+
DataMapper.setup :default, "sqlite3://#{self.database}"
|
27
|
+
if migrate
|
28
|
+
DataMapper.auto_migrate!
|
29
|
+
else
|
30
|
+
validate_connection
|
31
|
+
end
|
32
|
+
@connection = true
|
33
|
+
end
|
34
|
+
# Check the first record in the database and decrypt the password to see if the password is valid
|
35
|
+
def validate_connection
|
36
|
+
account = Account.first
|
37
|
+
if ! account.nil?
|
38
|
+
self.decrypt!(account)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(database=nil, password=nil)
|
43
|
+
@connection = false
|
44
|
+
if(!(database.nil?))
|
45
|
+
self.database = File.expand_path database
|
46
|
+
end
|
47
|
+
if (!(password.nil?))
|
48
|
+
self.encrypt_pass = password
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def list()
|
53
|
+
if @connection
|
54
|
+
return Account.all
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
def save!(account)
|
59
|
+
if @connection
|
60
|
+
if account.unencrypted?
|
61
|
+
account.encrypt_password(self.encrypt_pass)
|
62
|
+
end
|
63
|
+
return account.save()
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def get(id)
|
68
|
+
if @connection
|
69
|
+
return Account.get id
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def decrypt!(account)
|
74
|
+
begin
|
75
|
+
account.decrpyt_password self.encrypt_pass
|
76
|
+
rescue OpenSSL::Cipher::CipherError
|
77
|
+
raise PasswordException, "Password is invalid"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def delete(account)
|
82
|
+
account.destroy
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# To change this template, choose Tools | Templates
|
2
|
+
# and open the template in the editor.
|
3
|
+
require 'wx'
|
4
|
+
require 'dragons_keep/generate_password_dialog'
|
5
|
+
|
6
|
+
module DragonsKeep
|
7
|
+
class AccountDialog < Wx::Dialog
|
8
|
+
ID_USER_NAME = 103
|
9
|
+
ID_ACCOUNT_NAME = 101
|
10
|
+
ID_URL = 102
|
11
|
+
ID_PASSWORD = 104
|
12
|
+
ID_GENERATE_PASSWORD = 105
|
13
|
+
ID_SAVE = 106
|
14
|
+
ID_COPY_CLIP = 107
|
15
|
+
ID_CANCEL = 108
|
16
|
+
|
17
|
+
def initialize parent, id, title
|
18
|
+
super parent, id, title
|
19
|
+
self.set_size(Wx::Size.new(400, 300))
|
20
|
+
main_sizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
21
|
+
grid_sizer = Wx::FlexGridSizer.new(4,2,5,5)
|
22
|
+
|
23
|
+
# Create name entry
|
24
|
+
label = Wx::StaticText.new(self, :label=>"Name:")
|
25
|
+
grid_sizer.add label, 0, Wx::ALL |Wx::ALIGN_RIGHT
|
26
|
+
@name = Wx::TextCtrl.new self, ID_ACCOUNT_NAME
|
27
|
+
grid_sizer.add @name, 1, Wx::EXPAND
|
28
|
+
|
29
|
+
# Create url entry
|
30
|
+
label = Wx::StaticText.new(self, :label=>"URL:")
|
31
|
+
grid_sizer.add label, 0,Wx::ALL |Wx::ALIGN_RIGHT
|
32
|
+
@url = Wx::TextCtrl.new self, ID_URL
|
33
|
+
grid_sizer.add @url, 1, Wx::EXPAND
|
34
|
+
|
35
|
+
# Create User_name entry
|
36
|
+
label = Wx::StaticText.new(self, :label=>"User Name:")
|
37
|
+
grid_sizer.add label, 0, Wx::ALL |Wx::ALIGN_RIGHT
|
38
|
+
@user = Wx::TextCtrl.new self, ID_USER_NAME
|
39
|
+
grid_sizer.add @user, 1, Wx::EXPAND
|
40
|
+
|
41
|
+
# Create password entry
|
42
|
+
label = Wx::StaticText.new(self, :label=>"Password:")
|
43
|
+
grid_sizer.add label, 0, Wx::ALL |Wx::ALIGN_RIGHT
|
44
|
+
@pass = Wx::TextCtrl.new self, ID_PASSWORD, :style=> Wx::TE_PASSWORD
|
45
|
+
grid_sizer.add @pass, 1, Wx::EXPAND
|
46
|
+
main_sizer.add grid_sizer, 0, Wx::GROW|Wx::ALIGN_CENTER_VERTICAL|Wx::ALL, 5
|
47
|
+
grid_sizer.add_growable_col(1)
|
48
|
+
# add spacer to grid
|
49
|
+
grid_sizer.add(25, 25)
|
50
|
+
# Add generate password button
|
51
|
+
@gen_button = Wx::Button.new self, ID_GENERATE_PASSWORD, "Generate Password..."
|
52
|
+
grid_sizer.add @gen_button, 1, Wx::EXPAND
|
53
|
+
evt_button(ID_GENERATE_PASSWORD){|evt| self.gen_pass_click(evt)}
|
54
|
+
main_sizer.add self.create_separated_button_sizer(Wx::OK|Wx::CANCEL), 1, Wx::ALIGN_RIGHT
|
55
|
+
self.set_sizer main_sizer
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
def gen_pass_click(event)
|
60
|
+
# display generate pass dialog
|
61
|
+
gen_dialog = GeneratePasswordDialog.new self, -1, "Generate Password"
|
62
|
+
if gen_dialog.show_modal() == Wx::ID_OK
|
63
|
+
@account.generate_password gen_dialog.password_length, gen_dialog.use_special_chars?
|
64
|
+
@pass.value = @account.unencrypted_password
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def account
|
69
|
+
save_account
|
70
|
+
@account
|
71
|
+
end
|
72
|
+
|
73
|
+
def account=(account)
|
74
|
+
@account = account
|
75
|
+
load_account
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def save_account
|
80
|
+
if not @account.blank?
|
81
|
+
if @account.unencrypted_password != @pass.value && @account.password_confirmation.blank?
|
82
|
+
password_dialog = Wx::PasswordEntryDialog.new(self, "Please confirm the password")
|
83
|
+
if password_dialog.show_modal == Wx::ID_OK
|
84
|
+
@account.password_confirmation = password_dialog.get_value
|
85
|
+
@account.unencrypted_password = @pass.value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
@account.name = @name.value
|
89
|
+
@account.user_name = @user.value
|
90
|
+
@account.url = @url.value
|
91
|
+
end
|
92
|
+
end
|
93
|
+
def load_account
|
94
|
+
if not @account.name.blank?
|
95
|
+
@name.value = @account.name
|
96
|
+
@url.value = @account.url
|
97
|
+
@user.value = @account.user_name
|
98
|
+
@pass.value = @account.unencrypted_password.blank? ? @account.password : @account.unencrypted_password
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|