releaf-permissions 0.2.1

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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +24 -0
  3. data/app/assets/stylesheets/releaf/controllers/releaf/permissions/sessions.scss +70 -0
  4. data/app/builders/releaf/permissions/profile/form_builder.rb +7 -0
  5. data/app/builders/releaf/permissions/roles/form_builder.rb +28 -0
  6. data/app/builders/releaf/permissions/roles/table_builder.rb +16 -0
  7. data/app/builders/releaf/permissions/users/form_builder.rb +11 -0
  8. data/app/builders/releaf/permissions/users/table_builder.rb +11 -0
  9. data/app/controllers/releaf/permissions/home_controller.rb +32 -0
  10. data/app/controllers/releaf/permissions/profile_controller.rb +56 -0
  11. data/app/controllers/releaf/permissions/roles_controller.rb +7 -0
  12. data/app/controllers/releaf/permissions/sessions_controller.rb +34 -0
  13. data/app/controllers/releaf/permissions/users_controller.rb +19 -0
  14. data/app/lib/releaf/permissions/access_control.rb +36 -0
  15. data/app/models/releaf/permissions/permission.rb +6 -0
  16. data/app/models/releaf/permissions/role.rb +38 -0
  17. data/app/models/releaf/permissions/user.rb +31 -0
  18. data/app/views/releaf/permissions/sessions/new.html.haml +14 -0
  19. data/lib/releaf-permissions.rb +32 -0
  20. data/lib/releaf/permissions/builders_autoload.rb +11 -0
  21. data/lib/releaf/permissions/devise_component.rb +8 -0
  22. data/lib/releaf/permissions/engine.rb +24 -0
  23. data/lib/releaf/permissions/profile_component.rb +9 -0
  24. data/lib/releaf/permissions/roles_component.rb +7 -0
  25. data/lib/releaf/permissions/users_component.rb +7 -0
  26. data/releaf-permissions.gemspec +19 -0
  27. data/spec/builders/profile/form_builder_spec.rb +18 -0
  28. data/spec/builders/roles/form_builder_spec.rb +38 -0
  29. data/spec/builders/roles/table_builder_spec.rb +29 -0
  30. data/spec/builders/users/form_builder_spec.rb +23 -0
  31. data/spec/builders/users/table_builder_spec.rb +21 -0
  32. data/spec/controllers/permissions/home_controller_spec.rb +52 -0
  33. data/spec/controllers/permissions/profile_controller_spec.rb +66 -0
  34. data/spec/controllers/permissions/users_controller_spec.rb +28 -0
  35. data/spec/features/profile_updating_spec.rb +35 -0
  36. data/spec/features/roles_spec.rb +64 -0
  37. data/spec/features/users_spec.rb +107 -0
  38. data/spec/lib/access_control_spec.rb +81 -0
  39. data/spec/models/permissions/role_spec.rb +41 -0
  40. data/spec/models/permissions/user_spec.rb +23 -0
  41. metadata +124 -0
