mirror of
https://github.com/edcommonwealth/sqm-dashboards.git
synced 2026-03-07 21:48:16 -08:00
ECP-170 Remove login requirement for Trition. Switch to using predefined passwords stored in the database for district login.
This commit is contained in:
parent
72e38f5ee8
commit
2068758ae4
15 changed files with 146 additions and 16 deletions
|
|
@ -10,7 +10,7 @@ class SqmApplicationController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def authenticate_district
|
def authenticate_district
|
||||||
authenticate(district_name, "#{district_name}!")
|
authenticate(@district.username, @district.password)
|
||||||
end
|
end
|
||||||
|
|
||||||
def district_name
|
def district_name
|
||||||
|
|
@ -35,6 +35,8 @@ class SqmApplicationController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def authenticate(username, password)
|
def authenticate(username, password)
|
||||||
|
return unless @district.login_required
|
||||||
|
|
||||||
authenticate_or_request_with_http_basic do |u, p|
|
authenticate_or_request_with_http_basic do |u, p|
|
||||||
u == username && p == password
|
u == username && p == password
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,10 @@ class Seeder
|
||||||
EspLoader.load_data(filepath: esp_file)
|
EspLoader.load_data(filepath: esp_file)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def seed_district_credentials(file:)
|
||||||
|
CredentialsLoader.load_credentials(file:)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def value_from(pattern:, row:)
|
def value_from(pattern:, row:)
|
||||||
matches = row.headers.select do |header|
|
matches = row.headers.select do |header|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class District < ApplicationRecord
|
class District < ApplicationRecord
|
||||||
has_many :schools
|
has_many :schools
|
||||||
|
encrypts :password
|
||||||
|
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
|
|
||||||
|
|
|
||||||
45
app/services/credentials_loader.rb
Normal file
45
app/services/credentials_loader.rb
Normal file
|
|
@ -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
|
||||||
17
app/services/sftp/file.rb
Normal file
17
app/services/sftp/file.rb
Normal file
|
|
@ -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
|
||||||
7
db/migrate/20250611181510_add_credentials_to_district.rb
Normal file
7
db/migrate/20250611181510_add_credentials_to_district.rb
Normal file
|
|
@ -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.
|
# 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
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pg_catalog.plpgsql"
|
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.integer "qualtrics_code"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_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
|
end
|
||||||
|
|
||||||
create_table "ells", force: :cascade do |t|
|
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", "nj_staffing.csv")
|
||||||
seeder.seed_staffing Rails.root.join("data", "staffing", "wi_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")
|
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
|
describe CategoriesController, type: :controller do
|
||||||
include BasicAuthHelper
|
include BasicAuthHelper
|
||||||
let(:school) { create(:school) }
|
let(:school) { create(:school) }
|
||||||
let(:district) { create(:district) }
|
let(:district) { create(:district, username: 'maynard', password: 'maynard!', login_required: true) }
|
||||||
let!(:categories) do
|
let!(:categories) do
|
||||||
[create(:category, name: 'Second', sort_index: 2), create(:category, name: 'First', sort_index: 1)]
|
[create(:category, name: 'Second', sort_index: 2), create(:category, name: 'First', sort_index: 1)]
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,15 @@ include VarianceHelper
|
||||||
describe OverviewController, type: :controller do
|
describe OverviewController, type: :controller do
|
||||||
include BasicAuthHelper
|
include BasicAuthHelper
|
||||||
let(:school) { create(:school) }
|
let(:school) { create(:school) }
|
||||||
let(:district) { create(:district) }
|
let(:district) { create(:district, username: 'maynard', password: 'maynard!', login_required: true) }
|
||||||
let!(:categories) do
|
let!(:categories) do
|
||||||
[create(:category, name: 'Second', sort_index: 2), create(:category, name: 'First', sort_index: 1)]
|
[create(:category, name: 'Second', sort_index: 2), create(:category, name: 'First', sort_index: 1)]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
district
|
||||||
|
end
|
||||||
|
|
||||||
it 'fetches categories sorted by sort_index' do
|
it 'fetches categories sorted by sort_index' do
|
||||||
login_as district
|
login_as district
|
||||||
get :index, params: { school_id: school.to_param, district_id: district.to_param }
|
get :index, params: { school_id: school.to_param, district_id: district.to_param }
|
||||||
|
|
|
||||||
4
spec/fixtures/sample_district_credentials.csv
vendored
Normal file
4
spec/fixtures/sample_district_credentials.csv
vendored
Normal file
|
|
@ -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
|
||||||
|
37
spec/services/credentials_loader_spec.rb
Normal file
37
spec/services/credentials_loader_spec.rb
Normal file
|
|
@ -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
|
module BasicAuthHelper
|
||||||
def login_as(district)
|
def login_as(district)
|
||||||
user = district.short_name
|
user = district.username
|
||||||
pw = "#{user}!"
|
pw = district.password
|
||||||
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user, pw)
|
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user, pw)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
require "rails_helper"
|
require "rails_helper"
|
||||||
|
|
||||||
describe "SQM Application" do
|
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(:school) { create(:school, district:) }
|
||||||
let(:academic_year) { create(:academic_year) }
|
let(:academic_year) { create(:academic_year) }
|
||||||
let(:category) { create(:category) }
|
let(:category) { create(:category) }
|
||||||
|
|
@ -11,7 +11,7 @@ describe "SQM Application" do
|
||||||
|
|
||||||
before :each do
|
before :each do
|
||||||
driven_by :rack_test
|
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:)
|
create(:respondent, school:, academic_year:)
|
||||||
ResponseRate.create!(subcategory:, 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)
|
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
|
private
|
||||||
|
|
||||||
def username
|
|
||||||
district.short_name
|
|
||||||
end
|
|
||||||
|
|
||||||
def password
|
|
||||||
"#{username}!"
|
|
||||||
end
|
|
||||||
|
|
||||||
def overview_path
|
def overview_path
|
||||||
district_school_overview_index_path(district, school, year: academic_year.range)
|
district_school_overview_index_path(district, school, year: academic_year.range)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue