ditty 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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