mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-11 01:31:39 -05:00
Add brakeman and some tests
This commit is contained in:
parent
67916c10c4
commit
41604d71a6
9 changed files with 166 additions and 19 deletions
1
Gemfile
1
Gemfile
|
|
@ -45,6 +45,7 @@ gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]
|
||||||
gem 'jwt'
|
gem 'jwt'
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
|
gem 'brakeman', require: false
|
||||||
gem 'debug', platforms: %i[mri mingw x64_mingw]
|
gem 'debug', platforms: %i[mri mingw x64_mingw]
|
||||||
gem 'dotenv-rails'
|
gem 'dotenv-rails'
|
||||||
gem 'factory_bot_rails'
|
gem 'factory_bot_rails'
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,8 @@ GEM
|
||||||
bigdecimal (3.1.9)
|
bigdecimal (3.1.9)
|
||||||
bootsnap (1.18.4)
|
bootsnap (1.18.4)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
|
brakeman (7.0.2)
|
||||||
|
racc
|
||||||
builder (3.3.0)
|
builder (3.3.0)
|
||||||
byebug (11.1.3)
|
byebug (11.1.3)
|
||||||
chartkick (5.1.3)
|
chartkick (5.1.3)
|
||||||
|
|
@ -476,6 +478,7 @@ DEPENDENCIES
|
||||||
aws-sdk-kms (~> 1.96.0)
|
aws-sdk-kms (~> 1.96.0)
|
||||||
aws-sdk-s3 (~> 1.177.0)
|
aws-sdk-s3 (~> 1.177.0)
|
||||||
bootsnap
|
bootsnap
|
||||||
|
brakeman
|
||||||
chartkick
|
chartkick
|
||||||
data_migrate
|
data_migrate
|
||||||
database_consistency
|
database_consistency
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,12 @@ class ApplicationController < ActionController::Base
|
||||||
redirect_to root_path, notice: 'Your account is not active.', status: :see_other
|
redirect_to root_path, notice: 'Your account is not active.', status: :see_other
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def authenticate_non_self_hosted!
|
||||||
|
return unless DawarichSettings.self_hosted?
|
||||||
|
|
||||||
|
redirect_to root_path, notice: 'You are not authorized to perform this action.', status: :see_other
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_self_hosted_status
|
def set_self_hosted_status
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class Settings::SubscriptionsController < ApplicationController
|
class Settings::SubscriptionsController < ApplicationController
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
|
before_action :authenticate_non_self_hosted!
|
||||||
|
|
||||||
def index; end
|
def index; end
|
||||||
|
|
||||||
|
|
@ -16,13 +17,12 @@ class Settings::SubscriptionsController < ApplicationController
|
||||||
{ algorithm: 'HS256' }
|
{ algorithm: 'HS256' }
|
||||||
).first.symbolize_keys
|
).first.symbolize_keys
|
||||||
|
|
||||||
# Verify this is for the current user
|
|
||||||
unless decoded_token[:user_id] == current_user.id
|
unless decoded_token[:user_id] == current_user.id
|
||||||
redirect_to settings_subscriptions_path, alert: 'Invalid subscription update request.'
|
redirect_to settings_subscriptions_path, alert: 'Invalid subscription update request.'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
current_user.update!(status: decoded_token[:status])
|
current_user.update!(status: decoded_token[:status], active_until: decoded_token[:active_until])
|
||||||
|
|
||||||
redirect_to settings_subscriptions_path, notice: 'Your subscription has been updated successfully!'
|
redirect_to settings_subscriptions_path, notice: 'Your subscription has been updated successfully!'
|
||||||
rescue JWT::DecodeError
|
rescue JWT::DecodeError
|
||||||
|
|
|
||||||
|
|
@ -59,12 +59,11 @@ module Distanceable
|
||||||
return 0 if points.length < 2
|
return 0 if points.length < 2
|
||||||
|
|
||||||
total_meters = points.each_cons(2).sum do |point1, point2|
|
total_meters = points.each_cons(2).sum do |point1, point2|
|
||||||
connection.select_value(<<-SQL.squish)
|
connection.select_value(
|
||||||
SELECT ST_Distance(
|
'SELECT ST_Distance(ST_GeomFromEWKT($1)::geography, ST_GeomFromEWKT($2)::geography)',
|
||||||
ST_GeomFromEWKT('#{point1.lonlat}')::geography,
|
nil,
|
||||||
ST_GeomFromEWKT('#{point2.lonlat}')::geography
|
[point1.lonlat, point2.lonlat]
|
||||||
)
|
)
|
||||||
SQL
|
|
||||||
end
|
end
|
||||||
|
|
||||||
total_meters.to_f / DISTANCE_UNITS[unit.to_sym]
|
total_meters.to_f / DISTANCE_UNITS[unit.to_sym]
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,17 @@ module Visits
|
||||||
end
|
end
|
||||||
|
|
||||||
def call
|
def call
|
||||||
bounding_box = "ST_MakeEnvelope(#{sw_lng}, #{sw_lat}, #{ne_lng}, #{ne_lat}, 4326)"
|
|
||||||
|
|
||||||
Visit
|
Visit
|
||||||
.includes(:place)
|
.includes(:place)
|
||||||
.where(user:)
|
.where(user:)
|
||||||
.joins(:place)
|
.joins(:place)
|
||||||
.where("ST_Contains(#{bounding_box}, ST_SetSRID(places.lonlat::geometry, 4326))")
|
.where(
|
||||||
|
'ST_Contains(ST_MakeEnvelope(?, ?, ?, ?, 4326), ST_SetSRID(places.lonlat::geometry, 4326))',
|
||||||
|
sw_lng,
|
||||||
|
sw_lat,
|
||||||
|
ne_lng,
|
||||||
|
ne_lat
|
||||||
|
)
|
||||||
.order(started_at: :desc)
|
.order(started_at: :desc)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ RSpec.describe BulkVisitsSuggestingJob, type: :job do
|
||||||
let(:start_at) { 1.day.ago.beginning_of_day }
|
let(:start_at) { 1.day.ago.beginning_of_day }
|
||||||
let(:end_at) { 1.day.ago.end_of_day }
|
let(:end_at) { 1.day.ago.end_of_day }
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
let(:inactive_user) { create(:user, status: :inactive) }
|
let(:inactive_user) { create(:user, :inactive) }
|
||||||
let(:user_with_points) { create(:user) }
|
let(:user_with_points) { create(:user) }
|
||||||
let(:time_chunks) { [[start_at, end_at]] }
|
let(:time_chunks) { [[start_at, end_at]] }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ RSpec.describe User, type: :model do
|
||||||
|
|
||||||
describe '#activate' do
|
describe '#activate' do
|
||||||
context 'when self-hosted' do
|
context 'when self-hosted' do
|
||||||
let!(:user) { create(:user, status: :inactive, active_until: 1.day.ago) }
|
let!(:user) { create(:user, :inactive) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(DawarichSettings).to receive(:self_hosted?).and_return(true)
|
allow(DawarichSettings).to receive(:self_hosted?).and_return(true)
|
||||||
|
|
@ -49,7 +49,7 @@ RSpec.describe User, type: :model do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not activate user' do
|
it 'does not activate user' do
|
||||||
user = create(:user, status: :inactive, active_until: 1.day.ago)
|
user = create(:user, :inactive)
|
||||||
|
|
||||||
expect(user.active?).to be_falsey
|
expect(user.active?).to be_falsey
|
||||||
expect(user.active_until).to be_within(1.minute).of(1.day.ago)
|
expect(user.active_until).to be_within(1.minute).of(1.day.ago)
|
||||||
|
|
@ -194,7 +194,7 @@ RSpec.describe User, type: :model do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user is inactive' do
|
context 'when user is inactive' do
|
||||||
let(:user) { create(:user, status: :inactive, active_until: 1.day.ago) }
|
let(:user) { create(:user, :inactive) }
|
||||||
|
|
||||||
it 'returns false' do
|
it 'returns false' do
|
||||||
expect(user.can_subscribe?).to be_falsey
|
expect(user.can_subscribe?).to be_falsey
|
||||||
|
|
@ -216,7 +216,7 @@ RSpec.describe User, type: :model do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user is inactive' do
|
context 'when user is inactive' do
|
||||||
let(:user) { create(:user, status: :inactive, active_until: 1.day.ago) }
|
let(:user) { create(:user, :inactive) }
|
||||||
|
|
||||||
it 'returns true' do
|
it 'returns true' do
|
||||||
expect(user.can_subscribe?).to be_truthy
|
expect(user.can_subscribe?).to be_truthy
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,141 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe "Settings::Subscriptions", type: :request do
|
RSpec.describe 'Settings::Subscriptions', type: :request do
|
||||||
describe "GET /index" do
|
let(:user) { create(:user, :inactive) }
|
||||||
pending "add some examples (or delete) #{__FILE__}"
|
let(:jwt_secret) { ENV['JWT_SECRET_KEY'] }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_const('ENV', ENV.to_h.merge('JWT_SECRET_KEY' => 'test_secret'))
|
||||||
|
stub_request(:any, 'https://api.github.com/repos/Freika/dawarich/tags')
|
||||||
|
.to_return(status: 200, body: '[{"name": "1.0.0"}]', headers: {})
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when Dawarich is not self-hosted' do
|
||||||
|
before do
|
||||||
|
allow(DawarichSettings).to receive(:self_hosted?).and_return(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /settings/subscriptions' do
|
||||||
|
context 'when user is not authenticated' do
|
||||||
|
it 'redirects to login page' do
|
||||||
|
get settings_subscriptions_path
|
||||||
|
|
||||||
|
expect(response).to redirect_to(new_user_session_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is authenticated' do
|
||||||
|
before { sign_in user }
|
||||||
|
|
||||||
|
it 'returns successful response' do
|
||||||
|
get settings_subscriptions_path
|
||||||
|
|
||||||
|
expect(response).to be_successful
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /settings/subscriptions/callback' do
|
||||||
|
context 'when user is not authenticated' do
|
||||||
|
it 'redirects to login page' do
|
||||||
|
get subscription_callback_settings_subscriptions_path(token: 'invalid')
|
||||||
|
|
||||||
|
expect(response).to redirect_to(new_user_session_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is authenticated' do
|
||||||
|
before { sign_in user }
|
||||||
|
|
||||||
|
context 'with valid token' do
|
||||||
|
let(:token) do
|
||||||
|
JWT.encode(
|
||||||
|
{ user_id: user.id, status: 'active', active_until: 1.year.from_now },
|
||||||
|
jwt_secret,
|
||||||
|
'HS256'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates user status and redirects with success message' do
|
||||||
|
get subscription_callback_settings_subscriptions_path(token: token)
|
||||||
|
|
||||||
|
expect(user.reload.status).to eq('active')
|
||||||
|
expect(user.active_until).to be_within(1.day).of(1.year.from_now)
|
||||||
|
expect(response).to redirect_to(settings_subscriptions_path)
|
||||||
|
expect(flash[:notice]).to eq('Your subscription has been updated successfully!')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with token for different user' do
|
||||||
|
let(:other_user) { create(:user) }
|
||||||
|
let(:token) do
|
||||||
|
JWT.encode(
|
||||||
|
{ user_id: other_user.id, status: 'active' },
|
||||||
|
jwt_secret,
|
||||||
|
'HS256'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not update status and redirects with error' do
|
||||||
|
get subscription_callback_settings_subscriptions_path(token: token)
|
||||||
|
|
||||||
|
expect(user.reload.status).not_to eq('active')
|
||||||
|
expect(response).to redirect_to(settings_subscriptions_path)
|
||||||
|
expect(flash[:alert]).to eq('Invalid subscription update request.')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with invalid token' do
|
||||||
|
it 'redirects with decode error message' do
|
||||||
|
get subscription_callback_settings_subscriptions_path(token: 'invalid')
|
||||||
|
|
||||||
|
expect(response).to redirect_to(settings_subscriptions_path)
|
||||||
|
expect(flash[:alert]).to eq('Failed to verify subscription update.')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with malformed token data' do
|
||||||
|
let(:token) do
|
||||||
|
JWT.encode({ user_id: 'invalid', status: nil }, jwt_secret, 'HS256')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'redirects with invalid data message' do
|
||||||
|
get subscription_callback_settings_subscriptions_path(token: token)
|
||||||
|
|
||||||
|
expect(response).to redirect_to(settings_subscriptions_path)
|
||||||
|
expect(flash[:alert]).to eq('Invalid subscription update request.')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when Dawarich is self-hosted' do
|
||||||
|
before do
|
||||||
|
allow(DawarichSettings).to receive(:self_hosted?).and_return(true)
|
||||||
|
sign_in user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /settings/subscriptions' do
|
||||||
|
context 'when user is not authenticated' do
|
||||||
|
it 'redirects to root path' do
|
||||||
|
get settings_subscriptions_path
|
||||||
|
|
||||||
|
expect(response).to redirect_to(root_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /settings/subscriptions/callback' do
|
||||||
|
context 'when user is not authenticated' do
|
||||||
|
it 'redirects to root path' do
|
||||||
|
get subscription_callback_settings_subscriptions_path(token: 'invalid')
|
||||||
|
|
||||||
|
expect(response).to redirect_to(root_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue