appoxy_rails 0.0.6
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/README.markdown +1 -0
- data/lib/api/api_controller.rb +106 -0
- data/lib/api/client.rb +110 -0
- data/lib/api/client_helper.rb +32 -0
- data/lib/api/signatures.rb +26 -0
- data/lib/appoxy_api.rb +10 -0
- data/lib/appoxy_sessions.rb +6 -0
- data/lib/appoxy_ui.rb +2 -0
- data/lib/sessions/application_controller.rb +82 -0
- data/lib/sessions/sessions_controller.rb +119 -0
- data/lib/sessions/shareable.rb +206 -0
- data/lib/sessions/user.rb +76 -0
- data/lib/sessions/users_controller.rb +122 -0
- data/lib/ui/application_helper.rb +35 -0
- metadata +89 -0
data/README.markdown
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Nada.
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Appoxy
|
2
|
+
|
3
|
+
module Api
|
4
|
+
|
5
|
+
# The api controllers that use this should set:
|
6
|
+
# protect_from_forgery :only => [] # can add methods to here, eg: :create, :update, :destroy
|
7
|
+
|
8
|
+
# rescue_from SigError, :with => :send_error
|
9
|
+
# rescue_from Api::ApiError, :with => :send_error
|
10
|
+
# before_filter :verify_signature(params)
|
11
|
+
|
12
|
+
# Your Controller must define a secret_key_for_signature method which will return the secret key to use to generate signature.
|
13
|
+
|
14
|
+
module ApiController
|
15
|
+
|
16
|
+
def verify_signature
|
17
|
+
params2 = nil
|
18
|
+
if request.put? || request.post?
|
19
|
+
# We'll extract params from body instead here
|
20
|
+
# todo: maybe check for json format first in case this is a file or something?
|
21
|
+
body = request.body.read
|
22
|
+
puts 'body=' + body.inspect
|
23
|
+
params2 = ActiveSupport::JSON.decode(body)
|
24
|
+
puts 'params2=' + params2.inspect
|
25
|
+
params.merge! params2
|
26
|
+
end
|
27
|
+
|
28
|
+
#operation = "#{controller_name}/#{action_name}"
|
29
|
+
operation = request.env["PATH_INFO"].gsub(/\/api\//, "")# here we're getting original request url'
|
30
|
+
puts "XXX " + operation
|
31
|
+
|
32
|
+
#getting clean params (without parsed via routes)
|
33
|
+
params_for_signature = params2||request.query_parameters
|
34
|
+
#removing mandatory params
|
35
|
+
params_for_signature = params_for_signature.delete_if {|key, value| ["access_key", "sigv", "sig", "timestamp"].include? key}
|
36
|
+
|
37
|
+
|
38
|
+
#p "params " +operation+Appoxy::Api::Signatures.hash_to_s(params_for_signature)
|
39
|
+
access_key = params["access_key"]
|
40
|
+
sigv = params["sigv"]
|
41
|
+
timestamp = params["timestamp"]
|
42
|
+
sig = params["sig"]
|
43
|
+
|
44
|
+
raise Appoxy::Api::ApiError, "No access_key" if access_key.nil?
|
45
|
+
raise Appoxy::Api::ApiError, "No sigv" if sigv.nil?
|
46
|
+
raise Appoxy::Api::ApiError, "No timestamp" if timestamp.nil?
|
47
|
+
raise Appoxy::Api::ApiError, "No sig" if sig.nil?
|
48
|
+
timestamp2 = Appoxy::Api::Signatures.generate_timestamp(Time.now.gmtime)
|
49
|
+
raise Appoxy::Api::ApiError, "Request timed out!" unless (Time.parse(timestamp2)-Time.parse(timestamp))<60 # deny all requests older than 60 seconds
|
50
|
+
# Get application defined secret_key
|
51
|
+
secret_key = secret_key_for_signature(access_key)
|
52
|
+
sig2 = Appoxy::Api::Signatures.generate_signature(operation+Appoxy::Api::Signatures.hash_to_s(params_for_signature), timestamp, secret_key)
|
53
|
+
#p "signature 1 " + sig2 + " signature 2 " + sig
|
54
|
+
raise Appoxy::Api::ApiError, "Invalid signature!" unless sig == sig2
|
55
|
+
|
56
|
+
puts 'Verified OK'
|
57
|
+
true
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def sig_should
|
63
|
+
raise "You didn't define a sig_should method in your controller!"
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def send_ok(msg={})
|
68
|
+
response_as_string = '' # in case we want to add debugging or something
|
69
|
+
# respond_to do |format|
|
70
|
+
# format.json { render :json=>msg }
|
71
|
+
response_as_string = render_to_string :json => msg
|
72
|
+
render :json => response_as_string
|
73
|
+
# end
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def send_error(statuscode_or_error, msg=nil)
|
79
|
+
exc = nil
|
80
|
+
if statuscode_or_error.is_a? Exception
|
81
|
+
exc = statuscode_or_error
|
82
|
+
statuscode_or_error = 400
|
83
|
+
msg = exc.message
|
84
|
+
end
|
85
|
+
# deprecate status, should use status_code
|
86
|
+
json_msg = {"status_code"=>statuscode_or_error, "msg"=>msg}
|
87
|
+
render :json=>json_msg, :status=>statuscode_or_error
|
88
|
+
true
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
class ApiError < StandardError
|
96
|
+
|
97
|
+
def initialize(msg=nil)
|
98
|
+
super(msg)
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
data/lib/api/client.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
module Appoxy
|
2
|
+
module Api
|
3
|
+
|
4
|
+
require 'rest_client'
|
5
|
+
|
6
|
+
# Subclass must define:
|
7
|
+
# host: endpoint url for service
|
8
|
+
class Client
|
9
|
+
|
10
|
+
attr_accessor :host, :access_key, :secret_key
|
11
|
+
|
12
|
+
|
13
|
+
def initialize(host, access_key, secret_key, options={})
|
14
|
+
@host = host
|
15
|
+
@access_key = access_key
|
16
|
+
@secret_key = secret_key
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def get(method, params={}, options={})
|
21
|
+
begin
|
22
|
+
# ClientHelper.run_http(host, access_key, secret_key, :get, method, nil, params)
|
23
|
+
parse_response RestClient.get(append_params(url(method), add_params(method, params)), headers)
|
24
|
+
rescue RestClient::BadRequest => ex
|
25
|
+
# puts ex.http_body
|
26
|
+
raise "Bad Request: " + ActiveSupport::JSON.decode(ex.http_body)["msg"].to_s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def post(method, params={}, options={})
|
32
|
+
begin
|
33
|
+
parse_response RestClient.post(url(method), add_params(method, params).to_json, headers)
|
34
|
+
#ClientHelper.run_http(host, access_key, secret_key, :post, method, nil, params)
|
35
|
+
rescue RestClient::BadRequest => ex
|
36
|
+
# puts ex.http_body
|
37
|
+
raise "Bad Request: " + ActiveSupport::JSON.decode(ex.http_body)["msg"].to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def put(method, body, options={})
|
44
|
+
begin
|
45
|
+
parse_response RestClient.put(url(method), add_params(method, body).to_json, headers)
|
46
|
+
#ClientHelper.run_http(host, access_key, secret_key, :put, method, body, nil)
|
47
|
+
rescue RestClient::BadRequest => ex
|
48
|
+
# puts ex.http_body
|
49
|
+
raise "Bad Request: " + ActiveSupport::JSON.decode(ex.http_body)["msg"].to_s
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def delete(method, params={}, options={})
|
55
|
+
begin
|
56
|
+
parse_response RestClient.delete(append_params(url(method), add_params(method, params)))
|
57
|
+
rescue RestClient::BadRequest => ex
|
58
|
+
raise "Bad Request: " + ActiveSupport::JSON.decode(ex.http_body)["msg"].to_s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def url(command_path)
|
64
|
+
url = host + command_path
|
65
|
+
url
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def add_params(command_path, hash)
|
70
|
+
ts = Appoxy::Api::Signatures.generate_timestamp(Time.now.gmtime)
|
71
|
+
#p "hash_to s" + command_path + Appoxy::Api::Signatures.hash_to_s(hash)
|
72
|
+
sig = Appoxy::Api::Signatures.generate_signature(command_path + Appoxy::Api::Signatures.hash_to_s(hash), ts, secret_key)
|
73
|
+
extra_params = {'sigv'=>"0.1", 'sig' => sig, 'timestamp' => ts, 'access_key' => access_key}
|
74
|
+
hash.merge!(extra_params)
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def append_params(host, params)
|
80
|
+
host += "?"
|
81
|
+
i = 0
|
82
|
+
params.each_pair do |k, v|
|
83
|
+
host += "&" if i > 0
|
84
|
+
host += k + "=" + CGI.escape(v)
|
85
|
+
i+=1
|
86
|
+
end
|
87
|
+
return host
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def headers
|
92
|
+
user_agent = "Appoxy API Ruby Client"
|
93
|
+
headers = {'User-Agent' => user_agent}
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def parse_response(response)
|
98
|
+
begin
|
99
|
+
return ActiveSupport::JSON.decode(response.to_s)
|
100
|
+
rescue => ex
|
101
|
+
puts 'response that caused error = ' + response.to_s
|
102
|
+
raise ex
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Appoxy
|
2
|
+
module Api
|
3
|
+
|
4
|
+
require 'rest_client'
|
5
|
+
|
6
|
+
module ClientHelper
|
7
|
+
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
class ClientError < StandardError
|
12
|
+
|
13
|
+
attr_reader :response_hash
|
14
|
+
|
15
|
+
def initialize(class_name, response_hash)
|
16
|
+
puts 'response-hash=' + response_hash.inspect
|
17
|
+
super("#{class_name} - #{response_hash["msg"]}")
|
18
|
+
@response_hash = response_hash
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class ServiceError < StandardError
|
23
|
+
attr_reader :body
|
24
|
+
|
25
|
+
def initialize(class_name, body)
|
26
|
+
super("#{class_name}")
|
27
|
+
@body = body
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Appoxy
|
2
|
+
module Api
|
3
|
+
module Signatures
|
4
|
+
|
5
|
+
|
6
|
+
def self.generate_timestamp(gmtime)
|
7
|
+
return gmtime.strftime("%Y-%m-%dT%H:%M:%SZ")
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def self.generate_signature(operation, timestamp, secret_key)
|
12
|
+
my_sha_hmac = Digest::HMAC.digest(operation + timestamp, secret_key, Digest::SHA1)
|
13
|
+
my_b64_hmac_digest = Base64.encode64(my_sha_hmac).strip
|
14
|
+
return my_b64_hmac_digest
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def self.hash_to_s(hash)
|
19
|
+
str = ""
|
20
|
+
hash.each_pair {|key, value| str+= "#{key}#{value}" }
|
21
|
+
#removing all characters that could differ after parsing with rails
|
22
|
+
return str.delete "\"\/:{}[]\' T"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/appoxy_api.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
require 'digest/hmac'
|
3
|
+
require 'net/http'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
require File.join(File.dirname(__FILE__), "api", "api_controller")
|
7
|
+
require File.join(File.dirname(__FILE__), "api", "client_helper")
|
8
|
+
require File.join(File.dirname(__FILE__), "api", "signatures")
|
9
|
+
require File.join(File.dirname(__FILE__), "api", "client")
|
10
|
+
|
@@ -0,0 +1,6 @@
|
|
1
|
+
require 'simple_record'
|
2
|
+
require File.join(File.dirname(__FILE__), "sessions", "user")
|
3
|
+
require File.join(File.dirname(__FILE__), "sessions", "application_controller")
|
4
|
+
require File.join(File.dirname(__FILE__), "sessions", "sessions_controller")
|
5
|
+
require File.join(File.dirname(__FILE__), "sessions", "users_controller")
|
6
|
+
require File.join(File.dirname(__FILE__), "sessions", "shareable")
|
data/lib/appoxy_ui.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module Appoxy
|
2
|
+
|
3
|
+
module Sessions
|
4
|
+
module ApplicationController
|
5
|
+
|
6
|
+
|
7
|
+
def logout_keeping_session!
|
8
|
+
@current_user = nil # not logged in, and don't do it for me
|
9
|
+
session[:user_id] = nil # keeps the session but kill our variable
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def logged_in?
|
14
|
+
#puts 'logged_in??'
|
15
|
+
#puts 'current_user=' + current_user.inspect
|
16
|
+
current_user
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def current_user=(new_user)
|
21
|
+
session[:user_id] = new_user ? new_user.id : nil
|
22
|
+
@current_user = new_user
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def current_user
|
27
|
+
@current_user ||= (login_from_session)
|
28
|
+
@current_user
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def login_from_session
|
33
|
+
#puts 'Login from session=' + session[:user_id].inspect
|
34
|
+
::User.find_by_id(session[:user_id]) if session[:user_id]
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# helper_method :logged_in?
|
39
|
+
# helper_method :current_user
|
40
|
+
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def random_string(length=10)
|
45
|
+
chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'
|
46
|
+
password = ''
|
47
|
+
length.times { password << chars[rand(chars.size)] }
|
48
|
+
password
|
49
|
+
end
|
50
|
+
|
51
|
+
def authenticate
|
52
|
+
if !logged_in?
|
53
|
+
flash[:warning] = "You need to login to access this page."
|
54
|
+
session[:return_to] = request.request_uri # return to after logging in
|
55
|
+
puts "ac=" + params[:ac].inspect
|
56
|
+
if params[:user_id] && params[:ac]
|
57
|
+
# todo: should we store ac in cookie? Make it easier to pass around
|
58
|
+
cookies[:ac] = params[:ac]
|
59
|
+
# then from an invite
|
60
|
+
user = ::User.find(params[:user_id])
|
61
|
+
if user && user.password.blank? # is this the best way to decide of user has not logged in? Could also check status.
|
62
|
+
redirect_to :controller=>"users", :action=>"new", :email=>user.email, :ac=>params[:ac]
|
63
|
+
return
|
64
|
+
end
|
65
|
+
end
|
66
|
+
redirect_to :controller=>"sessions", :action=>"new", :ac=>params[:ac]
|
67
|
+
end
|
68
|
+
|
69
|
+
after_authenticate
|
70
|
+
|
71
|
+
end
|
72
|
+
def after_authenticate
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Appoxy
|
2
|
+
|
3
|
+
module Sessions
|
4
|
+
module SessionsController
|
5
|
+
|
6
|
+
def new
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
def create
|
11
|
+
before_create
|
12
|
+
|
13
|
+
logout_keeping_session!
|
14
|
+
|
15
|
+
#puts 'params=' + params.inspect
|
16
|
+
@email = params[:email]
|
17
|
+
@has_password = params[:has_password]
|
18
|
+
#puts 'has_pass? ' + @has_password.inspect
|
19
|
+
|
20
|
+
if params[:has_password].blank?
|
21
|
+
flash[:error] = "Please click the radio button to let us know if you have a password or not."
|
22
|
+
render :action=>"new"
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
if @has_password == "true"
|
27
|
+
user = ::User.find_by_email(@email)
|
28
|
+
# user = User.authenticate(@email, params[:password])
|
29
|
+
if user && user.authenticate(params[:password])
|
30
|
+
self.current_user = user
|
31
|
+
flash[:info] = "Logged in successfully."
|
32
|
+
orig_url = session[:return_to]
|
33
|
+
puts 'orig_url = ' + orig_url.to_s
|
34
|
+
session[:return_to] = nil
|
35
|
+
if !orig_url.nil?
|
36
|
+
redirect_to orig_url # if entered via a different url
|
37
|
+
else
|
38
|
+
after_create
|
39
|
+
end
|
40
|
+
user.last_login = Time.now
|
41
|
+
user.save(:dirty=>true)
|
42
|
+
else
|
43
|
+
flash[:info] = "Invalid email or password. Please try again."
|
44
|
+
render :action => 'new'
|
45
|
+
end
|
46
|
+
else
|
47
|
+
# new user
|
48
|
+
|
49
|
+
redirect_to (new_user_path + "?email=#{@email}")
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
def before_create
|
55
|
+
|
56
|
+
end
|
57
|
+
def after_create
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def reset_password
|
63
|
+
before_reset_password
|
64
|
+
|
65
|
+
unless verify_recaptcha
|
66
|
+
flash[:error] = "You are not human! Please try again."
|
67
|
+
render :action=>"forgot_password"
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
@email = params[:email]
|
72
|
+
unless User.email_is_valid? @email
|
73
|
+
flash[:error] = "You must enter a valid email."
|
74
|
+
render :action=>"forgot_password"
|
75
|
+
return
|
76
|
+
end
|
77
|
+
|
78
|
+
@user = ::User.find_by_email(@email)
|
79
|
+
unless @user
|
80
|
+
flash[:error] = "Email not found."
|
81
|
+
render :action=>"forgot_password"
|
82
|
+
return
|
83
|
+
end
|
84
|
+
|
85
|
+
@newpass = random_string(8)
|
86
|
+
|
87
|
+
@user.password = @newpass
|
88
|
+
@user.save(:dirty=>true)
|
89
|
+
|
90
|
+
flash[:success] = "Password reset. You should receive an email shortly with a new password."
|
91
|
+
redirect_to :action=>"new"
|
92
|
+
|
93
|
+
after_reset_password
|
94
|
+
end
|
95
|
+
|
96
|
+
def before_reset_password
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
# This is a great spot to send an email with the new password (the only spot actually).
|
101
|
+
def after_reset_password
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
def destroy
|
106
|
+
logout
|
107
|
+
end
|
108
|
+
|
109
|
+
def logout
|
110
|
+
@current_user = nil
|
111
|
+
reset_session
|
112
|
+
flash[:info] = "You have been logged out."
|
113
|
+
redirect_to('/')
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'aws'
|
2
|
+
require 'simple_record'
|
3
|
+
|
4
|
+
module Appoxy
|
5
|
+
module Sessions
|
6
|
+
module Shareable
|
7
|
+
|
8
|
+
# Call this method on your Sharable object to share it with the person.
|
9
|
+
# returns: a hash with :user (the user that the item was shared with), :ac (activation code that should be sent to the user)
|
10
|
+
# or false if couldn't be shared.
|
11
|
+
# You can check for errors by looking at the errors array of the object.
|
12
|
+
# Eg:
|
13
|
+
# if my_ob.share_with(x)
|
14
|
+
# # all good
|
15
|
+
# Mail the user a link that contains user_id and ac, this gem will take care of the rest.
|
16
|
+
# else
|
17
|
+
# # not all good, check errors
|
18
|
+
# errors = my_ob.errors
|
19
|
+
# end
|
20
|
+
|
21
|
+
def share_with(email, access_rights={}, options={})
|
22
|
+
|
23
|
+
access_rights = {} if access_rights.nil?
|
24
|
+
|
25
|
+
@email = email.strip
|
26
|
+
|
27
|
+
if @email == self.user.email
|
28
|
+
self.errors.add_to_base("User already owns this item.")
|
29
|
+
return false
|
30
|
+
end
|
31
|
+
|
32
|
+
user = ::User.find_by_email(@email)
|
33
|
+
if user.nil?
|
34
|
+
# lets create the user and send them an invite.
|
35
|
+
user = ::User.new(:email=>@email, :status=>"invited")
|
36
|
+
user.set_activation_code # todo: this shouldn't be on user anymore
|
37
|
+
if user.save
|
38
|
+
|
39
|
+
else
|
40
|
+
self.errors = user.errors
|
41
|
+
return false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
activation_code = user.activation_code
|
45
|
+
|
46
|
+
# check if exists
|
47
|
+
share_domain = self.share_domain
|
48
|
+
item_id_name = self.item_id_name
|
49
|
+
# puts 'share_domain = ' + share_domain.inspect
|
50
|
+
@sdb = SimpleRecord::Base.connection
|
51
|
+
# @shared_with = share_class.find(:first, :conditions=>["user_id = ? and item_id = ?", user.id, @item.id])
|
52
|
+
@project_user = Shareable.get_results(:first, ["select * from #{share_domain} where user_id=? and #{item_id_name} = ?", user.id, self.id])
|
53
|
+
puts 'sharing user=' + @project_user.inspect
|
54
|
+
unless @project_user.nil?
|
55
|
+
self.errors.add_to_base("This item is already shared with #{email}.")
|
56
|
+
return false
|
57
|
+
end
|
58
|
+
|
59
|
+
now = Time.now
|
60
|
+
id = share_id(user)
|
61
|
+
@sdb.put_attributes(share_domain, id, {:new_share=>true,
|
62
|
+
:id=>id,
|
63
|
+
:created=>SimpleRecord::Translations.pad_and_offset(now),
|
64
|
+
:updated=>SimpleRecord::Translations.pad_and_offset(now),
|
65
|
+
:user_id => user.id,
|
66
|
+
:activation_code=>activation_code,
|
67
|
+
:status=>"invited",
|
68
|
+
item_id_name => self.id}.merge(access_rights),
|
69
|
+
true,
|
70
|
+
:create_domain=>true)
|
71
|
+
|
72
|
+
# ret = {
|
73
|
+
# :user=>user,
|
74
|
+
# :ac=>activation_code
|
75
|
+
# }
|
76
|
+
# return ret
|
77
|
+
return user
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
def item_id_name
|
82
|
+
return self.class.name.foreign_key
|
83
|
+
end
|
84
|
+
|
85
|
+
def common_attributes
|
86
|
+
["new_share", "id", "created", "updated", "user_id", item_id_name]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns a list of users that this item is shared with.
|
90
|
+
def shared_with
|
91
|
+
project_users = Shareable.get_results(:all, ["select * from #{share_domain} where #{item_id_name} = ?", self.id])
|
92
|
+
user_ids = []
|
93
|
+
options_hash = {}
|
94
|
+
project_users.each do |puhash|
|
95
|
+
puhash.each_pair do |k, v|
|
96
|
+
puhash[k] = v[0]
|
97
|
+
end
|
98
|
+
puts 'puhash=' + puhash.inspect
|
99
|
+
user_ids << puhash["user_id"]
|
100
|
+
options_hash[puhash["user_id"]] = puhash
|
101
|
+
end
|
102
|
+
ret = ::User.find(:all, :conditions=>["id in ('#{user_ids.join("','")}')"]).collect do |u|
|
103
|
+
def u.share_options=(options=nil)
|
104
|
+
instance_variable_set(:@share_options, options)
|
105
|
+
end
|
106
|
+
|
107
|
+
def u.share_options
|
108
|
+
instance_variable_get(:@share_options)
|
109
|
+
end
|
110
|
+
|
111
|
+
u.share_options=options_hash[u.id]
|
112
|
+
u
|
113
|
+
end
|
114
|
+
ret
|
115
|
+
end
|
116
|
+
|
117
|
+
# this unshares by the
|
118
|
+
def unshare_by_id(id)
|
119
|
+
# @project_user = ProjectUser.find(params[:pu_id])
|
120
|
+
# @project_user.delete
|
121
|
+
# puts 'unsharing ' + id.to_s
|
122
|
+
@sdb = SimpleRecord::Base.connection
|
123
|
+
puts "delete_attributes=" + @sdb.delete_attributes(share_domain, id.to_s).inspect
|
124
|
+
# puts 'deleted?'
|
125
|
+
end
|
126
|
+
|
127
|
+
# Unshare by user.
|
128
|
+
def unshare(user)
|
129
|
+
@sdb = SimpleRecord::Base.connection
|
130
|
+
@sdb.delete_attributes(share_domain, share_id(user))
|
131
|
+
# @project_user = Shareable.get_results(:first, ["select * from #{share_domain} where user_id=? and #{item_id_name} = ?", user.id, self.id])
|
132
|
+
# @project_user.each do |pu|
|
133
|
+
# @sdb.delete_attributes(share_domain, pu["id"])
|
134
|
+
# end
|
135
|
+
end
|
136
|
+
|
137
|
+
def update_sharing_options(user, options={})
|
138
|
+
options={} if options.nil?
|
139
|
+
# puts 'options=' + ({ :updated=>Time.now }.merge(options)).inspect
|
140
|
+
@sdb = SimpleRecord::Base.connection
|
141
|
+
@project_user = Shareable.get_results(:first, ["select * from #{share_domain} where user_id=? and #{item_id_name} = ?", user.id, self.id])
|
142
|
+
# compare values
|
143
|
+
to_delete = []
|
144
|
+
@project_user.each_pair do |k, v|
|
145
|
+
if !common_attributes.include?(k) && !options.include?(k)
|
146
|
+
to_delete << k
|
147
|
+
end
|
148
|
+
end
|
149
|
+
if to_delete.size > 0
|
150
|
+
puts 'to_delete=' + to_delete.inspect
|
151
|
+
@sdb.delete_attributes(share_domain, share_id(user), to_delete)
|
152
|
+
end
|
153
|
+
@sdb.put_attributes(share_domain, share_id(user), {:updated=>Time.now}.merge(options), true)
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
def share_id(user)
|
158
|
+
"#{self.id}_#{user.id}"
|
159
|
+
end
|
160
|
+
|
161
|
+
def share_domain
|
162
|
+
# puts 'instance share_domain'
|
163
|
+
ret = self.class.name + "User"
|
164
|
+
# puts 'SHARE_NAME=' + ret
|
165
|
+
ret = ret.tableize
|
166
|
+
# puts 'ret=' + ret
|
167
|
+
ret
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
def self.get_results(which, q)
|
172
|
+
@sdb = SimpleRecord::Base.connection
|
173
|
+
next_token = nil
|
174
|
+
ret = []
|
175
|
+
begin
|
176
|
+
begin
|
177
|
+
response = @sdb.select(q, next_token)
|
178
|
+
rs = response[:items]
|
179
|
+
rs.each_with_index do |i, index|
|
180
|
+
puts 'i=' + i.inspect
|
181
|
+
i.each_key do |k|
|
182
|
+
puts 'key=' + k.inspect
|
183
|
+
if which == :first
|
184
|
+
return i[k].update("id"=>k)
|
185
|
+
end
|
186
|
+
ret << i[k]
|
187
|
+
end
|
188
|
+
# break if index > 100
|
189
|
+
end
|
190
|
+
next_token = response[:next_token]
|
191
|
+
end until next_token.nil?
|
192
|
+
rescue Aws::AwsError, Aws::ActiveSdb::ActiveSdbError
|
193
|
+
if ($!.message().index("NoSuchDomain") != nil)
|
194
|
+
puts 'NO SUCH DOMAIN!!!'
|
195
|
+
# this is ok
|
196
|
+
else
|
197
|
+
raise $!
|
198
|
+
end
|
199
|
+
end
|
200
|
+
which == :first ? nil : ret
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Appoxy
|
2
|
+
|
3
|
+
module Sessions
|
4
|
+
|
5
|
+
class User < SimpleRecord::Base
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
puts self.name + " included in " + base.name
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
has_strings :email,
|
13
|
+
{:name => :password, :hashed=>true},
|
14
|
+
:first_name,
|
15
|
+
:last_name,
|
16
|
+
:remember_me,
|
17
|
+
:activation_code,
|
18
|
+
:status # invited, active
|
19
|
+
|
20
|
+
has_dates :last_login
|
21
|
+
|
22
|
+
|
23
|
+
def validate
|
24
|
+
errors.add("email", "is not valid") unless User.email_is_valid?(email)
|
25
|
+
|
26
|
+
if status == "invited"
|
27
|
+
# doesn't need password
|
28
|
+
else
|
29
|
+
errors.add("password", "must be at least 6 characters long.") if password.blank?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def self.email_is_valid?(email)
|
35
|
+
return email.present? && email =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def is_active?
|
40
|
+
status == "active"
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
def set_activation_code
|
46
|
+
self.activation_code=Digest::SHA1.hexdigest(email.to_s+Time.now.to_s)
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def activate!
|
51
|
+
self.activation_code=nil
|
52
|
+
self.status = "active"
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
def authenticate(password)
|
58
|
+
|
59
|
+
return nil if attributes["password"].blank? # if the user has no password (will this happen? maybe for invites...)
|
60
|
+
|
61
|
+
# This is a normal unencrypted password, temporary
|
62
|
+
if attributes["password"][0].length < 100
|
63
|
+
self.password = attributes["password"][0]
|
64
|
+
self.save
|
65
|
+
end
|
66
|
+
|
67
|
+
(self.password == password) ? self : nil
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module Appoxy
|
2
|
+
|
3
|
+
module Sessions
|
4
|
+
module UsersController
|
5
|
+
|
6
|
+
|
7
|
+
def new
|
8
|
+
before_new
|
9
|
+
if params[:id]
|
10
|
+
@user = ::User.find params[:id]
|
11
|
+
else
|
12
|
+
@user = ::User.new
|
13
|
+
@user.email = params[:email] if params[:email]
|
14
|
+
end
|
15
|
+
@user.activation_code = params[:ac] if params[:ac]
|
16
|
+
after_new
|
17
|
+
end
|
18
|
+
|
19
|
+
def before_new
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
def after_new
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
def create
|
28
|
+
|
29
|
+
before_create
|
30
|
+
|
31
|
+
@user = ::User.new(params[:user])
|
32
|
+
|
33
|
+
if @user.password != params[:password_confirmation]
|
34
|
+
flash[:error] = "Confirmation password does not match. Please try again."
|
35
|
+
render :action=>"new"
|
36
|
+
return
|
37
|
+
end
|
38
|
+
|
39
|
+
if params[:user][:password].length < 6
|
40
|
+
flash[:error] = "Password can not be less than 6 characters."
|
41
|
+
render :action=>"new"
|
42
|
+
return
|
43
|
+
end
|
44
|
+
|
45
|
+
existing_user = ::User.find_by_email(@user.email)
|
46
|
+
|
47
|
+
if existing_user
|
48
|
+
if params[:ac]
|
49
|
+
|
50
|
+
end
|
51
|
+
# todo: remove activation_code on user
|
52
|
+
if @user.activation_code.present?
|
53
|
+
# hasn't logged in yet, probably invited, need to check access key
|
54
|
+
if existing_user.activation_code == @user.activation_code
|
55
|
+
existing_user.activate!
|
56
|
+
existing_user.password = @user.password
|
57
|
+
@user = existing_user
|
58
|
+
end
|
59
|
+
else
|
60
|
+
flash[:error] = "The email you entered already exists in our system. You might want to try logging in if you already have an account."
|
61
|
+
render :action=>"new"
|
62
|
+
return
|
63
|
+
end
|
64
|
+
else
|
65
|
+
@user.status = "active"
|
66
|
+
end
|
67
|
+
|
68
|
+
before_save_in_create
|
69
|
+
if @user.save
|
70
|
+
self.current_user = @user
|
71
|
+
flash[:success] = "Your account was created successfully."
|
72
|
+
after_save_in_create
|
73
|
+
after_create
|
74
|
+
else
|
75
|
+
render :action => "new"
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
def before_create
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
def before_save_in_create
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
def after_save_in_create
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
def after_create
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
# Usually a user gets here via an activation link in email.
|
98
|
+
def activate
|
99
|
+
logout_keeping_session!
|
100
|
+
@user = ::User.find_by_activation_code(params[:ac]) unless params[:ac].blank?
|
101
|
+
case
|
102
|
+
when (!params[:ac].blank?) && @user && !@user.is_active?
|
103
|
+
flash[:info] = "Account activated. please login."
|
104
|
+
@user.activate!
|
105
|
+
redirect_to login_url
|
106
|
+
when params[:ac].blank?
|
107
|
+
flash[:error] = "The activation code was missing. Please follow the URL from your email."
|
108
|
+
redirect_to(root_url)
|
109
|
+
else
|
110
|
+
flash[:error] = "We couldn't find a user with that activation code -- check your email? Or maybe you've already activated -- try signing in."
|
111
|
+
redirect_to(root_url)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
end
|
122
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Appoxy
|
2
|
+
|
3
|
+
module UI
|
4
|
+
|
5
|
+
module ApplicationHelper
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
# puts self.class.name + " included in " + base.class.name
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
def current_url
|
13
|
+
request.url
|
14
|
+
end
|
15
|
+
|
16
|
+
def flash_messages
|
17
|
+
s = ""
|
18
|
+
flash.each_pair do |type, msg|
|
19
|
+
if msg.is_a?(Array)
|
20
|
+
msg.each do |m|
|
21
|
+
s << content_tag(:div, m, :class => type)
|
22
|
+
end
|
23
|
+
else
|
24
|
+
s << content_tag(:div, msg, :class => type)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
s.html_safe
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: appoxy_rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 6
|
9
|
+
version: 0.0.6
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Travis Reeder
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-12-23 00:00:00 -08:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rest-client
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
description: Appoxy API Helper gem description...
|
34
|
+
email: travis@appoxy.com
|
35
|
+
executables: []
|
36
|
+
|
37
|
+
extensions: []
|
38
|
+
|
39
|
+
extra_rdoc_files:
|
40
|
+
- README.markdown
|
41
|
+
files:
|
42
|
+
- lib/api/api_controller.rb
|
43
|
+
- lib/api/client.rb
|
44
|
+
- lib/api/client_helper.rb
|
45
|
+
- lib/api/signatures.rb
|
46
|
+
- lib/appoxy_api.rb
|
47
|
+
- lib/appoxy_sessions.rb
|
48
|
+
- lib/appoxy_ui.rb
|
49
|
+
- lib/sessions/application_controller.rb
|
50
|
+
- lib/sessions/sessions_controller.rb
|
51
|
+
- lib/sessions/shareable.rb
|
52
|
+
- lib/sessions/user.rb
|
53
|
+
- lib/sessions/users_controller.rb
|
54
|
+
- lib/ui/application_helper.rb
|
55
|
+
- README.markdown
|
56
|
+
has_rdoc: true
|
57
|
+
homepage: http://www.appoxy.com
|
58
|
+
licenses: []
|
59
|
+
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
version: "0"
|
81
|
+
requirements: []
|
82
|
+
|
83
|
+
rubyforge_project:
|
84
|
+
rubygems_version: 1.3.7
|
85
|
+
signing_key:
|
86
|
+
specification_version: 3
|
87
|
+
summary: Appoxy Rails Helper gem
|
88
|
+
test_files: []
|
89
|
+
|