redsafe 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/GemcutterInstructions.rdoc +74 -0
- data/LICENSE +20 -0
- data/README.rdoc +13 -0
- data/Rakefile +30 -0
- data/lib/models/scarlet_letters.rb +90 -0
- data/lib/models/sl_aah.rb +50 -0
- data/lib/models/sl_user.rb +119 -0
- data/lib/redsafe/version.rb +9 -0
- data/lib/redsafe.rb +268 -0
- data/lib/views/edit.haml +42 -0
- data/lib/views/index.haml +30 -0
- data/lib/views/login.haml +21 -0
- data/lib/views/show.haml +10 -0
- data/lib/views/signup.haml +25 -0
- data/test/redsafe_test.rb +7 -0
- data/test/test_helper.rb +10 -0
- metadata +133 -0
data/.document
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
= RedSafe Gemcutter Setup
|
2
|
+
|
3
|
+
Gem Cutter set up was more time consuming than I had anticipated. Realized that I needed: task "default" => ["test"] inside the Rakefile to have rake command work for me. Additionally, I needed to use: gem cert before I could upload to Gemcutter.
|
4
|
+
|
5
|
+
== Notes on the Red Safe Gem Creation
|
6
|
+
|
7
|
+
* 1) rake
|
8
|
+
(Needed to set a default for the task.)
|
9
|
+
|
10
|
+
Added default task to Rakefile
|
11
|
+
task "default" => ["test"]
|
12
|
+
|
13
|
+
* 2) rake gemspec
|
14
|
+
(Worked for me first time.)
|
15
|
+
|
16
|
+
Generated: redsafe.gemspec
|
17
|
+
redsafe.gemspec is valid.
|
18
|
+
|
19
|
+
* 3) rdoc
|
20
|
+
(Created documentation for the redsafe project.)
|
21
|
+
|
22
|
+
redsafe.rb: mm.m.......c...
|
23
|
+
sl_user.rb: c.....cc....
|
24
|
+
sl_aah.rb: m.m.....m..
|
25
|
+
scarlet_letters.rb: c........
|
26
|
+
version.rb: cm
|
27
|
+
Generating HTML...
|
28
|
+
|
29
|
+
Files: 5
|
30
|
+
Classes: 6
|
31
|
+
Modules: 7
|
32
|
+
Methods: 36
|
33
|
+
Elapsed: 0.593s
|
34
|
+
|
35
|
+
* 4) gem build redsafe.gemspec
|
36
|
+
(Was able to build after I took author name "Ruby Shot" from an array structure and turned it into a string by removing the brackets, [ ].
|
37
|
+
|
38
|
+
# gem build redsafe.gemspec
|
39
|
+
ERROR: While executing gem ... (Gem::InvalidSpecificationException)
|
40
|
+
authors must be Array of Strings
|
41
|
+
|
42
|
+
|
43
|
+
* 5) gem push redsafe.gem
|
44
|
+
(As mentioned above, the gem certification was not set. Thus, I couldn't get an upload to Gemcutter.)
|
45
|
+
|
46
|
+
Your api key has been stored in ~/.gem/credentials
|
47
|
+
Pushing gem to Gemcutter...
|
48
|
+
ERROR: While executing gem ... (Errno::ENOENT)
|
49
|
+
No such file or directory - redsafe.gem
|
50
|
+
|
51
|
+
|
52
|
+
* 6) gem cert --build you@yourmail.com
|
53
|
+
(Use this command to create the certification for Gemcutter. I used my email address. I also moved the Public Cert and the Private Key to a safer location.)
|
54
|
+
|
55
|
+
Public Cert: gem-public_cert.pem
|
56
|
+
Private Key: gem-private_key.pem
|
57
|
+
Don't forget to move the key file to somewhere private...
|
58
|
+
|
59
|
+
|
60
|
+
* 7)gem push redsafe-0.1.1.gem
|
61
|
+
(I was successful when I used the above command. Used the Gemcutter search and verified that redsafe was being hosted on Gemcutter. Didn't test it, but left it in place.)
|
62
|
+
|
63
|
+
Pushing gem to Gemcutter...
|
64
|
+
Successfully registered gem: redsafe (0.1.1)
|
65
|
+
|
66
|
+
== Copyright
|
67
|
+
|
68
|
+
Copyright (c) 2010 Ruby Shot. See LICENSE for details.
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Ruby Shot
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
= RedSafe
|
2
|
+
|
3
|
+
The Red Safe Project deals with the creation of a gem. The gem's function is to streamline access to various websites through a connection consolidation of procedures.
|
4
|
+
|
5
|
+
== Notes on the Red Safe Project
|
6
|
+
* This Project's focus is learning based.
|
7
|
+
* It is designed to function with the appengine jruby.
|
8
|
+
* Clone it for your learning needs.
|
9
|
+
* git clone git@github.com:rubyshot/redsafe.git
|
10
|
+
|
11
|
+
== Copyright
|
12
|
+
|
13
|
+
Copyright (c) 2010 Ruby Shot. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
task "default" => ["test"]
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
require 'lib/redsafe/version'
|
7
|
+
Jeweler::Tasks.new do |gem|
|
8
|
+
gem.name = "redsafe"
|
9
|
+
gem.version = Redsafe::Version::STRING
|
10
|
+
gem.description = "Red Safe is a conveyor belt to get your web-based project on a fast roll."
|
11
|
+
gem.summary = "This is a gem tool which simplifies signup functionality using various web services for connection credentials."
|
12
|
+
gem.homepage = "http://github.com/rubyshot/redsafe"
|
13
|
+
gem.author = "Ruby Shot"
|
14
|
+
gem.email = "domochoice(at)yahoo:com"
|
15
|
+
gem.add_development_dependency "sinatra"
|
16
|
+
gem.add_development_dependency "dm-core"
|
17
|
+
gem.add_development_dependency "dm-validations"
|
18
|
+
gem.add_development_dependency "dm-timestamps"
|
19
|
+
gem.add_development_dependency "date"
|
20
|
+
gem.add_development_dependency "time"
|
21
|
+
end
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
|
@@ -0,0 +1,90 @@
|
|
1
|
+
if Object.const_defined?("DataMapper")
|
2
|
+
#require 'dm-core'
|
3
|
+
require 'dm-timestamps'
|
4
|
+
require 'dm-validations'
|
5
|
+
require 'date'
|
6
|
+
require 'time'
|
7
|
+
|
8
|
+
require Pathname(__FILE__).dirname.expand_path + "sl_user.rb"
|
9
|
+
require Pathname(__FILE__).dirname.expand_path + "sl_aah.rb"
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
class User
|
14
|
+
if Object.const_defined?("DataMapper")
|
15
|
+
include SlAah
|
16
|
+
else
|
17
|
+
throw "'dm-core' required for red safe "
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(interfacing_class_instance)
|
21
|
+
@instance = interfacing_class_instance
|
22
|
+
end
|
23
|
+
|
24
|
+
def id
|
25
|
+
@instance.id
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.authenticate(email, pass)
|
29
|
+
current_user = get(:email => email)
|
30
|
+
return nil if current_user.nil?
|
31
|
+
return current_user if User.encrypt(pass, current_user.salt) == current_user.hashed_password
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def self.encrypt(pass, salt)
|
38
|
+
Digest::SHA1.hexdigest(pass+salt)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.random_string(len)
|
42
|
+
#generate a random password consisting of strings and digits
|
43
|
+
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
|
44
|
+
newpass = ""
|
45
|
+
1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
|
46
|
+
return newpass
|
47
|
+
end
|
48
|
+
|
49
|
+
#Created 3 actions and terminology thereof:
|
50
|
+
#1. sist: generates a string
|
51
|
+
#2. desist: receives the sist from an authorized server and breaks it apart
|
52
|
+
#3. resist: receives the desist string and reviews its integrity
|
53
|
+
|
54
|
+
def self.sist
|
55
|
+
const_set(TIME_FMT, '%Y-%m-%dT%H:%M:%SZ')
|
56
|
+
junk = self.random_string(6)
|
57
|
+
t = Time.now.getutc
|
58
|
+
time_str = t.strftime(TIME_FMT)
|
59
|
+
the_sist_str = time_str + junk
|
60
|
+
return the_sist_str
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.desist( the_sist_str )
|
64
|
+
#"0000-00-00T00:00:00Z"
|
65
|
+
const_set(TIME_STR_LEN, 20 )
|
66
|
+
const_set(TIME_VALIDATOR,"/\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ/")
|
67
|
+
timestamp_str = the_sist_str[0...TIME_STR_LEN]
|
68
|
+
raise ArgumentError if timestamp_str.size < TIME_STR_LEN
|
69
|
+
raise ArgumentError unless timestamp_str.match(TIME_VALIDATOR)
|
70
|
+
timestamp = Time.parse(timestamp_str).to_i
|
71
|
+
raise ArgumentError if ts < 0
|
72
|
+
return timestamp, the_sist_str[20..-1]
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.resist(the_sist_str)
|
76
|
+
const_set(TIME_RANGE, 60*60*5)
|
77
|
+
timestamp, junk = self.desist(the_sist_str)
|
78
|
+
now = Time.now.to_i
|
79
|
+
start = now - TIME_RANGE
|
80
|
+
finish = now + TIME_RANGE
|
81
|
+
return true if (start <= timestamp and timestamp <= finish)
|
82
|
+
return false
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module SlAah
|
2
|
+
def self.included(base)
|
3
|
+
base.extend ClassMethods
|
4
|
+
base.class_eval { include SlAah::InstanceMethods }
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def all
|
9
|
+
result = SlUser.all
|
10
|
+
result.collect {|instance| self.new instance}
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(hash)
|
14
|
+
if user = SlUser.first(hash)
|
15
|
+
self.new user
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def set(attributes)
|
22
|
+
user = SlUser.new attributes
|
23
|
+
user.save
|
24
|
+
user
|
25
|
+
end
|
26
|
+
|
27
|
+
def set!(attributes)
|
28
|
+
user = SlUser.new attributes
|
29
|
+
user.save!
|
30
|
+
user
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete(pk)
|
34
|
+
user = User.first(:id => pk)
|
35
|
+
user.destroy
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module InstanceMethods
|
40
|
+
def update(attributes)
|
41
|
+
@instance.update_attributes attributes
|
42
|
+
@instance.save
|
43
|
+
end
|
44
|
+
|
45
|
+
def method_missing(meth, *args, &block)
|
46
|
+
#cool I just found out * on an array turns the array into a list of args for a function
|
47
|
+
@instance.send(meth, *args, &block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
class SlUser
|
2
|
+
include DataMapper::Resource
|
3
|
+
|
4
|
+
property :id, Serial
|
5
|
+
property :email, String, :length => (5..40), :unique => true, :format => :email_address
|
6
|
+
property :hashed_password, String
|
7
|
+
property :salt, String
|
8
|
+
property :created_at, DateTime
|
9
|
+
property :permission_level, Integer, :default => 1
|
10
|
+
property :nickname, String, :length => 255
|
11
|
+
property :identifier, String, :length => 255
|
12
|
+
property :photo_url, String, :length => 255
|
13
|
+
|
14
|
+
has n, :oohs
|
15
|
+
has n, :direct_messages, :model => "Ooh"
|
16
|
+
has n, :relationships
|
17
|
+
has n, :followers, :through => :relationships, :model => "SlUser", :child_key => [:SlUser_id]
|
18
|
+
has n, :follows, :through => :relationships, :model => "SlUser", :remote_name => :SlUser, :child_key => [:follower_id]
|
19
|
+
if Sinatra.const_defined?('FacebookObject')
|
20
|
+
property :fb_uid, String
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_accessor :password, :password_confirmation
|
24
|
+
#protected equievelant? :protected => true doesn't exist in dm 0.10.0
|
25
|
+
#protected :id, :salt
|
26
|
+
#doesn't behave correctly, I'm not even sure why I did this.
|
27
|
+
|
28
|
+
validates_present :password_confirmation, :unless => Proc.new { |t| t.hashed_password }
|
29
|
+
validates_present :password, :unless => Proc.new { |t| t.hashed_password }
|
30
|
+
validates_is_confirmed :password
|
31
|
+
|
32
|
+
def password=(pass)
|
33
|
+
@password = pass
|
34
|
+
self.salt = SlUser.random_string(10) if !self.salt
|
35
|
+
self.hashed_password = SlUser.encrypt(@password, self.salt)
|
36
|
+
end
|
37
|
+
|
38
|
+
def admin?
|
39
|
+
self.permission_level == -1 || self.id == 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def site_admin?
|
43
|
+
self.id == 1
|
44
|
+
end
|
45
|
+
|
46
|
+
def displayed_oohs
|
47
|
+
oohs = []
|
48
|
+
oohs += self.oohs.all(:recipient_id => nil, :limit => 10, :order => [:created_at.desc]) # don't show direct messsages
|
49
|
+
self.follows.each do |follows| oohs += follows.oohs.all(:recipient_id => nil, :limit => 10, :order => [:created_at.desc]) end if @myself == @user
|
50
|
+
oohs.sort! { |x,y| y.created_at <=> x.created_at }
|
51
|
+
oohs[0..10]
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def method_missing(m, *args)
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
class Relationship
|
61
|
+
include DataMapper::Resource
|
62
|
+
|
63
|
+
property :SlUser_id, Integer, :key => true
|
64
|
+
property :follower_id, Integer, :key => true
|
65
|
+
belongs_to :SlUser, :child_key => [:SlUser_id]
|
66
|
+
belongs_to :follower, :model => "SlUser", :child_key => [:follower_id]
|
67
|
+
end
|
68
|
+
|
69
|
+
class Ooh
|
70
|
+
include DataMapper::Resource
|
71
|
+
|
72
|
+
property :id, Serial
|
73
|
+
property :text, String, :length => 140
|
74
|
+
property :created_at, DateTime
|
75
|
+
belongs_to :recipient, :model => "SlUser", :child_key => [:recipient_id]
|
76
|
+
belongs_to :SlUser
|
77
|
+
|
78
|
+
before :save do
|
79
|
+
case
|
80
|
+
when starts_with?('dm ')
|
81
|
+
process_dm
|
82
|
+
when starts_with?('follow ')
|
83
|
+
process_follow
|
84
|
+
else
|
85
|
+
process
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# general scrubbing of an ooh
|
90
|
+
def process
|
91
|
+
# process url
|
92
|
+
urls = self.text.scan(URL_REGEXP)
|
93
|
+
urls.each { |url|
|
94
|
+
tiny_url = open("http://tinyurl.com/api-create.php?url=#{url[0]}") {|s| s.read}
|
95
|
+
self.text.sub!(url[0], "<a href='#{tiny_url}'>#{tiny_url}</a>")
|
96
|
+
}
|
97
|
+
# process @
|
98
|
+
ats = self.text.scan(AT_REGEXP)
|
99
|
+
ats.each { |at| self.text.sub!(at, "<a href='/#{at[2,at.length]}'>#{at}</a>") }
|
100
|
+
end
|
101
|
+
|
102
|
+
# process direct messages
|
103
|
+
def process_dm
|
104
|
+
self.recipient = SlUser.first(:email => self.text.split[1])
|
105
|
+
self.text = self.text.split[2..self.text.split.size].join(' ') # remove the first 2 words
|
106
|
+
process
|
107
|
+
end
|
108
|
+
|
109
|
+
# process follow commands
|
110
|
+
def process_follow
|
111
|
+
Relationship.create(:SlUser => SlUser.first(:email => self.text.split[1]), :follower => self.SlUser)
|
112
|
+
throw :halt # don't save
|
113
|
+
end
|
114
|
+
|
115
|
+
def starts_with?(prefix)
|
116
|
+
prefix = prefix.to_s
|
117
|
+
self.text[0, prefix.length] == prefix
|
118
|
+
end
|
119
|
+
end
|
data/lib/redsafe.rb
ADDED
@@ -0,0 +1,268 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'pathname'
|
3
|
+
require Pathname(__FILE__).dirname.expand_path + "models/scarlet_letters"
|
4
|
+
|
5
|
+
module Sinatra
|
6
|
+
module RedSlash
|
7
|
+
def self.registered(app)
|
8
|
+
#INVESTIGATE
|
9
|
+
#the possibility of sinatra having an array of view_paths to load from
|
10
|
+
#PROBLEM
|
11
|
+
#sinatra 9.1.1 doesn't have multiple view capability anywhere
|
12
|
+
#so to get around I have to do it totally manually by
|
13
|
+
#loading the view from this path into a string and rendering it
|
14
|
+
set :red_slash_view_path, Pathname(__FILE__).dirname.expand_path + "views/"
|
15
|
+
|
16
|
+
get '/users' do
|
17
|
+
login_required
|
18
|
+
redirect "/" unless current_user.admin?
|
19
|
+
|
20
|
+
@users = User.all
|
21
|
+
if @users != []
|
22
|
+
haml get_view_as_string("index.haml"), :layout => use_layout?
|
23
|
+
else
|
24
|
+
redirect '/signup'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/users/:id' do
|
29
|
+
login_required
|
30
|
+
|
31
|
+
@user = User.get(:id => params[:id])
|
32
|
+
haml get_view_as_string("show.haml"), :layout => use_layout?
|
33
|
+
end
|
34
|
+
|
35
|
+
#convenience for ajax but maybe entirely stupid and unnecesary
|
36
|
+
get '/logged_in' do
|
37
|
+
if session[:user]
|
38
|
+
"true"
|
39
|
+
else
|
40
|
+
"false"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
get '/login' do
|
45
|
+
haml get_view_as_string("login.haml"), :layout => use_layout?
|
46
|
+
end
|
47
|
+
|
48
|
+
post '/login' do
|
49
|
+
if user = User.authenticate(params[:email], params[:password])
|
50
|
+
session[:user] = user.id
|
51
|
+
if session[:return_to]
|
52
|
+
redirect_url = session[:return_to]
|
53
|
+
session[:return_to] = false
|
54
|
+
redirect redirect_url
|
55
|
+
else
|
56
|
+
redirect '/'
|
57
|
+
end
|
58
|
+
else
|
59
|
+
redirect '/login'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
get '/logout' do
|
64
|
+
session[:user] = nil
|
65
|
+
@message = "in case it weren't obvious, you've logged out"
|
66
|
+
redirect '/'
|
67
|
+
end
|
68
|
+
|
69
|
+
get '/signup' do
|
70
|
+
haml get_view_as_string("signup.haml"), :layout => use_layout?
|
71
|
+
end
|
72
|
+
|
73
|
+
post '/signup' do
|
74
|
+
@user = User.set(params[:user])
|
75
|
+
if @user
|
76
|
+
session[:user] = @user.id
|
77
|
+
redirect '/'
|
78
|
+
else
|
79
|
+
session[:flash] = "failure!"
|
80
|
+
redirect '/'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
get '/users/:id/edit' do
|
85
|
+
login_required
|
86
|
+
redirect "/users" unless current_user.admin? || current_user.id.to_s == params[:id]
|
87
|
+
|
88
|
+
@user = User.get(:id => params[:id])
|
89
|
+
haml get_view_as_string("edit.haml"), :layout => use_layout?
|
90
|
+
end
|
91
|
+
|
92
|
+
post '/users/:id/edit' do
|
93
|
+
login_required
|
94
|
+
redirect "/users" unless current_user.admin? || current_user.id.to_s == params[:id]
|
95
|
+
|
96
|
+
user = User.get(:id => params[:id])
|
97
|
+
user_attributes = params[:user]
|
98
|
+
if params[:user][:password] == ""
|
99
|
+
user_attributes.delete("password")
|
100
|
+
user_attributes.delete("password_confirmation")
|
101
|
+
end
|
102
|
+
|
103
|
+
if user.update(user_attributes)
|
104
|
+
redirect '/'
|
105
|
+
else
|
106
|
+
session[:notice] = 'whoops, looks like there were some problems with your updates'
|
107
|
+
redirect "/users/#{user.id}/edit"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
get '/users/:id/delete' do
|
112
|
+
login_required
|
113
|
+
redirect "/users" unless current_user.admin? || current_user.id.to_s == params[:id]
|
114
|
+
|
115
|
+
if User.delete(params[:id])
|
116
|
+
session[:flash] = "way to go, you deleted a user"
|
117
|
+
else
|
118
|
+
session[:flash] = "deletion failed, for whatever reason"
|
119
|
+
end
|
120
|
+
redirect '/'
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
if Sinatra.const_defined?('FacebookObject')
|
125
|
+
get '/connect' do
|
126
|
+
if fb[:user]
|
127
|
+
if current_user.class != GuestUser
|
128
|
+
user = current_user
|
129
|
+
else
|
130
|
+
user = User.get(:fb_uid => fb[:user])
|
131
|
+
end
|
132
|
+
|
133
|
+
if user
|
134
|
+
if !user.fb_uid || user.fb_uid != fb[:user]
|
135
|
+
user.update :fb_uid => fb[:user]
|
136
|
+
end
|
137
|
+
session[:user] = user.id
|
138
|
+
else
|
139
|
+
user = User.set!(:fb_uid => fb[:user])
|
140
|
+
session[:user] = user.id
|
141
|
+
end
|
142
|
+
end
|
143
|
+
redirect '/'
|
144
|
+
end
|
145
|
+
|
146
|
+
get '/receiver' do
|
147
|
+
%[<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
148
|
+
<html xmlns="http://www.w3.org/1999/xhtml" >
|
149
|
+
<body>
|
150
|
+
<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/XdCommReceiver.js" type="text/javascript"></script>
|
151
|
+
</body>
|
152
|
+
</html>]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
module Helpers
|
159
|
+
def login_required
|
160
|
+
#not as efficient as checking the session. but this inits the fb_user if they are logged in
|
161
|
+
if current_user.class != GuestUser
|
162
|
+
return true
|
163
|
+
else
|
164
|
+
session[:return_to] = request.fullpath
|
165
|
+
redirect '/login'
|
166
|
+
return false
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def current_user
|
171
|
+
if session[:user]
|
172
|
+
User.get(:id => session[:user])
|
173
|
+
else
|
174
|
+
GuestUser.new
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def logged_in?
|
179
|
+
!!session[:user]
|
180
|
+
end
|
181
|
+
|
182
|
+
def use_layout?
|
183
|
+
!request.xhr?
|
184
|
+
end
|
185
|
+
|
186
|
+
#BECAUSE sinatra 9.1.1 can't load views from different paths properly
|
187
|
+
def get_view_as_string(filename)
|
188
|
+
view = options.red_slash_view_path + filename
|
189
|
+
data = ""
|
190
|
+
f = File.open(view, "r")
|
191
|
+
f.each_line do |line|
|
192
|
+
data += line
|
193
|
+
end
|
194
|
+
return data
|
195
|
+
end
|
196
|
+
|
197
|
+
def render_login_logout(html_attributes = {:class => ""})
|
198
|
+
css_classes = html_attributes.delete(:class)
|
199
|
+
parameters = ''
|
200
|
+
html_attributes.each_pair do |attribute, value|
|
201
|
+
parameters += "#{attribute}=\"#{value}\" "
|
202
|
+
end
|
203
|
+
|
204
|
+
result = "<div id='sinatra-authentication-login-logout' >"
|
205
|
+
if logged_in?
|
206
|
+
logout_parameters = html_attributes
|
207
|
+
# a tad janky?
|
208
|
+
logout_parameters.delete(:rel)
|
209
|
+
result += "<a href='/users/#{current_user.id}/edit' class='#{css_classes} sinatra-authentication-edit' #{parameters}>Edit account</a> "
|
210
|
+
if Sinatra.const_defined?('FacebookObject')
|
211
|
+
if fb[:user]
|
212
|
+
result += "<a href='javascript:FB.Connect.logoutAndRedirect(\"/logout\");' class='#{css_classes} sinatra-authentication-logout' #{logout_parameters}>Logout</a>"
|
213
|
+
else
|
214
|
+
result += "<a href='/logout' class='#{css_classes} sinatra-authentication-logout' #{logout_parameters}>Logout</a>"
|
215
|
+
end
|
216
|
+
else
|
217
|
+
result += "<a href='/logout' class='#{css_classes} sinatra-authentication-logout' #{logout_parameters}>Logout</a>"
|
218
|
+
end
|
219
|
+
else
|
220
|
+
result += "<a href='/signup' class='#{css_classes} sinatra-authentication-signup' #{parameters}>Signup</a> "
|
221
|
+
result += "<a href='/login' class='#{css_classes} sinatra-authentication-login' #{parameters}>Login</a>"
|
222
|
+
end
|
223
|
+
|
224
|
+
result += "</div>"
|
225
|
+
end
|
226
|
+
|
227
|
+
if Sinatra.const_defined?('FacebookObject')
|
228
|
+
def render_facebook_connect_link(text = 'Login using facebook', options = {:size => 'small'})
|
229
|
+
if options[:size] == 'small'
|
230
|
+
size = 'Small'
|
231
|
+
elsif options[:size] == 'medium'
|
232
|
+
size = 'Medium'
|
233
|
+
elsif options[:size] == 'large'
|
234
|
+
size = 'Large'
|
235
|
+
elsif options[:size] == 'xlarge'
|
236
|
+
size = 'BigPun'
|
237
|
+
else
|
238
|
+
size = 'Small'
|
239
|
+
end
|
240
|
+
|
241
|
+
%[<a href="#" onclick="FB.Connect.requireSession(function(){document.location = '/connect';}); return false;" class="fbconnect_login_button FBConnectButton FBConnectButton_#{size}">
|
242
|
+
<span id="RES_ID_fb_login_text" class="FBConnectButton_Text">
|
243
|
+
#{text}
|
244
|
+
</span>
|
245
|
+
</a>]
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
register RedSlash
|
251
|
+
end
|
252
|
+
|
253
|
+
class GuestUser
|
254
|
+
def guest?
|
255
|
+
true
|
256
|
+
end
|
257
|
+
|
258
|
+
def permission_level
|
259
|
+
0
|
260
|
+
end
|
261
|
+
|
262
|
+
# current_user.admin? returns false. current_user.has_a_baby? returns false.
|
263
|
+
# (which is a bit of an assumption I suppose)
|
264
|
+
def method_missing(m, *args)
|
265
|
+
return false
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
data/lib/views/edit.haml
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#redsafe
|
2
|
+
%h1
|
3
|
+
Edit
|
4
|
+
- if @user.id == current_user.id
|
5
|
+
account
|
6
|
+
- else
|
7
|
+
- if @user.email
|
8
|
+
= @user.email
|
9
|
+
- elsif @user.fb_uid
|
10
|
+
<fb:name uid=#{@user.fb_uid} linked='false' />
|
11
|
+
- else
|
12
|
+
account
|
13
|
+
%form{:action => "/users/#{@user.id}/edit", :method => "post"}
|
14
|
+
.field
|
15
|
+
.label
|
16
|
+
%label{:for => "user_email"} Email
|
17
|
+
%input{ :id => "user_email", :name => "user[email]", :size => 30, :type => "text", :value => @user.email }
|
18
|
+
.field
|
19
|
+
.label
|
20
|
+
%label{:for => "user_password"} New password
|
21
|
+
%input{ :id => "user_password", :name => "user[password]", :size => 30, :type => "password" }
|
22
|
+
.field
|
23
|
+
.label
|
24
|
+
%label{:for => "user_password_confirmation"} Confirm
|
25
|
+
%input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
|
26
|
+
-# don't render permission field if admin and editing yourself so you don't shoot yourself in the foot
|
27
|
+
- if current_user.admin? && current_user.id != @user.id
|
28
|
+
.field
|
29
|
+
.label
|
30
|
+
%label{:for => 'permission_level'} Permission level
|
31
|
+
%select{ :id => "permission_level", :name => "user[permission_level]" }
|
32
|
+
%option{:value => -1, :selected => @user.admin?}
|
33
|
+
Admin
|
34
|
+
%option{:value => 1, :selected => @user.permission_level == 1}
|
35
|
+
Authenticated user
|
36
|
+
.buttons
|
37
|
+
%input{ :value => "Update", :type => "submit" }
|
38
|
+
- if Sinatra.const_defined?('FacebookObject')
|
39
|
+
- unless @user.fb_uid
|
40
|
+
|
|
41
|
+
= render_facebook_connect_link('Link account with Facebook')
|
42
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#redsafe
|
2
|
+
%h1.page_title Users
|
3
|
+
%table
|
4
|
+
%tr
|
5
|
+
%th
|
6
|
+
- if current_user.admin?
|
7
|
+
%th permission level
|
8
|
+
- @users.each do |user|
|
9
|
+
%tr
|
10
|
+
%td
|
11
|
+
- if user.email
|
12
|
+
= user.email
|
13
|
+
- elsif user.fb_uid
|
14
|
+
<fb:name uid=#{user.fb_uid} />
|
15
|
+
- else
|
16
|
+
"user #{user.id}"
|
17
|
+
- if current_user.admin?
|
18
|
+
%td= user.permission_level
|
19
|
+
%td
|
20
|
+
%a{:href => "/users/#{user.id}"} show
|
21
|
+
- if current_user.admin?
|
22
|
+
%td
|
23
|
+
%a{:href => "/users/#{user.id}/edit"} edit
|
24
|
+
%td
|
25
|
+
-# this doesn't work for tk
|
26
|
+
- if !user.site_admin?
|
27
|
+
%a{:href => "/users/#{user.id}/delete", :onclick => "return confirm('you sure?')"} delete
|
28
|
+
- else
|
29
|
+
site admin
|
30
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#redsafe
|
2
|
+
%h1.page_title Login
|
3
|
+
%form{:action => "/login", :method => "post"}
|
4
|
+
.field
|
5
|
+
.label
|
6
|
+
%label{:for => "user_email'"} Email
|
7
|
+
%input{:id => "user_email", :name => "email", :size => 30, :type => "text"}
|
8
|
+
.field
|
9
|
+
.label
|
10
|
+
%label{:for => "user_password"} Password
|
11
|
+
%input{:id => "user_password", :name => "password", :size => 30, :type => "password"}
|
12
|
+
.buttons
|
13
|
+
%input{:value => "login", :type => "submit"}
|
14
|
+
%a{:href => "/signup", :class => 'sinatra_authentication_link'}
|
15
|
+
Signup
|
16
|
+
- if Sinatra.const_defined?('FacebookObject')
|
17
|
+
.third_party_signup
|
18
|
+
%h3.section_title One click login:
|
19
|
+
.login_link.facebook_login
|
20
|
+
= render_facebook_connect_link('Login using facebook', :size => 'large')
|
21
|
+
|
data/lib/views/show.haml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#redsafe
|
2
|
+
%h1.page_title Signup
|
3
|
+
%form{:action => "/signup", :method => "post"}
|
4
|
+
.field
|
5
|
+
.label
|
6
|
+
%label{:for => "user_email"} Email
|
7
|
+
%input{ :id => "user_email", :name => "user[email]", :size => 30, :type => "text" }
|
8
|
+
.field
|
9
|
+
.label
|
10
|
+
%label{:for => "user_password"} Password
|
11
|
+
%input{ :id => "user_password", :name => "user[password]", :size => 30, :type => "password" }
|
12
|
+
.field
|
13
|
+
.label
|
14
|
+
%label{:for => "user_password_confirmation"} Confirm Password
|
15
|
+
%input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
|
16
|
+
.buttons
|
17
|
+
%input{ :value => "Create account", :type => "submit" }
|
18
|
+
%a{:href => "/login", :class => 'sinatra_authentication_link'}
|
19
|
+
Login
|
20
|
+
- if Sinatra.const_defined?('FacebookObject')
|
21
|
+
.third_party_signup
|
22
|
+
%h3.section_title One click signup:
|
23
|
+
.login_link.facebook_login
|
24
|
+
= render_facebook_connect_link('Signup using facebook', :size => 'large')
|
25
|
+
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redsafe
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ruby Shot
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-12 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: sinatra
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: dm-core
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: dm-validations
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: dm-timestamps
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: date
|
57
|
+
type: :development
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: time
|
67
|
+
type: :development
|
68
|
+
version_requirement:
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
75
|
+
description: Red Safe is a conveyor belt to get your web-based project on a fast roll.
|
76
|
+
email: domochoice(at)yahoo:com
|
77
|
+
executables: []
|
78
|
+
|
79
|
+
extensions: []
|
80
|
+
|
81
|
+
extra_rdoc_files:
|
82
|
+
- LICENSE
|
83
|
+
- README.rdoc
|
84
|
+
files:
|
85
|
+
- .document
|
86
|
+
- .gitignore
|
87
|
+
- GemcutterInstructions.rdoc
|
88
|
+
- LICENSE
|
89
|
+
- README.rdoc
|
90
|
+
- Rakefile
|
91
|
+
- lib/models/scarlet_letters.rb
|
92
|
+
- lib/models/sl_aah.rb
|
93
|
+
- lib/models/sl_user.rb
|
94
|
+
- lib/redsafe.rb
|
95
|
+
- lib/redsafe/version.rb
|
96
|
+
- lib/views/edit.haml
|
97
|
+
- lib/views/index.haml
|
98
|
+
- lib/views/login.haml
|
99
|
+
- lib/views/show.haml
|
100
|
+
- lib/views/signup.haml
|
101
|
+
- test/redsafe_test.rb
|
102
|
+
- test/test_helper.rb
|
103
|
+
has_rdoc: true
|
104
|
+
homepage: http://github.com/rubyshot/redsafe
|
105
|
+
licenses: []
|
106
|
+
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options:
|
109
|
+
- --charset=UTF-8
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: "0"
|
117
|
+
version:
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: "0"
|
123
|
+
version:
|
124
|
+
requirements: []
|
125
|
+
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 1.3.5
|
128
|
+
signing_key:
|
129
|
+
specification_version: 3
|
130
|
+
summary: This is a gem tool which simplifies signup functionality using various web services for connection credentials.
|
131
|
+
test_files:
|
132
|
+
- test/redsafe_test.rb
|
133
|
+
- test/test_helper.rb
|