Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
011386fb8d
|
|||
|
4d77f5d38c
|
|||
|
64de4deddd
|
@@ -44,8 +44,6 @@ gem 'pagy', '~> 6.0', '>= 6.0.2'
|
|||||||
gem 'flipper'
|
gem 'flipper'
|
||||||
gem 'flipper-active_record'
|
gem 'flipper-active_record'
|
||||||
gem 'flipper-ui'
|
gem 'flipper-ui'
|
||||||
gem 'gpgme', '~> 2.0.24'
|
|
||||||
gem 'zbase32', '~> 0.1.1'
|
|
||||||
|
|
||||||
# HTTP requests
|
# HTTP requests
|
||||||
gem 'faraday'
|
gem 'faraday'
|
||||||
|
|||||||
@@ -197,8 +197,6 @@ GEM
|
|||||||
raabro (~> 1.4)
|
raabro (~> 1.4)
|
||||||
globalid (1.2.1)
|
globalid (1.2.1)
|
||||||
activesupport (>= 6.1)
|
activesupport (>= 6.1)
|
||||||
gpgme (2.0.24)
|
|
||||||
mini_portile2 (~> 2.7)
|
|
||||||
hashdiff (1.1.0)
|
hashdiff (1.1.0)
|
||||||
i18n (1.14.1)
|
i18n (1.14.1)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
@@ -485,7 +483,6 @@ GEM
|
|||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
yard (0.9.34)
|
yard (0.9.34)
|
||||||
zbase32 (0.1.1)
|
|
||||||
zeitwerk (2.6.12)
|
zeitwerk (2.6.12)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
@@ -510,7 +507,6 @@ DEPENDENCIES
|
|||||||
flipper
|
flipper
|
||||||
flipper-active_record
|
flipper-active_record
|
||||||
flipper-ui
|
flipper-ui
|
||||||
gpgme (~> 2.0.24)
|
|
||||||
image_processing (~> 1.12.2)
|
image_processing (~> 1.12.2)
|
||||||
importmap-rails
|
importmap-rails
|
||||||
jbuilder (~> 2.7)
|
jbuilder (~> 2.7)
|
||||||
@@ -544,7 +540,6 @@ DEPENDENCIES
|
|||||||
warden
|
warden
|
||||||
web-console (~> 4.2)
|
web-console (~> 4.2)
|
||||||
webmock
|
webmock
|
||||||
zbase32 (~> 0.1.1)
|
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.5.5
|
2.5.5
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class Admin::UsersController < Admin::BaseController
|
|||||||
amount = params[:amount].to_i
|
amount = params[:amount].to_i
|
||||||
notify_user = ActiveRecord::Type::Boolean.new.cast(params[:notify_user])
|
notify_user = ActiveRecord::Type::Boolean.new.cast(params[:notify_user])
|
||||||
|
|
||||||
UserManager::CreateInvitations.call(user: @user, amount: amount, notify: notify_user)
|
CreateInvitations.call(user: @user, amount: amount, notify: notify_user)
|
||||||
|
|
||||||
redirect_to admin_user_path(@user.cn), flash: {
|
redirect_to admin_user_path(@user.cn), flash: {
|
||||||
success: "Added #{amount} invitations to #{@user.cn}'s account"
|
success: "Added #{amount} invitations to #{@user.cn}'s account"
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class SignupController < ApplicationController
|
|||||||
session[:new_user] = nil
|
session[:new_user] = nil
|
||||||
session[:validation_error] = nil
|
session[:validation_error] = nil
|
||||||
|
|
||||||
UserManager::CreateAccount.call(account: {
|
CreateAccount.call(account: {
|
||||||
username: @user.cn,
|
username: @user.cn,
|
||||||
domain: Setting.primary_domain,
|
domain: Setting.primary_domain,
|
||||||
email: @user.email,
|
email: @user.email,
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import { Controller } from "@hotwired/stimulus"
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
import { Nostrify } from "nostrify"
|
||||||
|
|
||||||
// Connects to data-controller="settings--nostr-pubkey"
|
// Connects to data-controller="settings--nostr-pubkey"
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static targets = [ "noExtension", "setPubkey", "pubkeyBech32Input" ]
|
static targets = [
|
||||||
|
"noExtension",
|
||||||
|
"setPubkey", "pubkeyBech32Input",
|
||||||
|
"relayList", "relayListStatus",
|
||||||
|
"profileStatusNip05", "profileStatusLud16"
|
||||||
|
]
|
||||||
static values = {
|
static values = {
|
||||||
userAddress: String,
|
userAddress: String,
|
||||||
pubkeyHex: String,
|
pubkeyHex: String,
|
||||||
@@ -15,6 +21,14 @@ export default class extends Controller {
|
|||||||
if (this.hasSetPubkeyTarget) {
|
if (this.hasSetPubkeyTarget) {
|
||||||
this.setPubkeyTarget.disabled = false
|
this.setPubkeyTarget.disabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.pubkeyHexValue) {
|
||||||
|
this.discoverUserOnNostr().then(() => {
|
||||||
|
this.renderRelayStatus()
|
||||||
|
this.renderProfileNip05Status()
|
||||||
|
this.renderProfileLud16Status()
|
||||||
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.noExtensionTarget.classList.remove("hidden")
|
this.noExtensionTarget.classList.remove("hidden")
|
||||||
}
|
}
|
||||||
@@ -49,8 +63,172 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async discoverUserOnNostr () {
|
||||||
|
this.nip65Relays = await this.findUserRelays()
|
||||||
|
this.profile = await this.findUserProfile()
|
||||||
|
}
|
||||||
|
|
||||||
|
async findUserRelays () {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const signal = controller.signal;
|
||||||
|
const filters = [{ kinds: [10002], authors: [this.pubkeyHexValue], limit: 1 }]
|
||||||
|
const messages = []
|
||||||
|
|
||||||
|
for await (const msg of this.discoveryPool.req(filters, { signal })) {
|
||||||
|
if (msg[0] === 'EVENT') {
|
||||||
|
if (!messages.find(m => m.id === msg[2].id)) {
|
||||||
|
messages.push(msg[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (msg[0] === 'EOSE') { break }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the relay subscription
|
||||||
|
controller.abort()
|
||||||
|
if (messages.length === 0) { return messages }
|
||||||
|
|
||||||
|
const sortedMessages = messages.sort((a, b) => a.createdAt - b.createdAt)
|
||||||
|
const newestMessage = messages[messages.length - 1]
|
||||||
|
|
||||||
|
return newestMessage.tags.filter(t => t[0] === 'r')
|
||||||
|
.map(t => { return { url: t[1], marker: t[2] } })
|
||||||
|
}
|
||||||
|
|
||||||
|
async findUserProfile () {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const signal = controller.signal;
|
||||||
|
const filters = [{ kinds: [0], authors: [this.pubkeyHexValue], limit: 1 }]
|
||||||
|
const messages = []
|
||||||
|
|
||||||
|
for await (const msg of this.discoveryPool.req(filters, { signal })) {
|
||||||
|
if (msg[0] === 'EVENT') {
|
||||||
|
if (!messages.find(m => m.id === msg[2].id)) {
|
||||||
|
messages.push(msg[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (msg[0] === 'EOSE') { break }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the relay subscription
|
||||||
|
controller.abort()
|
||||||
|
if (messages.length === 0) { return null }
|
||||||
|
|
||||||
|
const sortedMessages = messages.sort((a, b) => a.createdAt - b.createdAt)
|
||||||
|
const newestMessage = messages[messages.length - 1]
|
||||||
|
|
||||||
|
return JSON.parse(newestMessage.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRelayStatus () {
|
||||||
|
let showStatus
|
||||||
|
|
||||||
|
if (this.nip65Relays.length > 0) {
|
||||||
|
if (this.relaysContainAccountsRelay) {
|
||||||
|
showStatus = 'green'
|
||||||
|
} else {
|
||||||
|
showStatus = 'orange'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showStatus = 'red'
|
||||||
|
}
|
||||||
|
// showStatus = 'red'
|
||||||
|
|
||||||
|
this.relayListStatusTarget
|
||||||
|
.querySelector(`.status-${showStatus}`)
|
||||||
|
.classList.remove("hidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
renderProfileNip05Status () {
|
||||||
|
let showStatus
|
||||||
|
|
||||||
|
if (this.profile?.nip05) {
|
||||||
|
if (this.profile.nip05 === this.userAddressValue) {
|
||||||
|
showStatus = 'green'
|
||||||
|
} else {
|
||||||
|
showStatus = 'red'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showStatus = 'orange'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.profileStatusNip05Target
|
||||||
|
.querySelector(`.status-${showStatus}`)
|
||||||
|
.classList.remove("hidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
renderProfileLud16Status () {
|
||||||
|
let showStatus
|
||||||
|
|
||||||
|
if (this.profile?.lud16) {
|
||||||
|
if (this.profile.lud16 === this.userAddressValue) {
|
||||||
|
showStatus = 'green'
|
||||||
|
} else {
|
||||||
|
showStatus = 'red'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showStatus = 'orange'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.profileStatusLud16Target
|
||||||
|
.querySelector(`.status-${showStatus}`)
|
||||||
|
.classList.remove("hidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderRelayList (relays) {
|
||||||
|
// const html = relays.map(relay => `
|
||||||
|
// <li class="flex items-center justify-between p-2 border-b">
|
||||||
|
// <span>${relay.url}</span>
|
||||||
|
// <button
|
||||||
|
// data-action="click->list#handleItemClick"
|
||||||
|
// data-item="${relay.url}"
|
||||||
|
// class="bg-blue-500 text-white px-3 py-1 rounded">
|
||||||
|
// Action
|
||||||
|
// </button>
|
||||||
|
// </li>
|
||||||
|
// `).join("")
|
||||||
|
//
|
||||||
|
// this.relayListTarget.innerHTML = html
|
||||||
|
// }
|
||||||
|
|
||||||
get csrfToken () {
|
get csrfToken () {
|
||||||
const element = document.head.querySelector('meta[name="csrf-token"]')
|
const element = document.head.querySelector('meta[name="csrf-token"]')
|
||||||
return element.getAttribute("content")
|
return element.getAttribute("content")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used to find a user's profile and relays
|
||||||
|
get discoveryRelays () {
|
||||||
|
return [
|
||||||
|
'ws://localhost:4777',
|
||||||
|
'wss://nostr.kosmos.org',
|
||||||
|
'wss://purplepag.es',
|
||||||
|
// 'wss://relay.nostr.band',
|
||||||
|
// 'wss://njump.me',
|
||||||
|
// 'wss://relay.damus.io',
|
||||||
|
// 'wss://nos.lol',
|
||||||
|
// 'wss://eden.nostr.land',
|
||||||
|
// 'wss://relay.snort.social',
|
||||||
|
// 'wss://nostr.wine',
|
||||||
|
// 'wss://relay.primal.net',
|
||||||
|
// 'wss://nostr.bitcoiner.social',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
get discoveryPool () {
|
||||||
|
if (!this._discoveryPool) {
|
||||||
|
this._discoveryPool = new Nostrify.NPool({
|
||||||
|
open: (url) => new Nostrify.NRelay1(url),
|
||||||
|
reqRouter: async (filters) => new Map(
|
||||||
|
this.discoveryRelays.map(relayUrl => [ relayUrl, filters ])
|
||||||
|
),
|
||||||
|
eventRouter: async (event) => [],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._discoveryPool
|
||||||
|
}
|
||||||
|
|
||||||
|
get relaysContainAccountsRelay () {
|
||||||
|
// TODO use URL from view/settings
|
||||||
|
return !!this.nip65Relays.find(r => r.url.match('wss://nostr.kosmos.org'))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-28
@@ -3,10 +3,9 @@ require 'nostr'
|
|||||||
class User < ApplicationRecord
|
class User < ApplicationRecord
|
||||||
include EmailValidatable
|
include EmailValidatable
|
||||||
|
|
||||||
attr_accessor :current_password
|
|
||||||
attr_accessor :avatar_new
|
|
||||||
attr_accessor :display_name
|
attr_accessor :display_name
|
||||||
attr_accessor :pgp_pubkey
|
attr_accessor :avatar_new
|
||||||
|
attr_accessor :current_password
|
||||||
|
|
||||||
serialize :preferences, coder: UserPreferences
|
serialize :preferences, coder: UserPreferences
|
||||||
|
|
||||||
@@ -52,8 +51,6 @@ class User < ApplicationRecord
|
|||||||
|
|
||||||
validate :acceptable_avatar
|
validate :acceptable_avatar
|
||||||
|
|
||||||
validate :acceptable_pgp_key_format, if: -> { defined?(@pgp_pubkey) && @pgp_pubkey != "" }
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Scopes
|
# Scopes
|
||||||
#
|
#
|
||||||
@@ -168,23 +165,6 @@ class User < ApplicationRecord
|
|||||||
Nostr::PublicKey.new(nostr_pubkey).to_bech32
|
Nostr::PublicKey.new(nostr_pubkey).to_bech32
|
||||||
end
|
end
|
||||||
|
|
||||||
def pgp_pubkey
|
|
||||||
@pgp_pubkey ||= ldap_entry[:pgp_key]
|
|
||||||
end
|
|
||||||
|
|
||||||
def gnupg_key
|
|
||||||
return nil unless pgp_pubkey.present?
|
|
||||||
@gnupg_key ||= GPGME::Key.get(pgp_fpr)
|
|
||||||
end
|
|
||||||
|
|
||||||
def pgp_pubkey_contains_user_address?
|
|
||||||
gnupg_key.uids.map(&:email).include?(address)
|
|
||||||
end
|
|
||||||
|
|
||||||
def wkd_hash
|
|
||||||
ZBase32.encode(Digest::SHA1.digest(cn))
|
|
||||||
end
|
|
||||||
|
|
||||||
def avatar
|
def avatar
|
||||||
@avatar_base64 ||= LdapManager::FetchAvatar.call(cn: cn)
|
@avatar_base64 ||= LdapManager::FetchAvatar.call(cn: cn)
|
||||||
end
|
end
|
||||||
@@ -234,10 +214,4 @@ class User < ApplicationRecord
|
|||||||
errors.add(:avatar, "must be a JPEG or PNG file")
|
errors.add(:avatar, "must be a JPEG or PNG file")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def acceptable_pgp_key_format
|
|
||||||
unless GPGME::Key.valid?(pgp_pubkey)
|
|
||||||
errors.add(:pgp_pubkey, 'is not a valid armored PGP public key block')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
class CreateAccount < ApplicationService
|
||||||
|
def initialize(account:)
|
||||||
|
@username = account[:username]
|
||||||
|
@domain = account[:ou] || Setting.primary_domain
|
||||||
|
@email = account[:email]
|
||||||
|
@password = account[:password]
|
||||||
|
@invitation = account[:invitation]
|
||||||
|
@confirmed = account[:confirmed]
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
user = create_user_in_database
|
||||||
|
add_ldap_document
|
||||||
|
create_lndhub_account(user) if Setting.lndhub_enabled
|
||||||
|
|
||||||
|
if @invitation.present?
|
||||||
|
update_invitation(user.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def create_user_in_database
|
||||||
|
User.create!(
|
||||||
|
cn: @username,
|
||||||
|
ou: @domain,
|
||||||
|
email: @email,
|
||||||
|
password: @password,
|
||||||
|
password_confirmation: @password,
|
||||||
|
confirmed_at: @confirmed ? DateTime.now : nil
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_invitation(user_id)
|
||||||
|
@invitation.update! invited_user_id: user_id, used_at: DateTime.now
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_ldap_document
|
||||||
|
hashed_pw = Devise.ldap_auth_password_builder.call(@password)
|
||||||
|
CreateLdapUserJob.perform_later(
|
||||||
|
username: @username,
|
||||||
|
domain: @domain,
|
||||||
|
email: @email,
|
||||||
|
hashed_pw: hashed_pw,
|
||||||
|
confirmed: @confirmed
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_lndhub_account(user)
|
||||||
|
#TODO enable in development when we have a local lndhub (mock?) API
|
||||||
|
return if Rails.env.development?
|
||||||
|
CreateLndhubAccountJob.perform_later(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
class CreateInvitations < ApplicationService
|
||||||
|
def initialize(user:, amount:, notify: true)
|
||||||
|
@user = user
|
||||||
|
@amount = amount
|
||||||
|
@notify = notify
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
@amount.times do
|
||||||
|
Invitation.create(user: @user)
|
||||||
|
end
|
||||||
|
|
||||||
|
if @notify
|
||||||
|
NotificationMailer.with(user: @user).new_invitations_available.deliver_later
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -58,7 +58,7 @@ class LdapService < ApplicationService
|
|||||||
|
|
||||||
attributes = %w[
|
attributes = %w[
|
||||||
dn cn uid mail displayName admin serviceEnabled
|
dn cn uid mail displayName admin serviceEnabled
|
||||||
mailRoutingAddress mailpassword nostrKey pgpKey
|
mailRoutingAddress mailpassword nostrKey
|
||||||
]
|
]
|
||||||
filter = Net::LDAP::Filter.eq("uid", args[:uid] || "*")
|
filter = Net::LDAP::Filter.eq("uid", args[:uid] || "*")
|
||||||
|
|
||||||
@@ -73,8 +73,7 @@ class LdapService < ApplicationService
|
|||||||
services_enabled: e.try(:serviceEnabled),
|
services_enabled: e.try(:serviceEnabled),
|
||||||
email_maildrop: e.try(:mailRoutingAddress),
|
email_maildrop: e.try(:mailRoutingAddress),
|
||||||
email_password: e.try(:mailpassword),
|
email_password: e.try(:mailpassword),
|
||||||
nostr_key: e.try(:nostrKey) ? e.nostrKey.first : nil,
|
nostr_key: e.try(:nostrKey) ? e.nostrKey.first : nil
|
||||||
pgp_key: e.try(:pgpKey) ? e.pgpKey.first : nil
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -102,7 +101,7 @@ class LdapService < ApplicationService
|
|||||||
dn = "ou=#{ou},cn=users,#{ldap_suffix}"
|
dn = "ou=#{ou},cn=users,#{ldap_suffix}"
|
||||||
|
|
||||||
aci = <<-EOS
|
aci = <<-EOS
|
||||||
(target="ldap:///cn=*,ou=#{ou},cn=users,#{ldap_suffix}")(targetattr="cn || sn || uid || userPassword || mail || mailRoutingAddress || serviceEnabled || nostrKey || pgpKey || nsRole || objectClass") (version 3.0; acl "service-#{ou.gsub(".", "-")}-read-search"; allow (read,search) userdn="ldap:///uid=service,ou=#{ou},cn=applications,#{ldap_suffix}";)
|
(target="ldap:///cn=*,ou=#{ou},cn=users,#{ldap_suffix}")(targetattr="cn || sn || uid || userPassword || mail || mailRoutingAddress || serviceEnabled || nostrKey || nsRole || objectClass") (version 3.0; acl "service-#{ou.gsub(".", "-")}-read-search"; allow (read,search) userdn="ldap:///uid=service,ou=#{ou},cn=applications,#{ldap_suffix}";)
|
||||||
EOS
|
EOS
|
||||||
|
|
||||||
attrs = {
|
attrs = {
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
module UserManager
|
|
||||||
class CreateAccount < UserManagerService
|
|
||||||
def initialize(account:)
|
|
||||||
@username = account[:username]
|
|
||||||
@domain = account[:ou] || Setting.primary_domain
|
|
||||||
@email = account[:email]
|
|
||||||
@password = account[:password]
|
|
||||||
@invitation = account[:invitation]
|
|
||||||
@confirmed = account[:confirmed]
|
|
||||||
end
|
|
||||||
|
|
||||||
def call
|
|
||||||
user = create_user_in_database
|
|
||||||
add_ldap_document
|
|
||||||
create_lndhub_account(user) if Setting.lndhub_enabled
|
|
||||||
|
|
||||||
if @invitation.present?
|
|
||||||
update_invitation(user.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def create_user_in_database
|
|
||||||
User.create!(
|
|
||||||
cn: @username,
|
|
||||||
ou: @domain,
|
|
||||||
email: @email,
|
|
||||||
password: @password,
|
|
||||||
password_confirmation: @password,
|
|
||||||
confirmed_at: @confirmed ? DateTime.now : nil
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_invitation(user_id)
|
|
||||||
@invitation.update! invited_user_id: user_id, used_at: DateTime.now
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_ldap_document
|
|
||||||
hashed_pw = Devise.ldap_auth_password_builder.call(@password)
|
|
||||||
CreateLdapUserJob.perform_later(
|
|
||||||
username: @username,
|
|
||||||
domain: @domain,
|
|
||||||
email: @email,
|
|
||||||
hashed_pw: hashed_pw,
|
|
||||||
confirmed: @confirmed
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_lndhub_account(user)
|
|
||||||
#TODO enable in development when we have a local lndhub (mock?) API
|
|
||||||
return if Rails.env.development?
|
|
||||||
CreateLndhubAccountJob.perform_later(user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
module UserManager
|
|
||||||
class CreateInvitations < UserManagerService
|
|
||||||
def initialize(user:, amount:, notify: true)
|
|
||||||
@user = user
|
|
||||||
@amount = amount
|
|
||||||
@notify = notify
|
|
||||||
end
|
|
||||||
|
|
||||||
def call
|
|
||||||
@amount.times do
|
|
||||||
Invitation.create(user: @user)
|
|
||||||
end
|
|
||||||
|
|
||||||
if @notify
|
|
||||||
NotificationMailer.with(user: @user).new_invitations_available.deliver_later
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
class UserManagerService < ApplicationService
|
|
||||||
end
|
|
||||||
@@ -184,7 +184,7 @@
|
|||||||
<td>XMPP (ejabberd)</td>
|
<td>XMPP (ejabberd)</td>
|
||||||
<td>
|
<td>
|
||||||
<%= render FormElements::ToggleComponent.new(
|
<%= render FormElements::ToggleComponent.new(
|
||||||
enabled: @services_enabled.include?("xmpp"),
|
enabled: @services_enabled.include?("ejabberd"),
|
||||||
input_enabled: false
|
input_enabled: false
|
||||||
) %>
|
) %>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -1,46 +1,32 @@
|
|||||||
<section>
|
<div data-controller="settings--nostr-pubkey"
|
||||||
<h3>Nostr</h3>
|
data-settings--nostr-pubkey-user-address-value="<%= current_user.address %>"
|
||||||
<h4 class="mb-0">Public Key</h4>
|
data-settings--nostr-pubkey-site-value="<%= Setting.accounts_domain %>"
|
||||||
<div data-controller="settings--nostr-pubkey"
|
data-settings--nostr-pubkey-shared-secret-value="<%= session[:shared_secret] %>"
|
||||||
data-settings--nostr-pubkey-user-address-value="<%= current_user.address %>"
|
data-settings--nostr-pubkey-pubkey-hex-value="<%= current_user.nostr_pubkey %>">
|
||||||
data-settings--nostr-pubkey-site-value="<%= Setting.accounts_domain %>"
|
<section>
|
||||||
data-settings--nostr-pubkey-shared-secret-value="<%= session[:shared_secret] %>"
|
<h3>Nostr</h3>
|
||||||
data-settings--nostr-pubkey-pubkey-hex-value="<%= current_user.nostr_pubkey %>">
|
<h4 class="mb-0">
|
||||||
|
Public Key
|
||||||
<p class="<%= current_user.nostr_pubkey.present? ? '' : 'hidden' %> mt-2 flex gap-1">
|
</h4>
|
||||||
|
<p class="<%= current_user.nostr_pubkey.present? ? '' : 'hidden' %> mt-2 flex gap-x-1">
|
||||||
<input type="text" value="<%= current_user.nostr_pubkey_bech32 %>" disabled
|
<input type="text" value="<%= current_user.nostr_pubkey_bech32 %>" disabled
|
||||||
data-settings--nostr-pubkey-target="pubkeyBech32Input"
|
data-settings--nostr-pubkey-target="pubkeyBech32Input"
|
||||||
name="nostr_public_key" class="relative grow" />
|
name="nostr_public_key" class="w-full" />
|
||||||
<%= link_to nostr_pubkey_settings_path,
|
<%= link_to nostr_pubkey_settings_path,
|
||||||
class: 'btn-md btn-outline text-red-700 relative shrink-0',
|
class: 'btn-md btn-outline relative grow-0 shrink-0 text-red-700',
|
||||||
data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' } do %>
|
data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' } do %>
|
||||||
Remove
|
Remove
|
||||||
<% end %>
|
<% end %>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<% if current_user.nostr_pubkey.present? %>
|
<% if current_user.nostr_pubkey.present? %>
|
||||||
<div class="rounded-md bg-blue-50 p-4">
|
<!-- <div> -->
|
||||||
<div class="flex">
|
<!-- Pubkey present -->
|
||||||
<div class="flex-shrink-0">
|
<!-- </div> -->
|
||||||
<svg class="h-5 w-5 text-blue-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
||||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="ml-3 flex-1">
|
|
||||||
<p class="text-sm text-blue-800">
|
|
||||||
Your user address <strong><%= current_user.address %></strong> is
|
|
||||||
also a Nostr address now. Use your favorite Nostr app, or for
|
|
||||||
example <a href="http://metadata.nostr.com" target="_blank"
|
|
||||||
class="underline">metadata.nostr.com</a>, to add this
|
|
||||||
<strong>NIP-05</strong> address to your public profile.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% else %>
|
<% else %>
|
||||||
<p class="my-4">
|
<p class="my-4">
|
||||||
If you use any apps on the Nostr network, you can verify your public key
|
Verify your Nostr public key with us in order to enable Nostr-specific
|
||||||
with us in order to enable Nostr-specific features for your account.
|
features for your account.
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
@@ -58,8 +44,8 @@
|
|||||||
</h3>
|
</h3>
|
||||||
<div class="mt-2 mb-0 text-sm text-blue-800">
|
<div class="mt-2 mb-0 text-sm text-blue-800">
|
||||||
<p>
|
<p>
|
||||||
We recommend Alby, which you can also use for your Lightning
|
We recommend Alby, which you can also use a wallet for your
|
||||||
Wallet.
|
Lightning account.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
@@ -86,5 +72,113 @@
|
|||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
|
||||||
|
<% if current_user.nostr_pubkey.present? %>
|
||||||
|
<section>
|
||||||
|
<h3>Profile</h3>
|
||||||
|
<div data-settings--nostr-pubkey-target="profileStatus" class="mb-4">
|
||||||
|
<p class="status-green hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-emerald-500 %>">
|
||||||
|
<%= render "icons/check-circle" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
You already have a profile for your public key
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-orange hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<strong><%= current_user.address %></strong> is not set as your Nostr address
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div data-settings--nostr-pubkey-target="profileStatusNip05" class="mb-4">
|
||||||
|
<p class="status-green hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-emerald-500 %>">
|
||||||
|
<%= render "icons/check-circle" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<strong><%= current_user.address %></strong> is set as your Nostr address
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-orange hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<strong><%= current_user.address %></strong> is not set as your Nostr address
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-red hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Your profile's Nostr address is not set to <strong><%= current_user.address %></strong> yet
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div data-settings--nostr-pubkey-target="profileStatusLud16">
|
||||||
|
<p class="status-green hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-emerald-500 %>">
|
||||||
|
<%= render "icons/check-circle" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<strong><%= current_user.address %></strong> is set as your Lightning address
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-orange hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<strong><%= current_user.address %></strong> is not set as your Lightning address yet
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-red hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Your profile's Lightning address is not set to <strong><%= current_user.address %></strong> yet
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>Relays</h3>
|
||||||
|
<div data-settings--nostr-pubkey-target="relayListStatus">
|
||||||
|
<p class="status-green hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-emerald-500 %>">
|
||||||
|
<%= render "icons/check-circle" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
You have a relay list, and the Kosmos relay is part of it
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-orange hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
The Kosmos relay is missing from your relay list
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="status-red hidden flex gap-x-4 items-center">
|
||||||
|
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
|
||||||
|
<%= render "icons/alert-octagon" %>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
We could not find a relay list for your public key
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<ul data-settings--nostr-pubkey-target="relayList">
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -6,3 +6,4 @@ pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
|
|||||||
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
|
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
|
||||||
pin_all_from "app/javascript/controllers", under: "controllers"
|
pin_all_from "app/javascript/controllers", under: "controllers"
|
||||||
pin "tailwindcss-stimulus-components" # @4.0.3
|
pin "tailwindcss-stimulus-components" # @4.0.3
|
||||||
|
pin "nostrify"
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
class AddPgpFprToUsers < ActiveRecord::Migration[7.1]
|
|
||||||
def change
|
|
||||||
add_column :users, :pgp_fpr, :string
|
|
||||||
end
|
|
||||||
end
|
|
||||||
+1
-2
@@ -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[7.1].define(version: 2024_09_22_205634) do
|
ActiveRecord::Schema[7.1].define(version: 2024_06_07_123654) 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
|
||||||
@@ -132,7 +132,6 @@ ActiveRecord::Schema[7.1].define(version: 2024_09_22_205634) do
|
|||||||
t.datetime "remember_created_at"
|
t.datetime "remember_created_at"
|
||||||
t.string "remember_token"
|
t.string "remember_token"
|
||||||
t.text "preferences"
|
t.text "preferences"
|
||||||
t.string "pgp_fpr"
|
|
||||||
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
|
||||||
|
|||||||
+2
-2
@@ -21,7 +21,7 @@ namespace :ldap do
|
|||||||
|
|
||||||
desc "Add custom attributes to schema"
|
desc "Add custom attributes to schema"
|
||||||
task add_custom_attributes: :environment do |t, args|
|
task add_custom_attributes: :environment do |t, args|
|
||||||
%w[ admin service_enabled nostr_key pgp_key ].each do |name|
|
%w[ admin service_enabled nostr_key ].each do |name|
|
||||||
Rake::Task["ldap:modify_ldap_schema"].invoke(name, "add")
|
Rake::Task["ldap:modify_ldap_schema"].invoke(name, "add")
|
||||||
Rake::Task['ldap:modify_ldap_schema'].reenable
|
Rake::Task['ldap:modify_ldap_schema'].reenable
|
||||||
end
|
end
|
||||||
@@ -29,7 +29,7 @@ namespace :ldap do
|
|||||||
|
|
||||||
desc "Delete custom attributes from schema"
|
desc "Delete custom attributes from schema"
|
||||||
task delete_custom_attributes: :environment do |t, args|
|
task delete_custom_attributes: :environment do |t, args|
|
||||||
%w[ admin service_enabled nostr_key pgp_key ].each do |name|
|
%w[ admin service_enabled nostr_key ].each do |name|
|
||||||
Rake::Task["ldap:modify_ldap_schema"].invoke(name, "delete")
|
Rake::Task["ldap:modify_ldap_schema"].invoke(name, "delete")
|
||||||
Rake::Task['ldap:modify_ldap_schema'].reenable
|
Rake::Task['ldap:modify_ldap_schema'].reenable
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
dn: cn=schema
|
|
||||||
changetype: modify
|
|
||||||
add: attributeTypes
|
|
||||||
attributeTypes: ( 1.3.6.1.4.1.3401.8.2.11
|
|
||||||
NAME 'pgpKey'
|
|
||||||
DESC 'OpenPGP public key block'
|
|
||||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
|
|
||||||
SINGLE-VALUE )
|
|
||||||
@@ -52,7 +52,7 @@ RSpec.describe "Signup", type: :feature do
|
|||||||
click_button "Continue"
|
click_button "Continue"
|
||||||
expect(page).to have_content("Choose a password")
|
expect(page).to have_content("Choose a password")
|
||||||
|
|
||||||
expect(UserManager::CreateAccount).to receive(:call)
|
expect(CreateAccount).to receive(:call)
|
||||||
.with(account: {
|
.with(account: {
|
||||||
username: "tony", domain: "kosmos.org",
|
username: "tony", domain: "kosmos.org",
|
||||||
email: "tony@example.com", password: "a-valid-password",
|
email: "tony@example.com", password: "a-valid-password",
|
||||||
@@ -96,7 +96,7 @@ RSpec.describe "Signup", type: :feature do
|
|||||||
click_button "Create account"
|
click_button "Create account"
|
||||||
expect(page).to have_content("Password is too short")
|
expect(page).to have_content("Password is too short")
|
||||||
|
|
||||||
expect(UserManager::CreateAccount).to receive(:call)
|
expect(CreateAccount).to receive(:call)
|
||||||
.with(account: {
|
.with(account: {
|
||||||
username: "tony", domain: "kosmos.org",
|
username: "tony", domain: "kosmos.org",
|
||||||
email: "tony@example.com", password: "a-valid-password",
|
email: "tony@example.com", password: "a-valid-password",
|
||||||
|
|||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
||||||
b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE
|
|
||||||
ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy
|
|
||||||
MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO
|
|
||||||
dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4
|
|
||||||
OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s
|
|
||||||
E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb
|
|
||||||
DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn
|
|
||||||
0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE=
|
|
||||||
=iIGO
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
||||||
-16
@@ -1,16 +0,0 @@
|
|||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
||||||
Comment: Alice's OpenPGP certificate
|
|
||||||
Comment: https://www.ietf.org/id/draft-bre-openpgp-samples-01.html
|
|
||||||
|
|
||||||
mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U
|
|
||||||
b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE
|
|
||||||
ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy
|
|
||||||
MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO
|
|
||||||
dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4
|
|
||||||
OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s
|
|
||||||
E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb
|
|
||||||
DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn
|
|
||||||
0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE=
|
|
||||||
=iIGO
|
|
||||||
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
||||||
|
|
||||||
mDMEZvFjRhYJKwYBBAHaRw8BAQdACUxVX9bGlbuNR0MNYUyHHxTcOgm4qjwq8Bjg
|
|
||||||
7P41OFK0GEppbW15IDxqaW1teUBrb3Ntb3Mub3JnPoiZBBMWCgBBFiEEMWv1FiNt
|
|
||||||
r3cjaxX2BX2Tly+4YsMFAmbxY0YCGwMFCQWjmoAFCwkIBwICIgIGFQoJCAsCBBYC
|
|
||||||
AwECHgcCF4AACgkQBX2Tly+4YsMjHgEAoOOLrv9pWbi8hhrSMkqJ7FJvsBTQF//U
|
|
||||||
aJUQRa8CTgoBAI3kyGKZ8gOC8UOOKsUC0LiNCVXPyX45h8T4QFRdEVYKuDgEZvFj
|
|
||||||
RhIKKwYBBAGXVQEFAQEHQIomqcQ59UjtQex54pz8qGqyxCj2DPJYUat9pXinDgN8
|
|
||||||
AwEIB4h+BBgWCgAmFiEEMWv1FiNtr3cjaxX2BX2Tly+4YsMFAmbxY0YCGwwFCQWj
|
|
||||||
moAACgkQBX2Tly+4YsPoVgEA/9Q5Gs1klP4u/nw343V57e9s4RKmEiRSkErnC9wW
|
|
||||||
Iu0A/jp6Elz2pDQPB2XLwcb+n7JlgA05HI0zWj1+EoM7TC4J
|
|
||||||
=KQbn
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
||||||
+10
-76
@@ -1,16 +1,20 @@
|
|||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe User, type: :model do
|
RSpec.describe User, type: :model do
|
||||||
let(:user) { create :user, cn: "philipp", ou: "kosmos.org", email: "philipp@example.com" }
|
let(:user) { create :user, cn: "philipp" }
|
||||||
let(:dn) { "cn=philipp,ou=kosmos.org,cn=users,dc=kosmos,dc=org" }
|
let(:dn) { "cn=philipp,ou=kosmos.org,cn=users,dc=kosmos,dc=org" }
|
||||||
|
|
||||||
describe "#address" do
|
describe "#address" do
|
||||||
|
let(:user) { build :user, cn: "jimmy", ou: "kosmos.org" }
|
||||||
|
|
||||||
it "returns the user address" do
|
it "returns the user address" do
|
||||||
expect(user.address).to eq("philipp@kosmos.org")
|
expect(user.address).to eq("jimmy@kosmos.org")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#mastodon_address" do
|
describe "#mastodon_address" do
|
||||||
|
let(:user) { build :user, cn: "jimmy", ou: "kosmos.org" }
|
||||||
|
|
||||||
context "Mastodon service not configured" do
|
context "Mastodon service not configured" do
|
||||||
before do
|
before do
|
||||||
Setting.mastodon_enabled = false
|
Setting.mastodon_enabled = false
|
||||||
@@ -28,7 +32,7 @@ RSpec.describe User, type: :model do
|
|||||||
|
|
||||||
describe "domain is the same as primary domain" do
|
describe "domain is the same as primary domain" do
|
||||||
it "returns the user address" do
|
it "returns the user address" do
|
||||||
expect(user.mastodon_address).to eq("philipp@kosmos.org")
|
expect(user.mastodon_address).to eq("jimmy@kosmos.org")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -38,7 +42,7 @@ RSpec.describe User, type: :model do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "returns the user address" do
|
it "returns the user address" do
|
||||||
expect(user.mastodon_address).to eq("philipp@kosmos.social")
|
expect(user.mastodon_address).to eq("jimmy@kosmos.social")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -235,7 +239,7 @@ RSpec.describe User, type: :model do
|
|||||||
|
|
||||||
describe "#nostr_pubkey" do
|
describe "#nostr_pubkey" do
|
||||||
before do
|
before do
|
||||||
allow(user).to receive(:ldap_entry)
|
allow_any_instance_of(User).to receive(:ldap_entry)
|
||||||
.and_return({ nostr_key: "07e188a1ff87ce171d517b8ed2bb7a31b1d3453a0db3b15379ec07b724d232f3" })
|
.and_return({ nostr_key: "07e188a1ff87ce171d517b8ed2bb7a31b1d3453a0db3b15379ec07b724d232f3" })
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -246,7 +250,7 @@ RSpec.describe User, type: :model do
|
|||||||
|
|
||||||
describe "#nostr_pubkey_bech32" do
|
describe "#nostr_pubkey_bech32" do
|
||||||
before do
|
before do
|
||||||
allow(user).to receive(:ldap_entry)
|
allow_any_instance_of(User).to receive(:ldap_entry)
|
||||||
.and_return({ nostr_key: "07e188a1ff87ce171d517b8ed2bb7a31b1d3453a0db3b15379ec07b724d232f3" })
|
.and_return({ nostr_key: "07e188a1ff87ce171d517b8ed2bb7a31b1d3453a0db3b15379ec07b724d232f3" })
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -254,74 +258,4 @@ RSpec.describe User, type: :model do
|
|||||||
expect(user.nostr_pubkey_bech32).to eq("npub1qlsc3g0lsl8pw8230w8d9wm6xxcax3f6pkemz5measrmwfxjxteslf2hac")
|
expect(user.nostr_pubkey_bech32).to eq("npub1qlsc3g0lsl8pw8230w8d9wm6xxcax3f6pkemz5measrmwfxjxteslf2hac")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "OpenPGP key" do
|
|
||||||
let(:alice) { create :user, id: 2, cn: "alice", email: "alice@example.com" }
|
|
||||||
let(:jimmy) { create :user, id: 3, cn: "jimmy", email: "jimmy@example.com" }
|
|
||||||
let(:valid_key_alice) { File.read("#{Rails.root}/spec/fixtures/files/pgp_key_valid_alice.asc") }
|
|
||||||
let(:valid_key_jimmy) { File.read("#{Rails.root}/spec/fixtures/files/pgp_key_valid_jimmy.asc") }
|
|
||||||
let(:fingerprint_alice) { "EB85BB5FA33A75E15E944E63F231550C4F47E38E" }
|
|
||||||
let(:fingerprint_jimmy) { "316BF516236DAF77236B15F6057D93972FB862C3" }
|
|
||||||
let(:gnupg_key_alice) { }
|
|
||||||
let(:invalid_key) { File.read("#{Rails.root}/spec/fixtures/files/pgp_key_invalid.asc") }
|
|
||||||
|
|
||||||
before do
|
|
||||||
GPGME::Key.import(valid_key_alice)
|
|
||||||
GPGME::Key.import(valid_key_jimmy)
|
|
||||||
alice.update pgp_fpr: fingerprint_alice
|
|
||||||
jimmy.update pgp_fpr: fingerprint_jimmy
|
|
||||||
allow(alice).to receive(:ldap_entry).and_return({ pgp_key: valid_key_alice })
|
|
||||||
allow(jimmy).to receive(:ldap_entry).and_return({ pgp_key: valid_key_jimmy })
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
|
||||||
GPGME::Key.get(fingerprint_alice).delete!
|
|
||||||
GPGME::Key.get(fingerprint_jimmy).delete!
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#acceptable_pgp_key_format" do
|
|
||||||
it "validates the record when the key is valid" do
|
|
||||||
alice.pgp_pubkey = valid_key_alice
|
|
||||||
expect(alice).to be_valid
|
|
||||||
end
|
|
||||||
|
|
||||||
it "adds a validation error when the key is not valid" do
|
|
||||||
user.pgp_pubkey = invalid_key
|
|
||||||
expect(user).to_not be_valid
|
|
||||||
expect(user.errors[:pgp_pubkey]).to be_present
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#pgp_pubkey" do
|
|
||||||
it "returns the raw pubkey from LDAP" do
|
|
||||||
expect(alice.pgp_pubkey).to eq(valid_key_alice)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#gnupg_key" do
|
|
||||||
subject { alice.gnupg_key }
|
|
||||||
|
|
||||||
it "returns a GPGME::Key object from the system's GPG keyring" do
|
|
||||||
expect(subject).to be_a(GPGME::Key)
|
|
||||||
expect(subject.fingerprint).to eq(fingerprint_alice)
|
|
||||||
expect(subject.email).to eq("alice@openpgp.example")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#pgp_pubkey_contains_user_address?" do
|
|
||||||
it "returns false when the user address is one of the UIDs of the key" do
|
|
||||||
expect(alice.pgp_pubkey_contains_user_address?).to eq(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns true when the user address is missing from the UIDs of the key" do
|
|
||||||
expect(jimmy.pgp_pubkey_contains_user_address?).to eq(true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "wkd_hash" do
|
|
||||||
it "returns a z-base32 encoded SHA-1 digest of the username" do
|
|
||||||
expect(alice.wkd_hash).to eq("kei1q4tipxxu1yj79k9kfukdhfy631xe")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
+6
-6
@@ -1,8 +1,8 @@
|
|||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe UserManager::CreateAccount, type: :model do
|
RSpec.describe CreateAccount, type: :model do
|
||||||
describe "#create_user_in_database" do
|
describe "#create_user_in_database" do
|
||||||
let(:service) { described_class.new(account: {
|
let(:service) { CreateAccount.new(account: {
|
||||||
username: 'isaacnewton',
|
username: 'isaacnewton',
|
||||||
email: 'isaacnewton@example.com',
|
email: 'isaacnewton@example.com',
|
||||||
password: 'bright-ideas-in-autumn'
|
password: 'bright-ideas-in-autumn'
|
||||||
@@ -19,7 +19,7 @@ RSpec.describe UserManager::CreateAccount, type: :model do
|
|||||||
|
|
||||||
describe "#update_invitation" do
|
describe "#update_invitation" do
|
||||||
let(:invitation) { create :invitation }
|
let(:invitation) { create :invitation }
|
||||||
let(:service) { described_class.new(account: {
|
let(:service) { CreateAccount.new(account: {
|
||||||
username: 'isaacnewton',
|
username: 'isaacnewton',
|
||||||
email: 'isaacnewton@example.com',
|
email: 'isaacnewton@example.com',
|
||||||
password: 'bright-ideas-in-autumn',
|
password: 'bright-ideas-in-autumn',
|
||||||
@@ -42,7 +42,7 @@ RSpec.describe UserManager::CreateAccount, type: :model do
|
|||||||
describe "#add_ldap_document" do
|
describe "#add_ldap_document" do
|
||||||
include ActiveJob::TestHelper
|
include ActiveJob::TestHelper
|
||||||
|
|
||||||
let(:service) { described_class.new(account: {
|
let(:service) { CreateAccount.new(account: {
|
||||||
username: 'halfinney',
|
username: 'halfinney',
|
||||||
email: 'halfinney@example.com',
|
email: 'halfinney@example.com',
|
||||||
password: 'remember-remember-the-5th-of-november'
|
password: 'remember-remember-the-5th-of-november'
|
||||||
@@ -68,7 +68,7 @@ RSpec.describe UserManager::CreateAccount, type: :model do
|
|||||||
describe "#add_ldap_document for pre-confirmed account" do
|
describe "#add_ldap_document for pre-confirmed account" do
|
||||||
include ActiveJob::TestHelper
|
include ActiveJob::TestHelper
|
||||||
|
|
||||||
let(:service) { described_class.new(account: {
|
let(:service) { CreateAccount.new(account: {
|
||||||
username: 'halfinney',
|
username: 'halfinney',
|
||||||
email: 'halfinney@example.com',
|
email: 'halfinney@example.com',
|
||||||
password: 'remember-remember-the-5th-of-november',
|
password: 'remember-remember-the-5th-of-november',
|
||||||
@@ -89,7 +89,7 @@ RSpec.describe UserManager::CreateAccount, type: :model do
|
|||||||
describe "#create_lndhub_account" do
|
describe "#create_lndhub_account" do
|
||||||
include ActiveJob::TestHelper
|
include ActiveJob::TestHelper
|
||||||
|
|
||||||
let(:service) { described_class.new(account: {
|
let(:service) { CreateAccount.new(account: {
|
||||||
username: 'halfinney', email: 'halfinney@example.com',
|
username: 'halfinney', email: 'halfinney@example.com',
|
||||||
password: 'bright-ideas-in-winter'
|
password: 'bright-ideas-in-winter'
|
||||||
})}
|
})}
|
||||||
+3
-3
@@ -1,13 +1,13 @@
|
|||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe UserManager::CreateInvitations, type: :model do
|
RSpec.describe CreateInvitations, type: :model do
|
||||||
include ActiveJob::TestHelper
|
include ActiveJob::TestHelper
|
||||||
|
|
||||||
let(:user) { create :user }
|
let(:user) { create :user }
|
||||||
|
|
||||||
describe "#call" do
|
describe "#call" do
|
||||||
before do
|
before do
|
||||||
described_class.call(user: user, amount: 5)
|
CreateInvitations.call(user: user, amount: 5)
|
||||||
end
|
end
|
||||||
|
|
||||||
after(:each) { clear_enqueued_jobs }
|
after(:each) { clear_enqueued_jobs }
|
||||||
@@ -28,7 +28,7 @@ RSpec.describe UserManager::CreateInvitations, type: :model do
|
|||||||
|
|
||||||
describe "#call with notification disabled" do
|
describe "#call with notification disabled" do
|
||||||
before do
|
before do
|
||||||
described_class.call(user: user, amount: 3, notify: false)
|
CreateInvitations.call(user: user, amount: 3, notify: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
after(:each) { clear_enqueued_jobs }
|
after(:each) { clear_enqueued_jobs }
|
||||||
Vendored
+11690
File diff suppressed because it is too large
Load Diff
Vendored
+1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user