From f002864a0840d5c8a0bd2b94737945b301a2fb95 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Fri, 23 Oct 2015 14:42:51 -0500 Subject: [PATCH] * most of VRFS-3674 (detect browser-based fraud) done --- admin/app/admin/ip_blacklist.rb | 13 ++ admin/app/admin/user_blacklist.rb | 13 ++ admin/config/application.rb | 4 + db/manifest | 3 +- db/up/track_downloads.sql | 30 ++++ ruby/lib/jam_ruby.rb | 3 + ruby/lib/jam_ruby/models/download_tracker.rb | 116 +++++++++++++ ruby/lib/jam_ruby/models/ip_blacklist.rb | 18 ++ ruby/lib/jam_ruby/models/user_blacklist.rb | 19 ++ ruby/spec/factories.rb | 7 + .../jam_ruby/models/download_tracker_spec.rb | 162 ++++++++++++++++++ ruby/spec/support/utilities.rb | 4 + web/app/assets/javascripts/jamkazam.js | 32 +++- .../stores/JamTrackPlayerStore.js.coffee | 2 +- web/app/assets/javascripts/redeem_complete.js | 4 +- .../api_jam_track_mixdowns_controller.rb | 3 + .../controllers/api_jam_tracks_controller.rb | 7 +- web/app/controllers/api_recurly_controller.rb | 4 + web/app/controllers/api_users_controller.rb | 1 + web/app/controllers/users_controller.rb | 1 + web/app/helpers/sessions_helper.rb | 15 ++ web/config/application.rb | 4 + web/spec/features/checkout_spec.rb | 28 +-- web/spec/support/app_config.rb | 4 + 24 files changed, 469 insertions(+), 28 deletions(-) create mode 100644 admin/app/admin/ip_blacklist.rb create mode 100644 admin/app/admin/user_blacklist.rb create mode 100644 db/up/track_downloads.sql create mode 100644 ruby/lib/jam_ruby/models/download_tracker.rb create mode 100644 ruby/lib/jam_ruby/models/ip_blacklist.rb create mode 100644 ruby/lib/jam_ruby/models/user_blacklist.rb create mode 100644 ruby/spec/jam_ruby/models/download_tracker_spec.rb diff --git a/admin/app/admin/ip_blacklist.rb b/admin/app/admin/ip_blacklist.rb new file mode 100644 index 000000000..3a8f1e14e --- /dev/null +++ b/admin/app/admin/ip_blacklist.rb @@ -0,0 +1,13 @@ +ActiveAdmin.register JamRuby::IpBlacklist, :as => 'IP Blacklist' do + + menu :label => 'IP Blacklist', :parent => 'Operations' + + config.sort_order = 'created_at desc' + config.batch_actions = false + + index do + column :remote_ip + column :notes + column :created_at + end +end \ No newline at end of file diff --git a/admin/app/admin/user_blacklist.rb b/admin/app/admin/user_blacklist.rb new file mode 100644 index 000000000..538125600 --- /dev/null +++ b/admin/app/admin/user_blacklist.rb @@ -0,0 +1,13 @@ +ActiveAdmin.register JamRuby::UserBlacklist, :as => 'User Blacklist' do + + menu :label => 'User Blacklist', :parent => 'Operations' + + config.sort_order = 'created_at desc' + config.batch_actions = false + + index do + column :user + column :notes + column :created_at + end +end \ No newline at end of file diff --git a/admin/config/application.rb b/admin/config/application.rb index be5ce3803..22e3df96f 100644 --- a/admin/config/application.rb +++ b/admin/config/application.rb @@ -157,5 +157,9 @@ module JamAdmin config.admin_port = ENV['ADMIN_PORT'] || 3333 config.admin_root_url = "#{config.external_protocol}#{config.external_hostname}#{(config.admin_port == 80 || config.admin_port == 443) ? '' : ':' + config.admin_port.to_s}" + + config.download_tracker_day_range = 30 + config.max_user_ip_address = 10 + config.max_multiple_users_same_ip = 2 end end diff --git a/db/manifest b/db/manifest index bd1318bc9..e0042ccb4 100755 --- a/db/manifest +++ b/db/manifest @@ -307,4 +307,5 @@ mixdown.sql aac_master.sql video_recording.sql web_playable_jamtracks.sql -affiliate_partner_rate.sql \ No newline at end of file +affiliate_partner_rate.sql +track_downloads.sql \ No newline at end of file diff --git a/db/up/track_downloads.sql b/db/up/track_downloads.sql new file mode 100644 index 000000000..f328a9579 --- /dev/null +++ b/db/up/track_downloads.sql @@ -0,0 +1,30 @@ +CREATE TABLE download_trackers ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id VARCHAR(64) REFERENCES users(id) ON DELETE CASCADE, + remote_ip VARCHAR(400) NOT NULL, + jam_track_id VARCHAR (64) NOT NULL REFERENCES jam_tracks(id) ON DELETE CASCADE, + paid BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX index_download_trackers_on_user_id ON download_trackers USING btree (user_id); +CREATE INDEX index_download_trackers_on_remote_ip ON download_trackers USING btree (remote_ip); +CREATE INDEX index_download_trackers_on_created_at ON download_trackers USING btree (created_at, paid); + + +CREATE TABLE ip_blacklists ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + remote_ip VARCHAR(400) UNIQUE NOT NULL, + notes VARCHAR, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + + +CREATE TABLE user_blacklists ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id VARCHAR(64) UNIQUE NOT NULL REFERENCES users(id) ON DELETE CASCADE, + notes VARCHAR, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index 7dc214728..b003362eb 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -111,6 +111,9 @@ require "jam_ruby/models/anonymous_user" require "jam_ruby/models/signup_hint" require "jam_ruby/models/machine_fingerprint" require "jam_ruby/models/machine_extra" +require "jam_ruby/models/download_tracker" +require "jam_ruby/models/ip_blacklist" +require "jam_ruby/models/user_blacklist" require "jam_ruby/models/fraud_alert" require "jam_ruby/models/fingerprint_whitelist" require "jam_ruby/models/rsvp_request" diff --git a/ruby/lib/jam_ruby/models/download_tracker.rb b/ruby/lib/jam_ruby/models/download_tracker.rb new file mode 100644 index 000000000..2d6d5a9e9 --- /dev/null +++ b/ruby/lib/jam_ruby/models/download_tracker.rb @@ -0,0 +1,116 @@ +module JamRuby + class DownloadTracker < ActiveRecord::Base + + @@log = Logging.logger[DownloadTracker] + + belongs_to :user, :class_name => "JamRuby::User" + belongs_to :mixdown, :class_name => "JamRuby::JamTrackMixdownPackage", foreign_key: 'mixdown_id' + belongs_to :stem, :class_name => "JamRuby::JamTrackTrack", foreign_key: 'stem_id' + belongs_to :jam_track, :class_name => "JamRuby::JamTrack" + + # one of mixdown or stem need to be specified. could validate this? + validates :user, presence:true + validates :remote_ip, presence: true + #validates :paid, presence: true + validates :jam_track, presence: :true + + def self.create(user, remote_ip, target, owned) + dt = DownloadTracker.new + dt.user = user + dt.remote_ip = remote_ip + dt.paid = owned + if target.is_a?(JamTrackTrack) + dt.jam_track_id = target.jam_track_id + elsif target.is_a?(JamTrackMixdownPackage) + dt.jam_track_id = target.jam_track_mixdown.jam_track_id + end + if !dt.save + @@log.error("unable to create Download Tracker: #{dt.errors.inspect}") + end + dt + end + + def self.check(user, remote_ip, target, owned) + + create(user, remote_ip, target, owned) + + # let's check the following + alert_freebies_snarfer(remote_ip) + + alert_user_sharer(user) + end + + # somebody who has shared account info with a large number of people + # high number of downloads of the same user from different IP addresses that were or were not paid for + # raw query created by this code: + # SELECT distinct(user_id), count(user_id) FROM "download_trackers" WHERE (created_at > NOW() - '30 days'::interval) GROUP BY user_id HAVING count(distinct(remote_ip)) >= 2 + def self.check_user_sharer(max, user_id = nil) + query = DownloadTracker.select('distinct(user_id), count(user_id)') + query = query.where("created_at > NOW() - '#{APP_CONFIG.download_tracker_day_range} days'::interval") + if !user_id.nil? + query = query.where('user_id = ?', user_id) + end + + query.group(:user_id).having("count(distinct(remote_ip)) >= #{max}") + end + + + + + # somebody who has figured out how to bypass cookie based method of identity checking, and is getting lots of free JamTracks + # high number of downloads of different jam tracks from different users for the same IP address that weren't paid for + # raw query created by this code: + # SELECT distinct(remote_ip), count(remote_ip) FROM "download_trackers" WHERE (paid = false) AND (created_at > NOW() - '30 days'::interval) GROUP BY remote_ip HAVING count(distinct(jam_track_id)) >= 2 + def self.check_freebie_snarfer(max, remote_ip = nil) + + query = DownloadTracker.select('distinct(remote_ip), count(remote_ip)').where("paid = false") + query = query.where("created_at > NOW() - '#{APP_CONFIG.download_tracker_day_range} days'::interval") + if !remote_ip.nil? + query = query.where('remote_ip = ?', remote_ip) + end + query.group(:remote_ip).having("count(distinct(jam_track_id)) >= #{max}") + end + + def self.alert_user_sharer(user) + violation = check_user_sharer(APP_CONFIG.max_user_ip_address, user.id).first + + if violation + body = "User has downloaded from too many IP addresses #{user.id}\n" + body << "Download Count: #{violation['count']}\n" + body << "User URL #{user.admin_url}\n" + + AdminMailer.alerts({ + subject:"Account IP Access Violation. USER: #{user.email}", + body:body + }).deliver + end + end + + def self.alert_freebies_snarfer(remote_ip) + + violation = check_freebie_snarfer(APP_CONFIG.max_multiple_users_same_ip, remote_ip).first + + if violation + body = "IP Address: #{remote_ip}\n" + body << "Download Count: #{violation['count']}\n" + + AdminMailer.alerts({ + subject:"Single IP Access Violation. IP:#{remote_ip}", + body:body + }).deliver + end + end + + def admin_url + APP_CONFIG.admin_root_url + "/admin/download_trackers/" + id + end + + def to_s + if stem? + "stem:#{stem} #{remote_ip} #{user}" + else + "mixdown:#{mixdown} #{remote_ip} #{user}" + end + end + end +end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/ip_blacklist.rb b/ruby/lib/jam_ruby/models/ip_blacklist.rb new file mode 100644 index 000000000..5e50a2fba --- /dev/null +++ b/ruby/lib/jam_ruby/models/ip_blacklist.rb @@ -0,0 +1,18 @@ +module JamRuby + class IpBlacklist < ActiveRecord::Base + + attr_accessible :remote_ip, :notes, as: :admin + + @@log = Logging.logger[IpBlacklist] + + validates :remote_ip, presence:true, uniqueness:true + + def self.listed(remote_ip) + IpBlacklist.count(:conditions => "remote_ip = '#{remote_ip}'") == 1 + end + + def to_s + remote_ip + end + end +end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/user_blacklist.rb b/ruby/lib/jam_ruby/models/user_blacklist.rb new file mode 100644 index 000000000..6d5796af8 --- /dev/null +++ b/ruby/lib/jam_ruby/models/user_blacklist.rb @@ -0,0 +1,19 @@ +module JamRuby + class UserBlacklist < ActiveRecord::Base + + attr_accessible :user_id, :notes, as: :admin + @@log = Logging.logger[UserBlacklist] + + belongs_to :user, :class_name => "JamRuby::User" + + validates :user, presence:true, uniqueness: true + + def self.listed(user) + UserBlacklist.count(:conditions => "user_id= '#{user.id}'") == 1 + end + + def to_s + user + end + end +end \ No newline at end of file diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 5de311a87..c8c91241e 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -790,6 +790,13 @@ FactoryGirl.define do tap_in_count 3 end + factory :download_tracker, :class => JamRuby::DownloadTracker do + remote_ip '1.1.1.1' + paid false + association :user, factory: :user + association :jam_track, factory: :jam_track + end + factory :sale, :class => JamRuby::Sale do order_total 0 association :user, factory:user diff --git a/ruby/spec/jam_ruby/models/download_tracker_spec.rb b/ruby/spec/jam_ruby/models/download_tracker_spec.rb new file mode 100644 index 000000000..f50c4c903 --- /dev/null +++ b/ruby/spec/jam_ruby/models/download_tracker_spec.rb @@ -0,0 +1,162 @@ +require 'spec_helper' + +describe DownloadTracker do + + let(:user1) {FactoryGirl.create(:user)} + let(:user2) {FactoryGirl.create(:user)} + let(:user3) {FactoryGirl.create(:user)} + let(:jam_track) {FactoryGirl.create(:jam_track)} + + describe "check_user_sharer" do + describe "with max 1" do + it "and there is one row that is not paid for" do + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '1.1.1.1') + + results = DownloadTracker.check_user_sharer(1) + results.all.count.should eq 1 + results[0]['user_id'].should eql (user1.id) + end + end + + describe "with max 2" do + + it "and there is one row that is not paid for" do + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '1.1.1.1') + + results = DownloadTracker.check_user_sharer(2) + results.all.count.should eq 0 + + # and then add that same user at different IP, and see that something shows up + + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '2.2.2.2') + results = DownloadTracker.check_user_sharer(2) + results.all.count.should eq 1 + end + + it "and there are two rows from different IP address, different jam tracks" do + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '1.1.1.1') + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '2.2.2.2') + + results = DownloadTracker.check_user_sharer(2) + results.all.count.should eq 1 + results[0]['user_id'].should eql(user1.id) + results[0]['count'].should eql('2') + + # now add a second user with one of the previous IP addresses; shouldn't matter yet + tracker1 = FactoryGirl.create(:download_tracker, user: user2, paid: false, remote_ip: '1.1.1.1') + + results = DownloadTracker.check_user_sharer(2) + results.all.count.should eq 1 + results[0]['user_id'].should eql(user1.id) + results[0]['count'].should eql('2') + end + + it "and there are two rows from same IP adresss, same jam track" do + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '1.1.1.1', jam_track: jam_track) + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '1.1.1.1', jam_track: jam_track) + + results = DownloadTracker.check_user_sharer(2) + results.all.count.should eq 0 + + tracker1 = FactoryGirl.create(:download_tracker, user: user2, paid: false, remote_ip: '1.1.1.1') + + results = DownloadTracker.check_user_sharer(2) + results.all.count.should eq 0 + + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '2.2.2.2') + + results = DownloadTracker.check_user_sharer(2) + results.all.count.should eq 1 + results[0]['user_id'].should eql(user1.id) + results[0]['count'].should eql('3') + end + + it "and there are two rows from same user one paid for, one not" do + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: true, remote_ip: '1.1.1.1') + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '2.2.2.2') + + results = DownloadTracker.check_user_sharer(2) + results.all.count.should eq 1 + end + end + end + + describe "check_freebie_snarfer" do + + describe "with max 1" do + + it "and there is one row that is not paid for" do + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '1.1.1.1') + + results = DownloadTracker.check_freebie_snarfer(1) + results.all.count.should eq 1 + end + + it "and there is one row that is paid for" do + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: true, remote_ip: '1.1.1.1') + + results = DownloadTracker.check_freebie_snarfer(1) + results.all.count.should eq 0 + end + end + describe "with max 2" do + + it "and there is one row that is not paid for" do + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '1.1.1.1') + + results = DownloadTracker.check_freebie_snarfer(2) + results.all.count.should eq 0 + + # and then add a second user at same IP, and see that something shows up + + tracker1 = FactoryGirl.create(:download_tracker, user: user2, paid: false, remote_ip: '1.1.1.1') + results = DownloadTracker.check_freebie_snarfer(2) + results.all.count.should eq 1 + end + + it "and there are two rows from same user, different jam tracks" do + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '1.1.1.1') + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '1.1.1.1') + + results = DownloadTracker.check_freebie_snarfer(2) + results.all.count.should eq 1 + results[0]['remote_ip'].should eql('1.1.1.1') + results[0]['count'].should eql('2') + + tracker1 = FactoryGirl.create(:download_tracker, user: user2, paid: false, remote_ip: '2.2.2.2') + + results = DownloadTracker.check_freebie_snarfer(2) + results.all.count.should eq 1 + results[0]['remote_ip'].should eql('1.1.1.1') + results[0]['count'].should eql('2') + end + + it "and there are two rows from same user, same jam track" do + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '1.1.1.1', jam_track: jam_track) + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '1.1.1.1', jam_track: jam_track) + + results = DownloadTracker.check_freebie_snarfer(2) + results.all.count.should eq 0 + + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '2.2.2.2') + + results = DownloadTracker.check_freebie_snarfer(2) + results.all.count.should eq 0 + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '1.1.1.1') + + results = DownloadTracker.check_freebie_snarfer(2) + results.all.count.should eq 1 + results[0]['remote_ip'].should eql('1.1.1.1') + results[0]['count'].should eql('3') + end + + it "and there are two rows from same user one paid for, one not" do + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: true, remote_ip: '1.1.1.1') + tracker1 = FactoryGirl.create(:download_tracker, user: user1, paid: false, remote_ip: '1.1.1.1') + + results = DownloadTracker.check_freebie_snarfer(2) + results.all.count.should eq 0 + end + end + end +end diff --git a/ruby/spec/support/utilities.rb b/ruby/spec/support/utilities.rb index e33df4093..75c46f26b 100644 --- a/ruby/spec/support/utilities.rb +++ b/ruby/spec/support/utilities.rb @@ -242,6 +242,10 @@ def app_config 300 end + def download_tracker_day_range + 30 + end + private def audiomixer_workspace_path diff --git a/web/app/assets/javascripts/jamkazam.js b/web/app/assets/javascripts/jamkazam.js index e32b0f041..efb07aa0c 100644 --- a/web/app/assets/javascripts/jamkazam.js +++ b/web/app/assets/javascripts/jamkazam.js @@ -133,7 +133,22 @@ app.notify({title: "Oops!", text: "What you were looking for is gone now."}); } else if(jqXHR.status === 403) { - logger.debug("not logged in"); + logger.debug("not logged in or something else"); + + try { + var responseJson = JSON.parse(jqXHR.responseText) + + if (responseJson['message'] == 'IP blacklisted') { + app.notify({title: 'Suspicious Activity', text: 'The IP address you are accessing JamKazam from has shown suspicious activity in the past. If you believe this is in error, please contact support@jamkazam.com.', icon_url: "/assets/content/icon_alert_big.png"}) + return; + } + if (responseJson['message'] == 'User blacklisted') { + app.notify({title: 'Suspicious Activity', text: 'The user account that you are accessing JamKazam from has shown suspicious activity in the past. If you believe this is in error, please contact support@jamkazam.com.', icon_url: "/assets/content/icon_alert_big.png"}) + return; + } + }catch(e) { } + + app.notify({title: 'Not Logged In', text: 'Please log in.', icon_url: "/assets/content/icon_alert_big.png"}) } else if (jqXHR.status === 422) { logger.error("Unexpected ajax error: " + textStatus + ", msg: " + errorMessage + ", response: " + jqXHR.responseText); @@ -230,6 +245,21 @@ } else if(jqXHR.status == 403) { logger.debug("permission error sent from server:", jqXHR.responseText) + + try { + var responseJson = JSON.parse(jqXHR.responseText) + + if (responseJson['message'] == 'IP blacklisted') { + this.notify({title: 'Suspicious Activity', text: 'The IP address you are accessing JamKazam from has shown suspicious activity in the past. If you believe this is in error, please contact support@jamkazam.com.', icon_url: "/assets/content/icon_alert_big.png"}) + return; + } + if (responseJson['message'] == 'User blacklisted') { + this.notify({title: 'Suspicious Activity', text: 'The user account that you are accessing JamKazam from has shown suspicious activity in the past. If you believe this is in error, please contact support@jamkazam.com.', icon_url: "/assets/content/icon_alert_big.png"}) + return; + } + }catch(e) { } + + // default this.notify({title: 'Permission Error', text: 'You do not have permission to access this information', icon_url: "/assets/content/icon_alert_big.png"}) } else { diff --git a/web/app/assets/javascripts/react-components/stores/JamTrackPlayerStore.js.coffee b/web/app/assets/javascripts/react-components/stores/JamTrackPlayerStore.js.coffee index c4ba3b6cf..4bc6206e3 100644 --- a/web/app/assets/javascripts/react-components/stores/JamTrackPlayerStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/JamTrackPlayerStore.js.coffee @@ -24,7 +24,7 @@ BrowserMediaActions = @BrowserMediaActions # Register with the app store to get @app this.listenTo(context.AppStore, this.onAppInit) this.listenTo(context.BrowserMediaStore, this.onBrowserMediaChanged) - + @browserMediaState = {} onAppInit: (app) -> @app = app diff --git a/web/app/assets/javascripts/redeem_complete.js b/web/app/assets/javascripts/redeem_complete.js index c04289d4c..0b89e1c9a 100644 --- a/web/app/assets/javascripts/redeem_complete.js +++ b/web/app/assets/javascripts/redeem_complete.js @@ -79,9 +79,7 @@ prepThanks(); }) - .fail(function() { - - }) + .fail(app.ajaxError) } } diff --git a/web/app/controllers/api_jam_track_mixdowns_controller.rb b/web/app/controllers/api_jam_track_mixdowns_controller.rb index 55edbc586..958f8b9b1 100644 --- a/web/app/controllers/api_jam_track_mixdowns_controller.rb +++ b/web/app/controllers/api_jam_track_mixdowns_controller.rb @@ -4,6 +4,8 @@ class ApiJamTrackMixdownsController < ApiController before_filter :api_signed_in_user before_filter :lookup_jam_track_mixdown, :only => [:download, :enqueue, :update] before_filter :lookup_jam_track_right, :only => [:download, :enqueue, :update] + before_filter :ip_blacklist, :only => [:download] + before_filter :user_blacklist, :only => [:download] respond_to :json def log @@ -78,6 +80,7 @@ class ApiJamTrackMixdownsController < ApiController end if @package.ready? + DownloadTracker.check(current_user, request.remote_ip, @package, !@jam_track_right.redeemed) @package.update_download_count now = Time.now @package.last_downloaded_at = now diff --git a/web/app/controllers/api_jam_tracks_controller.rb b/web/app/controllers/api_jam_tracks_controller.rb index 3dc6d8502..e1ddbb4c6 100644 --- a/web/app/controllers/api_jam_tracks_controller.rb +++ b/web/app/controllers/api_jam_tracks_controller.rb @@ -4,7 +4,10 @@ class ApiJamTracksController < ApiController before_filter :api_signed_in_user, :except => [:index, :autocomplete, :show_with_artist_info, :artist_index] before_filter :api_any_user, :only => [:index, :autocomplete, :show_with_artist_info, :artist_index] before_filter :lookup_jam_track_right, :only => [:download,:enqueue, :show_jam_track_right, :mark_active, :download_stem] - + before_filter :ip_blacklist, :only => [:download_stem, :download] + before_filter :user_blacklist, :only => [:download_stem, :download] + + respond_to :json def log @@ -113,6 +116,8 @@ class ApiJamTracksController < ApiController jam_track_track = JamTrackTrack.find(params[:stem_id]) end + DownloadTracker.check(current_user, request.remote_ip, @jam_track_track, !@jam_track_right.redeemed) + if params[:download] redirect_to jam_track_track.web_download_sign_url(120, params[:file_type], 'application/octet-stream', "attachment; filename=\"#{@jam_track_right.jam_track.name + '-' + jam_track_track.display_name}.mp3\"") else diff --git a/web/app/controllers/api_recurly_controller.rb b/web/app/controllers/api_recurly_controller.rb index 41c9c041e..a76113739 100644 --- a/web/app/controllers/api_recurly_controller.rb +++ b/web/app/controllers/api_recurly_controller.rb @@ -2,6 +2,10 @@ require 'jam_ruby/recurly_client' class ApiRecurlyController < ApiController before_filter :api_signed_in_user, :except => [:create_account] before_filter :create_client + before_filter :ip_blacklist, :only => [:place_order] + before_filter :user_blacklist, :only => [:place_order] + + respond_to :json # create Recurly account diff --git a/web/app/controllers/api_users_controller.rb b/web/app/controllers/api_users_controller.rb index f96373279..ad8ec6583 100644 --- a/web/app/controllers/api_users_controller.rb +++ b/web/app/controllers/api_users_controller.rb @@ -15,6 +15,7 @@ ApiUsersController < ApiController :set_password, :begin_update_email, :update_avatar, :delete_avatar, :generate_filepicker_policy, :share_session, :share_recording, :affiliate_report, :audio_latency, :broadcast_notification] + before_filter :ip_blacklist, :only => [:create] respond_to :json, :except => :calendar respond_to :ics, :only => :calendar diff --git a/web/app/controllers/users_controller.rb b/web/app/controllers/users_controller.rb index 7824a0fe8..bc4c64e88 100644 --- a/web/app/controllers/users_controller.rb +++ b/web/app/controllers/users_controller.rb @@ -21,6 +21,7 @@ class UsersController < ApplicationController before_filter :correct_user, only: [:edit, :update] before_filter :admin_user, only: :destroy before_filter :is_native_client + before_filter :ip_blacklist, :only => [:create] rescue_from 'JamRuby::PermissionError' do |exception| diff --git a/web/app/helpers/sessions_helper.rb b/web/app/helpers/sessions_helper.rb index 553ad5233..b19d252fa 100644 --- a/web/app/helpers/sessions_helper.rb +++ b/web/app/helpers/sessions_helper.rb @@ -139,6 +139,21 @@ module SessionsHelper end end + def ip_blacklist + if IpBlacklist.listed(request.remote_ip) + Stats.write('web.blacklist.ip', {value: 1, remote_ip: request.remote_ip}) + render :json => { :message => "IP blacklisted"}, :status => 403 + end + end + + def user_blacklist + if UserBlacklist.listed(current_user) + Stats.write('web.blacklist.user', {value: 1, user_id: current_user.id}) + render :json => { :message => "User blacklisted"}, :status => 403 + end + + end + # take either the signed in user, or if that fails, try the anonymous user def api_any_user unless signed_in? || has_anonymous_user? diff --git a/web/config/application.rb b/web/config/application.rb index 49724235a..5661b2252 100644 --- a/web/config/application.rb +++ b/web/config/application.rb @@ -370,5 +370,9 @@ if defined?(Bundler) config.react.addons = true config.time_shift_style = :sbsms # or sox + + config.download_tracker_day_range = 30 + config.max_user_ip_address = 10 + config.max_multiple_users_same_ip = 2 end end diff --git a/web/spec/features/checkout_spec.rb b/web/spec/features/checkout_spec.rb index 3bbb08704..c7e3e47d3 100644 --- a/web/spec/features/checkout_spec.rb +++ b/web/spec/features/checkout_spec.rb @@ -833,31 +833,17 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d affiliate_params = partner.affiliate_query_params visit '/landing/jamtracks/acdc-backinblack?' + affiliate_params - find('.browse-band a').trigger(:click) - find('h1', text: 'search jamtracks') - #find('a', text: 'What is a JamTrack?') - find("a.jamtrack-add-cart[data-jamtrack-id=\"#{jamtrack_acdc_backinblack.id}\"]", text: 'GET IT FREE!').trigger(:click) - find('h3', text: 'OR SIGN UP USING YOUR EMAIL') - shopping_carts = ShoppingCart.all - shopping_carts.count.should eq(1) - shopping_cart = shopping_carts[0] - shopping_cart.anonymous_user_id.should_not be_nil - shopping_cart.user_id.should be_nil + fill_in "email", with: 'testuser_12345@jamkazam.com' + fill_in "password", with: 'jam123' + find('.register-area ins').trigger(:click) + find('button.cta-button', text: 'GET IT FREE!').trigger(:click) - fill_in 'first_name', with: 'Seth' - fill_in 'last_name', with: 'Call' - fill_in 'email', with: 'guy_referral@jamkazam.com' - fill_in 'password', with: 'jam123' - find('.right-side .terms_of_service input').trigger(:click) # accept TOS + # this should show on the /client#/jamtrack page + find('.no-free-jamtrack') - # try to submit, and see order page - find('.signup-submit').trigger(:click) - - find('.jam-tracks-in-browser') - - guy = User.find_by_email('guy_referral@jamkazam.com') + guy = User.find_by_email('testuser_12345@jamkazam.com') guy.affiliate_referral.should eq(partner) guy.reload diff --git a/web/spec/support/app_config.rb b/web/spec/support/app_config.rb index 76ff7c8f2..8f64686f1 100644 --- a/web/spec/support/app_config.rb +++ b/web/spec/support/app_config.rb @@ -122,6 +122,10 @@ def web_config def num_packaging_nodes 2 end + + def download_tracker_day_range + 30 + end end klass.new end