Migrate from lockbox to ActiveRecord encryption (1/2)
This commit is contained in:
		
							parent
							
								
									15a9fdec3e
								
							
						
					
					
						commit
						eae370b737
					
				| @ -1,6 +1,14 @@ | |||||||
| # PRIMARY_DOMAIN=kosmos.org | # PRIMARY_DOMAIN=kosmos.org | ||||||
| # AKKOUNTS_DOMAIN=accounts.example.com | # AKKOUNTS_DOMAIN=accounts.example.com | ||||||
| 
 | 
 | ||||||
|  | # Generate this using `rails secret` | ||||||
|  | # SECRET_KEY_BASE= | ||||||
|  | 
 | ||||||
|  | # Generate these using `rails db:encryption:init` | ||||||
|  | # (Optional, needed for LndHub integration) | ||||||
|  | # ENCRYPTION_PRIMARY_KEY= | ||||||
|  | # ENCRYPTION_KEY_DERIVATION_SALT= | ||||||
|  | 
 | ||||||
| # The default backend is SQLite | # The default backend is SQLite | ||||||
| # DB_ADAPTER=postgresql | # DB_ADAPTER=postgresql | ||||||
| # PG_HOST=localhost | # PG_HOST=localhost | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ class Admin::LightningController < Admin::BaseController | |||||||
|   def index |   def index | ||||||
|     @current_section = :lightning |     @current_section = :lightning | ||||||
| 
 | 
 | ||||||
|     @users = User.pluck(:cn, :ou, :ln_account) |     @users = User.pluck(:cn, :ou, :lndhub_username) | ||||||
|     @accounts = LndhubAccount.with_balances.order(balance: :desc).to_a |     @accounts = LndhubAccount.with_balances.order(balance: :desc).to_a | ||||||
| 
 | 
 | ||||||
|     @ln = {} |     @ln = {} | ||||||
|  | |||||||
| @ -37,7 +37,7 @@ class LnurlpayController < ApplicationController | |||||||
|       pubkey: Setting.lndhub_public_key, |       pubkey: Setting.lndhub_public_key, | ||||||
|       customData: [{ |       customData: [{ | ||||||
|         customKey: "696969", |         customKey: "696969", | ||||||
|         customValue: @user.ln_account |         customValue: @user.lndhub_username | ||||||
|       }] |       }] | ||||||
|     } |     } | ||||||
|   end |   end | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ class Services::LightningController < ApplicationController | |||||||
|   before_action :lndhub_fetch_balance |   before_action :lndhub_fetch_balance | ||||||
| 
 | 
 | ||||||
|   def index |   def index | ||||||
|     @wallet_setup_url = "lndhub://#{current_user.ln_account}:#{current_user.ln_password}@#{ENV['LNDHUB_PUBLIC_URL']}" |     @wallet_setup_url = "lndhub://#{current_user.lndhub_username}:#{current_user.lndhub_password}@#{ENV['LNDHUB_PUBLIC_URL']}" | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def transactions |   def transactions | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ class WebhooksController < ApplicationController | |||||||
|   before_action :process_payload |   before_action :process_payload | ||||||
| 
 | 
 | ||||||
|   def lndhub |   def lndhub | ||||||
|     @user = User.find_by!(ln_account: @payload[:user_login]) |     @user = User.find_by!(lndhub_username: @payload[:user_login]) | ||||||
| 
 | 
 | ||||||
|     if @zap = @user.zaps.find_by(payment_request: @payload[:payment_request]) |     if @zap = @user.zaps.find_by(payment_request: @payload[:payment_request]) | ||||||
|       settled_at = Time.parse(@payload[:settled_at]) |       settled_at = Time.parse(@payload[:settled_at]) | ||||||
|  | |||||||
| @ -2,12 +2,12 @@ class CreateLndhubAccountJob < ApplicationJob | |||||||
|   queue_as :default |   queue_as :default | ||||||
| 
 | 
 | ||||||
|   def perform(user) |   def perform(user) | ||||||
|     return if user.ln_account.present? && user.ln_password.present? |     return if user.lndhub_username.present? && user.lndhub_password.present? | ||||||
| 
 | 
 | ||||||
