From 638acc59a241e141cf0fc9ccbf4e3c5578b98f0c Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 4 Jul 2016 20:19:21 +0200 Subject: Fix db:migrate and similar tasks We saw errors from duplicate loading of LocalEmail and LoginFormatValidation. The latter resulted in a crash. In an attempt to ensure all subclasses of Couchrest::Model::Base are loaded Couchrest::Model::Utils::Migrate requires all files in app/models. We have an extension that does the same for the engines. During this process LoginFormatValidation and LocalEmail were autoloaded when 'identity' was required. Afterwards they were required again. It looks like rails' autoload mechanism does not play nicely with require. So to make sure they are not autoloaded first move the concerns and helper classes into the lib directory and require them explicitly. --- app/models/email.rb | 31 ---------- app/models/identity.rb | 2 + app/models/local_email.rb | 66 --------------------- app/models/login_format_validation.rb | 21 ------- app/models/user.rb | 2 + .../app/models/account_extension/tickets.rb | 13 ----- .../config/initializers/account_lifecycle.rb | 2 + engines/support/lib/account_extension/tickets.rb | 15 +++++ lib/email.rb | 31 ++++++++++ lib/local_email.rb | 67 ++++++++++++++++++++++ lib/login_format_validation.rb | 21 +++++++ 11 files changed, 140 insertions(+), 131 deletions(-) delete mode 100644 app/models/email.rb delete mode 100644 app/models/local_email.rb delete mode 100644 app/models/login_format_validation.rb delete mode 100644 engines/support/app/models/account_extension/tickets.rb create mode 100644 engines/support/lib/account_extension/tickets.rb create mode 100644 lib/email.rb create mode 100644 lib/local_email.rb create mode 100644 lib/login_format_validation.rb diff --git a/app/models/email.rb b/app/models/email.rb deleted file mode 100644 index 4090275..0000000 --- a/app/models/email.rb +++ /dev/null @@ -1,31 +0,0 @@ -class Email < String - include ActiveModel::Validations - - validates :email, - :format => { - :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/, #local part of email is case-sensitive, so allow uppercase letter. - :message => "needs to be a valid email address" - } - - # Make sure we can call Email.new(nil) and get an invalid email address - def initialize(s) - super(s.to_s) - end - - def to_partial_path - "emails/email" - end - - def to_param - to_s - end - - def email - self - end - - def handle - self.split('@').first - end - -end diff --git a/app/models/identity.rb b/app/models/identity.rb index f987e4e..92f8f7a 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -1,3 +1,5 @@ +require 'login_format_validation' +require 'local_email' # # Identity states: # diff --git a/app/models/local_email.rb b/app/models/local_email.rb deleted file mode 100644 index ded7baf..0000000 --- a/app/models/local_email.rb +++ /dev/null @@ -1,66 +0,0 @@ -class LocalEmail < Email - - BLACKLIST_FROM_RFC2142 = [ - 'postmaster', 'hostmaster', 'domainadmin', 'webmaster', 'www', - 'abuse', 'noc', 'security', 'usenet', 'news', 'uucp', - 'ftp', 'sales', 'marketing', 'support', 'info' - ] - - def self.domain - APP_CONFIG[:domain] - end - - validates :email, - :format => { - :with => /@#{domain}\Z/i, - :message => "needs to end in @#{domain}" - } - - validate :handle_allowed - - def initialize(s) - super - append_domain_if_needed - end - - def to_key - [handle] - end - - def domain - LocalEmail.domain - end - - protected - - def append_domain_if_needed - unless self.index('@') - self << '@' + domain - end - end - - def handle_allowed - errors.add(:handle, "is reserved.") if handle_reserved? - end - - def handle_reserved? - # *ARRAY in a case statement tests if ARRAY includes the handle. - case handle - when *APP_CONFIG[:handle_blacklist] - true - when *APP_CONFIG[:handle_whitelist] - false - when *BLACKLIST_FROM_RFC2142 - true - else - handle_in_passwd? - end - end - - def handle_in_passwd? - Etc.getpwnam(handle).present? - rescue ArgumentError - # handle was not found - return false - end -end diff --git a/app/models/login_format_validation.rb b/app/models/login_format_validation.rb deleted file mode 100644 index c1fcf70..0000000 --- a/app/models/login_format_validation.rb +++ /dev/null @@ -1,21 +0,0 @@ -module LoginFormatValidation - extend ActiveSupport::Concern - - #TODO: Probably will replace this. Playing with using it for aliases too, but won't want it connected to login field. - - included do - # Have multiple regular expression validations so we can get specific error messages: - validates :login, - :format => { :with => /\A.{2,}\z/, - :message => "Must have at least two characters"} - validates :login, - :format => { :with => /\A[a-z\d_\.-]+\z/, - :message => "Only lowercase letters, digits, . - and _ allowed."} - validates :login, - :format => { :with => /\A[a-z].*\z/, - :message => "Must begin with a lowercase letter"} - validates :login, - :format => { :with => /\A.*[a-z\d]\z/, - :message => "Must end with a letter or digit"} - end -end diff --git a/app/models/user.rb b/app/models/user.rb index cb093cf..206c0df 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,3 +1,5 @@ +require 'login_format_validation' + class User < CouchRest::Model::Base include LoginFormatValidation diff --git a/engines/support/app/models/account_extension/tickets.rb b/engines/support/app/models/account_extension/tickets.rb deleted file mode 100644 index f38d5fd..0000000 --- a/engines/support/app/models/account_extension/tickets.rb +++ /dev/null @@ -1,13 +0,0 @@ -module AccountExtension::Tickets - extend ActiveSupport::Concern - - def destroy_with_tickets(destroy_identities=false) - Ticket.destroy_all_from(self.user) - destroy_without_tickets(destroy_identities) - end - - included do - alias_method_chain :destroy, :tickets - end - -end diff --git a/engines/support/config/initializers/account_lifecycle.rb b/engines/support/config/initializers/account_lifecycle.rb index d9f04c1..9060757 100644 --- a/engines/support/config/initializers/account_lifecycle.rb +++ b/engines/support/config/initializers/account_lifecycle.rb @@ -1,3 +1,5 @@ +require 'account_extension/tickets' + ActiveSupport.on_load(:account) do include AccountExtension::Tickets end diff --git a/engines/support/lib/account_extension/tickets.rb b/engines/support/lib/account_extension/tickets.rb new file mode 100644 index 0000000..63f4873 --- /dev/null +++ b/engines/support/lib/account_extension/tickets.rb @@ -0,0 +1,15 @@ +module AccountExtension + module Tickets + extend ActiveSupport::Concern + + def destroy_with_tickets(destroy_identities=false) + Ticket.destroy_all_from(self.user) + destroy_without_tickets(destroy_identities) + end + + included do + alias_method_chain :destroy, :tickets + end + + end +end diff --git a/lib/email.rb b/lib/email.rb new file mode 100644 index 0000000..4090275 --- /dev/null +++ b/lib/email.rb @@ -0,0 +1,31 @@ +class Email < String + include ActiveModel::Validations + + validates :email, + :format => { + :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/, #local part of email is case-sensitive, so allow uppercase letter. + :message => "needs to be a valid email address" + } + + # Make sure we can call Email.new(nil) and get an invalid email address + def initialize(s) + super(s.to_s) + end + + def to_partial_path + "emails/email" + end + + def to_param + to_s + end + + def email + self + end + + def handle + self.split('@').first + end + +end diff --git a/lib/local_email.rb b/lib/local_email.rb new file mode 100644 index 0000000..7c592e1 --- /dev/null +++ b/lib/local_email.rb @@ -0,0 +1,67 @@ +require 'email' +class LocalEmail < Email + + BLACKLIST_FROM_RFC2142 = [ + 'postmaster', 'hostmaster', 'domainadmin', 'webmaster', 'www', + 'abuse', 'noc', 'security', 'usenet', 'news', 'uucp', + 'ftp', 'sales', 'marketing', 'support', 'info' + ] + + def self.domain + APP_CONFIG[:domain] + end + + validates :email, + :format => { + :with => /@#{domain}\Z/i, + :message => "needs to end in @#{domain}" + } + + validate :handle_allowed + + def initialize(s) + super + append_domain_if_needed + end + + def to_key + [handle] + end + + def domain + LocalEmail.domain + end + + protected + + def append_domain_if_needed + unless self.index('@') + self << '@' + domain + end + end + + def handle_allowed + errors.add(:handle, "is reserved.") if handle_reserved? + end + + def handle_reserved? + # *ARRAY in a case statement tests if ARRAY includes the handle. + case handle + when *APP_CONFIG[:handle_blacklist] + true + when *APP_CONFIG[:handle_whitelist] + false + when *BLACKLIST_FROM_RFC2142 + true + else + handle_in_passwd? + end + end + + def handle_in_passwd? + Etc.getpwnam(handle).present? + rescue ArgumentError + # handle was not found + return false + end +end diff --git a/lib/login_format_validation.rb b/lib/login_format_validation.rb new file mode 100644 index 0000000..c1fcf70 --- /dev/null +++ b/lib/login_format_validation.rb @@ -0,0 +1,21 @@ +module LoginFormatValidation + extend ActiveSupport::Concern + + #TODO: Probably will replace this. Playing with using it for aliases too, but won't want it connected to login field. + + included do + # Have multiple regular expression validations so we can get specific error messages: + validates :login, + :format => { :with => /\A.{2,}\z/, + :message => "Must have at least two characters"} + validates :login, + :format => { :with => /\A[a-z\d_\.-]+\z/, + :message => "Only lowercase letters, digits, . - and _ allowed."} + validates :login, + :format => { :with => /\A[a-z].*\z/, + :message => "Must begin with a lowercase letter"} + validates :login, + :format => { :with => /\A.*[a-z\d]\z/, + :message => "Must end with a letter or digit"} + end +end -- cgit v1.2.3 From 6b08e8c89d26225786736ad69ba335b275a1a048 Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 4 Jul 2016 21:26:46 +0200 Subject: [doc] update install documentation --- README.md | 46 ++++++++++++++++++++++++++++------------------ doc/DEPLOY.md | 9 +++++++-- doc/DEVELOP.md | 27 +++++++++++++++++---------- doc/TROUBLESHOOT.md | 14 ++++++++++---- 4 files changed, 62 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index b6f3d1b..6d8a504 100644 --- a/README.md +++ b/README.md @@ -46,20 +46,6 @@ External docs: * Overview of the main code repositories * Ideas for discrete, unclaimed development projects that would greatly benefit the LEAP ecosystem. -Known problems ---------------------------- - -* Client certificates are generated without a CSR. The problem is that - this makes the web application extremely vulnerable to denial of - service attacks. This is not an issue unless the provider enables the - possibility of anonymously fetching a client certificate without - authenticating first. - -* By its very nature, the user database is vulnerable to enumeration - attacks. These are very hard to prevent, because our protocol is - designed to allow query of a user database via proxy in order to - provide network perspective. - Installation --------------------------- @@ -69,14 +55,24 @@ these instructions: ### Install system requirements +You'll need git, ruby (2.1.5), couchdb and bundler installed. +On a recent debian based distribution run + sudo apt install git ruby couchdb bundler -Your actual requirements might differ if you are running an older OS that defaults to ruby 1.9. +For other operation systems please lookup the install instructions of these +tools. ### Download source +We host our own git repository. In order to create a local clone run + git clone --recursive git://leap.se/leap_web +The repo is mirrored on github and we accept pull requests there: + + https://github.com/leapcode/leap_web + ### Install required ruby libraries cd leap_web @@ -88,13 +84,13 @@ have sudo, run ``bundle`` as root. ### Installation for development purposes -Please see `doc/DEVELOP.md` for further required steps when installing +Please see `doc/DEVELOP.md` for details about installing leap_web for development purposes. -Configuration +Configuration for Production ---------------------------- -The configuration file `config/defaults.yml` providers good defaults for +The configuration file `config/defaults.yml` provides good defaults for most values. You can override these defaults by creating a file `config/config.yml`. @@ -167,3 +163,17 @@ To run an individual test: or ruby -Itest certs/test/unit/client_certificate_test.rb +Known problems +--------------------------- + +* Client certificates are generated without a CSR. The problem is that + this makes the web application extremely vulnerable to denial of + service attacks. This is not an issue unless the provider enables the + possibility of anonymously fetching a client certificate without + authenticating first. + +* By its very nature, the user database is vulnerable to enumeration + attacks. These are very hard to prevent, because our protocol is + designed to allow query of a user database via proxy in order to + provide network perspective. + diff --git a/doc/DEPLOY.md b/doc/DEPLOY.md index 33d5598..4d59701 100644 --- a/doc/DEPLOY.md +++ b/doc/DEPLOY.md @@ -1,5 +1,10 @@ # Deployment # +LEAP Web is provisioned and run as part of the overall [LEAP platform](https://leap.se/en/docs/platform). +We strongly recomment using the whole Platform and following its instructions. +If you want to directly deploy the webapp never the less these instructions are +for you. + These instructions are targeting a Debian GNU/Linux system. You might need to change the commands to match your own needs. @@ -10,9 +15,9 @@ change the commands to match your own needs. The following packages need to be installed: * git -* ruby1.9 -* rubygems1.9 +* ruby (2.1.5) * couchdb (if you want to use a local couch) +* bundler ### Setup Capistrano ### diff --git a/doc/DEVELOP.md b/doc/DEVELOP.md index cdd0867..e0c07d6 100644 --- a/doc/DEVELOP.md +++ b/doc/DEVELOP.md @@ -7,17 +7,24 @@ control checks. This is handy for local development. However, there is the risk that running tests with Couch in Admin Party yields false results. -You have two options: +We recommend keeping the default CouchDB configuration locally and testing +the more complex setup with access control in Continuous Integration. -1) Use Admin Party and accept the risk -2) Stop Admin Party by creating user accounts & security docs by running the -following script: +Please see .travis.yml for the configuration of our CI runs. - test/travis/setup_couch.sh +In order to prepare you local couch for development run +``` +bin/rake db:rotate +bin/rake db:migrate +``` -### Database configuration +### Customized database configuration (advanced) + +If you want to stop Admin Party mode you need to create user accounts & +security docs. You can use the following script as a guideline: + test/travis/setup_couch.sh -Copy & adapt the default database configuration: +Afterwards copy & adapt the default database configuration: ``` mv config/couchdb.example.yml config/couchdb.yml @@ -37,14 +44,14 @@ Some tips on modifying the views: ## Engines ## -Leap Web contains some. They live in their own subdirectory and are included through bundler via their path. This way changes to the engines immediately affect the server as if they were in the main `app` directory. +We use engines to separate optional functionality from the core. They live in their own subdirectory and are included through bundler via their path. This way changes to the engines immediately affect the server as if they were in the main `app` directory. Currently Leap Web includes 2 Engines: * [support](https://github.com/leapcode/leap_web/blob/master/engines/support) - Help ticket management * [billing](https://github.com/leapcode/leap_web/blob/master/engines/billing) - Billing System -## Creating a new engine ## +## Creating a new engine (advanced) ## If you want to add functionality to the webapp but keep it easy to remove you might consider adding an engine. This only makes sense if your engine really is a plugin - so no other pieces of code depend on it. @@ -99,7 +106,7 @@ For example: visit robot_path(@robot, :locale => nil) end -## Debugging +## Debugging Production (advanced) Sometimes bugs only show up when deployed to the live production server. Debugging can be tricky, because the open source mod_passenger does not support debugger. You can't just run diff --git a/doc/TROUBLESHOOT.md b/doc/TROUBLESHOOT.md index f3db006..0e2957d 100644 --- a/doc/TROUBLESHOOT.md +++ b/doc/TROUBLESHOOT.md @@ -13,15 +13,19 @@ Here are some less common issues you might run into when installing Leap Web. Make sure bundler is installed. `gem list bundler` should list `bundler`. You also need to be able to access the `bundler` executable in your PATH. -## Outdated version of rubygems ## +## Incompatible ruby version ## -### Error Messages ### +### Detecting the problem ### +The rubyversion we use for development and testing is noted in the file + + .ruby-version -`bundler requires rubygems >= 1.3.6` +It should match what `ruby --version` prints. ### Solution ### -`gem update --system` will install the latest rubygems +Install the matching ruby version. For some operation systems this may require +the use of rbenv or rvm. ## Missing development tools ## @@ -42,5 +46,7 @@ Some gem dependencies might not compile because they lack the needed c libraries ### Solution ### Install the libraries in question including their development files. +Usually the missing library is mentioned in the error message. Searching the +internet for similar errors is a good starting point aswell. -- cgit v1.2.3 From 78d1b0daae8b7d667c8cb5a8da0b1efba029801f Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 4 Jul 2016 22:02:05 +0200 Subject: [doc] make sure to mention develop branch in dev docs --- README.md | 12 +++++++++++- doc/DEVELOP.md | 14 ++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d8a504..e68c117 100644 --- a/README.md +++ b/README.md @@ -68,14 +68,24 @@ tools. We host our own git repository. In order to create a local clone run git clone --recursive git://leap.se/leap_web + cd leap_web The repo is mirrored on github and we accept pull requests there: https://github.com/leapcode/leap_web +### Pick branch (development only) + +We use the master branch for the stable version deployed to production. +Development usually happens on the develop branch. So for development you +want to run + + git checkout origin/develop -b develop + +This will create a local branch called develop based on our develop branch. + ### Install required ruby libraries - cd leap_web bundle --binstubs Typically, you run ``bundle`` as a normal user and it will ask you for a diff --git a/doc/DEVELOP.md b/doc/DEVELOP.md index e0c07d6..97ecd25 100644 --- a/doc/DEVELOP.md +++ b/doc/DEVELOP.md @@ -1,5 +1,19 @@ # Development # +## Branches + +We use the 'master' branch to hold the version currently deployed to the +production servers. Only hotfixes are applied here. + +Most of development happens based upon the 'develop' branch. So unless +you are investigating a specific issue that occured in production you +probably want to base your changes on 'develop': +``` +git checkout origin/develop -b my-new-feature +``` +This will create a new branch called 'my-new-feature' based on the develop +branch from the origin remote. + ## Setting up the local CouchDB CouchDB operates in Admin Party by default, meaning there are no access -- cgit v1.2.3 From bf77b0b1f53753ba239ef8c2668bc76603cd96e5 Mon Sep 17 00:00:00 2001 From: Azul Date: Tue, 5 Jul 2016 09:18:43 +0200 Subject: fix email unit test - need to require now --- test/unit/email_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/email_test.rb b/test/unit/email_test.rb index e858bd5..739b43e 100644 --- a/test/unit/email_test.rb +++ b/test/unit/email_test.rb @@ -1,4 +1,5 @@ require 'test_helper' +require 'email' class EmailTest < ActiveSupport::TestCase -- cgit v1.2.3