ditty 0.3.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 671a4f94844586df6b6f93e6b52555b4676c5571
4
- data.tar.gz: ae067afab7015874c78e682a1ca1cd354860473d
3
+ metadata.gz: 5c1aeccd30ff2045fdc743913e43590e7cc5ca03
4
+ data.tar.gz: 5c85c18dfd395210a4c64a11b9d801d8afaf2360
5
5
  SHA512:
6
- metadata.gz: 2c55cc8ad699496e26a57ed594da2e5779da9208e172119e8dd9df142ff8ed963ea9f06969846990e10ab3303264bc7038c1586d3a09b232723c6d47d0a9e7e1
7
- data.tar.gz: 4d7b7e184449f4bac9287ac6588c5b30162617a68e76bf54524a7ad12671c996296fc2bd26c2c3a0ca8f2ca06e9ab7a222ed06f2c60e37c719a550af77b1dd15
6
+ metadata.gz: 9152764039916756b7c355de8466597f83f64921122e969f5dec13a5376c3310ab4d2c5e95576abf583bc86aa6a0ad0e1a60a7c3fdb0a8cde3b5946523d3fd22
7
+ data.tar.gz: c1df6170f906e47a460abb59a52ee4054ae5434daa926c6da8062cecda9e1554227efdfb936fd5204a848436cb08af55d1a7bcffa5b8f05870c8fba7fb57513d
data/ditty.gemspec CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency 'timecop'
30
30
 
31
31
  spec.add_dependency 'activesupport', '>= 3'
32
- spec.add_dependency 'bcrypt', '~> 3.0'
32
+ spec.add_dependency 'bcrypt', '~> 3.1'
33
33
  spec.add_dependency 'haml', '~> 5.0'
34
34
  spec.add_dependency 'logger', '~> 1.0'
35
35
  spec.add_dependency 'omniauth', '~> 1.0'
@@ -8,6 +8,21 @@ module Ditty
8
8
  class AuditLogs < Ditty::Component
9
9
  set model_class: AuditLog
10
10
 
11
+ FILTERS = [
12
+ { name: :user, field: 'user.email' },
13
+ { name: :action }
14
+ ].freeze
15
+
16
+ helpers do
17
+ def user_options
18
+ policy_scope(::Ditty::User).as_hash(:email, :email)
19
+ end
20
+
21
+ def action_options
22
+ policy_scope(::Ditty::AuditLog).as_hash(:action, :action)
23
+ end
24
+ end
25
+
11
26
  def find_template(views, name, engine, &block)
12
27
  super(views, name, engine, &block) # Root
13
28
  super(::Ditty::App.view_folder, name, engine, &block) # Ditty
@@ -45,12 +45,16 @@ module Ditty
45
45
 
46
46
  # Register Page
47
47
  get '/auth/identity/register' do
48
+ authorize ::Ditty::Identity, :register
49
+
48
50
  identity = Identity.new
49
51
  haml :'identity/register', locals: { title: 'Register', identity: identity }
50
52
  end
51
53
 
52
54
  # Register Action
53
55
  post '/auth/identity/new' do
56
+ authorize ::Ditty::Identity, :register
57
+
54
58
  identity = Identity.new(params['identity'])
55
59
  if identity.valid? && identity.save
56
60
  user = User.find_or_create(email: identity.username)
@@ -95,9 +95,12 @@ module Ditty
95
95
  roles = values.delete('role_id')
96
96
  entity.set values
97
97
  if entity.valid? && entity.save
98
- entity.remove_all_roles
99
- roles.each { |role_id| entity.add_role(role_id) } if roles
100
- entity.check_roles
98
+ if roles
99
+ entity.remove_all_roles
100
+ roles.each { |role_id| entity.add_role(role_id) }
101
+ entity.check_roles
102
+ end
103
+
101
104
  log_action("#{dehumanized}_update".to_sym) if settings.track_actions
102
105
  respond_to do |format|
103
106
  format.html do
@@ -112,7 +115,14 @@ module Ditty
112
115
  end
113
116
  end
114
117
  else
115
- haml :"#{view_location}/edit", locals: { entity: entity, title: heading(:edit) }
118
+ respond_to do |format|
119
+ format.html do
120
+ haml :"#{view_location}/edit", locals: { entity: entity, title: heading(:edit) }
121
+ end
122
+ format.json do
123
+ 400
124
+ end
125
+ end
116
126
  end
117
127
  end
118
128
 
@@ -140,8 +150,8 @@ module Ditty
140
150
  if identity.valid? && identity.save
141
151
  log_action("#{dehumanized}_update_password".to_sym) if settings.track_actions