|     lndhub = LndhubV2.new |     lndhub = LndhubV2.new | ||||||
|     credentials = lndhub.create_account |     credentials = lndhub.create_account | ||||||
| 
 | 
 | ||||||
|     user.update! ln_account: credentials["login"], |     user.update! lndhub_username: credentials["login"], | ||||||
|                  ln_password: credentials["password"] |                  lndhub_password: credentials["password"] | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ class LndhubUser < LndhubBase | |||||||
|                       foreign_key: "user_id" |                       foreign_key: "user_id" | ||||||
| 
 | 
 | ||||||
|   belongs_to :user, class_name: "User", |   belongs_to :user, class_name: "User", | ||||||
|                     primary_key: "ln_account", |                     primary_key: "lndhub_username", | ||||||
|                     foreign_key: "login" |                     foreign_key: "login" | ||||||
| 
 | 
 | ||||||
|   def balance |   def balance | ||||||
|  | |||||||
| @ -23,7 +23,7 @@ class User < ApplicationRecord | |||||||
|   has_many :zaps |   has_many :zaps | ||||||
| 
 | 
 | ||||||
|   has_one  :lndhub_user, class_name: "LndhubUser", inverse_of: "user", |   has_one  :lndhub_user, class_name: "LndhubUser", inverse_of: "user", | ||||||
|                          primary_key: "ln_account", foreign_key: "login" |                          primary_key: "lndhub_username", foreign_key: "login" | ||||||
| 
 | 
 | ||||||
|   has_many :accounts, through: :lndhub_user |   has_many :accounts, through: :lndhub_user | ||||||
| 
 | 
 | ||||||
| @ -66,7 +66,8 @@ class User < ApplicationRecord | |||||||
|   # Encrypted database columns |   # Encrypted database columns | ||||||
|   # |   # | ||||||
| 
 | 
 | ||||||
|   has_encrypted :ln_login, :ln_password |   has_encrypted :ln_password | ||||||
|  |   encrypts :lndhub_password | ||||||
| 
 | 
 | ||||||
|   # Include default devise modules. Others available are: |   # Include default devise modules. Others available are: | ||||||
|   # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable |   # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable | ||||||
|  | |||||||
| @ -33,7 +33,10 @@ class Lndhub < ApplicationService | |||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def authenticate(user) |   def authenticate(user) | ||||||
|     credentials = post "auth?type=auth", { login: user.ln_account, password: user.ln_password } |     credentials = post "auth?type=auth", { | ||||||
|  |       login: user.lndhub_username, | ||||||
|  |       password: user.lndhub_password | ||||||
|  |     } | ||||||
|     self.auth_token = credentials["access_token"] |     self.auth_token = credentials["access_token"] | ||||||
|     self.auth_token |     self.auth_token | ||||||
|   end |   end | ||||||
|  | |||||||
| @ -276,7 +276,7 @@ | |||||||
|       </thead> |       </thead> | ||||||
|       <tbody> |       <tbody> | ||||||
|         <tr> |         <tr> | ||||||
|           <td><%= @user.ln_account %></td> |           <td><%= @user.lndhub_username %></td> | ||||||
|           <td><%= number_with_delimiter @lndhub_user.balance %> sats</td> |           <td><%= number_with_delimiter @lndhub_user.balance %> sats</td> | ||||||
|           <td><%= number_with_delimiter @lndhub_user.sum_incoming %> sats</td> |           <td><%= number_with_delimiter @lndhub_user.sum_incoming %> sats</td> | ||||||
|           <td><%= number_with_delimiter @lndhub_user.sum_outgoing %> sats</td> |           <td><%= number_with_delimiter @lndhub_user.sum_outgoing %> sats</td> | ||||||
| @ -285,7 +285,7 @@ | |||||||
|       </tbody> |       </tbody> | ||||||
|     </table> |     </table> | ||||||
|     <% else %> |     <% else %> | ||||||
|       <p>No LndHub user found for account <strong class="font-mono"><%= @user.ln_account %></strong>. |       <p>No LndHub user found for account <strong class="font-mono"><%= @user.lndhub_username %></strong>. | ||||||
|     <% end %> |     <% end %> | ||||||
|   </section> |   </section> | ||||||
|   <% end %> |   <% end %> | ||||||
|  | |||||||
| @ -54,5 +54,8 @@ module Akkounts | |||||||
| 
 | 
 | ||||||