@@ -0,0 +1,14 @@
1
+ .container
2
+ .box
3
+ .logo
4
+ = form_for resource, as: resource_name, builder: Releaf::Builders::FormBuilder, url: session_path(resource_name), html: {class: "login"} do |f|
5
+ - if params[:redirect_to]
6
+ = hidden_field_tag :redirect_to, params[:redirect_to]
7
+ .field
8
+ %label{for: :email}= t("Email", scope: 'admin.sessions')
9
+ = f.email_field :email, autofocus: "autofocus", id: :email, class: "text"
10
+ .field
11
+ %label{for: :password}= t("Password", scope: 'admin.sessions')
12
+ = f.password_field :password, id: :password, class: "text"
13
+ .field
14
+ %button.button{type: "submit"}= t("Sign in", scope: 'admin.sessions')
@@ -0,0 +1,32 @@
1
+ require 'releaf/permissions/engine'
2
+
3
+ module Releaf::Permissions
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_filter :authenticate!, :verify_controller_access!, :set_locale
8
+ end
9
+
10
+ # set locale for interface translating from current admin user
11
+ def set_locale
12
+ I18n.locale = access_control.user.locale
13
+ end
14
+
15
+ def layout_settings(key)
16
+ access_control.user.try(:settings).try(:[], key)
17
+ end
18
+
19
+ def authenticate!
20
+ access_control.authenticate!
21
+ end
22
+
23
+ def verify_controller_access!
24
+ unless access_control.controller_permitted?(access_control.current_controller_name)
25
+ raise Releaf::Core::AccessDenied.new(access_control.current_controller_name)
26
+ end
27
+ end
28
+
29
+ def access_control
30
+ @access_control ||= Releaf::Permissions::AccessControl.new(controller: self)
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ root_path = File.expand_path('../..', File.dirname(__dir__))
2
+ files = %w(
3
+ roles/form_builder
4
+ roles/table_builder
5
+ users/form_builder
6
+ users/table_builder
7
+ profile/form_builder
8
+ )
9
+ files.each do|file|
10
+ require "#{root_path}/app/builders/releaf/permissions/#{file}"
11
+ end
@@ -0,0 +1,8 @@
1
+ module Releaf::Permissions::DeviseComponent
2
+ def self.draw_component_routes router
3
+ router.devise_for Releaf.application.config.devise_for, path: "", controllers: { sessions: "releaf/permissions/sessions" }
4
+ router.namespace :releaf, path: nil do
5
+ router.root to: "permissions/home#home", as: :root
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,24 @@
1
+ require 'devise'
2
+
3
+ module Releaf::Permissions
4
+ require 'releaf/permissions/devise_component'
5
+ require 'releaf/permissions/profile_component'
6
+ require 'releaf/permissions/roles_component'
7
+ require 'releaf/permissions/users_component'
8
+ require 'releaf/permissions/builders_autoload'
9
+
10
+ class Engine < ::Rails::Engine
11
+ initializer 'precompile', group: :all do |app|
12
+ app.config.assets.precompile += %w(releaf/controllers/releaf/permissions/*)
13
+ end
14
+ end
15
+
16
+ def self.components
17
+ [
18
+ Releaf::Permissions::DeviseComponent,
19
+ Releaf::Permissions::RolesComponent,
20
+ Releaf::Permissions::UsersComponent,
21
+ Releaf::Permissions::ProfileComponent
22
+ ]
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ module Releaf::Permissions::ProfileComponent
2
+ def self.draw_component_routes router
3
+ router.namespace :releaf, path: nil do
4
+ router.get "profile", to: "permissions/profile#edit", as: :permissions_user_profile
5
+ router.patch "profile", to: "permissions/profile#update"
6
+ router.post "profile/settings", to: "permissions/profile#settings", as: :permissions_user_profile_settings
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module Releaf::Permissions::RolesComponent
2
+ extend Releaf::Core::Component
3
+
4
+ def self.draw_component_routes router
5
+ resource_route(router, :permissions, :roles)
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Releaf::Permissions::UsersComponent
2
+ extend Releaf::Core::Component
3
+
4
+ def self.draw_component_routes(router)
5
+ resource_route(router, :permissions, :users)
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ require File.expand_path("../../releaf-core/lib/releaf/version.rb", __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "releaf-permissions"
5
+ s.version = Releaf::VERSION
6
+
7
+ s.summary = "Built-in admin and role support for releaf"
8
+ s.description = "Admin/role subsystem for releaf"
9
+ s.authors = ["CubeSystems"]
10
+ s.email = 'info@cubesystems.lv'
11
+ s.homepage = 'https://github.com/cubesystems/releaf'
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = Dir["spec/**/*"]
15
+
16
+ s.add_dependency 'releaf-core', Releaf::VERSION
17
+ s.add_dependency 'devise'
18
+
19
+ end
@@ -0,0 +1,18 @@
1
+ require "rails_helper"
2
+
3
+ describe Releaf::Permissions::Profile::FormBuilder, type: :class do
4
+ class FormBuilderTestHelper < ActionView::Base; end
5
+ let(:template){ FormBuilderTestHelper.new }
6
+ let(:object){ Releaf::Permissions::User.new }
7
+ let(:subject){ described_class.new(:resource, object, template, {}) }
8
+
9
+ it "inherits Releaf::Permissions::Users::FormBuilder" do
10
+ expect(described_class.superclass).to eq(Releaf::Permissions::Users::FormBuilder)
11
+ end
12
+
13
+ describe "#field_names" do
14
+ it "returns name, surname, locale, email, password and password_confirmation as field names array" do
15
+ expect(subject.field_names).to eq(%w(name surname locale email password password_confirmation))
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,38 @@
1
+ require 'rails_helper'
2
+
3
+ describe Releaf::Permissions::Roles::FormBuilder, type: :class do
4
+ class FormBuilderTestHelper < ActionView::Base; end
5
+ let(:template){ FormBuilderTestHelper.new }
6
+ let(:object){ Releaf::Permissions::Role.new }
7
+ let(:subject){ described_class.new(:resource, object, template, {}) }
8
+
9
+ describe "#render_default_controller" do
10
+ it "pass localized controller options to releaf item field" do
11
+ allow(Releaf.application.config).to receive(:available_controllers)
12
+ .and_return(["releaf/i18n_database/translations", "releaf/content/nodes"])
13
+ translated_controllers = {"Releaf/i18n database/translations"=>"releaf/i18n_database/translations", "Releaf/content/nodes"=>"releaf/content/nodes"}
14
+
15
+ allow(subject).to receive(:releaf_item_field).with(:default_controller, options: {select_options: translated_controllers}).and_return("x")
16
+ expect(subject.render_default_controller).to eq("x")
17
+ end
18
+ end
19
+
20
+ describe "#render_permissions" do
21
+ it "returns associated set field" do
22
+ options = {association: {items: "x", field: :permission}}
23
+ allow(subject).to receive(:permission_items).and_return("x")
24
+ allow(subject).to receive(:releaf_associated_set_field).with(:permissions, options: options).and_return("y")
25
+ expect(subject.render_permissions).to eq("y")
26
+ end
27
+ end
28
+
29
+ describe "#permission_items" do
30
+ it "returns scoped and translated controller values" do
31
+ allow(Releaf.application.config).to receive(:available_controllers)
32
+ .and_return(["releaf/content/nodes", "admin/chapters"])
33
+ allow(subject).to receive(:t).with("releaf/content/nodes", scope: "admin.controllers").and_return("controller 1")
34
+ allow(subject).to receive(:t).with("admin/chapters", scope: "admin.controllers").and_return("controller 2")
35
+ expect(subject.permission_items).to eq("controller.releaf/content/nodes" => "controller 1", "controller.admin/chapters" => "controller 2")
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ require "rails_helper"
2
+
3
+ describe Releaf::Permissions::Roles::TableBuilder, type: :class do
4
+ class TableBuilderTestHelper < ActionView::Base; end
5
+ let(:template){ TableBuilderTestHelper.new }
6
+ let(:resource_class){ Releaf::Permissions::Role }
7
+ let(:subject){ described_class.new([], resource_class, template, {}) }
8
+
9
+ describe "#column_names" do
10
+ it "returns name and default_controller as column names array" do
11
+ expect(subject.column_names).to eq([:name, :default_controller])
12
+ end
13
+ end
14
+
15
+ describe "#default_controller_content" do
16
+ context "when default controller is defined for given resource" do
17
+ it "returns translated value" do
18
+ allow(I18n).to receive(:t).with("releaf/i18n/database/translations", scope: "admin.controllers").and_return("x")
19
+ expect(subject.default_controller_content(resource_class.new(default_controller: "releaf/i18n_database/translations"))).to eq("x")
20
+ end
21
+ end
22
+
23
+ context "when default controller is not defined for given resource" do
24
+ it "returns dash" do
25
+ expect(subject.default_controller_content(resource_class.new)).to eq("-")
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ require "rails_helper"
2
+
3
+ describe Releaf::Permissions::Users::FormBuilder, type: :class do
4
+ class FormBuilderTestHelper < ActionView::Base; end
5
+ let(:template){ FormBuilderTestHelper.new }
6
+ let(:object){ Releaf::Permissions::Role.new }
7
+ let(:subject){ described_class.new(:resource, object, template, {}) }
8
+
9
+ describe "#field_names" do
10
+ it "returns name, surname, locale, role_id, email, password and password_confirmation as field names array" do
11
+ expect(subject.field_names).to eq(%w(name surname locale role_id email password password_confirmation))
12
+ end
13
+ end
14
+
15
+ describe "#render_locale" do
16
+ it "pass localized controller options to releaf item field" do
17
+ allow(Releaf.application.config).to receive(:available_admin_locales).and_return(["de", "ze"])
18
+ allow(subject).to receive(:locale_options).with(["de", "ze"]).and_return(["xx", "yy"])
19
+ allow(subject).to receive(:releaf_item_field).with(:locale, options: {select_options: ["xx", "yy"]}).and_return("x")
20
+ expect(subject.render_locale).to eq("x")
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ require "rails_helper"
2
+
3
+ describe Releaf::Permissions::Users::TableBuilder, type: :class do
4
+ class TableBuilderTestHelper < ActionView::Base; end
5
+ let(:template){ TableBuilderTestHelper.new }
6
+ let(:resource_class){ Releaf::Permissions::User }
7
+ let(:subject){ described_class.new([], resource_class, template, {}) }
8
+
9
+ describe "#column_names" do
10
+ it "returns name, surname, role, email and locale as column names array" do
11
+ expect(subject.column_names).to eq([:name, :surname, :role, :email, :locale])
12
+ end
13
+ end
14
+
15
+ describe "#locale_content" do
16
+ it "returns translated locale" do
17
+ allow(subject).to receive(:translate_locale).with("de").and_return("deutch")
18
+ expect(subject.locale_content(resource_class.new(locale: "de"))).to eq("deutch")
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,52 @@
1
+ require 'rails_helper'
2
+
3
+ describe Releaf::Permissions::HomeController do
4
+
5
+ describe "GET home" do
6
+ context "when authorized as user" do
7
+ login_as_user :user
8
+
9
+ before do
10
+ @role = subject.current_releaf_permissions_user.role
11
+ end
12
+
13
+ it "redirects to users controller" do
14
+ get :home
15
+ expect(response).to redirect_to(url_for(action: 'index', controller: @role.default_controller, only_path: true))
16
+ end
17
+
18
+ context "when users default controller doesn't exist" do
19
+ before do
20
+ @role.update_attribute(:default_controller, 'non_existing/controllers_name')
21
+ end
22
+
23
+ it "redirects to first available controller" do
24
+ get :home
25
+ expect(response).to redirect_to(url_for(action: 'index', controller: "releaf/content/nodes", only_path: true))
26
+ end
27
+
28
+ context "when no releaf controller is available" do
29
+ before do
30
+ @role.permissions = []
31
+ @role.save!
32
+ end
33
+
34
+ it "redirects to root_path" do
35
+ get :home
36
+ expect(response).to redirect_to(root_path)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ context "when authorized as content user" do
43
+ login_as_user :user
44
+
45
+ it "redirects to content controller" do
46
+ get :home
47
+ expect(response)
48
+ .to redirect_to(url_for(action: 'index', controller: subject.current_releaf_permissions_user.role.default_controller, only_path: true))
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,66 @@
1
+ require 'rails_helper'
2
+
3
+ describe Releaf::Permissions::ProfileController do
4
+ let(:another_role){ FactoryGirl.create(:content_role) }
5
+ let(:user){ subject.current_releaf_permissions_user }
6
+ login_as_user :user
7
+
8
+ describe "#resource_class" do
9
+ it "returns current releaf user user class" do
10
+ expect(described_class.new.resource_class).to eq(Releaf::Permissions::User)
11
+ end
12
+ end
13
+
14
+ describe "PATCH update" do
15
+ context 'when attributes contain role_id' do
16
+ it "does not update it" do
17
+ expect{ patch :update, {resource: {role_id: another_role.id}} }.to_not change{ user.role_id }
18
+ end
19
+ end
20
+
21
+ context 'with allowed attributes' do
22
+ it "saves new attributes" do
23
+ attributes = {
24
+ "name" => "new name",
25
+ "surname" => "new surname",
26
+ "email" => "new.email@example.com",
27
+ "locale" => "lv"
28
+ }
29
+
30
+ # This is needed in order to get same instance as we expect.
31
+ # Otherwise we'll get same record, but different instance and test will fail
32
+ allow( user ).to receive(:becomes).with(Releaf::Permissions::User).and_return(user)
33
+
34
+ expect(user).to receive(:update_attributes).with(attributes)
35
+ patch :update, {resource: attributes}
36
+ end
37
+ end
38
+ end
39
+
40
+ describe "PUT settings", db_strategy: :truncation do
41
+ context 'when params[:settings] is not Hash' do
42
+ it "has a 422 status code" do
43
+ put :settings
44
+ expect(response.status).to eq(422)
45
+ end
46
+ end
47
+
48
+ context 'when params[:settings] is Hash' do
49
+ it "has a 200 status code" do
50
+ put :settings, {settings: {dummy: 'maybe'}}
51
+ expect(response.status).to eq(200)
52
+ end
53
+
54
+ it "saves given data within current user settings" do
55
+ put :settings, {settings: {dummy: 'maybe'}}
56
+ expect(user.settings.dummy).to eq('maybe')
57
+ end
58
+
59
+ it "casts bolean values from strings to booleans" do
60
+ put :settings, {settings: {be_true: 'true', be_false: 'false'}}
61
+ expect(user.settings.be_true).to be true
62
+ expect(user.settings.be_false).to be false
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,28 @@
1
+ require 'rails_helper'
2
+
3
+ # use Admin::BooksController as it inherit Releaf::BaseController and
4
+ # have no extra methods or overrides
5
+ describe Releaf::Permissions::UsersController do
6
+ before do
7
+ sign_in FactoryGirl.create(:user)
8
+ end
9
+
10
+ describe "GET #new" do
11
+ it "assigns default role" do
12
+ get :new
13
+ expect(assigns(:resource).role).to eq(Releaf::Permissions::Role.first)
14
+ end
15
+ end
16
+
17
+ describe "GET #index" do
18
+ before do
19
+ FactoryGirl.create(:content_user, name: "John")
20
+ FactoryGirl.create(:content_user, name: "Bill", surname: "Green", email: "another@example.com")
21
+ end
22
+
23
+ it "searches by name, surname and email" do
24
+ get :index, search: "bill green another@example"
25
+ expect(assigns(:collection).count).to eq(1)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,35 @@
1
+ require 'rails_helper'
2
+ feature "User profile" do
3
+ background do
4
+ auth_as_user(false, FactoryGirl.create(:user, email: "email@example.com"))
5
+ visit releaf_permissions_user_profile_path
6
+ end
7
+
8
+ scenario "name, surname and locale" do
9
+ fill_in 'Name', with: "Edward"
10
+ fill_in 'Surname', with: "Bat"
11
+ select "Lv", from: "Locale"
12
+ click_button 'Save'
13
+
14
+ expect(page).to have_css('header .profile .name', text: "Edward Bat")
15
+ end
16
+
17
+ scenario "password and email" do
18
+ # update
19
+ fill_in 'Email', with: "new.email@example.com"
20
+ fill_in 'Password', with: "newpassword123", match: :prefer_exact
21
+ fill_in 'Password confirmation', with: "newpassword123", match: :prefer_exact
22
+ click_button 'Save'
23
+
24
+ # logout
25
+ find('body > header form.sign-out button').click
26
+
27
+ # login
28
+ visit releaf_root_path
29
+ fill_in 'Email', with: "new.email@example.com"
30
+ fill_in 'Password', with: "newpassword123"
31
+ click_button 'Sign in'
32
+
33
+ expect(page).to have_css('.sign-out')
34
+ end
35
+ end