142
152
  flash[:success] = 'Password Updated'
143
- redirect "#{base_path}/#{entity.id}"
144
- elsif current_user.super_admin?
153
+ redirect back
154
+ elsif current_user.super_admin? && current_user.id != id
145
155
  haml :"#{view_location}/display", locals: { entity: entity, identity: identity, title: heading }
146
156
  else
147
157
  haml :"#{view_location}/profile", locals: { entity: entity, identity: identity, title: heading }
data/lib/ditty/db.rb CHANGED
@@ -3,16 +3,18 @@
3
3
  require 'sequel'
4
4
  require 'ditty/services/logger'
5
5
 
6
- # Delete DATABASE_URL from the environment, so it isn't accidently
7
- # passed to subprocesses. DATABASE_URL may contain passwords.
8
- DB = Sequel.connect(ENV['RACK_ENV'] == 'production' ? ENV.delete('DATABASE_URL') : ENV['DATABASE_URL'])
6
+ if ENV['DATABASE_URL']
7
+ # Delete DATABASE_URL from the environment, so it isn't accidently
8
+ # passed to subprocesses. DATABASE_URL may contain passwords.
9
+ DB = Sequel.connect(ENV['RACK_ENV'] == 'production' ? ENV.delete('DATABASE_URL') : ENV['DATABASE_URL'])
9
10
 
10
- log_level = (ENV['SEQUEL_LOGGING_LEVEL'] || :debug).to_sym
11
- DB.sql_log_level = log_level
12
- DB.loggers << Ditty::Services::Logger.instance
11
+ DB.sql_log_level = (ENV['SEQUEL_LOGGING_LEVEL'] || :debug).to_sym
12
+ DB.loggers << Ditty::Services::Logger.instance
13
+ DB.extension(:pagination)
13
14
 
14
- Sequel::Model.plugin :validation_helpers
15
- Sequel::Model.plugin :update_or_create
16
- Sequel::Model.plugin :timestamps, update_on_create: true
17
-
18
- DB.extension(:pagination)
15
+ Sequel::Model.plugin :validation_helpers
16
+ Sequel::Model.plugin :update_or_create
17
+ Sequel::Model.plugin :timestamps, update_on_create: true
18
+ else
19
+ Ditty::Services::Logger.instance.error 'No database connection set up'
20
+ end
@@ -49,23 +49,42 @@ module Ditty
49
49
 
50
50
  def filtered(dataset)
51
51
  filters.each do |filter|
52
- next unless params[filter[:name].to_s]
52
+ next if [nil, ''].include? params[filter[:name].to_s]
53
53
  filter[:field] ||= filter[:name]
54
+ filter[:modifier] ||= :to_s
54
55
  dataset = apply_filter(dataset, filter)
55
56
  end
56
57
  dataset
57
58
  end
58
59
 
59
60
  def apply_filter(dataset, filter)
60
- value = params[filter[:name].to_s]
61
- return dataset if value == '' || value.nil?
62
- value = value.send(filter[:modifier]) if filter[:modifier]
63
- dataset.where(filter[:field].to_sym => value)
61
+ value = params[filter[:name].to_s].send(filter[:modifier])
62
+ return dataset.where(filter[:field] => value) unless filter[:field].to_s.include? '.'
63
+
64
+ dataset.where(filter_field(filter) => filter_value(filter))
65
+ end
66
+
67
+ def filter_field(filter)
68
+ filter[:field].to_s.split('.').first.to_sym
69
+ end
70
+
71
+ def filter_value(filter)
72
+ field = filter[:field].to_s.split('.').last.to_sym
73
+ assoc = filter_association(filter)
74
+ value = params[filter[:name].to_s].send(filter[:modifier])
75
+ value = assoc.associated_dataset.first(field => value)
76
+ value.nil? ? assoc.associated_class.new : value
77
+ end
78
+
79
+ def filter_association(filter)
80
+ assoc = filter[:field].to_s.split('.').first.to_sym
81
+ assoc = settings.model_class.association_reflection(assoc)
82
+ raise "Unknown association #{assoc}" if assoc.nil?
83
+ assoc
64
84
  end
65
85
 
66
86
  def search(dataset)
67
- return dataset if params['q'] == '' || params['q'].nil?
68
- return dataset if searchable_fields == []
87
+ return dataset if ['', nil].include?(params['q']) || searchable_fields == []
69
88
 
70
89
  filters = searchable_fields.map { |f| Sequel.ilike(f.to_sym, "%#{params[:q]}%") }
71
90
  dataset.where Sequel.|(*filters)