|     # The default includes webp, which requires webp support everywhere |     # The default includes webp, which requires webp support everywhere | ||||||
|     config.active_storage.web_image_content_types = %w[image/png image/jpeg image/gif] |     config.active_storage.web_image_content_types = %w[image/png image/jpeg image/gif] | ||||||
|  | 
 | ||||||
|  |     config.active_record.encryption.primary_key = ENV["ENCRYPTION_PRIMARY_KEY"] | ||||||
|  |     config.active_record.encryption.key_derivation_salt = ENV["ENCRYPTION_KEY_DERIVATION_SALT"] | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| wVGTGBCsJ2bLSXxn/cYKcYyljVARvZGhi2gOQbiJy/r3Ia4gUmurlKFFKF0m6wmUMIlj+W11Mvu4at3c5h9fzODeIJ+EwkbwLcO8KECUyuXwVxVm2sH2TixWRwhyokT+UwS8J5c7lJTgmFAPlZiRQ+YyrqmhyPzq1fEdErk3btsWNPpJpOsdv1YPBCFFN96zMfY8h+Ttr53a9S58h+fwA+ZF5ePVqeIpJshQ+21UjUIKb5qSLEIECsarI/QJDMQwyKcvYiOEPny8nZL/7bE9TxBgC7v6UnsN+ZXVUB36aw7LOPj+21NVIdWjwOgHYRK1H2Co+stS8bDieuqV29iTTL+F8afHm/6yRc7EAtfKJe3nWf4woI+hHw7p7g/6t451F4nv9Nu1Mmt6YvJjzbSIDbf6Q6yfuYyRAv7uZdXrfsezjyhTDNGQ/SgBDpQ7CUzRoruc--0WsH7dH/QP2Hzvya--8eFWc0g5dVAvrPhC5JpO5Q== | WggqpaaABNxKXAassUpqN2L5mOeA5WME8DjSg8FZqWgjFD6Dmod/Z0ipJyvSbfLC0WbbbuEzcyutcaXTmIVUBcDyaeZq/qmk3SYT+5QJgTKLY3fskxZvxKoClvQ=--WDmAiOfUn+BLP0Db--7Rw8p+ZSgp6nuGW2X3PK2A== | ||||||
| @ -0,0 +1,6 @@ | |||||||
|  | class AddLndhubPasswordToUsers < ActiveRecord::Migration[8.0] | ||||||
|  |   def change | ||||||
|  |     add_column :users, :lndhub_username, :string | ||||||
|  |     add_column :users, :lndhub_password, :text | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										11
									
								
								db/migrate/20250506125947_migrate_lockbox_data.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								db/migrate/20250506125947_migrate_lockbox_data.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | class MigrateLockboxData < ActiveRecord::Migration[8.0] | ||||||
|  |   def up | ||||||
|  |     User.find_each do |user| | ||||||
|  |       ln_account = user.ln_account | ||||||
|  |       ln_password = user.ln_password | ||||||
|  |       user.lndhub_username = ln_account | ||||||
|  |       user.lndhub_password = ln_password | ||||||
|  |       user.save! | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
| @ -1,4 +1,16 @@ | |||||||
| ActiveRecord::Schema[7.1].define(version: 1) do | # This file is auto-generated from the current state of the database. Instead | ||||||
|  | # of editing this file, please use the migrations feature of Active Record to | ||||||
|  | # incrementally modify your database, and then regenerate this schema definition. | ||||||
|  | # | ||||||
|  | # This file is the source Rails uses to define your schema when running `bin/rails | ||||||
|  | # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to | ||||||
|  | # be faster and is potentially less error prone than running all of your | ||||||
|  | # migrations from scratch. Old migrations may fail to apply correctly if those | ||||||
|  | # migrations use external dependencies or application code. | ||||||
|  | # | ||||||
|  | # It's strongly recommended that you check this file into your version control system. | ||||||
|  | 
 | ||||||
