Add brakeman and some tests

This commit is contained in:
Eugene Burmakin 2025-04-04 22:16:52 +02:00
parent 67916c10c4
commit 41604d71a6
9 changed files with 166 additions and 19 deletions

View file

@ -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'

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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]

View file

@ -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

View file

@ -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]] }

View file

@ -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

View file

@ -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