ECP-170 Remove login requirement for Trition. Switch to using predefined passwords stored in the database for district login.

main-eol
rebuilt 6 months ago
parent 72e38f5ee8
commit 2068758ae4

@ -10,7 +10,7 @@ class SqmApplicationController < ApplicationController
private
def authenticate_district
authenticate(district_name, "#{district_name}!")
authenticate(@district.username, @district.password)
end
def district_name
@ -35,6 +35,8 @@ class SqmApplicationController < ApplicationController
end
def authenticate(username, password)
return unless @district.login_required
authenticate_or_request_with_http_basic do |u, p|
u == username && p == password
end

@ -147,6 +147,10 @@ class Seeder
EspLoader.load_data(filepath: esp_file)
end
def seed_district_credentials(file:)
CredentialsLoader.load_credentials(file:)
end
private
def value_from(pattern:, row:)
matches = row.headers.select do |header|

@ -2,6 +2,7 @@
class District < ApplicationRecord
has_many :schools
encrypts :password
validates :name, presence: true

@ -0,0 +1,45 @@
require "csv"
class CredentialsLoader
def self.load_credentials(file:)
credentials = []
CSV.parse(file, headers: true) do |row|
values = CredentialRowValues.new(row:)
next unless values.district.present?
credentials << values.district
end
District.import(credentials, batch_size: 100, on_duplicate_key_update: [:username, :password, :login_required])
end
end
class CredentialRowValues
attr_reader :row
def initialize(row:)
@row = row
end
def district
@district ||= begin
name = row["Districts"]&.strip
district = District.find_or_initialize_by(name:)
district.username = username
district.password = password
district.login_required = login_required?
district
end
end
def username
row["Username"]&.strip
end
def password
row["PW"]&.strip
end
def login_required?
row["Login Required"]&.strip == "Y"
end
end

@ -0,0 +1,17 @@
require 'net/sftp'
require 'uri'
module Sftp
class File
def self.open(filepath:, &block)
sftp_url = ENV['SFTP_URL']
uri = URI.parse(sftp_url)
Net::SFTP.start(uri.host, uri.user, password: uri.password) do |sftp|
sftp.file.open(filepath, 'r', &block)
end
rescue Net::SFTP::StatusException => e
puts "Error opening file: #{e.message}"
nil
end
end
end

@ -0,0 +1,7 @@
class AddCredentialsToDistrict < ActiveRecord::Migration[8.0]
def change
add_column :districts, :username, :string, null: true, default: nil
add_column :districts, :password, :string, null: true, default: nil
add_column :districts, :login_required, :boolean, null: false, default: true
end
end

@ -0,0 +1,7 @@
class AddNameSlugAndQualtricsCodeIndexesToDistrict < ActiveRecord::Migration[8.0]
def change
add_index :districts, :name, unique: true
add_index :districts, :slug, unique: true
add_index :districts, :qualtrics_code, unique: true
end
end

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_05_23_222834) do
ActiveRecord::Schema[8.0].define(version: 2025_06_11_182208) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
@ -69,6 +69,12 @@ ActiveRecord::Schema[8.0].define(version: 2025_05_23_222834) do
t.integer "qualtrics_code"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "username"
t.string "password"
t.boolean "login_required", default: true, null: false
t.index ["name"], name: "index_districts_on_name", unique: true
t.index ["qualtrics_code"], name: "index_districts_on_qualtrics_code", unique: true
t.index ["slug"], name: "index_districts_on_slug", unique: true
end
create_table "ells", force: :cascade do |t|

@ -14,3 +14,7 @@ seeder.seed_staffing Rails.root.join("data", "staffing", "staffing.csv")
seeder.seed_staffing Rails.root.join("data", "staffing", "nj_staffing.csv")
seeder.seed_staffing Rails.root.join("data", "staffing", "wi_staffing.csv")
seeder.seed_esp_counts Rails.root.join("data", "staffing", "esp_counts.csv")
Sftp::File.open(filepath: "/ecp/district_credentials.csv") do |file|
seeder.seed_district_credentials file:
end