|  | ActiveRecord::Schema[8.0].define(version: 1) do | ||||||
|   create_table "solid_queue_blocked_executions", force: :cascade do |t| |   create_table "solid_queue_blocked_executions", force: :cascade do |t| | ||||||
|     t.bigint "job_id", null: false |     t.bigint "job_id", null: false | ||||||
|     t.string "queue_name", null: false |     t.string "queue_name", null: false | ||||||
|  | |||||||
| @ -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_04_28_123317) do | ActiveRecord::Schema[8.0].define(version: 2025_05_06_125947) do | ||||||
|   create_table "active_storage_attachments", force: :cascade do |t| |   create_table "active_storage_attachments", force: :cascade do |t| | ||||||
|     t.string "name", null: false |     t.string "name", null: false | ||||||
|     t.string "record_type", null: false |     t.string "record_type", null: false | ||||||
| @ -133,6 +133,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_04_28_123317) do | |||||||
|     t.string "remember_token" |     t.string "remember_token" | ||||||
|     t.text "preferences" |     t.text "preferences" | ||||||
|     t.string "pgp_fpr" |     t.string "pgp_fpr" | ||||||
|  |     t.string "lndhub_username" | ||||||
|  |     t.text "lndhub_password" | ||||||
|     t.index ["email"], name: "index_users_on_email", unique: true |     t.index ["email"], name: "index_users_on_email", unique: true | ||||||
|     t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true |     t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true | ||||||
|   end |   end | ||||||
|  | |||||||
| @ -40,6 +40,8 @@ services: | |||||||
|       SOLID_QUEUE_IN_PUMA: true |       SOLID_QUEUE_IN_PUMA: true | ||||||
|       LAUNCHY_DRY_RUN: true |       LAUNCHY_DRY_RUN: true | ||||||
|       BROWSER: /dev/null |       BROWSER: /dev/null | ||||||
|  |       ENCRYPTION_PRIMARY_KEY: YhNLBgCFMAzw5dV3gISxnGrhNDMQwRdn | ||||||
|  |       ENCRYPTION_KEY_DERIVATION_SALT: h28g16MRZ1sghF2jTCos1DiLZXUswinR | ||||||
|       PRIMARY_DOMAIN: kosmos.org |       PRIMARY_DOMAIN: kosmos.org | ||||||
|       AKKOUNTS_DOMAIN: accounts.kosmos.org |       AKKOUNTS_DOMAIN: accounts.kosmos.org | ||||||
|       LDAP_HOST: ldap |       LDAP_HOST: ldap | ||||||
|  | |||||||
| @ -6,6 +6,6 @@ FactoryBot.define do | |||||||
|     email { "jimmy@example.com" } |     email { "jimmy@example.com" } | ||||||
|     password { "dis-muh-password" } |     password { "dis-muh-password" } | ||||||
|     confirmed_at { DateTime.now } |     confirmed_at { DateTime.now } | ||||||
|     ln_account { "123456" } |     lndhub_username { "123456" } | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  | |||||||
| @ -19,14 +19,14 @@ RSpec.describe CreateLndhubAccountJob, type: :job do | |||||||
|       .with { |req| req.body == '{}' } |       .with { |req| req.body == '{}' } | ||||||
| 
 | 
 | ||||||
|     user.reload |     user.reload | ||||||
|     expect(user.ln_account).to eq("abc123") |     expect(user.lndhub_username).to eq("abc123") | ||||||
|     expect(user.ln_password).to eq("def456") |     expect(user.lndhub_password).to eq("def456") | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   context "with existing credentials stored" do |   context "with existing credentials stored" do | ||||||
|     before do |     before do | ||||||
|       user.ln_account = "foo" |       user.lndhub_username = "foo" | ||||||
|       user.ln_password = "bar" |       user.lndhub_password = "bar" | ||||||
|       user.save! |       user.save! | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
| @ -36,8 +36,8 @@ RSpec.describe CreateLndhubAccountJob, type: :job do | |||||||
|       expect(WebMock).to_not have_requested(:post, "http://localhost:3023/create") |       expect(WebMock).to_not have_requested(:post, "http://localhost:3023/create") | ||||||
| 
 | 
 | ||||||
|       user.reload |       user.reload | ||||||
|       expect(user.ln_account).to eq("foo") |       expect(user.lndhub_username).to eq("foo") | ||||||
|       expect(user.ln_password).to eq("bar") |       expect(user.lndhub_password).to eq("bar") | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ require 'rails_helper' | |||||||
| 
 | 
 | ||||||
