Commit 102f3bff authored by lauren j's avatar lauren j
Browse files

Initial code commit

parent 825bfd55
source 'https://rubygems.org'
gem 'sinatra', '~> 2.0.5'
gem 'haml', '~> 5.0.4'
gem 'puma', '~> 3.12.0'
gem 'dotenv', '~> 2.5.0'
gem 'sequel', '~> 5.16.0'
gem 'rbnacl', '~> 6.0.0'
gem 'typhoeus', '~> 1.3.1'
gem 'json', '~> 2.1.0'
gem 'rake', '~> 12.3.2'
gem 'rotp', '~> 4.0.2'
gem 'rqrcode', '~> 0.10.1'
gem 'sass', '~> 3.7.2'
gem 'mail', '~> 2.7.1'
gem 'addressable', '~> 2.5.2'
gem 'u2f', '~> 1.0.0'
gem 'sanitize', '~> 5.0.0'
gem 'memoist', '~> 0.16.0'
group :development do
gem 'sqlite3', '~> 1.3.13'
gem 'pry', '~> 0.12.2'
end
group :production do
gem 'pg', '~> 1.1.3'
end
GEM
remote: https://rubygems.org/
specs:
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
chunky_png (1.3.11)
coderay (1.1.2)
crass (1.0.4)
dotenv (2.5.0)
ethon (0.11.0)
ffi (>= 1.3.0)
ffi (1.10.0)
haml (5.0.4)
temple (>= 0.8.0)
tilt
json (2.1.0)
mail (2.7.1)
mini_mime (>= 0.1.1)
memoist (0.16.0)
method_source (0.9.2)
mini_mime (1.0.1)
mini_portile2 (2.4.0)
mustermann (1.0.3)
nokogiri (1.10.0)
mini_portile2 (~> 2.4.0)
nokogumbo (2.0.1)
nokogiri (~> 1.8, >= 1.8.4)
pg (1.1.4)
pry (0.12.2)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
public_suffix (3.0.3)
puma (3.12.0)
rack (2.0.6)
rack-protection (2.0.5)
rack
rake (12.3.2)
rb-fsevent (0.10.3)
rb-inotify (0.10.0)
ffi (~> 1.0)
rbnacl (6.0.0)
ffi
rotp (4.0.2)
addressable (~> 2.5)
rqrcode (0.10.1)
chunky_png (~> 1.0)
sanitize (5.0.0)
crass (~> 1.0.2)
nokogiri (>= 1.8.0)
nokogumbo (~> 2.0)
sass (3.7.3)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sequel (5.16.0)
sinatra (2.0.5)
mustermann (~> 1.0)
rack (~> 2.0)
rack-protection (= 2.0.5)
tilt (~> 2.0)
sqlite3 (1.3.13)
temple (0.8.0)
tilt (2.0.9)
typhoeus (1.3.1)
ethon (>= 0.9.0)
u2f (1.0.0)
PLATFORMS
ruby
DEPENDENCIES
addressable (~> 2.5.2)
dotenv (~> 2.5.0)
haml (~> 5.0.4)
json (~> 2.1.0)
mail (~> 2.7.1)
memoist (~> 0.16.0)
pg (~> 1.1.3)
pry (~> 0.12.2)
puma (~> 3.12.0)
rake (~> 12.3.2)
rbnacl (~> 6.0.0)
rotp (~> 4.0.2)
rqrcode (~> 0.10.1)
sanitize (~> 5.0.0)
sass (~> 3.7.2)
sequel (~> 5.16.0)
sinatra (~> 2.0.5)
sqlite3 (~> 1.3.13)
typhoeus (~> 1.3.1)
u2f (~> 1.0.0)
BUNDLED WITH
1.17.1
require File.expand_path("../config/reconnect.rb", __FILE__)
ReConnect.initialize :no_load_models => true, :no_load_configs => true, :no_check_keyderiv => true
def do_setup
ReConnect::Models.load_models
ReConnect.load_configs
end
namespace :db do
desc "Run database migrations"
task :migrate, [:version] do |t, args|
Sequel.extension(:migration)
migration_dir = File.expand_path("../migrations", __FILE__)
version = nil
version = args[:version].to_i if args[:version]
Sequel::Migrator.run(ReConnect.database, migration_dir, :target => version)
end
end
namespace :release do
desc "Change version to given version"
task :change_version, [:version] do |t, args|
abort "version not provided" unless args[:version]
puts "Updating version to #{args[:version]}"
# Change app/version.rb
version_rb_path = File.expand_path("../app/version.rb", __FILE__)
version_rb = File.read(version_rb_path)
old_version_rb = /VERSION = "(.*)"/.match(version_rb)[1]
puts "version.rb was at #{old_version_rb}"
version_rb.gsub!("VERSION = \"#{old_version_rb}\"", "VERSION = \"#{args[:version]}\"")
File.open(version_rb_path, 'w') do |f|
f.write(version_rb)
end
# Change package.json
package_json_path = File.expand_path("../package.json", __FILE__)
package_json = JSON.parse(File.read(package_json_path))
puts "package.json was at #{package_json["version"]}"
package_json["version"] = args[:version]
File.open(package_json_path, 'w') do |f|
f.write(JSON.pretty_generate(package_json) + "\n")
end
puts "You should now run 'npm install' to update lockfiles, and then tag the release as \"v#{args[:version]}\" and 'git push --tags'."
end
end
desc "Run an interactive console with the application loaded"
task :console do
do_setup
require 'pry'
Pry.start
end
class ReConnect::Application < Sinatra::Base
helpers ReConnect::Helpers::ApplicationHelpers
set :environment, ENV["RACK_ENV"] ||= "production"
set :default_encoding, "UTF-8"
set :views, File.join('app', 'views')
set :haml, :format => :html5, :default_encoding => "UTF-8"
enable :sessions
before do
# Check if maintenance mode
if is_maintenance? && !maintenance_path_allowed?
next haml(:'maintenance/index', :layout => :layout_minimal, :locals => {
:title => "Maintenance"
})
end
# Set and check CSRF
csrf_set!
unless request.safe?
next halt 403, "CSRF failed" unless csrf_ok?
end
end
not_found do
haml :'errors/not_found', :locals => {
:title => t(:'errors/not_found/title'),
}
end
end
module ReConnect::Controllers
@@controllers_yml = YAML.load_file(File.join(ReConnect.root, 'config', 'controllers.yml'))
@@preload_controllers = @@controllers_yml["preload"]
@@active_controllers = @@controllers_yml["controllers"].map do |c|
{
:path => c["path"],
:controller => c["controller"],
}
end
def self.preload_controllers
@@preload_controllers
end
def self.active_controllers
@@active_controllers
end
def self.load_controllers
# Load in the controllers specified as preload
preload_controllers.each do |c|
cname = ReConnect::Utils.camel_case_to_snake_case(c)
require File.join(ReConnect.root, "app", "controllers", cname)
end
# Load in all the other controllers
active_controllers.each do |c|
cname = ReConnect::Utils.camel_case_to_snake_case(c[:controller])
require File.join(ReConnect.root, "app", "controllers", cname)
end
end
end
class ReConnect::Controllers::AccountIndexController < ReConnect::Controllers::ApplicationController
add_route :get, "/"
def index
unless logged_in?
session[:after_login] = request.path
flash :error, t(:must_log_in)
return redirect "/auth"
end
@title = t(:'account/title')
if request.get?
return haml(:'account/layout', :locals => {:title => @title}) do
haml(:'account/index', :locals => {
:title => @title
})
end
end
end
end
class ReConnect::Controllers::ApplicationController
extend ReConnect::Route
def initialize(app)
@app = app
end
def method_missing(meth, *args, &bk)
@app.instance_eval do
self.send(meth, *args, &bk)
end
end
end
class ReConnect::Controllers::AuthLoginController < ReConnect::Controllers::ApplicationController
add_route :get, "/"
add_route :post, "/"
def index
return redirect "/" if logged_in?
@title = t(:'auth/login/title')
if request.get?
return haml(:'auth/layout', :locals => {:title => @title}) do
haml(:'auth/login', :locals => {
:title => @title,
})
end
end
required = [
!request.params["email"].nil?,
request.params["email"] != "",
!request.params["password"].nil?,
request.params["password"] != "",
]
unless required.all?
flash :error, t(:required_field_missing)
return redirect request.path
end
email = request.params["email"].strip.downcase
password = request.params["password"]
# check if user exists with this email
user = ReConnect::Models::User.where(email: email).first
unless user
flash :error, t(:'auth/login/failure')
return redirect request.path
end
# check password confirmation
unless user.password_correct?(password)
flash :error, t(:'auth/login/failure')
return redirect request.path
end
# if we get here, user has successfully logged in
token = user.login!
session[:token] = token.token
if user.preferred_language
lang = user.decrypt(:preferred_language)
session[:lang] = lang
end
flash :success, t(:'auth/login/success', :site_name => site_name)
after_login = session.delete(:after_login)
return redirect after_login if after_login
redirect "/"
end
end
class ReConnect::Controllers::AuthLogoutController < ReConnect::Controllers::ApplicationController
add_route :get, "/"
def index
return redirect "/auth" unless logged_in?
token = current_token
token.valid = false
token.save
session.delete(:token)
flash :success, t(:'auth/logout/success')
redirect '/auth'
end
end
class ReConnect::Controllers::AuthSignupController < ReConnect::Controllers::ApplicationController
add_route :get, "/"
add_route :post, "/"
def index
return redirect "/" if logged_in?
@title = t(:'auth/signup/title')
if request.get?
return haml(:'auth/layout', :locals => {:title => @title}) do
haml(:'auth/signup', :locals => {
:title => @title,
})
end
end
required = [
!request.params["email"].nil?,
request.params["email"] != "",
!request.params["password"].nil?,
request.params["password"] != "",
!request.params["password_confirm"].nil?,
request.params["password_confirm"] != "",
]
unless required.all?
flash :error, t(:required_field_missing)
return redirect request.path
end
email = request.params["email"].strip.downcase
password = request.params["password"]
password_confirm = request.params["password_confirm"]
# check if user exists with this email
user_exists = ReConnect::Models::User.where(email: email).count.positive?
if user_exists
flash :error, t(:'auth/signup/email_already_used')
return redirect request.path
end
# check password confirmation
unless Rack::Utils.secure_compare(password, password_confirm)
flash :error, t(:'auth/signup/password_confirm_incorrect')
return redirect request.path
end
# if we get here, we can create the new user
user = ReConnect::Models::User.new(email: email)
user.password = password
user.save
token = user.login!
session[:token] = token.token
flash :success, t(:'auth/signup/success', :site_name => site_name)
after_login = session.delete(:after_login)
return redirect after_login if after_login
redirect "/"
end
end
class ReConnect::Controllers::IndexController < ReConnect::Controllers::ApplicationController
add_route :get, "/"
def index
@title = t(:'index/title')
haml :'index/index', :locals => {
:title => @title,
}
end
end
class ReConnect::Controllers::LanguageController < ReConnect::Controllers::ApplicationController
add_route :post, "/"
def index
lang = request.params['language']
if lang.nil? || lang == ""
lang = session[:lang] || ReConnect.default_language
end
lang = lang.to_s
unless ReConnect.languages.key?(lang)
flash :error, t(:'language/invalid')
return redirect request.referer
end
session[:lang] = lang
if logged_in?
u = current_user
u.encrypt(:preferred_language, lang)
u.save
end
redirect request.referer
end
end
class ReConnect::Controllers::StaticController < ReConnect::Controllers::ApplicationController
VENDOR_WHITELIST = [
"/purecss/build",
"/font-awesome/css",
"/font-awesome/fonts",
]
add_route :get, "/styles.css", :method => 'styles_css'
add_route :get, "/theme.css", :method => 'theme_css'
add_route :get, "/vendor/*", :method => 'vendor'
add_route :get, "/*"
def before
settings.views = {
:scss => File.join("assets", "scss"),
:default => File.join("app", "views"),
}
end
def styles_css
scss :styles, :style => :compressed
end
def theme_css
return 404 unless ReConnect.theme_dir
scss :theme, :style => :compressed
end
def vendor(splat)
fn = File.expand_path(splat, "/")
return 404 unless VENDOR_WHITELIST.map{|x| fn.start_with?(x)}.any?
path = File.join(ReConnect.root, "node_modules", fn)
return 404 unless File.file? path
send_file path
end
def index(splat)
fn = File.expand_path(splat, "/")
path = File.join(ReConnect.root, "public", fn)
if ReConnect.theme_dir
themepath = File.join(ReConnect.theme_dir, "public", fn)
path = themepath if File.file? themepath
end
return 404 unless File.file? path
send_file path
end
end
require 'rack'
require 'rbnacl'
require 'typhoeus'
require 'memoist'
module ReConnect::Crypto
@opslimit = 2**20
@memlimit = 2**24
@tokenlength = 32
class << self
extend Memoist
def keyderiv_url
unless ENV.key?("KEYDERIV_URL")
raise RuntimeError, "KEYDERIV_URL not specified in environment"
end
return ENV["KEYDERIV_URL"]
end
def keyderiv_secret
unless ENV.key?("KEYDERIV_SECRET")
raise RuntimeError, "KEYDERIV_SECRET not specified in environment"
end
return ReConnect::Utils.hex_to_bin(ENV["KEYDERIV_SECRET"])
end
memoize :keyderiv_url, :keyderiv_secret
#####
# Encryption/decryption
#####
def get_encryption_key(table, column, row)
box = RbNaCl::SecretBox.new(self.keyderiv_secret)
nonce = RbNaCl::Random.random_bytes(box.nonce_bytes)
body = {
"mode" => "encrypt",
"table" => table.to_s,
"column" => column.to_s,
"row" => row.to_s,
"nonce" => ReConnect::Utils.bin_to_hex(nonce),
}
out = Typhoeus.get(self.keyderiv_url, :method => :post, :body => body)
raise "Non-200 from keyderiv" unless out.response_code == 200
return box.decrypt(nonce, ReConnect::Utils.hex_to_bin(out.body.strip))
end
memoize :get_encryption_key
def decrypt(table, column, row, data)
return "" unless data
data = ReConnect::Utils.hex_to_bin(data)
key = self.get_encryption_key(table, column, row)
box = RbNaCl::SecretBox.new(ReConnect::Utils.hex_to_bin(key))
nonce = data[0..(box.nonce_bytes - 1)]
data = data[(box.nonce_bytes)..(data.length)]
return box.decrypt(nonce, data)
end
def encrypt(table, column, row, data)
key = self.get_encryption_key(table, column, row)
box = RbNaCl::SecretBox.new(ReConnect::Utils.hex_to_bin(key))
nonce = RbNaCl::Random.random_bytes(box.nonce_bytes)
return ReConnect::Utils.bin_to_hex(nonce + box.encrypt(nonce, data))
end
#####
# Indexes
#####
def get_index_key(table, column)
box = RbNaCl::SecretBox.new(self.keyderiv_secret)