MUHAMMAD
FARI
MADYAN
[ Press ESC or Click to Skip ]

Rebuild Ruby on Rails + React E-Learning

1

Part 1: Pengenalan & Arsitektur: Dari Monolit ke Arsitektur Multithreaded Terpisah

2

Part 2: Penerapan Clean Architecture di Ruby on Rails & React: Repository Pattern, Use Cases, Result Monad & Typed Services (PR #1)

3

Part 3: Engine Ujian CBT Presisi Tinggi & Telemetri Pengawasan Real-Time Action Cable

4

Part 4: Pipeline Data Berkecepatan Tinggi dengan Roo & Penguatan Keamanan DevSecOps

5

Part 5: Single VPS Production Deployment: Docker Compose, Tuning Multithreading Puma & Monitoring Pemulihan Otomatis

Artikel ini tersedia dalam Bahasa Inggris

🇬🇧 Read in English
🇮🇩 Bahasa Indonesia📚 Rebuild Ruby on Rails + React E-Learning

Ruby on Rails 8 & React E-Learning Part 4: Pipeline Data Berkecepatan Tinggi dengan Roo & Penguatan Keamanan DevSecOps

Kuasai impor data spreadsheet massal dan pengujian keamanan otomatis di Ruby on Rails 8: parsing data siswa dan bank soal dengan gem Roo, transaksi database atomik, stateless JWT admin impersonation, serta otomasi DevSecOps via Brakeman dan Bundler-Audit.

Muhammad Fari MadyanPenulis

5 menit baca

·

15 Oktober 2026


Prioritas Ingestion Data Enterprise & Keamanan DevSecOps

Dalam ekosistem aplikasi manajemen akademik berskala besar, operasional administrator sistem terbagi ke dalam dua kutub utama:

  1. Pemrosesan Data Massal Berkecepatan Tinggi: Pada awal tahun ajaran baru, panitia sekolah mengunggah berkas spreadsheet berisi 2.000+ data siswa baru, dan guru mengunggah ratusan butir soal ujian CBT lengkap beserta opsi pilihan ganda dan bobot nilai.
  2. Kepatuhan Keamanan Data Peserta Didik: Sistem akademik menyimpan informasi pribadi sensitif (NISN, nomor kontak orang tua, dan alamat rumah). Kerentanan keamanan sekecil apa pun—seperti SQL injection, mass assignment, atau celah pustaka dependensi pihak ketiga—dapat membahayakan privasi seluruh sekolah.

Pada proyek mfarim/ruby-rails-elearning-react, kami memperkuat aplikasi menggunakan dua pilar utama:

  • Pipeline Spreadsheet Tangguh: Menggunakan gem roo yang ringan dan efisien dipadukan dengan transaksi atomik Active Record.
  • Automasi DevSecOps Berkelanjutan: Memanfaatkan analisis kode statis (Static Application Security Testing) via Brakeman, audit dependensi rentan via Bundler-Audit, dan integrasi pipeline bin/ci native Rails 8.
┌────────────────────────────────────────────────────────────────────────────────────────┐ │ TOPOLOGI PIPELINE DATA & OTOMASI DEVSECOPS │ └────────────────────────────────────────────────────────────────────────────────────────┘ 📥 Admin mengunggah spreadsheet .xlsx / .xls / .csv │ ▼ Upload Form Multipart ┌──────────────────────────────────────────────────────────────────────────────────┐ │ SERVICE EXCEL (Roo Streaming Reader): │ │ • Adapter polimorfik: Roo::Excelx, Roo::Excel, Roo::CSV │ │ • Membaca hash baris data secara langsung tanpa alokasi memori berlebih │ │ • Normalisasi header kolom (.to_s.strip.downcase.underscore) │ └───────────────────────┬──────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────────────────────────┐ │ TRANSAKSI DATABASE ATOMIK (ActiveRecord::Base.transaction): │ │ • Upsert akun tabel User dengan hash kata sandi aman BCrypt │ │ • Menghubungkan profil tabel Student ke kelas terkait │ │ • Otomatis rollback jika terjadi duplikasi NIS atau pelanggaran validasi │ └──────────────────────────────────────────────────────────────────────────────────┘ 🛡️ OTOMASI PEMINDAIAN KEAMANAN BERKELANJUTAN (bin/ci): ┌───────────────────────┐ ┌───────────────────────┐ ┌───────────────────────┐ │ Linter RuboCop │──▶│ Bundler-Audit (CVEs) │──▶│ Scan SAST Brakeman │ │ Standar sintaks kode │ │ Cek celah keamanan gem│ │ Deteksi SQL Injection│ └───────────────────────┘ └───────────────────────┘ └───────────────────────┘

1. Pemrosesan Data Spreadsheet Hemat Memori dengan Gem roo

Pada berkas backend/app/services/excel_service.rb, kami merancang service parsing yang sanggup memproses format .xlsx, .xls, dan .csv secara seragam:

RUBY

# backend/app/services/excel_service.rb
require 'roo'

class ExcelService
  def self.parse_students(file_path)
    spreadsheet = open_spreadsheet(file_path)
    header = spreadsheet.row(1).map { |h| h.to_s.strip.downcase.underscore }
    rows = []

    (2..spreadsheet.last_row).each do |i|
      row = Hash[[header, spreadsheet.row(i)].transpose]
      next if row['name'].blank? || row['nis'].blank?
      rows << row
    end

    rows
  end

  def self.parse_questions(file_path)
    spreadsheet = open_spreadsheet(file_path)
    header = spreadsheet.row(1).map { |h| h.to_s.strip.downcase.underscore }
    rows = []

    (2..spreadsheet.last_row).each do |i|
      row = Hash[[header, spreadsheet.row(i)].transpose]
      next if row['question_text'].blank?
      rows << row
    end

    rows
  end

  private

  def self.open_spreadsheet(file_path)
    ext = File.extname(file_path).downcase
    case ext
    when '.xlsx'
      Roo::Excelx.new(file_path)
    when '.xls'
      Roo::Excel.new(file_path)
    when '.csv'
      Roo::CSV.new(file_path)
    else
      raise "Format berkas tidak didukung: #{ext}. Harap unggah .xlsx, .xls, atau .csv"
    end
  end
end

Mengapa Gem Roo Lebih Unggul?

Banyak parser spreadsheet lama membangun pohon objek sel rumit di memori untuk mencatat informasi warna latar, jenis font, dan formula Excel. Untuk berkas data 2.000 siswa, hal ini menghabiskan puluhan megabyte RAM. Sebaliknya, Roo membaca data mentah baris demi baris, menjaga konsumsi memori server di bawah 15MB.


2. Transaksi Database Batch Atomik dengan Active Record

Pada backend/app/controllers/api/v1/students_controller.rb, baris data yang dibaca dimasukkan ke dalam database di dalam blok transaksi atomik. Jika baris ke-1.999 melanggar duplikasi NIS unik, seluruh transaksi dibatalkan untuk mencegah data gantung (orphaned records):

RUBY

# In backend/app/controllers/api/v1/students_controller.rb
def import_excel
  require_admin!
  return if performed?

  file = params[:file]
  raise "Berkas tidak ditemukan" unless file.present?

  records = ExcelService.parse_students(file.path)
  imported_count = 0

  ActiveRecord::Base.transaction do
    records.each do |row|
      # 1. Buat atau perbarui akun pengguna
      user = User.find_or_initialize_by(email: row['email'].downcase.strip)
      user.name = row['name'].strip
      user.password = "password" if user.new_record?
      user.role = "student"
      user.active = true
      user.save!

      # 2. Buat atau perbarui profil siswa terkait
      student = Student.find_or_initialize_by(user: user)
      student.nis = row['nis'].to_s.strip
      student.nisn = row['nisn'].to_s.strip if row['nisn'].present?
      student.classroom_id = params[:classroom_id] if params[:classroom_id].present?
      student.gender = row['gender'].to_s.upcase.in?(%w[L P M F]) ? row['gender'].to_s.upcase : 'L'
      student.phone = row['phone']
      student.address = row['address']
      student.save!

      imported_count += 1
    end
  end

  json_response(data: { imported: imported_count }, message: "Berhasil mengimpor #{imported_count} siswa")
rescue => e
  json_response(success: false, message: "Proses impor dibatalkan: #{e.message}", status: :unprocessable_entity)
end

3. Fitur 1-Click Admin Impersonation Stateless

Pada berkas backend/app/controllers/api/v1/auth_controller.rb, administrator dapat beralih peran (impersonate) menjadi siswa atau guru untuk melihat tampilan persis yang dialami pengguna tanpa menanyakan kata sandi mereka:

RUBY

# backend/app/controllers/api/v1/auth_controller.rb
def impersonate
  require_admin!
  return if performed?

  target_user = user_repository.find(params[:user_id])
  token = JsonWebToken.encode(user_id: target_user.id)
  payload = build_auth_payload(target_user, token, impersonated_by: current_user.id)
  json_response(data: payload, message: "Sesi impersonasi berhasil dimulai")
end

def stop_impersonate
  admin_user = user_repository.find_admin || current_user
  token = JsonWebToken.encode(user_id: admin_user.id)
  payload = build_auth_payload(admin_user, token)
  json_response(data: payload, message: "Kembali ke sesi administrator")
end

Manfaat Keamanan Implementasi Ini:

  1. Nol Penyimpanan Sesi di Server: Autentikasi tetap bersifat murni stateless. Frontend React menyimpan token sementara dan menampilkan banner peringatan bahwa mode impersonasi sedang aktif.
  2. Pemulihan Sesi Instan: Pemanggilan stop_impersonate langsung menerbitkan token administrator kembali ke super admin tanpa perlu memasukkan kata sandi ulang.

4. Automasi DevSecOps di Rails 8: Brakeman & Bundler-Audit

Pada Rails 8, audit keamanan diintegrasikan langsung ke dalam pipeline pengujian otomatis melalui backend/bin/ci dan backend/config/ci.rb:

RUBY

# backend/config/ci.rb
CI.run do
  step "Setup", "bin/setup --skip-server"
  step "Style: Ruby", "bin/rubocop"
  step "Security: Gem audit", "bin/bundler-audit"
  step "Security: Brakeman code analysis", "bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error"
end

Pengujian Keamanan Kode Statis (SAST) dengan Brakeman

Brakeman memindai pohon sintaksis abstrak (Abstract Syntax Tree) dari kode aplikasi Rails tanpa membutuhkan koneksi database:

  • Mass Assignment: Mendeteksi kelalaian strong parameters.
  • SQL Injection: Menemukan interpolasi string SQL mentah yang tidak aman.
  • Cross-Site Scripting (XSS): Mendeteksi keluaran HTML yang tidak di-escape.

Hasil eksekusi bin/brakeman memvalidasi bahwa seluruh controller dan repository memenuhi standar keamanan nol peringatan:

BASH

$ bin/brakeman --quiet --exit-on-warn
== Brakeman Report ==
Security Warnings: 0
Checks run: 74
No vulnerabilities found!

Pemindaian Dependensi Rentan dengan Bundler-Audit

Bundler-Audit mencocokkan berkas Gemfile.lock dengan Ruby Advisory Database. Jika terdapat gem yang memiliki celah keamanan CVE belum ditambal, pipeline bin/ci akan gagal secara otomatis, mencegah kode rentan naik ke lingkungan produksi.


Ringkasan Rekomendasi Arsitektural

  1. Gunakan gem Roo untuk pembacaan spreadsheet massal: Menghindari alokasi objek sel rumit menjaga konsumsi memori server tetap di bawah 15MB bahkan saat memproses ribuan data siswa.
  2. Bungkus operasi batch dalam blok transaksi: ActiveRecord::Base.transaction memastikan integritas data berprinsip all-or-nothing.
  3. Wajibkan pemindaian keamanan otomatis di bin/ci: Mengintegrasikan Brakeman dan Bundler-Audit ke dalam runner CI bawaan Rails 8 menjamin setiap commit git diverifikasi dari potensi celah keamanan.

Pada bab penutup, Part 5, kita akan membahas arsitektur rilis server: Single VPS Production Deployment, Multi-Container Docker Compose, Tuning Multithreading Puma, dan Skrip Watchdog Auto-Recovery.

Lanjut Membaca

Previous article thumbnail

← Artikel Sebelumnya

Ruby on Rails 8 & React E-Learning Part 3: High-Precision CBT Exam Engine & Real-Time Action Cable Proctoring

Artikel Selanjutnya →

Ruby on Rails 8 & React E-Learning Part 5: Single VPS Production Deployment – Docker Compose, Puma Multithreading & Auto-Recovery

Next article thumbnail