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 +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/lib/safetynet/version.rb +3 -0
- data/lib/safetynet.rb +149 -0
- data/safetynet.gemspec +23 -0
- metadata +85 -0
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
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
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: []
|