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