cadenero 0.0.2.a3 → 0.0.2.b1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/README.md +4 -3
- data/app/controllers/cadenero/v1/account/sessions_controller.rb +3 -1
- data/app/controllers/cadenero/v1/account/users_controller.rb +8 -2
- data/app/controllers/cadenero/v1/accounts_controller.rb +13 -1
- data/app/extenders/middleware/robustness.rb +19 -0
- data/app/models/cadenero/member.rb +1 -0
- data/app/models/cadenero/user.rb +2 -0
- data/app/models/cadenero/v1/account.rb +16 -1
- data/app/serializers/cadenero/account_serializer.rb +1 -0
- data/app/serializers/cadenero/user_serializer.rb +1 -0
- data/config/initializers/apartment.rb +4 -1
- data/config/initializers/warden/strategies/password.rb +1 -1
- data/config/routes.rb +1 -0
- data/lib/cadenero/constraints/subdomain_required.rb +2 -0
- data/lib/cadenero/engine.rb +1 -1
- data/lib/cadenero/testing_support/subdomain_helpers.rb +15 -0
- data/lib/cadenero/version.rb +1 -1
- data/lib/cadenero.rb +2 -0
- data/lib/generators/cadenero/install_generator.rb +17 -0
- data/spec/dummy/log/development.log +209 -0
- data/spec/dummy/log/test.log +26783 -5135
- data/spec/dummy/tmp/ember-rails/ember.js +1693 -676
- data/spec/features/users/sign_in_spec.rb +13 -3
- data/spec/models/cadenero/account_spec.rb +31 -1
- data/spec/spec_helper.rb +1 -0
- metadata +11 -44
- data/app/helpers/cadenero/application_helper.rb +0 -4
- data/app/helpers/cadenero/v1/accounts_helper.rb +0 -4
- data/app/helpers/cadenero/v1/users_helper.rb +0 -4
- data/spec/features/cadenero/account_spec.rb +0 -23
- data/spec/support/subdomain_helpers.rb +0 -8
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
N2Y4NzhjNmE2MmVlYTlkNjNjMWFlYzI4MTYwZTkwMTQyMDk1MzBjNQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MWVkNDAwNjIwMDEwNjM2YWYzZmNiOTQxNTdkZGE2OTJjMmU5YjQ2Mg==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
Y2RiYTg0YTM0NzdiOGQwYWEzZWZjNGU3NGUwNzU5MjZhYmU0NjcxZTllY2I0
|
10
|
+
OWE2YTJkMmMzMDE0ZGFhNzY0N2YxMGQ1ZDg2Yjk1MWFiMjU0OGYzYjBlOTk4
|
11
|
+
ZWUzNmEyMjYwMDIyZmU0YjQ4MjgwM2I0NjcxZGIzOWFmYWE3OTA=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MjNlM2Y3YmEyZmQ2MjU0MzZlNmEwYzJlZTgwNGUxZTc3NWY5NGE1OGY4NjIy
|
14
|
+
NzYyOTlhYzAyOGFhMzhjZDBjN2M5NWExYjdkMDUyY2MxNTdiOTlkMGJmYjQ2
|
15
|
+
MzVjNzhjYTM4NzVjZjQzODdjYmU1ZDdlZDYyNWZjMWM5Yjg3NmE=
|
data/README.md
CHANGED
@@ -6,7 +6,6 @@ By [![Agiltec Logo](https://launchrock-assets.s3.amazonaws.com/logo-files/Gpujzv
|
|
6
6
|
[![Code Climate](https://codeclimate.com/github/AgilTec/cadenero.png)](https://codeclimate.com/github/AgilTec/cadenero)
|
7
7
|
[![Coverage Status](https://coveralls.io/repos/AgilTec/cadenero/badge.png?branch=master)](https://coveralls.io/r/AgilTec/cadenero?branch=master)
|
8
8
|
[![Dependency Status](https://gemnasium.com/AgilTec/cadenero.png)](https://gemnasium.com/AgilTec/cadenero)
|
9
|
-
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/AgilTec/cadenero/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|
10
9
|
|
11
10
|
Authentication Engine for Rails.API multitenant RESTful APIs based on Warden. It:
|
12
11
|
* Is Racked based
|
@@ -28,7 +27,7 @@ Generate first your Rails.API app as usual using:
|
|
28
27
|
|
29
28
|
In the `Gemfile` add the following lines:
|
30
29
|
```ruby
|
31
|
-
gem 'cadenero', '~> 0.0.2.
|
30
|
+
gem 'cadenero', '~> 0.0.2.b1'
|
32
31
|
gem 'pg'
|
33
32
|
```
|
34
33
|
|
@@ -110,10 +109,12 @@ You can check them running:
|
|
110
109
|
```
|
111
110
|
rake routes
|
112
111
|
```
|
112
|
+
### Documentation
|
113
|
+
You can review the YARD docs in: http://rubydoc.info/github/AgilTec/cadenero/frames
|
113
114
|
|
114
115
|
### The Cadenero Task List
|
115
116
|
- [x] Specs for the code 100% Coverage using BDD with [Rspec](https://github.com/rspec/rspec) and [Capybara](https://github.com/jnicklas/capybara)
|
116
|
-
- [
|
117
|
+
- [x] Documatation for all the code
|
117
118
|
- [ ] Examples of use and demo
|
118
119
|
|
119
120
|
### Versions
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require_dependency "cadenero/application_controller"
|
2
|
-
#
|
2
|
+
#NameSpace for the V1 API in Cadenero
|
3
3
|
module Cadenero::V1
|
4
|
+
# Controller for managing sessions for the API if you are using the :password Strategy
|
4
5
|
class Account::SessionsController < Cadenero::ApplicationController
|
5
6
|
# create the session for the user using the password strategy and returning the user JSON
|
6
7
|
def create
|
@@ -13,6 +14,7 @@ module Cadenero::V1
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
17
|
+
# destroy the session for the user using params id
|
16
18
|
def delete
|
17
19
|
user = Cadenero::User.find_by_id(params[:id])
|
18
20
|
if user_signed_in?
|
@@ -7,21 +7,27 @@ require_dependency "cadenero/application_controller"
|
|
7
7
|
|
8
8
|
module Cadenero
|
9
9
|
module V1
|
10
|
+
# Controller for managing users for specific accounts
|
10
11
|
class Account::UsersController < Cadenero::ApplicationController
|
11
12
|
# Create a [Cadenero::User] based on the params sended by the client as a JSON with the user inrormation
|
12
13
|
#
|
13
14
|
# @example Posting the user data to be created in an account via the subdomain
|
14
|
-
# post "http://#{account.subdomain}.example.com/v1/
|
15
|
+
# post "http://#{account.subdomain}.example.com/v1/users",
|
15
16
|
# user: { email: "user@example.com", password: "password", password_confirmation: "password" }
|
16
17
|
#
|
17
18
|
# @return render JSON of [Cadenero::User] created and the status 201 Created: The request has been
|
18
|
-
# fulfilled and resulted in a new resource being created
|
19
|
+
# fulfilled and resulted in a new resource being created.
|
19
20
|
def create
|
20
21
|
account = Cadenero::V1::Account.where(subdomain: request.subdomain).first
|
21
22
|
@user = account.users.create(params[:user])
|
22
23
|
force_authentication!(@user)
|
23
24
|
render json: @user, status: :created
|
24
25
|
end
|
26
|
+
# Send as JSON the user that match the params[:user]
|
27
|
+
def show
|
28
|
+
@user = account.users.where(params[:user]).first
|
29
|
+
render json: @user, status: :ok
|
30
|
+
end
|
25
31
|
end
|
26
32
|
end
|
27
33
|
end
|
@@ -2,11 +2,23 @@ require_dependency "cadenero/application_controller"
|
|
2
2
|
|
3
3
|
module Cadenero
|
4
4
|
module V1
|
5
|
+
# Handles the Accounts for the multitenant. An account creates a subdomain in the server as a route
|
6
|
+
# the account has an owner that is the default admin for that account
|
5
7
|
class AccountsController < Cadenero::ApplicationController
|
8
|
+
# For API consistency provides the example for creating a new account
|
6
9
|
def new
|
7
10
|
errors = %Q{Please sign up. posting the account json data as {account: { name: "Testy", subdomain: "test", owner_attributes: {email: "testy@example.com", password: "changeme", password_confirmation: "changeme"} }} to /v1/accounts/sign_up}
|
8
|
-
render json: {errors: errors, links: "/v1/accounts/sign_up"}, status:
|
11
|
+
render json: {errors: errors, links: "/v1/accounts/sign_up"}, status: :unprocessable_entity
|
9
12
|
end
|
13
|
+
# Create a [Cadenero::V1::Account] based on the params sended by the client as a JSON with the account inrormation
|
14
|
+
#
|
15
|
+
# @example Posting the account data to be created in a subdomain
|
16
|
+
# post "http://www.example.com/v1/accounts",
|
17
|
+
# account: { name: "Testy", subdomain: "test",
|
18
|
+
# owner_attributes: {email: "testy@example.com", password: "changeme", password_confirmation: "changeme"} }
|
19
|
+
#
|
20
|
+
# @return render JSON of [Cadenero::V1::Account] created and the status 201 Created: The request has been
|
21
|
+
# fulfilled and resulted in a new resource being created.
|
10
22
|
def create
|
11
23
|
@account = Cadenero::V1::Account.create_with_owner(params[:account])
|
12
24
|
if @account.valid?
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Intercepts excpetion from the middlewares to handled those as JSON responses
|
2
|
+
class Robustness
|
3
|
+
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
# Rack API call for rescue from the exceptions
|
9
|
+
def call(env)
|
10
|
+
@app.call(env)
|
11
|
+
rescue Apartment::SchemaNotFound => ex
|
12
|
+
[422, { 'Content-Type' => 'application/json' }, [ {errors: {subdomain:["Invalid subdomain"]}}.to_json ] ] # suppose the message can be safely used
|
13
|
+
rescue SecurityError => ex
|
14
|
+
[403, { 'Content-Type' => 'application/json' }, [ ex.message ] ]
|
15
|
+
ensure
|
16
|
+
env['rack.errors'].write(ex.message) if ex
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
data/app/models/cadenero/user.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Cadenero
|
2
|
+
# Defines a user of one or more accounts for the multitenant Rails App
|
2
3
|
class User < ActiveRecord::Base
|
3
4
|
attr_accessible :email, :password, :password_confirmation
|
4
5
|
has_secure_password
|
@@ -6,6 +7,7 @@ module Cadenero
|
|
6
7
|
has_many :members, class_name: "Cadenero::Member"
|
7
8
|
has_many :memberships, through: :members, source: :account
|
8
9
|
|
10
|
+
# Obtain the authentication_token from the account to be use for the User
|
9
11
|
def auth_token
|
10
12
|
accounts[0].authentication_token if accounts[0]
|
11
13
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module Cadenero::V1
|
2
|
+
# Defines a subdomain with a default admin (owner) as a tenant in the Rails App
|
2
3
|
class Account < ActiveRecord::Base
|
3
4
|
belongs_to :owner, :class_name => "Cadenero::User"
|
4
5
|
has_many :members, :class_name => "Cadenero::Member"
|
@@ -9,7 +10,7 @@ module Cadenero::V1
|
|
9
10
|
validates :subdomain, :presence => true, :uniqueness => true
|
10
11
|
validates :owner, :presence => true
|
11
12
|
|
12
|
-
# Creates an
|
13
|
+
# Creates an account and assign the provided [Cadenero::User] as owner to the account
|
13
14
|
# @param [Hash] params list
|
14
15
|
# @example
|
15
16
|
# Example for the params JSON: {name: "Testy", subdomain: "test",
|
@@ -25,6 +26,20 @@ module Cadenero::V1
|
|
25
26
|
account
|
26
27
|
end
|
27
28
|
|
29
|
+
# Gets the account for the specified subdomain and guards errors
|
30
|
+
# @param [String] params subdomain
|
31
|
+
# @example
|
32
|
+
# get_by_subdomain("www")
|
33
|
+
# @return the [Cadenero::V1::Account] for that subdomain
|
34
|
+
def self.get_by_subdomain(subdomain)
|
35
|
+
account = find_by_subdomain(subdomain)
|
36
|
+
if account
|
37
|
+
account
|
38
|
+
else
|
39
|
+
raise Apartment::SchemaNotFound, "Subdomain is not valid"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
28
43
|
# Create a database schema using the subdomain
|
29
44
|
def create_schema
|
30
45
|
Apartment::Database.create(subdomain)
|
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
require File.expand_path('../../../app/extenders/middleware/robustness', __FILE__)
|
2
|
+
|
3
|
+
Rails.application.config.middleware.use(Robustness)
|
4
|
+
Rails.application.config.middleware.use(Apartment::Elevators::Subdomain)
|
2
5
|
|
3
6
|
|
4
7
|
Apartment.configure do |config|
|
@@ -17,7 +17,7 @@ Warden::Strategies.add(:password) do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def authenticate!
|
20
|
-
account = Cadenero::V1::Account.
|
20
|
+
account = Cadenero::V1::Account.get_by_subdomain(subdomain)
|
21
21
|
if account
|
22
22
|
u = account.users.find_by_email(json_params["user"]["email"])
|
23
23
|
if u.nil? || u.blank?
|
data/config/routes.rb
CHANGED
@@ -8,6 +8,7 @@ Cadenero::Engine.routes.draw do
|
|
8
8
|
post '/sessions', :to => "sessions#create", default: :json
|
9
9
|
delete '/sessions', :to => "sessions#delete", default: :json
|
10
10
|
post '/users', :to => "users#create", default: :json
|
11
|
+
get '/users', :to => "users#show", :as => :users, default: :json
|
11
12
|
end
|
12
13
|
end
|
13
14
|
post '/accounts', :to => "accounts#create", :as => :accounts, default: :json
|
data/lib/cadenero/engine.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Cadenero
|
2
|
+
# Helper Methods for testing
|
3
|
+
module TestingSupport
|
4
|
+
# RSpec Helper for subdomains
|
5
|
+
module SubdomainHelpers
|
6
|
+
# To be use for RSpec features for defining and account with a subdomain visible for Capybara
|
7
|
+
def within_account_subdomain
|
8
|
+
let(:subdomain_url) { "http://#{account.subdomain}.example.com" }
|
9
|
+
before { Capybara.default_host = subdomain_url }
|
10
|
+
after { Capybara.default_host = "http://example.com" }
|
11
|
+
yield
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/cadenero/version.rb
CHANGED
data/lib/cadenero.rb
CHANGED
@@ -34,10 +34,12 @@ module Cadenero
|
|
34
34
|
:default_user_password
|
35
35
|
|
36
36
|
class << self
|
37
|
+
# @return the base path for the Cadenero named routes
|
37
38
|
def base_path
|
38
39
|
@@base_path ||= Rails.application.routes.named_routes[:cadenero].path
|
39
40
|
end
|
40
41
|
|
42
|
+
# defines which class will be used as User Class in Cadenero
|
41
43
|
def user_class
|
42
44
|
if @@user_class.is_a?(Class)
|
43
45
|
raise "You can no longer set Cadenero.user_class to be a class. Please use a string instead."
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'rails/generators'
|
2
2
|
module Cadenero
|
3
|
+
# Bootstrap Cadenero in a new Rails App
|
3
4
|
module Generators
|
5
|
+
# Cadenero generator to be used as `rails-api g cadenero:install`
|
4
6
|
class InstallGenerator < Rails::Generators::Base
|
5
7
|
class_option "user-class", :type => :string
|
6
8
|
class_option "default-account-name", :type => :string
|
@@ -13,6 +15,7 @@ module Cadenero
|
|
13
15
|
source_root File.expand_path("../install/templates", __FILE__)
|
14
16
|
desc "Used to install Cadenero"
|
15
17
|
|
18
|
+
# Copy the Cadenero Migrations in the New App
|
16
19
|
def install_migrations
|
17
20
|
puts "Copying over Cadenero migrations..."
|
18
21
|
Dir.chdir(Rails.root) do
|
@@ -20,12 +23,14 @@ module Cadenero
|
|
20
23
|
end
|
21
24
|
end
|
22
25
|
|
26
|
+
# Defines which class will be used as User Class for Cadenero
|
23
27
|
def determine_user_class
|
24
28
|
@user_class = options["user-class"].presence ||
|
25
29
|
ask("What will be the name for your user class? [User]").presence ||
|
26
30
|
'User'
|
27
31
|
end
|
28
32
|
|
33
|
+
# Defines which helper will be used as User Class Helper for Cadenero and inject that in the application_controller.rb
|
29
34
|
def determine_current_user_helper
|
30
35
|
current_user_helper = options["current-user-helper"].presence ||
|
31
36
|
ask("What will be the current_user helper called in your app? [current_user]").presence ||
|
@@ -47,30 +52,35 @@ module Cadenero
|
|
47
52
|
|
48
53
|
end
|
49
54
|
|
55
|
+
# Define which will be the Root Account for the Multitnant Rails App
|
50
56
|
def determine_default_account_name
|
51
57
|
Cadenero.default_account_name = options["default-account-name"].presence ||
|
52
58
|
ask("What will be the name for the default account? [Root Account]").presence ||
|
53
59
|
'Root Account'
|
54
60
|
end
|
55
61
|
|
62
|
+
# Define which will be the root subdomain for the Multitnant Rails App. (Default: www)
|
56
63
|
def determine_default_account_subdomain
|
57
64
|
Cadenero.default_account_subdomain = options["default-account-subdomain"].presence ||
|
58
65
|
ask("What will be the subdomain for the default account? [www]").presence ||
|
59
66
|
'www'
|
60
67
|
end
|
61
68
|
|
69
|
+
# Define which will be the root owner for the defaul accout Multitnant Rails App.
|
62
70
|
def determine_default_user_email
|
63
71
|
Cadenero.default_user_email = options["default-user-email"].presence ||
|
64
72
|
ask("What will be the email for the default user owner of the default account? [testy@example.com]").presence ||
|
65
73
|
'testy@example.com'
|
66
74
|
end
|
67
75
|
|
76
|
+
# Define which will be the password for the root owner for the defaul accout Multitnant Rails App.
|
68
77
|
def determine_default_user_password
|
69
78
|
Cadenero.default_user_password = options["default-user-password"].presence ||
|
70
79
|
ask("What will be the password for the default user owner of the default account? [change-me]").presence ||
|
71
80
|
'change-me'
|
72
81
|
end
|
73
82
|
|
83
|
+
# Create the Cadenero initilizar based on the Template.
|
74
84
|
def add_cadenero_initializer
|
75
85
|
path = "#{Rails.root}/config/initializers/cadenero.rb"
|
76
86
|
if File.exists?(path)
|
@@ -82,6 +92,7 @@ module Cadenero
|
|
82
92
|
end
|
83
93
|
end
|
84
94
|
|
95
|
+
# Run the Cadenero Migrations in the New App
|
85
96
|
def run_migrations
|
86
97
|
unless options["no-migrate"]
|
87
98
|
puts "Running rake db:migrate"
|
@@ -89,6 +100,7 @@ module Cadenero
|
|
89
100
|
end
|
90
101
|
end
|
91
102
|
|
103
|
+
# Seed the databas using the options selected as defaults
|
92
104
|
def seed_database
|
93
105
|
puts "default_account_name: #{Cadenero.default_account_name}"
|
94
106
|
puts "default_account_subdomain: #{Cadenero.default_account_subdomain}"
|
@@ -102,6 +114,7 @@ module Cadenero
|
|
102
114
|
end
|
103
115
|
end
|
104
116
|
|
117
|
+
# Injects the code in the `routes.rb` for mounting the Cadenero Engine
|
105
118
|
def mount_engine
|
106
119
|
puts "Mounting Cadenero::Engine at \"/\" in config/routes.rb..."
|
107
120
|
insert_into_file("#{Rails.root}/config/routes.rb", :after => /routes.draw.do\n/) do
|
@@ -115,6 +128,7 @@ module Cadenero
|
|
115
128
|
end
|
116
129
|
end
|
117
130
|
|
131
|
+
# Wrap-ups the installation giving appropiate messages
|
118
132
|
def finished
|
119
133
|
output = "\n\n" + ("*" * 53)
|
120
134
|
output += %Q{\nDone! Cadenero has been successfully installed. Yaaaaay! He will keep the intoxicated JSON's out
|
@@ -146,16 +160,19 @@ output += step("`rake db:migrate` was run, running all the migrations against yo
|
|
146
160
|
|
147
161
|
private
|
148
162
|
|
163
|
+
# Keep track of the step number for the installation process
|
149
164
|
def step(words)
|
150
165
|
@step ||= 0
|
151
166
|
@step += 1
|
152
167
|
"#{@step}) #{words}\n"
|
153
168
|
end
|
154
169
|
|
170
|
+
# @return the user_class
|
155
171
|
def user_class
|
156
172
|
@user_class
|
157
173
|
end
|
158
174
|
|
175
|
+
# keep track of the migration numbers
|
159
176
|
def next_migration_number
|
160
177
|
last_migration = Dir[Rails.root + "db/migrate/*.rb"].sort.last.split("/").last
|
161
178
|
current_migration_number = /^(\d+)_/.match(last_migration)[1]
|