@@ -27,7 +27,7 @@ module Ditty
27
27
  return unless respond_to? meth
28
28
  haml :'partials/filter_control', locals: {
29
29
  name: filter[:name],
30
- label: opts[:label] || filter[:name].titlecase,
30
+ label: opts[:label] || filter[:name].to_s.titlecase,
31
31
  options: send(meth)
32
32
  }
33
33
  end
@@ -46,7 +46,16 @@ module Ditty
46
46
  if password_required
47
47
  validates_presence :password
48
48
  validates_presence :password_confirmation
49
- validates_min_length 8, :password
49
+ validates_format(
50
+ # 1 Uppercase
51
+ # 1 lowercase
52
+ # 1 Special Character
53
+ # 1 Number
54
+ # At least 8 characters
55
+ %r[\A(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#&$*)(}{%^=_+|\\:";'<>,.\-\/?\[\]])(?=.*[0-9]).{8,}\Z],
56
+ :password,
57
+ message: 'is not strong enough'
58
+ )
50
59
  end
51
60
 
52
61
  errors.add(:password_confirmation, 'must match password') if !password.blank? && password != password_confirmation
@@ -5,7 +5,7 @@ require 'ditty/policies/application_policy'
5
5
  module Ditty
6
6
  class IdentityPolicy < ApplicationPolicy
7
7
  def register?
8
- true
8
+ !['1', 1, 'true', true, 'yes'].include? ENV['DITTY_REGISTERING_DISABLED']
9
9
  end
10
10
 
11
11
  def permitted_attributes
@@ -24,10 +24,6 @@ module Ditty
24
24
  create?
25
25
  end
26
26
 
27
- def register?
28
- true
29
- end
30
-
31
27
  def permitted_attributes
32
28
  attribs = %i[email name surname]
33
29
  attribs << :role_id if user.super_admin?
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'rake'
4
4
  require 'rake/tasklib'
5
+ require 'ditty/db'
5
6
 
6
7
  module Ditty
7
8
  class Tasks < ::Rake::TaskLib
@@ -50,35 +51,42 @@ module Ditty
50
51
  end
51
52
 
52
53
  namespace :migrate do
54
+ require 'logger'
55
+
53
56
  folder = 'migrations'
54
57
 
55
58
  desc 'Check if the migration is current'
56
59
  task :check do
57
- require 'sequel'
60
+ ::DB.loggers << Logger.new($stdout)
58
61
  puts 'Running Ditty Migrations check'
59
62
  ::Sequel.extension :migration
60
- ::Sequel::Migrator.check_current(::DB, folder)
63
+ begin
64
+ ::Sequel::Migrator.check_current(::DB, folder)
65
+ puts 'Migrations up to date'
66
+ rescue Sequel::Migrator::Error => _e
67
+ puts 'Migrations NOT up to date'
68
+ end
61
69
  end
62
70
 
63
71
  desc 'Migrate Ditty database to latest version'
64
72
  task :up do
65
- require 'sequel'
73
+ ::DB.loggers << Logger.new($stdout)
66
74
  puts 'Running Ditty Migrations up'
67
75
  ::Sequel.extension :migration
68
76
  ::Sequel::Migrator.apply(::DB, folder)
69
77
  end
70
78
 
71
- desc 'Roll back the Ditty database'
79
+ desc 'Remove the whole Ditty database. You WILL lose data'
72
80
  task :down do
73
- require 'sequel'
81
+ ::DB.loggers << Logger.new($stdout)
74
82
  puts 'Running Ditty Migrations down'
75
83
  ::Sequel.extension :migration
76
84
  ::Sequel::Migrator.apply(::DB, folder, 0)
77
85
  end
78
86
 
79
- desc 'Reset the Ditty database'
87
+ desc 'Reset the Ditty database. You WILL lose data'
80
88
  task :bounce do
81
- require 'sequel'
89
+ ::DB.loggers << Logger.new($stdout)
82
90
  puts 'Running Ditty Migrations bounce'
83
91
  ::Sequel.extension :migration
84
92
  ::Sequel::Migrator.apply(::DB, folder, 0)
data/lib/ditty/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ditty
4
- VERSION = '0.3.0'.freeze
4
+ VERSION = '0.3.1'.freeze
5
5
  end
data/lib/ditty.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'ditty/version'
4
+ require 'ditty/services/logger'
4
5
 
5
6
  module Ditty
6
7
  class ComponentError < StandardError; end
@@ -57,7 +58,7 @@ module Ditty
57
58
  #
58
59
  # Ditty::Components.register_component(:component_name, ComponentModule)
59
60
  def self.register_component(name, mod)
60
- puts "Registering #{mod} as #{name}"
61
+ Ditty::Services::Logger.instance.info "Registering #{mod} as #{name}"
61
62
  @components[name] = mod
62
63
  end
63
64
 
@@ -1,6 +1,8 @@
1
1
  .row
2
2
  .col-md-12
3
3
  .panel.panel-default
4
+ .panel-body
5
+ = haml :'partials/search'
4
6
  %table.table.table-striped
5
7
  %thead
6
8
  %tr
@@ -13,7 +13,8 @@
13
13
  %label.control-label Password
14
14
  %input.form-control.border-input{ name: 'password', type: 'password' }
15
15
  %button.btn.btn-primary{ type: 'submit' } Log In
16
- .pull-right
17
- No account yet?
18
- %a.btn.btn-default{ href: "#{settings.map_path}/auth/identity/register" } Register
16
+ - if policy(::Ditty::Identity).register?
17
+ .pull-right
18
+ No account yet?
19
+ %a.btn.btn-default{ href: "#{settings.map_path}/auth/identity/register" } Register
19
20
  .col-sm-3
@@ -3,10 +3,10 @@
3
3
  .col-md-8
4
4
  .panel.panel-default
5
5
  .panel-heading
6
- %h4 Ditty Registration
6
+ %h4 Register
7
7
  .panel-body
8
8
  %form.form-horizontal{ method: 'post', action: "#{settings.map_path}/auth/identity/new" }
9
- = form_control(:username, identity, label: 'Email', placeholder: 'Your email address')
9
+ = form_control(:username, identity, label: 'Username', placeholder: 'Your username')
10
10
  = form_control(:password, identity, label: 'Password', type: :password)
11
11
  = form_control(:password_confirmation, identity, label: 'Confirm Password', type: :password)
12
12
 
@@ -30,7 +30,8 @@
30
30
  %a{ href: "#{settings.map_path}/auth/identity" }
31
31
  %i.fa.fa-user.fa-fw
32
32
  Log In
33
- %li
34
- %a{ href: "#{settings.map_path}/auth/identity/register" }
35
- %i.fa.fa-pencil-square-o.fa-fw
36
- Register
33
+ - if policy(::Ditty::Identity).register?
34
+ %li
35
+ %a{ href: "#{settings.map_path}/auth/identity/register" }
36
+ %i.fa.fa-pencil-square-o.fa-fw
37
+ Register
@@ -23,7 +23,8 @@
23
23
 
24
24
  .row
25
25
  .col-md-6
26
- %a.btn.btn-default{ href: "#{base_path}/#{entity.id}/edit" } Edit
26
+ - if policy(entity).update?
27
+ %a.btn.btn-default{ href: "#{base_path}/#{entity.id}/edit" } Edit
27
28
  .col-md-6.text-right
28
29
  - if policy(entity).delete?
29
30
  %form{ method: 'post', action: "#{base_path}/#{entity.id}" }
@@ -20,6 +20,15 @@
20
20
  %p.description
21
21
  %label Signed up:
22
22
  = entity.created_at.strftime('%Y-%m-%d %H:%M:%S')
23
+ .row
24
+ .col-md-6
25
+ - if policy(entity).update?
26
+ %a.btn.btn-default{ href: "#{base_path}/#{entity.id}/edit" } Edit
27
+ .col-md-6.text-right
28
+ - if policy(entity).delete?
29
+ %form{ method: 'post', action: "#{base_path}/#{entity.id}" }
30
+ %input{ name: '_method', value: 'DELETE', type: 'hidden' }
31
+ %button.btn.btn-warning{ type: 'submit' } Delete
23
32
  .col-md-2
24
33
 
25
34
  .row
@@ -1,3 +1,4 @@
1
1
  = form_control(:name, user)
2
2
  = form_control(:surname, user)
3
- = form_control(:role_id, user, type: 'select', options: Ditty::Role.as_hash(:id, :name), name: 'user[role_id][]', field: :roles, multiple: true)
3
+ - if policy(user).permitted_attributes.include? :role_id
4
+ = form_control(:role_id, user, type: 'select', options: Ditty::Role.as_hash(:id, :name), name: 'user[role_id][]', field: :roles, multiple: true)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ditty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jurgens du Toit
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-14 00:00:00.000000000 Z
11
+ date: 2017-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -128,14 +128,14 @@ dependencies:
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: '3.0'
131
+ version: '3.1'
132
132
  type: :runtime
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: '3.0'
138
+ version: '3.1'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: haml
141
141
  requirement: !ruby/object:Gem::Requirement