Compare commits
33 Commits
v0.2.0
...
46a7345ce9
| Author | SHA1 | Date | |
|---|---|---|---|
| 46a7345ce9 | |||
|
e12d02a988
|
|||
| 5e8618f25a | |||
|
2bdf08a523
|
|||
|
9ddd36c414
|
|||
|
9372ea7343
|
|||
|
c62ce00184
|
|||
|
4d8cd740ba
|
|||
|
9858572a2f
|
|||
|
51edf55ae9
|
|||
| 75485ce8e9 | |||
|
fcbfcc4007
|
|||
|
cdcb7b3aef
|
|||
| bcf5172956 | |||
|
26c6c5a3b2
|
|||
|
4a65573934
|
|||
|
5e2d5c3b28
|
|||
|
2f70bae523
|
|||
|
40f3e8327a
|
|||
|
f3d6e29e4e
|
|||
| 8903ae2624 | |||
|
26e9073674
|
|||
| 73a89c2601 | |||
|
7d4dee17b7
|
|||
| 602ca6ee94 | |||
|
69fc1ca57e
|
|||
|
ee72a32c7e
|
|||
|
8a0d89ef60
|
|||
|
54af949c7d
|
|||
|
6dac732a7f
|
|||
|
e8c1a6066a
|
|||
| 44fadb12d6 | |||
|
533452469b
|
@@ -1,8 +1 @@
|
||||
LDAP_HOST=192.168.33.10
|
||||
LDAP_PORT=389
|
||||
#
|
||||
# Production LDAP server:
|
||||
#
|
||||
# LDAP_HOST=ldap.kosmos.org
|
||||
# LDAP_PORT=636
|
||||
# LDAP_USE_TLS=true
|
||||
EJABBERD_API_URL='https://xmpp.kosmos.org/api'
|
||||
|
||||
1
.env.production
Normal file
1
.env.production
Normal file
@@ -0,0 +1 @@
|
||||
EJABBERD_API_URL='https://xmpp.kosmos.org:5443/api'
|
||||
5
Gemfile
5
Gemfile
@@ -21,13 +21,15 @@ gem 'jbuilder', '~> 2.7'
|
||||
# Reduces boot times through caching; required in config/boot.rb
|
||||
gem 'bootsnap', '>= 1.4.2', require: false
|
||||
|
||||
gem 'dotenv-rails', groups: [:development, :test]
|
||||
gem 'dotenv-rails'
|
||||
|
||||
gem 'warden'
|
||||
gem 'devise'
|
||||
gem 'devise_ldap_authenticatable'
|
||||
gem 'net-ldap'
|
||||
|
||||
gem 'faraday'
|
||||
|
||||
group :development, :test do
|
||||
# Use sqlite3 as the database for Active Record
|
||||
gem 'sqlite3', '~> 1.4'
|
||||
@@ -51,6 +53,7 @@ group :test do
|
||||
gem 'factory_bot_rails'
|
||||
gem 'capybara'
|
||||
gem 'database_cleaner'
|
||||
gem 'webmock'
|
||||
end
|
||||
|
||||
group :production do
|
||||
|
||||
16
Gemfile.lock
16
Gemfile.lock
@@ -73,6 +73,8 @@ GEM
|
||||
regexp_parser (~> 1.5)
|
||||
xpath (~> 3.2)
|
||||
concurrent-ruby (1.1.7)
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
crass (1.0.6)
|
||||
database_cleaner (1.8.5)
|
||||
devise (4.7.3)
|
||||
@@ -95,9 +97,12 @@ GEM
|
||||
factory_bot_rails (6.1.0)
|
||||
factory_bot (~> 6.1.0)
|
||||
railties (>= 5.0.0)
|
||||
faraday (0.17.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffi (1.13.1)
|
||||
globalid (0.4.2)
|
||||
activesupport (>= 4.2.0)
|
||||
hashdiff (0.4.0)
|
||||
i18n (1.8.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jbuilder (2.10.1)
|
||||
@@ -126,6 +131,7 @@ GEM
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.14.2)
|
||||
msgpack (1.3.3)
|
||||
multipart-post (2.1.1)
|
||||
net-ldap (0.16.3)
|
||||
nio4r (2.5.4)
|
||||
nokogiri (1.10.10)
|
||||
@@ -191,6 +197,7 @@ GEM
|
||||
rspec-mocks (~> 3.9)
|
||||
rspec-support (~> 3.9)
|
||||
rspec-support (3.10.0)
|
||||
safe_yaml (1.0.5)
|
||||
sass-rails (6.0.0)
|
||||
sassc-rails (~> 2.1, >= 2.1.1)
|
||||
sassc (2.4.0)
|
||||
@@ -228,6 +235,10 @@ GEM
|
||||
activemodel (>= 6.0.0)
|
||||
bindex (>= 0.4.0)
|
||||
railties (>= 6.0.0)
|
||||
webmock (3.6.0)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
webpacker (4.3.0)
|
||||
activesupport (>= 4.2)
|
||||
rack-proxy (>= 0.6.1)
|
||||
@@ -241,6 +252,7 @@ GEM
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
bootsnap (>= 1.4.2)
|
||||
@@ -251,6 +263,7 @@ DEPENDENCIES
|
||||
devise_ldap_authenticatable
|
||||
dotenv-rails
|
||||
factory_bot_rails
|
||||
faraday
|
||||
jbuilder (~> 2.7)
|
||||
letter_opener
|
||||
letter_opener_web
|
||||
@@ -268,7 +281,8 @@ DEPENDENCIES
|
||||
tzinfo-data
|
||||
warden
|
||||
web-console (>= 3.3.0)
|
||||
webmock
|
||||
webpacker (~> 4.0)
|
||||
|
||||
BUNDLED WITH
|
||||
2.0.2
|
||||
2.2.2
|
||||
|
||||
@@ -11,7 +11,7 @@ credentials, invites, donations, etc..
|
||||
* [x] Log in with admin permissions
|
||||
* [x] View LDAP users as admin
|
||||
* [x] Sign up for a new account via invitation
|
||||
* [ ] List my donations
|
||||
* [x] List my donations
|
||||
* [ ] Invite new users from your account
|
||||
* [ ] Sign up for a new account by donating upfront
|
||||
* [ ] Sign up for a new account via proving contributions (via cryptographic signature)
|
||||
|
||||
13
app/assets/stylesheets/_variables.scss
Normal file
13
app/assets/stylesheets/_variables.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
$content-width: 800px;
|
||||
$content-max-width: 100%;
|
||||
|
||||
$text-color-body: #222;
|
||||
$text-color-discreet: #888;
|
||||
|
||||
$background-color-notice: #efffc4;
|
||||
$background-color-alert: #fff4c2;
|
||||
|
||||
$color-blue: #0d4f99;
|
||||
$color-purple: #8955a0;
|
||||
$color-red-bright: #c00;
|
||||
$color-red-dark: #990c0e;
|
||||
22
app/assets/stylesheets/admin.scss
Normal file
22
app/assets/stylesheets/admin.scss
Normal file
@@ -0,0 +1,22 @@
|
||||
@import "variables";
|
||||
|
||||
body#admin-panel {
|
||||
#wrapper {
|
||||
> header {
|
||||
background: $color-red-bright;
|
||||
background: linear-gradient(35deg, $color-purple 0, $color-red-dark 100%);
|
||||
}
|
||||
}
|
||||
|
||||
#main-nav {
|
||||
ul {
|
||||
li {
|
||||
a {
|
||||
&.active {
|
||||
border-bottom: 2px solid $color-red-bright;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
app/assets/stylesheets/donations.scss
Normal file
40
app/assets/stylesheets/donations.scss
Normal file
@@ -0,0 +1,40 @@
|
||||
ul.donations {
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
margin-bottom: 2rem;
|
||||
display: grid;
|
||||
grid-row-gap: 0.5rem;
|
||||
grid-column-gap: 2rem;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-areas:
|
||||
"date amount-btc"
|
||||
"public-name amounts-fiat";
|
||||
|
||||
h3 {
|
||||
grid-area: "date";
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
|
||||
&.amount-btc {
|
||||
grid-area: amount-btc;
|
||||
text-align: right;
|
||||
font-family: monospace;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
&.amounts-fiat {
|
||||
grid-area: amounts-fiat;
|
||||
text-align: right;
|
||||
font-family: monospace;
|
||||
font-size: 0.85rem;
|
||||
color: #888;
|
||||
}
|
||||
&.public-name {
|
||||
grid-area: public-name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
@import "variables";
|
||||
|
||||
@font-face {
|
||||
font-family: 'Raleway';
|
||||
src: url('/fonts/raleway-light.woff') format('woff2');
|
||||
@@ -8,6 +10,7 @@
|
||||
body {
|
||||
font-family: "Open Sans", Helvetica, Arial, sans-serif;
|
||||
font-weight: 400;
|
||||
color: $text-color-body;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
|
||||
@@ -22,10 +22,6 @@ form {
|
||||
color: #bc0101;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.accept-terms {
|
||||
margin-top: 2rem;
|
||||
font-size: 0.85rem;
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
@import "variables";
|
||||
@import "mediaqueries";
|
||||
|
||||
$content-width: 800px;
|
||||
$content-max-width: 100%;
|
||||
|
||||
body {
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
@@ -14,8 +9,8 @@ body {
|
||||
margin: 0 auto;
|
||||
padding: 4rem 0;
|
||||
text-align: center;
|
||||
background: #0d4f99;
|
||||
background: linear-gradient(35deg, #8955a0 0, #0d4f99 100%);
|
||||
background: $color-blue;
|
||||
background: linear-gradient(35deg, $color-purple 0, $color-blue 100%);
|
||||
|
||||
@include media-max(small) {
|
||||
padding: 3rem 0;
|
||||
@@ -77,18 +72,18 @@ body {
|
||||
padding: 2rem 0;
|
||||
|
||||
&.notice {
|
||||
background: #efffc4;
|
||||
background: $background-color-notice;
|
||||
}
|
||||
|
||||
&.alert {
|
||||
background: #fff4c2;
|
||||
background: $background-color-alert;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
width: $content-width;
|
||||
max-width: $content-max-width;
|
||||
margin: 4rem auto;
|
||||
margin: 4rem auto 6rem auto;
|
||||
text-align: left;
|
||||
|
||||
@include media-max(medium) {
|
||||
@@ -135,14 +130,18 @@ main {
|
||||
|
||||
section {
|
||||
border-bottom: 1px dotted #ccc;
|
||||
padding-bottom: 4rem;
|
||||
margin-bottom: 4rem;
|
||||
padding-bottom: 3rem;
|
||||
margin-bottom: 3rem;
|
||||
|
||||
@include media-max(small) {
|
||||
padding-bottom: 3rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.grid {
|
||||
@@ -165,9 +164,13 @@ main {
|
||||
|
||||
.grid-item {
|
||||
p {
|
||||
color: #888;
|
||||
color: $text-color-discreet;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text-centered {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
51
app/assets/stylesheets/main_nav.scss
Normal file
51
app/assets/stylesheets/main_nav.scss
Normal file
@@ -0,0 +1,51 @@
|
||||
@import "variables";
|
||||
@import "mediaqueries";
|
||||
|
||||
#main-nav {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
background-color: #efefef;
|
||||
|
||||
.wrapper {
|
||||
width: $content-width;
|
||||
max-width: $content-max-width;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
ul {
|
||||
@include media-max(large) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1fr);
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
li {
|
||||
@include media-min(large) {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
@include media-max(large) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
padding: 1.5rem 2rem;
|
||||
text-decoration: none;
|
||||
color: $text-color-discreet;
|
||||
|
||||
@include media-max(large) {
|
||||
display: block;
|
||||
text-align: center;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: $text-color-body;
|
||||
border-bottom: 2px solid #ffd000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,6 @@ class Admin::BaseController < ApplicationController
|
||||
before_action :authenticate_user!
|
||||
before_action :authorize_admin
|
||||
|
||||
layout "admin"
|
||||
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Admin::DashboardController < Admin::BaseController
|
||||
def index
|
||||
@current_section = :dashboard
|
||||
end
|
||||
end
|
||||
|
||||
79
app/controllers/admin/donations_controller.rb
Normal file
79
app/controllers/admin/donations_controller.rb
Normal file
@@ -0,0 +1,79 @@
|
||||
class Admin::DonationsController < Admin::BaseController
|
||||
before_action :set_donation, only: [:show, :edit, :update, :destroy]
|
||||
before_action :set_current_section, only: [:index, :show, :new, :edit]
|
||||
|
||||
# GET /donations
|
||||
# GET /donations.json
|
||||
def index
|
||||
@donations = Donation.all
|
||||
end
|
||||
|
||||
# GET /donations/1
|
||||
# GET /donations/1.json
|
||||
def show
|
||||
end
|
||||
|
||||
# GET /donations/new
|
||||
def new
|
||||
@donation = Donation.new
|
||||
end
|
||||
|
||||
# GET /donations/1/edit
|
||||
def edit
|
||||
end
|
||||
|
||||
# POST /donations
|
||||
# POST /donations.json
|
||||
def create
|
||||
@donation = Donation.new(donation_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @donation.save
|
||||
format.html { redirect_to admin_donation_url(@donation), notice: 'Donation was successfully created.' }
|
||||
format.json { render :show, status: :created, location: @donation }
|
||||
else
|
||||
format.html { render :new }
|
||||
format.json { render json: @donation.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PATCH/PUT /donations/1
|
||||
# PATCH/PUT /donations/1.json
|
||||
def update
|
||||
respond_to do |format|
|
||||
if @donation.update(donation_params)
|
||||
format.html { redirect_to admin_donation_url(@donation), notice: 'Donation was successfully updated.' }
|
||||
format.json { render :show, status: :ok, location: @donation }
|
||||
else
|
||||
format.html { render :edit }
|
||||
format.json { render json: @donation.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /donations/1
|
||||
# DELETE /donations/1.json
|
||||
def destroy
|
||||
@donation.destroy
|
||||
respond_to do |format|
|
||||
format.html { redirect_to admin_donations_url, notice: 'Donation was successfully destroyed.' }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_donation
|
||||
@donation = Donation.find(params[:id])
|
||||
end
|
||||
|
||||
# Only allow a list of trusted parameters through.
|
||||
def donation_params
|
||||
params.require(:donation).permit(:user_id, :amount_sats, :amount_eur, :amount_usd, :public_name, :paid_at)
|
||||
end
|
||||
|
||||
def set_current_section
|
||||
@current_section = :donations
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,6 @@
|
||||
class Admin::LdapUsersController < Admin::BaseController
|
||||
before_action :set_current_section
|
||||
|
||||
def index
|
||||
attributes = %w{dn cn uid mail admin}
|
||||
filter = Net::LDAP::Filter.eq("uid", "*")
|
||||
@@ -38,4 +40,8 @@ class Admin::LdapUsersController < Admin::BaseController
|
||||
def ldap_config
|
||||
ldap_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/ldap.yml")).result)[Rails.env]
|
||||
end
|
||||
|
||||
def set_current_section
|
||||
@current_section = :ldap_users
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,5 +2,6 @@ class DashboardController < ApplicationController
|
||||
before_action :require_user_signed_in
|
||||
|
||||
def index
|
||||
@current_section = :dashboard
|
||||
end
|
||||
end
|
||||
|
||||
10
app/controllers/donations_controller.rb
Normal file
10
app/controllers/donations_controller.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
class DonationsController < ApplicationController
|
||||
before_action :require_user_signed_in
|
||||
|
||||
# GET /donations
|
||||
# GET /donations.json
|
||||
def index
|
||||
@donations = current_user.donations.completed
|
||||
@current_section = :contributions
|
||||
end
|
||||
end
|
||||
@@ -8,6 +8,7 @@ class InvitationsController < ApplicationController
|
||||
def index
|
||||
@invitations_unused = current_user.invitations.unused
|
||||
@invitations_used = current_user.invitations.used
|
||||
@current_section = :invitations
|
||||
end
|
||||
|
||||
# GET /invitations/a-random-invitation-token
|
||||
|
||||
@@ -94,16 +94,15 @@ class SignupController < ApplicationController
|
||||
end
|
||||
|
||||
def complete_signup
|
||||
@user.save!
|
||||
session[:new_user] = nil
|
||||
session[:validation_error] = nil
|
||||
|
||||
CreateAccount.call(
|
||||
username: @user.cn,
|
||||
domain: "kosmos.org",
|
||||
email: @user.email,
|
||||
password: @user.password
|
||||
password: @user.password,
|
||||
invitation: @invitation
|
||||
)
|
||||
|
||||
@invitation.update! invited_user_id: @user.id, used_at: DateTime.now
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
module ApplicationHelper
|
||||
def sats_to_btc(sats)
|
||||
sats.to_f / 100000000
|
||||
end
|
||||
end
|
||||
|
||||
2
app/helpers/donations_helper.rb
Normal file
2
app/helpers/donations_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
||||
module DonationsHelper
|
||||
end
|
||||
13
app/models/donation.rb
Normal file
13
app/models/donation.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
class Donation < ApplicationRecord
|
||||
# Relations
|
||||
belongs_to :user
|
||||
|
||||
# Validations
|
||||
validates_presence_of :amount_sats
|
||||
|
||||
# Hooks
|
||||
# TODO before_create :store_fiat_value
|
||||
|
||||
#Scopes
|
||||
scope :completed, -> { where.not(paid_at: nil) }
|
||||
end
|
||||
@@ -3,6 +3,7 @@ class User < ApplicationRecord
|
||||
|
||||
# Relations
|
||||
has_many :invitations, dependent: :destroy
|
||||
has_many :donations, dependent: :nullify
|
||||
|
||||
validates_uniqueness_of :cn
|
||||
validates_length_of :cn, :minimum => 3
|
||||
|
||||
@@ -1,16 +1,38 @@
|
||||
class CreateAccount < ApplicationService
|
||||
def initialize(args)
|
||||
@username = args[:username]
|
||||
@email = args[:email]
|
||||
@password = args[:password]
|
||||
@username = args[:username]
|
||||
@domain = args[:ou] || "kosmos.org"
|
||||
@email = args[:email]
|
||||
@password = args[:password]
|
||||
@invitation = args[:invitation]
|
||||
end
|
||||
|
||||
def call
|
||||
user = create_user_in_database
|
||||
add_ldap_document
|
||||
|
||||
if @invitation.present?
|
||||
update_invitation(user.id)
|
||||
exchange_xmpp_contacts
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_user_in_database
|
||||
User.create!(
|
||||
cn: @username,
|
||||
ou: @domain,
|
||||
email: @email,
|
||||
password: @password,
|
||||
password_confirmation: @password
|
||||
)
|
||||
end
|
||||
|
||||
def update_invitation(user_id)
|
||||
@invitation.update! invited_user_id: user_id, used_at: DateTime.now
|
||||
end
|
||||
|
||||
def add_ldap_document
|
||||
dn = "cn=#{@username},ou=kosmos.org,cn=users,dc=kosmos,dc=org"
|
||||
attr = {
|
||||
@@ -39,4 +61,23 @@ class CreateAccount < ApplicationService
|
||||
def ldap_config
|
||||
ldap_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/ldap.yml")).result)[Rails.env]
|
||||
end
|
||||
|
||||
def exchange_xmpp_contacts
|
||||
#TODO enable in development when we have easy setup of ejabberd etc.
|
||||
return if Rails.env.development?
|
||||
|
||||
ejabberd = EjabberdApiClient.new
|
||||
inviter = @invitation.user
|
||||
|
||||
ejabberd.add_rosteritem({
|
||||
"localuser": @username, "localhost": @domain,
|
||||
"user": inviter.cn, "host": inviter.ou,
|
||||
"nick": inviter.cn, "group": "Friends", "subs": "both"
|
||||
})
|
||||
ejabberd.add_rosteritem({
|
||||
"localuser": inviter.cn, "localhost": inviter.ou,
|
||||
"user": @username, "host": @domain,
|
||||
"nick": @username, "group": "Friends", "subs": "both"
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
20
app/services/ejabberd_api_client.rb
Normal file
20
app/services/ejabberd_api_client.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
class EjabberdApiClient
|
||||
def initialize
|
||||
@base_url = ENV["EJABBERD_API_URL"]
|
||||
end
|
||||
|
||||
def post(endpoint, payload)
|
||||
res = Faraday.post("#{@base_url}/#{endpoint}", payload.to_json,
|
||||
"Content-Type" => "application/json")
|
||||
|
||||
if res.status != 200
|
||||
Rails.logger.error "[ejabberd] API request failed:"
|
||||
Rails.logger.error res.body
|
||||
#TODO add some kind of exception tracking/notifications
|
||||
end
|
||||
end
|
||||
|
||||
def add_rosteritem(payload)
|
||||
post "add_rosteritem", payload
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,3 @@
|
||||
<h2>Admin Panel</h2>
|
||||
<p>
|
||||
Ohai there, admin human.
|
||||
</p>
|
||||
<p>
|
||||
<%= link_to 'LDAP users', admin_ldap_users_path %>
|
||||
<p class="text-centered">
|
||||
With great power comes great responsibility.
|
||||
</p>
|
||||
|
||||
2
app/views/admin/donations/_donation.json.jbuilder
Normal file
2
app/views/admin/donations/_donation.json.jbuilder
Normal file
@@ -0,0 +1,2 @@
|
||||
json.extract! donation, :id, :user_id, :amount_sats, :amount_eur, :amount_usd, :public_name, :created_at, :updated_at
|
||||
json.url donation_url(donation, format: :json)
|
||||
60
app/views/admin/donations/_form.html.erb
Normal file
60
app/views/admin/donations/_form.html.erb
Normal file
@@ -0,0 +1,60 @@
|
||||
<%= form_with(url: url, model: donation, local: true) do |form| %>
|
||||
<% if donation.errors.any? %>
|
||||
<div id="error_explanation">
|
||||
<h3><%= pluralize(donation.errors.count, "error") %> prohibited this donation from being saved:</h3>
|
||||
<ul>
|
||||
<% donation.errors.full_messages.each do |message| %>
|
||||
<li><%= message %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="field">
|
||||
<p>
|
||||
<%= form.label :user_id %>
|
||||
<%= form.collection_select :user_id, User.where(ou: "kosmos.org").order(:cn), :id, :cn %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<p>
|
||||
<%= form.label :amount_sats, "Amount BTC (sats)" %>
|
||||
<%= form.number_field :amount_sats %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<p>
|
||||
<%= form.label :amount_eur, "Amount EUR (cents)" %>
|
||||
<%= form.number_field :amount_eur %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<p>
|
||||
<%= form.label :amount_usd, "Amount USD (cents)"%>
|
||||
<%= form.number_field :amount_usd %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<p>
|
||||
<%= form.label :public_name %>
|
||||
<%= form.text_field :public_name %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<p>
|
||||
<%= form.label :paid_at %>
|
||||
<%= form.text_field :paid_at %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<p>
|
||||
<%= form.submit %>
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
8
app/views/admin/donations/edit.html.erb
Normal file
8
app/views/admin/donations/edit.html.erb
Normal file
@@ -0,0 +1,8 @@
|
||||
<h2>Editing Donation</h2>
|
||||
|
||||
<%= render 'form', donation: @donation, url: admin_donation_path(@donation) %>
|
||||
|
||||
<p class="actions">
|
||||
<%= link_to 'Show', admin_donation_path(@donation) %> |
|
||||
<%= link_to 'Back', admin_donations_path %>
|
||||
<p>
|
||||
41
app/views/admin/donations/index.html.erb
Normal file
41
app/views/admin/donations/index.html.erb
Normal file
@@ -0,0 +1,41 @@
|
||||
<h2>Donations</h2>
|
||||
|
||||
<% if @donations.any? %>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
<th>Amount BTC</th>
|
||||
<th>in EUR</th>
|
||||
<th>in USD</th>
|
||||
<th>Public name</th>
|
||||
<th>Date</th>
|
||||
<th colspan="3"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% @donations.each do |donation| %>
|
||||
<tr>
|
||||
<td><%= donation.user.cn %></td>
|
||||
<td><%= sats_to_btc donation.amount_sats %> BTC</td>
|
||||
<td><%= number_to_currency donation.amount_eur / 100, unit: "" %></td>
|
||||
<td><%= number_to_currency donation.amount_usd / 100, unit: "" %></td>
|
||||
<td><%= donation.public_name %></td>
|
||||
<td><%= donation.paid_at ? donation.paid_at.strftime("%Y-%m-%d") : "" %></td>
|
||||
<td><%= link_to 'Show', admin_donation_path(donation) %></td>
|
||||
<td><%= link_to 'Edit', edit_admin_donation_path(donation) %></td>
|
||||
<td><%= link_to 'Destroy', admin_donation_path(donation), method: :delete, data: { confirm: 'Are you sure?' } %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% else %>
|
||||
<p>
|
||||
No donations yet.
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<p class="actions">
|
||||
<%= link_to 'Record an out-of-system donation', new_admin_donation_path %>
|
||||
</p>
|
||||
1
app/views/admin/donations/index.json.jbuilder
Normal file
1
app/views/admin/donations/index.json.jbuilder
Normal file
@@ -0,0 +1 @@
|
||||
json.array! @donations, partial: "donations/donation", as: :donation
|
||||
7
app/views/admin/donations/new.html.erb
Normal file
7
app/views/admin/donations/new.html.erb
Normal file
@@ -0,0 +1,7 @@
|
||||
<h2>New Donation</h2>
|
||||
|
||||
<%= render 'form', donation: @donation, url: admin_donations_path %>
|
||||
|
||||
<p class="actions">
|
||||
<%= link_to 'Back', admin_donations_path %>
|
||||
</p>
|
||||
34
app/views/admin/donations/show.html.erb
Normal file
34
app/views/admin/donations/show.html.erb
Normal file
@@ -0,0 +1,34 @@
|
||||
<p id="notice"><%= notice %></p>
|
||||
|
||||
<p>
|
||||
<strong>User:</strong>
|
||||
<%= @donation.user_id %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Amount sats:</strong>
|
||||
<%= @donation.amount_sats %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Amount eur:</strong>
|
||||
<%= @donation.amount_eur %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Amount usd:</strong>
|
||||
<%= @donation.amount_usd %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Public name:</strong>
|
||||
<%= @donation.public_name %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Date:</strong>
|
||||
<%= @donation.paid_at %>
|
||||
</p>
|
||||
|
||||
<%= link_to 'Edit', edit_admin_donation_path(@donation) %> |
|
||||
<%= link_to 'Back', admin_donations_path %>
|
||||
1
app/views/admin/donations/show.json.jbuilder
Normal file
1
app/views/admin/donations/show.json.jbuilder
Normal file
@@ -0,0 +1 @@
|
||||
json.partial! "donations/donation", donation: @donation
|
||||
@@ -6,7 +6,7 @@
|
||||
</p>
|
||||
<div class="grid services">
|
||||
<div class="grid-item chat">
|
||||
<h3><%= link_to "Chat", "https://wiki.kosmos.org/Services:XMPP" %></h3>
|
||||
<h3><%= link_to "Chat", "https://wiki.kosmos.org/Services:Chat" %></h3>
|
||||
<p>
|
||||
Chat rooms and instant messaging (XMPP/Jabber)
|
||||
</p>
|
||||
|
||||
38
app/views/donations/index.html.erb
Normal file
38
app/views/donations/index.html.erb
Normal file
@@ -0,0 +1,38 @@
|
||||
<section>
|
||||
<h2>Donations</h2>
|
||||
<p>
|
||||
Your financial contributions to the development and
|
||||
upkeep of Kosmos software and services.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<% if @donations.any? %>
|
||||
<ul class="donations">
|
||||
<% @donations.each do |donation| %>
|
||||
<li>
|
||||
<h3>
|
||||
<%= donation.paid_at.strftime("%B %d, %Y") %>
|
||||
</h3>
|
||||
<p class="amount-btc">
|
||||
<%= sats_to_btc donation.amount_sats %> BTC
|
||||
</p>
|
||||
<p class="amounts-fiat">
|
||||
(~ <%= number_to_currency donation.amount_eur / 100, unit: "" %> EUR)
|
||||
</p>
|
||||
<p class="public-name">
|
||||
<% if donation.public_name.present? %>
|
||||
Public name: <%= donation.public_name %>
|
||||
<% else %>
|
||||
Anonymous
|
||||
<% end %>
|
||||
</p>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% else %>
|
||||
<p>
|
||||
No donations to show.
|
||||
</p>
|
||||
<% end %>
|
||||
</section>
|
||||
1
app/views/donations/index.json.jbuilder
Normal file
1
app/views/donations/index.json.jbuilder
Normal file
@@ -0,0 +1 @@
|
||||
json.array! @donations, partial: "donations/donation", as: :donation
|
||||
@@ -24,23 +24,25 @@
|
||||
</section>
|
||||
|
||||
<% if @invitations_used.any? %>
|
||||
<h3>Accepted Invitations</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>URL</th>
|
||||
<th>Used at</th>
|
||||
<th>Invited user</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @invitations_used.each do |invitation| %>
|
||||
<section>
|
||||
<h3>Accepted Invitations</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td><%= invitation_url(invitation.token) %></td>
|
||||
<td><%= invitation.used_at %></td>
|
||||
<td><%= User.find(invitation.invited_user_id).address %></td>
|
||||
<th>URL</th>
|
||||
<th>Accepted</th>
|
||||
<th>Invited user</th>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @invitations_used.each do |invitation| %>
|
||||
<tr>
|
||||
<td class="overflow-ellipsis"><%= invitation_url(invitation.token) %></td>
|
||||
<td><%= invitation.used_at.strftime("%Y-%m-%d") %></td>
|
||||
<td><%= User.find(invitation.invited_user_id).address %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
45
app/views/layouts/admin.html.erb
Normal file
45
app/views/layouts/admin.html.erb
Normal file
@@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Admin Panel | Kosmos Accounts</title>
|
||||
<%= csrf_meta_tags %>
|
||||
<%= csp_meta_tag %>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
||||
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
|
||||
</head>
|
||||
|
||||
<body id="admin-panel">
|
||||
<div id="wrapper">
|
||||
<header>
|
||||
<h1>
|
||||
<span class ="project-name">Kosmos</span>
|
||||
<span class ="site-name">Akkounts</span>
|
||||
<span class="beta"><span class="bolt">⚡</span> beta</span>
|
||||
</h1>
|
||||
<% if user_signed_in? %>
|
||||
<p class="current-user">
|
||||
Signed in as <strong><%= current_user.cn %>@kosmos.org</strong>.
|
||||
<%= link_to "Log out", destroy_user_session_path, method: :delete %>
|
||||
</p>
|
||||
<% end %>
|
||||
</header>
|
||||
|
||||
<% if user_signed_in? && current_user.confirmed? %>
|
||||
<%= render partial: 'shared/admin_nav' %>
|
||||
<% end %>
|
||||
|
||||
<% flash.each do |type, msg| %>
|
||||
<div class="flash-msg <%= type %>">
|
||||
<p><%= msg %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<main>
|
||||
<%= yield %>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -27,6 +27,10 @@
|
||||
<% end %>
|
||||
</header>
|
||||
|
||||
<% if user_signed_in? && current_user.confirmed? %>
|
||||
<%= render partial: 'shared/main_nav' %>
|
||||
<% end %>
|
||||
|
||||
<% flash.each do |type, msg| %>
|
||||
<div class="flash-msg <%= type %>">
|
||||
<p><%= msg %></p>
|
||||
|
||||
18
app/views/shared/_admin_nav.html.erb
Normal file
18
app/views/shared/_admin_nav.html.erb
Normal file
@@ -0,0 +1,18 @@
|
||||
<nav id="main-nav">
|
||||
<div class="wrapper">
|
||||
<ul class="pages">
|
||||
<li>
|
||||
<%= link_to "Dashboard", admin_root_path,
|
||||
class: @current_section == :dashboard ? "active" : nil %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to "Donations", admin_donations_path,
|
||||
class: @current_section == :donations ? "active" : nil %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to "LDAP Users", admin_ldap_users_path,
|
||||
class: @current_section == :ldap_users ? "active" : nil %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
18
app/views/shared/_main_nav.html.erb
Normal file
18
app/views/shared/_main_nav.html.erb
Normal file
@@ -0,0 +1,18 @@
|
||||
<nav id="main-nav">
|
||||
<div class="wrapper">
|
||||
<ul class="pages">
|
||||
<li>
|
||||
<%= link_to "Services", root_path,
|
||||
class: @current_section == :dashboard ? "active" : nil %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to "Donations", donations_path,
|
||||
class: @current_section == :contributions ? "active" : nil %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to "Invitations", invitations_path,
|
||||
class: @current_section == :invitations ? "active" : nil %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -1 +1 @@
|
||||
LWyKwPZq9Kd97rn/7+q3MEkh7kITScDMHD3JvVuaV3A4YIHJHU+460k+PaEGlsH1xkbuClGiAb57rk1XLyDnmVGtbSueYOtinkw6kar8ZfKWZob061LwGjpMVRQkS49TjCUZlqCFrXeKxlH03mXWBnqAj9RUIPrm7eibb3c7qmJFglR1380RSVsfZnp8A3QwGm4Wh9OWtpUa6P2lne0jQsOuSe8ur3DUF0LplzS4CbkMxAUDOom+pXB13AlxOH9NQE7F4dsYHugHkh1tG3r3ER3xAUD/9Kn6UZZP7BnwUs3zqhoZdULRpRgA5dK7ueTIAnO/jtJDF4562VS8ECo7AnNoVxNe8/mBMFIOUfqg+db/72N2pIk3r4lK7Uzm/4jJ5/99ItnQjHQPcApiwZXIr3OyDLUvq5+d0UVmAMXdwcAjvctVQXFx5imG149Y0ISHKWVm1ca37aAspxWPU+CIj8/HW0yEpjp3vhwDUbjCaZeAPm8UQC14MxZwSK3N+EUSQXdltiweFynabDB7zGGQsjMM8LwMtyo9bTBzJA78Cl96MDyd20i1zSF9ntLuKulwGm3oZowpbNuvo2anY6r9yBlDJBOEISbtXv2tLX4SqcM=--bRcsE4K/29XzyZat--+G3iQCLBqgSwLaQ+7+4YvA==
|
||||
IUCQBycRu4CEN/PXSJGZNq3SHgrEnDZfPo2suTPBxLXqKk59+cZa5ID3CBXY+CNhCnV9gAhn2GTLm/kq6HNEGFsV6AmKh/rYy9YGqoS+vWrUdgecvqYQfEz0KsAzYn4iEJ4EoQYpJ3/Y6Cen9zzeq/h1OIkMuzsTiKIG4q09j5rWBIaUyHQe75OdE4Ik681xitAUcS3sBJzhnnar9LqiLvx9fgRlVqWNGG1eAl926apptsgv/VoQ81HmkY0Lt7UWSOWRL1D4FhvjQiAiwc9xwTeKNH/BWdMhoXqDmrMsvuIqwIYFrbE9X2oP7KJYu3Cch14qDN/1JEMd4dGuq9uHTXAKVK9M9sFHeEUoqtVTQw+Vp/cCCg4oVtTOOv5IPIrJc8LuOhRPE/Iu5N36OYsEV1+6KunZ/4Ys8309K94fUgOBltQ8V6Gky6OutnlaOhDBGSc8IVkzlPuBNrmOBYBzVl8r9VAu8hGonN4BI6Y+Aicrq/ZMH0P1gZnEsPKyRaoP2Pvx7wVIX27luWfGrV0i/fJG0qitlyZ7nCEtnw/6OXp2bIcHv+UiQEX4AYZO3XW3QNZjNeng6HVrSJlRK/vcWVEFHNZPKi4iRN6qXH0D7Q==--z6FJEn+mmxweg7jD--wwT5g/XEd+U9eBwqK234cQ==
|
||||
@@ -1,4 +1,5 @@
|
||||
Rails.application.routes.draw do
|
||||
resources :donations
|
||||
devise_for :users
|
||||
|
||||
get 'welcome', to: 'welcome#index'
|
||||
@@ -16,6 +17,7 @@ Rails.application.routes.draw do
|
||||
namespace :admin do
|
||||
root to: 'dashboard#index'
|
||||
get 'ldap_users', to: 'ldap_users#index'
|
||||
resources :donations
|
||||
end
|
||||
|
||||
# Letter Opener (open "sent" emails in dev and staging)
|
||||
|
||||
@@ -8,6 +8,7 @@ class CreateInvitations < ActiveRecord::Migration[6.0]
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_index :invitations, :user_id
|
||||
add_index :invitations, :invited_user_id
|
||||
end
|
||||
|
||||
15
db/migrate/20201217161544_create_donations.rb
Normal file
15
db/migrate/20201217161544_create_donations.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
class CreateDonations < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
create_table :donations do |t|
|
||||
t.integer :user_id
|
||||
t.integer :amount_sats
|
||||
t.integer :amount_eur
|
||||
t.integer :amount_usd
|
||||
t.string :public_name
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_index :donations, :user_id
|
||||
end
|
||||
end
|
||||
5
db/migrate/20201219121808_add_paid_at_to_donations.rb
Normal file
5
db/migrate/20201219121808_add_paid_at_to_donations.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class AddPaidAtToDonations < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :donations, :paid_at, :datetime
|
||||
end
|
||||
end
|
||||
14
db/schema.rb
14
db/schema.rb
@@ -10,7 +10,19 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2020_11_30_132533) do
|
||||
ActiveRecord::Schema.define(version: 2020_12_19_121808) do
|
||||
|
||||
create_table "donations", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
t.integer "amount_sats"
|
||||
t.integer "amount_eur"
|
||||
t.integer "amount_usd"
|
||||
t.string "public_name"
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.datetime "paid_at"
|
||||
t.index ["user_id"], name: "index_donations_on_user_id"
|
||||
end
|
||||
|
||||
create_table "invitations", force: :cascade do |t|
|
||||
t.string "token"
|
||||
|
||||
9
spec/factories/donations.rb
Normal file
9
spec/factories/donations.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
FactoryBot.define do
|
||||
factory :donation do
|
||||
user_id { 1 }
|
||||
amount_sats { 100000 }
|
||||
amount_eur { 10 }
|
||||
amount_usd { 13 }
|
||||
public_name { nil }
|
||||
end
|
||||
end
|
||||
@@ -10,6 +10,6 @@ RSpec.describe 'Admin dashboard', type: :feature do
|
||||
|
||||
scenario 'View dashboard' do
|
||||
visit admin_root_path
|
||||
expect(page).to have_content('Admin Panel')
|
||||
expect(page).to have_content('great power')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -53,8 +53,11 @@ RSpec.describe "Signup", type: :feature do
|
||||
expect(page).to have_content("Choose a password")
|
||||
|
||||
expect(CreateAccount).to receive(:call)
|
||||
.with(username: "tony", email: "tony@example.com", password: "a-valid-password")
|
||||
.and_return(true)
|
||||
.with(
|
||||
username: "tony", domain: "kosmos.org",
|
||||
email: "tony@example.com", password: "a-valid-password",
|
||||
invitation: Invitation.last
|
||||
).and_return(true)
|
||||
|
||||
fill_in "user_password", with: "a-valid-password"
|
||||
click_button "Create account"
|
||||
@@ -62,7 +65,6 @@ RSpec.describe "Signup", type: :feature do
|
||||
expect(page).to have_content("confirm your address")
|
||||
end
|
||||
expect(page).to have_content("close this window or tab now")
|
||||
expect(User.last.confirmed_at).to be_nil
|
||||
end
|
||||
|
||||
scenario "Validation errors" do
|
||||
@@ -89,15 +91,17 @@ RSpec.describe "Signup", type: :feature do
|
||||
expect(page).to have_content("Password is too short")
|
||||
|
||||
expect(CreateAccount).to receive(:call)
|
||||
.with(username: "tony", email: "tony@example.com", password: "a-valid-password")
|
||||
.and_return(true)
|
||||
.with(
|
||||
username: "tony", domain: "kosmos.org",
|
||||
email: "tony@example.com", password: "a-valid-password",
|
||||
invitation: Invitation.last
|
||||
).and_return(true)
|
||||
|
||||
fill_in "user_password", with: "a-valid-password"
|
||||
click_button "Create account"
|
||||
within ".flash-msg.notice" do
|
||||
expect(page).to have_content("confirm your address")
|
||||
end
|
||||
expect(User.last.cn).to eq("tony")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
9
spec/helpers/application_helper_spec.rb
Normal file
9
spec/helpers/application_helper_spec.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe ApplicationHelper do
|
||||
describe "sats_to_btc" do
|
||||
it "converts satoshis to BTC" do
|
||||
expect(helper.sats_to_btc(120000000)).to eq(1.2)
|
||||
end
|
||||
end
|
||||
end
|
||||
15
spec/helpers/donations_helper_spec.rb
Normal file
15
spec/helpers/donations_helper_spec.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
require 'rails_helper'
|
||||
|
||||
# Specs in this file have access to a helper object that includes
|
||||
# the DonationsHelper. For example:
|
||||
#
|
||||
# describe DonationsHelper do
|
||||
# describe "string concat" do
|
||||
# it "concats two strings with spaces" do
|
||||
# expect(helper.concat_strings("this","that")).to eq("this that")
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
RSpec.describe DonationsHelper, type: :helper do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
5
spec/models/donation_spec.rb
Normal file
5
spec/models/donation_spec.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Donation, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
@@ -1,4 +1,6 @@
|
||||
require 'rails_helper'
|
||||
require 'webmock/rspec'
|
||||
require 'json'
|
||||
|
||||
RSpec.describe CreateAccount, type: :model do
|
||||
let(:ldap_client_mock) { instance_double(Net::LDAP) }
|
||||
@@ -7,6 +9,44 @@ RSpec.describe CreateAccount, type: :model do
|
||||
allow(service).to receive(:ldap_client).and_return(ldap_client_mock)
|
||||
end
|
||||
|
||||
describe "#create_user_in_database" do
|
||||
let(:service) { CreateAccount.new(
|
||||
username: 'isaacnewton',
|
||||
email: 'isaacnewton@example.com',
|
||||
password: 'bright-ideas-in-autumn'
|
||||
)}
|
||||
|
||||
it "creates a new user record in the akkounts database" do
|
||||
expect(User.count).to eq(0)
|
||||
service.send(:create_user_in_database)
|
||||
expect(User.count).to eq(1)
|
||||
expect(User.last.cn).to eq("isaacnewton")
|
||||
expect(User.last.email).to eq("isaacnewton@example.com")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#update_invitation" do
|
||||
let(:invitation) { create :invitation }
|
||||
let(:service) { CreateAccount.new(
|
||||
username: 'isaacnewton',
|
||||
email: 'isaacnewton@example.com',
|
||||
password: 'bright-ideas-in-autumn',
|
||||
invitation: invitation
|
||||
)}
|
||||
|
||||
before(:each) do
|
||||
service.send(:update_invitation, 23)
|
||||
end
|
||||
|
||||
it "marks the invitation as used" do
|
||||
expect(invitation.used_at).not_to be_nil
|
||||
end
|
||||
|
||||
it "saves the invited user's ID" do
|
||||
expect(invitation.invited_user_id).to eq(23)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#add_ldap_document" do
|
||||
let(:service) { CreateAccount.new(
|
||||
username: 'halfinney',
|
||||
@@ -30,4 +70,29 @@ RSpec.describe CreateAccount, type: :model do
|
||||
service.send(:add_ldap_document)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#exchange_xmpp_contacts" do
|
||||
let(:inviter) { create :user, cn: "willherschel", ou: "kosmos.org" }
|
||||
let(:invitation) { create :invitation, user: inviter }
|
||||
let(:service) { CreateAccount.new(
|
||||
username: 'isaacnewton',
|
||||
email: 'isaacnewton@example.com',
|
||||
password: 'bright-ideas-in-autumn',
|
||||
invitation: invitation
|
||||
)}
|
||||
|
||||
before do
|
||||
stub_request(:post, "http://xmpp.example.com/api/add_rosteritem")
|
||||
.to_return(status: 200, body: "", headers: {})
|
||||
end
|
||||
|
||||
it "posts add_rosteritem commands to the ejabberd API" do
|
||||
service.send(:exchange_xmpp_contacts)
|
||||
|
||||
expect(WebMock).to have_requested(:post, "http://xmpp.example.com/api/add_rosteritem")
|
||||
.with { |req| req.body == '{"localuser":"isaacnewton","localhost":"kosmos.org","user":"willherschel","host":"kosmos.org","nick":"willherschel","group":"Friends","subs":"both"}' }
|
||||
expect(WebMock).to have_requested(:post, "http://xmpp.example.com/api/add_rosteritem")
|
||||
.with { |req| req.body == '{"localuser":"willherschel","localhost":"kosmos.org","user":"isaacnewton","host":"kosmos.org","nick":"isaacnewton","group":"Friends","subs":"both"}' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user