| RSpec.describe Zap, type: :model do | RSpec.describe Zap, type: :model do | ||||||
|   describe "#request_event" do |   describe "#request_event" do | ||||||
|     let(:user) { create :user, cn: 'satoshi', ou: 'kosmos.org', ln_account: 'abcdefg123456' } |     let(:user) { create :user, cn: 'satoshi', ou: 'kosmos.org', lndhub_username: 'abcdefg123456' } | ||||||
|     let(:zap) { create :zap, user: user } |     let(:zap) { create :zap, user: user } | ||||||
| 
 | 
 | ||||||
|     it "returns the stored request as a Nostr::Event" do |     it "returns the stored request as a Nostr::Event" do | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ RSpec.describe "/lnurlpay", type: :request do | |||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   context "Valid user" do |   context "Valid user" do | ||||||
|     let(:user) { create :user, cn: 'satoshi', ou: 'kosmos.org', ln_account: 'abcdefg123456' } |     let(:user) { create :user, cn: 'satoshi', ou: 'kosmos.org', lndhub_username: 'abcdefg123456' } | ||||||
| 
 | 
 | ||||||
|     before do |     before do | ||||||
|       login_as user, :scope => :user |       login_as user, :scope => :user | ||||||
|  | |||||||
| @ -50,7 +50,7 @@ RSpec.describe "Webhooks", type: :request do | |||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     describe "Valid payload for incoming payment" do |     describe "Valid payload for incoming payment" do | ||||||
|       let(:user) { create :user, ln_account: "123456abcdef" } |       let(:user) { create :user, lndhub_username: "123456abcdef" } | ||||||
|       let(:payload) { JSON.parse(File.read(File.expand_path("../fixtures/lndhub/incoming.json", File.dirname(__FILE__)))) } |       let(:payload) { JSON.parse(File.read(File.expand_path("../fixtures/lndhub/incoming.json", File.dirname(__FILE__)))) } | ||||||
| 
 | 
 | ||||||
|       before { user.save! } #FIXME this should not be necessary |       before { user.save! } #FIXME this should not be necessary | ||||||
| @ -132,7 +132,7 @@ RSpec.describe "Webhooks", type: :request do | |||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     describe "Valid payload for zap transaction" do |     describe "Valid payload for zap transaction" do | ||||||
|       let(:user) { create :user, ln_account: "123456abcdef" } |       let(:user) { create :user, lndhub_username: "123456abcdef" } | ||||||
|       let(:zap) { create :zap, user: user } |       let(:zap) { create :zap, user: user } | ||||||
|       let(:payload) { JSON.parse(File.read(File.expand_path("../fixtures/lndhub/incoming-zap.json", File.dirname(__FILE__)))) } |       let(:payload) { JSON.parse(File.read(File.expand_path("../fixtures/lndhub/incoming-zap.json", File.dirname(__FILE__)))) } | ||||||
|       let(:zap_receipt) { |       let(:zap_receipt) { | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| require 'rails_helper' | require 'rails_helper' | ||||||
| 
 | 
 | ||||||
| RSpec.describe NostrManager::CreateZapReceipt, type: :model do | RSpec.describe NostrManager::CreateZapReceipt, type: :model do | ||||||
|   let(:user) { create :user, ln_account: "123456abcdef" } |   let(:user) { create :user, lndhub_username: "123456abcdef" } | ||||||
|   let(:zap) { create :zap, user: user } |   let(:zap) { create :zap, user: user } | ||||||
| 
 | 
 | ||||||
|   # before do |   # before do | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| require 'rails_helper' | require 'rails_helper' | ||||||
| 
 | 
 | ||||||
| RSpec.describe NostrManager::PublishZapReceipt, type: :model do | RSpec.describe NostrManager::PublishZapReceipt, type: :model do | ||||||
|   let(:user) { create :user, ln_account: "123456abcdef" } |   let(:user) { create :user, lndhub_username: "123456abcdef" } | ||||||
|   let(:zap) { create :zap, user: user } |   let(:zap) { create :zap, user: user } | ||||||
| 
 | 
 | ||||||
|   before do |   before do | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user