safetynet 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2572c7d4bb6b380f19be2ac509527dd742d3f1ee
4
+ data.tar.gz: 4fd376f510b59f5d3a44a6462aa67622a2b1d4ff
5
+ SHA512:
6
+ metadata.gz: 36b3699df9defb2a5926a544a2bc0737c57f1083826d83b265e395e9a11732db4ad592faebc7300162044ca0f9399022abf74af71d2c1516888aea66d6eb68c4
7
+ data.tar.gz: 869a7e230e379985b11ae7f014bc0127a83479e34928c04512e023c19c92e786e2331bee6bff0e2d20a74fc91a7722e9a422c39386af886311dfd9d2612c43a3
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in safetynet.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 arktisklada
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Safetynet
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'safetynet'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install safetynet
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( https://github.com/[my-github-username]/safetynet/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,3 @@
1
+ module Safetynet
2
+ VERSION = "0.0.1"
3
+ end
data/lib/safetynet.rb ADDED
@@ -0,0 +1,149 @@
1
+ require "safetynet/version"
2
+ require 'active_support/concern'
3
+
4
+ module Safetynet
5
+ extend ActiveSupport::Concern
6
+
7
+ # Saves a record for the current method
8
+ def safe_safetynet_delivery(user, channel, method)
9
+ options = self.class.safetynet_options
10
+ log = SafetynetLog.new({
11
+ user_id: user.id,
12
+ method: method,
13
+ channel: channel.to_s
14
+ })
15
+ log.save
16
+ end
17
+
18
+ # Returns true if delivery is permitted through the given channel
19
+ # user A model to monitor
20
+ # channel email|sms (symbol)
21
+ # method Name of watched method
22
+ # limit Maximum # of methods for this channel
23
+ # timeframe Minimum allowed timeframe between last method and next (default: 30.minutes)
24
+ #
25
+ # Standalone usage on a User model
26
+ # * Defaults to configured channel, caller method,
27
+ # * and rails configuration limit & timeframe for the channel
28
+ #
29
+ # class User < ActiveRecord::Base
30
+ # include Safetynet
31
+ # safetynet :sms
32
+ #
33
+ # def send_sms
34
+ # if permit_delivery?(self)
35
+ # ...
36
+ # end
37
+ # end
38
+ # end
39
+ def permit_delivery?(address, channel=nil, method=nil, limit=nil, timeframe=nil)
40
+ # Skip check if email is whitelisted
41
+ return true if is_whitelisted?(address)
42
+
43
+ options = self.class.safetynet_options
44
+ # Set defaults from current config and call stack
45
+ channel = self.class.safetynet_channel if channel.nil?
46
+ method = caller_locations(1, 1)[0].label if method.nil?
47
+ limit = options[channel][:limit] if limit.nil?
48
+ timeframe = options[channel][:timeframe] if timeframe.nil?
49
+
50
+ # Query the model to determine if delivery is permitted
51
+ permit_delivery = true
52
+ if limit != false
53
+ count_query = SafetynetLog.where({
54
+ address: address,
55
+ channel: channel,
56
+ method: method
57
+ })
58
+ if timeframe != false
59
+ count_query = count_query.where('created_at >= ?', Time.now - timeframe)
60
+ end
61
+
62
+ # If our sending is over the limit, deny
63
+ if count_query.count >= limit
64
+ permit_delivery = false
65
+ end
66
+ end
67
+
68
+ if permit_delivery
69
+ safe_safetynet_delivery(address, channel, method)
70
+ else
71
+ AdminMailer.delivery_denied_notification(address, channel, method, {
72
+ limit: limit,
73
+ timeframe: timeframe,
74
+ message: 'Safetynet has caught a method!'
75
+ }).deliver
76
+ end
77
+ permit_delivery
78
+ end
79
+
80
+ # Hook added to after_filter to disable current action or mail delivery
81
+ def check_safetynet_window
82
+ # Skip check if email is already stopped
83
+ return false if mail.perform_deliveries == false
84
+
85
+ channel = self.class.safetynet_channel
86
+ options = self.class.safetynet_options
87
+ method = action_name
88
+ limit = options[channel][:limit]
89
+ timeframe = options[channel][:timeframe]
90
+
91
+ # Update the mail.to array with those who are whitelisted and permitted
92
+ mail.to = mail.to.keep_if do |email|
93
+ next true if is_whitelisted?(email) || !defined?(@user) || email == @user.email
94
+ permit_delivery?(address, channel, method, limit, timeframe)
95
+ end
96
+
97
+ permit_delivery = mail.to.any?
98
+
99
+ if channel == :email
100
+ mail.perform_deliveries = permit_delivery
101
+ end
102
+ permit_delivery
103
+ end
104
+
105
+ # All example.com emails are whitelisted
106
+ def is_whitelisted?(email)
107
+ !!(self.class.safetynet_options[:whitelist].match(email))
108
+ end
109
+
110
+ module ClassMethods
111
+ # safetynet
112
+ # channel email|sms (symbol)
113
+ # filters Hash of method names to watch (email only) that fits after_action requirements
114
+ # options = {
115
+ # email: { Hash for each channel (matches to field on User model) and includes:
116
+ # limit: 1, Maximum # of methods for this channel (default: 1)
117
+ # timeframe: 30.minutes Minimum allowed timeframe between last method and next (default: 30.minutes)
118
+ # }
119
+ # }
120
+ # Options merges Rails.configuration.safetynet before any class-specific options
121
+ def safetynet(channel, filters={}, options={})
122
+ @safetynet_channel = channel
123
+ @safetynet_options = {
124
+ whitelist: /@example.com/,
125
+ email: {
126
+ limit: 1,
127
+ timeframe: 30.minutes
128
+ }
129
+ }.merge(Rails.configuration.safetynet).merge(options)
130
+
131
+ # If class is a rails mailer, add after_action hook
132
+ if (self.ancestors & [ActionMailer::Base]).any?
133
+ if filters.empty?
134
+ after_action :check_safetynet_window
135
+ else
136
+ after_action :check_safetynet_window, filters
137
+ end
138
+ end
139
+ end
140
+ # Accessed in instance methods by calling self.class.safetynet_channel
141
+ def safetynet_channel
142
+ @safetynet_channel
143
+ end
144
+ # Accessed in instance methods by calling self.class.safetynet_options
145
+ def safetynet_options
146
+ @safetynet_options
147
+ end
148
+ end
149
+ end
data/safetynet.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'safetynet/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "safetynet"
8
+ spec.version = Safetynet::VERSION
9
+ spec.authors = ["arktisklada"]
10
+ spec.email = ["mail@enorganik.com"]
11
+ spec.summary = %q{Stop communication problems before they happen}
12
+ spec.description = %q{Safetynet keeps track of email communications by user/email and ActionMailer message. If the same message is sent to the same user multiple times within the allowed timeframe, it will be blocked from delivery and notify system admins. The messages are filterable in a way similar to controller filters, and the checking method can be called by itself outside the normal hooks for additional throttling (i.e. SMS sending, push notifications, etc.). }
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ # spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake"
23
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: safetynet
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - arktisklada
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: 'Safetynet keeps track of email communications by user/email and ActionMailer
42
+ message. If the same message is sent to the same user multiple times within the
43
+ allowed timeframe, it will be blocked from delivery and notify system admins. The
44
+ messages are filterable in a way similar to controller filters, and the checking
45
+ method can be called by itself outside the normal hooks for additional throttling
46
+ (i.e. SMS sending, push notifications, etc.). '
47
+ email:
48
+ - mail@enorganik.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - ".gitignore"
54
+ - Gemfile
55
+ - LICENSE.txt
56
+ - README.md
57
+ - Rakefile
58
+ - lib/safetynet.rb
59
+ - lib/safetynet/version.rb
60
+ - safetynet.gemspec
61
+ homepage: ''
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.2.2
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: Stop communication problems before they happen
85
+ test_files: []