@ -3,7 +3,7 @@ require 'rails_helper'
describe CategoriesController, type: :controller do
include BasicAuthHelper
let(:school) { create(:school) }
let(:district) { create(:district) }
let(:district) { create(:district, username: 'maynard', password: 'maynard!', login_required: true) }
let!(:categories) do
[create(:category, name: 'Second', sort_index: 2), create(:category, name: 'First', sort_index: 1)]
end

@ -4,11 +4,15 @@ include VarianceHelper
describe OverviewController, type: :controller do
include BasicAuthHelper
let(:school) { create(:school) }
let(:district) { create(:district) }
let(:district) { create(:district, username: 'maynard', password: 'maynard!', login_required: true) }
let!(:categories) do
[create(:category, name: 'Second', sort_index: 2), create(:category, name: 'First', sort_index: 1)]
end
before do
district
end
it 'fetches categories sorted by sort_index' do
login_as district
get :index, params: { school_id: school.to_param, district_id: district.to_param }

@ -0,0 +1,4 @@
Districts,Login Required,Username,PW
Maynard Public Schools,Y,maynard_admin,password123
Springfield Public Schools,N,springfield_admin,password456
Boston Public Schools,Y,boston_admin,password789
1 Districts Login Required Username PW
2 Maynard Public Schools Y maynard_admin password123
3 Springfield Public Schools N springfield_admin password456
4 Boston Public Schools Y boston_admin password789

@ -0,0 +1,37 @@
require "rails_helper"
require "fileutils"
RSpec.describe CredentialsLoader do
let(:path) do
Rails.root.join("spec", "fixtures", "credentials", "credentials.csv")
end
context ".load_credentials" do
before do
create(:district, name: "Maynard Public Schools")
create(:district, name: "Springfield Public Schools")
create(:district, name: "Boston Public Schools")
end
it "loads credentials from the CSV file into the database" do
file = File.open(Rails.root.join("spec", "fixtures", "sample_district_credentials.csv"))
# Seeder.new.seed_district_credentials(file:)
expect { CredentialsLoader.load_credentials(file:) }.to change { District.count }.by(0)
district = District.find_by(name: "Maynard Public Schools")
expect(district.username).to eq("maynard_admin")
expect(district.password).to eq("password123")
expect(district.login_required).to be true
district = District.find_by(name: "Springfield Public Schools")
expect(district.username).to eq("springfield_admin")
expect(district.password).to eq("password456")
expect(district.login_required).to be false
district = District.find_by(name: "Boston Public Schools")
expect(district.username).to eq("boston_admin")
expect(district.password).to eq("password789")
expect(district.login_required).to be true
end
end
end

@ -1,7 +1,7 @@
module BasicAuthHelper
def login_as(district)
user = district.short_name
pw = "#{user}!"
user = district.username
pw = district.password
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user, pw)
end
end

@ -1,7 +1,7 @@
require "rails_helper"
describe "SQM Application" do
let(:district) { create(:district) }
let(:district) { create(:district, username: 'maynard', password: 'maynard!', login_required: true) }
let(:school) { create(:school, district:) }
let(:academic_year) { create(:academic_year) }
let(:category) { create(:category) }
@ -11,7 +11,7 @@ describe "SQM Application" do
before :each do
driven_by :rack_test
page.driver.browser.basic_authorize(username, password)
page.driver.browser.basic_authorize(district.username, district.password)
create(:respondent, school:, academic_year:)
ResponseRate.create!(subcategory:, school:, academic_year:,
student_response_rate: 0, teacher_response_rate: 0, meets_student_threshold: false, meets_teacher_threshold: false)
@ -46,14 +46,6 @@ describe "SQM Application" do
private
def username
district.short_name
end
def password
"#{username}!"
end
def overview_path
district_school_overview_index_path(district, school, year: academic_year.range)
end

Loading…
Cancel
Save