add validators from raven24

This commit is contained in:
Benjamin Neff 2015-07-19 17:26:41 +02:00
parent 7be77154dc
commit 2301b1433e
19 changed files with 735 additions and 2 deletions

View file

@ -3,6 +3,7 @@ require "diaspora_federation/logging"
require "diaspora_federation/callbacks"
require "diaspora_federation/properties_dsl"
require "diaspora_federation/entity"
require "diaspora_federation/validators"
require "diaspora_federation/fetcher"

View file

@ -0,0 +1,32 @@
require "validation"
require "validation/rule/not_empty"
require "validation/rule/email"
require "validation/rule/regular_expression"
require "validation/rule/uri"
# +valid+ gem namespace
module Validation
# This module contains custom validation rules for various data field types.
# That includes types for which there are no provided rules by the +valid+ gem
# or types that are very specific to Diaspora* federation and need special handling.
# The rules are used inside the {DiasporaFederation::Validators validator classes}
# to perform basic sanity-checks on {DiasporaFederation::Entities federation entities}.
module Rule
end
end
require "diaspora_federation/validators/rules/birthday"
require "diaspora_federation/validators/rules/boolean"
require "diaspora_federation/validators/rules/guid"
require "diaspora_federation/validators/rules/not_nil"
require "diaspora_federation/validators/rules/public_key"
require "diaspora_federation/validators/rules/tag_count"
module DiasporaFederation
# Validators to perform basic sanity-checks on {DiasporaFederation::Entities federation entities}.
module Validators
end
end
require "diaspora_federation/validators/person_validator"
require "diaspora_federation/validators/profile_validator"

View file

@ -0,0 +1,17 @@
module DiasporaFederation
module Validators
class PersonValidator < Validation::Validator
include Validation
rule :guid, :guid
rule :diaspora_handle, %i(not_empty email)
rule :url, :u_r_i # WTF? :uri -> Uri -> "uninitialized constant Uri", :u_r_i -> URI -> \o/
rule :profile, :not_nil
rule :exported_key, :public_key
end
end
end

View file

@ -0,0 +1,22 @@
module DiasporaFederation
module Validators
class ProfileValidator < Validation::Validator
include Validation
rule :diaspora_handle, %i(not_empty email)
# the name must not contain a semicolon because of mentions
# @{<name> ; <handle>}
rule :first_name, regular_expression: {regex: /\A[^;]{,32}\z/}
rule :last_name, regular_expression: {regex: /\A[^;]{,32}\z/}
rule :tag_string, tag_count: {maximum: 5}
rule :birthday, :birthday
rule :searchable, :boolean
rule :nsfw, :boolean
end
end
end

View file

@ -0,0 +1,30 @@
require "date"
module Validation
module Rule
class Birthday
attr_reader :params
# no parameters
def initialize
@params = {}
end
def error_key
:birthday
end
def valid_value?(value)
return true if value.nil? || (value.is_a?(String) && value.empty?)
return true if value.is_a? Date
if value =~ /[0-9]{4}\-[0-9]{2}\-[0-9]{2}/
date_field = value.split("-").map(&:to_i)
return Date.valid_civil?(date_field[0], date_field[1], date_field[2])
end
false
end
end
end
end

View file

@ -0,0 +1,30 @@
module Validation
module Rule
class Boolean
attr_reader :params
# no parameters
def initialize
@params = {}
end
def error_key
:numeric
end
def valid_value?(value)
return false if value.nil?
if value.is_a?(String)
true if value =~ /\A(true|false|t|f|yes|no|y|n|1|0)\z/i
elsif value.is_a?(Fixnum)
true if value == 1 || value == 0
elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
true
else
false
end
end
end
end
end

View file

@ -0,0 +1,20 @@
module Validation
module Rule
class Guid
attr_reader :params
# no parameters
def initialize
@params = {}
end
def error_key
:guid
end
def valid_value?(value)
value.is_a?(String) && value.downcase =~ /\A[0-9a-z\-_@.:]{16,}\z/
end
end
end
end

View file

@ -0,0 +1,20 @@
module Validation
module Rule
class NotNil
attr_reader :params
# no parameters
def initialize
@params = {}
end
def error_key
:not_nil
end
def valid_value?(value)
!value.nil?
end
end
end
end

View file

@ -0,0 +1,24 @@
module Validation
module Rule
class PublicKey
attr_reader :params
# no parameters
def initialize
@params = {}
end
def error_key
:public_key
end
# allow both "PUBLIC KEY" and "RSA PUBLIC KEY"
def valid_value?(value)
(value.strip.start_with?("-----BEGIN PUBLIC KEY-----") &&
value.strip.end_with?("-----END PUBLIC KEY-----")) ||
(value.strip.start_with?("-----BEGIN RSA PUBLIC KEY-----") &&
value.strip.end_with?("-----END RSA PUBLIC KEY-----"))
end
end
end
end

View file

@ -0,0 +1,27 @@
module Validation
module Rule
# Rule for validating the number of tags in a string.
# Only the "#" characters will be counted.
class TagCount
attr_reader :params
# @param [Hash] params
# @option params [Fixnum] :maximum maximum allowed tag count
def initialize(params)
unless params.include?(:maximum) && params[:maximum].is_a?(Fixnum)
raise "A number has to be specified for :maximum"
end
@params = params
end
def error_key
:tag_count
end
def valid_value?(value)
value.count("#") <= params[:maximum]
end
end
end
end

View file

@ -5,12 +5,36 @@ def r_str
end
FactoryGirl.define do
sequence(:diaspora_handle) {|n| "person-#{n}-#{r_str}@localhost:3000" }
sequence(:public_key) { OpenSSL::PKey::RSA.generate(1024).public_key.export }
factory :person do
sequence(:diaspora_handle) {|n| "person-#{n}-#{r_str}@localhost:3000" }
diaspora_handle
url "http://localhost:3000/"
serialized_public_key OpenSSL::PKey::RSA.generate(1024).public_key.export
serialized_public_key { generate(:public_key) }
after(:create) do |u|
u.save
end
end
factory :person_entity, class: DiasporaFederation::Entities::Person do
guid UUID.generate :compact
diaspora_handle "testing@example.com"
url "http://localhost:3000/"
exported_key { generate(:public_key) }
profile {
DiasporaFederation::Entities::Profile.new(
FactoryGirl.attributes_for(:profile_entity, diaspora_handle: diaspora_handle))
}
end
factory :profile_entity, class: DiasporaFederation::Entities::Profile do
diaspora_handle "testing@example.com"
first_name "my_name"
last_name nil
tag_string "#i #love #tags"
birthday "1988-07-15"
searchable true
nsfw false
end
end

View file

@ -0,0 +1,41 @@
module DiasporaFederation
describe Validators::PersonValidator do
it "validates a well-formed instance" do
instance = OpenStruct.new(FactoryGirl.attributes_for(:person_entity))
validator = Validators::PersonValidator.new(instance)
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it_behaves_like "a diaspora_handle validator" do
let(:entity) { :person_entity }
let(:validator_class) { Validators::PersonValidator }
let(:property) { :diaspora_handle }
end
it_behaves_like "a guid validator" do
let(:entity) { :person_entity }
let(:validator_class) { Validators::PersonValidator }
let(:property) { :guid }
end
context "#exported_key" do
it "fails for malformed rsa key" do
instance = OpenStruct.new(FactoryGirl.attributes_for(:person_entity, exported_key: "ASDF"))
validator = Validators::PersonValidator.new(instance)
expect(validator).not_to be_valid
expect(validator.errors).to include(:exported_key)
end
it "must not be empty" do
instance = OpenStruct.new(FactoryGirl.attributes_for(:person_entity, exported_key: ""))
validator = Validators::PersonValidator.new(instance)
expect(validator).not_to be_valid
expect(validator.errors).to include(:exported_key)
end
end
end
end

View file

@ -0,0 +1,94 @@
module DiasporaFederation
describe Validators::ProfileValidator do
def profile_stub(data={})
OpenStruct.new(FactoryGirl.attributes_for(:profile_entity).merge(data))
end
it "validates a well-formed instance" do
validator = Validators::ProfileValidator.new(profile_stub)
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it_behaves_like "a diaspora_handle validator" do
let(:entity) { :profile_entity }
let(:validator_class) { Validators::ProfileValidator }
let(:property) { :diaspora_handle }
end
%i(first_name last_name).each do |prop|
describe "##{prop}" do
it "allowed to contain special chars" do
validator = Validators::ProfileValidator.new(profile_stub(prop => "cool name ©"))
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "must not exceed 32 chars" do
validator = Validators::ProfileValidator.new(profile_stub(prop => "abcdefghijklmnopqrstuvwxyz_aaaaaaaaaa"))
expect(validator).not_to be_valid
expect(validator.errors).to include(prop)
end
it "must not contain semicolons" do
validator = Validators::ProfileValidator.new(profile_stub(prop => "asdf;qwer;yxcv"))
expect(validator).not_to be_valid
expect(validator.errors).to include(prop)
end
end
end
describe "#tag_string" do
it "must not contain more than 5 tags" do
validator = Validators::ProfileValidator.new(
profile_stub(tag_string: "#i #have #too #many #tags #in #my #profile"))
expect(validator).not_to be_valid
expect(validator.errors).to include(:tag_string)
end
end
describe "#birthday" do
it "may be empty or nil" do
[nil, ""].each do |val|
validator = Validators::ProfileValidator.new(profile_stub(birthday: val))
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
end
it "may be a Date or date string" do
[Date.parse("2013-06-29"), "2013-06-29"].each do |val|
validator = Validators::ProfileValidator.new(profile_stub(birthday: val))
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
end
it "must not be an arbitrary string or other object" do
["asdf asdf", true, 1234].each do |val|
validator = Validators::ProfileValidator.new(profile_stub(birthday: val))
expect(validator).not_to be_valid
expect(validator.errors).to include(:birthday)
end
end
end
%i(searchable nsfw).each do |prop|
describe "##{prop}" do
it_behaves_like "a boolean validator" do
let(:entity) { :profile_entity }
let(:validator_class) { Validators::ProfileValidator }
let(:property) { prop }
end
end
end
end
end

View file

@ -0,0 +1,50 @@
describe Validation::Rule::Birthday do
it "will not accept parameters" do
validator = Validation::Validator.new({})
expect {
validator.rule(:birthday, birthday: {param: true})
}.to raise_error ArgumentError
end
context "validation" do
it "validates a date object" do
validator = Validation::Validator.new(OpenStruct.new(birthday: Date.new))
validator.rule(:birthday, :birthday)
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "validates a string" do
validator = Validation::Validator.new(OpenStruct.new(birthday: "2015-07-19"))
validator.rule(:birthday, :birthday)
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "validates an empty string" do
validator = Validation::Validator.new(OpenStruct.new(birthday: ""))
validator.rule(:birthday, :birthday)
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "validates nil" do
validator = Validation::Validator.new(OpenStruct.new(birthday: nil))
validator.rule(:birthday, :birthday)
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "fails for invalid date string" do
validator = Validation::Validator.new(OpenStruct.new(birthday: "i'm no date"))
validator.rule(:birthday, :birthday)
expect(validator).not_to be_valid
expect(validator.errors).to include(:birthday)
end
end
end

View file

@ -0,0 +1,70 @@
describe Validation::Rule::Boolean do
it "will not accept parameters" do
validator = Validation::Validator.new({})
expect {
validator.rule(:number, numeric: {param: true})
}.to raise_error ArgumentError
end
context "validation" do
context "strings" do
it "validates boolean-esque strings" do
%w(true false yes no t f y n 1 0).each do |str|
validator = Validation::Validator.new(OpenStruct.new(boolean: str))
validator.rule(:boolean, :boolean)
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
end
it "fails for non-boolean-esque strings" do
validator = Validation::Validator.new(OpenStruct.new(boolean: "asdf"))
validator.rule(:boolean, :boolean)
expect(validator).not_to be_valid
expect(validator.errors).to include(:boolean)
end
end
context "numbers" do
it "validates 0 and 1 to boolean" do
[0, 1].each do |num|
validator = Validation::Validator.new(OpenStruct.new(boolean: num))
validator.rule(:boolean, :boolean)
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
end
it "fails for all other numbers" do
validator = Validation::Validator.new(OpenStruct.new(boolean: 1234))
validator.rule(:boolean, :boolean)
expect(validator).not_to be_valid
expect(validator.errors).to include(:boolean)
end
end
context "boolean types" do
it "validates true and false" do
[true, false].each do |bln|
validator = Validation::Validator.new(OpenStruct.new(boolean: bln))
validator.rule(:boolean, :boolean)
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
end
end
it "fails for nil" do
validator = Validation::Validator.new(OpenStruct.new(boolean: nil))
validator.rule(:boolean, :boolean)
expect(validator).not_to be_valid
expect(validator.errors).to include(:boolean)
end
end
end

View file

@ -0,0 +1,59 @@
describe Validation::Rule::Guid do
it "will not accept parameters" do
validator = Validation::Validator.new({})
expect {
validator.rule(:guid, guid: {param: true})
}.to raise_error ArgumentError
end
context "validation" do
it "validates a string at least 16 chars long, consisting of [0-9a-f] (diaspora)" do
validator = Validation::Validator.new(OpenStruct.new(guid: "abcdef0123456789"))
validator.rule(:guid, :guid)
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "validates a long string with random characters and [-_@.:] (redmatrix)" do
validator = Validation::Validator.new(
OpenStruct.new(guid: "1234567890ABCDefgh_ijkl-mnopqrSTUVwxyz@example.com:3000"))
validator.rule(:guid, :guid)
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "fails if the string is too short" do
validator = Validation::Validator.new(OpenStruct.new(guid: "012345"))
validator.rule(:guid, :guid)
expect(validator).not_to be_valid
expect(validator.errors).to include(:guid)
end
it "fails if the string contains invalid chars" do
validator = Validation::Validator.new(OpenStruct.new(guid: "ghijklmnopqrstuvwxyz++"))
validator.rule(:guid, :guid)
expect(validator).not_to be_valid
expect(validator.errors).to include(:guid)
end
it "fails if the string is empty" do
validator = Validation::Validator.new(OpenStruct.new(guid: ""))
validator.rule(:guid, :guid)
expect(validator).not_to be_valid
expect(validator.errors).to include(:guid)
end
it "fails if the string is nil" do
validator = Validation::Validator.new(OpenStruct.new(guid: nil))
validator.rule(:guid, :guid)
expect(validator).not_to be_valid
expect(validator.errors).to include(:guid)
end
end
end

View file

@ -0,0 +1,51 @@
describe Validation::Rule::Guid do
it "will not accept parameters" do
validator = Validation::Validator.new({})
expect {
validator.rule(:key, public_key: {param: true})
}.to raise_error ArgumentError
end
context "validation" do
["PUBLIC KEY", "RSA PUBLIC KEY"].each do |key_type|
context key_type do
let(:prefix) { "-----BEGIN #{key_type}-----" }
let(:suffix) { "-----END #{key_type}-----" }
let(:key) { "#{prefix}\nAAAAAA==\n#{suffix}\n" }
it "validates an exported RSA key" do
validator = Validation::Validator.new(OpenStruct.new(key: key))
validator.rule(:key, :public_key)
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "strips whitespace" do
validator = Validation::Validator.new(OpenStruct.new(key: " \n #{key}\n \n "))
validator.rule(:key, :public_key)
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "fails if the prefix is missing" do
validator = Validation::Validator.new(OpenStruct.new(key: "\nAAAAAA==\n#{suffix}\n"))
validator.rule(:key, :public_key)
expect(validator).not_to be_valid
expect(validator.errors).to include(:key)
end
it "fails if the suffix is missing" do
validator = Validation::Validator.new(OpenStruct.new(key: "#{prefix}\nAAAAAA==\n\n"))
validator.rule(:key, :public_key)
expect(validator).not_to be_valid
expect(validator.errors).to include(:key)
end
end
end
end
end

View file

@ -0,0 +1,36 @@
describe Validation::Rule::TagCount do
it "requires a parameter" do
validator = Validation::Validator.new({})
expect {
validator.rule(:tags, :tag_count)
}.to raise_error ArgumentError
end
context "validation" do
let(:tag_str) { "#i #love #tags" }
it "validates less tags" do
validator = Validation::Validator.new(OpenStruct.new(tags: tag_str))
validator.rule(:tags, tag_count: {maximum: 5})
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "validates exactly as many tags" do
validator = Validation::Validator.new(OpenStruct.new(tags: tag_str))
validator.rule(:tags, tag_count: {maximum: 3})
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "fails for too many tags" do
validator = Validation::Validator.new(OpenStruct.new(tags: tag_str))
validator.rule(:tags, tag_count: {maximum: 1})
expect(validator).not_to be_valid
expect(validator.errors).to include(:tags)
end
end
end

View file

@ -0,0 +1,85 @@
def entity_stub(entity, property, val=nil)
instance = OpenStruct.new(FactoryGirl.attributes_for(entity))
instance.public_send("#{property}=", val) unless val.nil?
instance
end
shared_examples "a diaspora_handle validator" do
it "validates a well-formed diaspora_handle" do
validator = validator_class.new(entity_stub(entity, property))
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "must not be empty" do
validator = validator_class.new(entity_stub(entity, property, ""))
expect(validator).not_to be_valid
expect(validator.errors).to include(property)
end
it "must resemble an email address" do
validator = validator_class.new(entity_stub(entity, property, "i am a weird handle @@@ ### 12345"))
expect(validator).not_to be_valid
expect(validator.errors).to include(property)
end
end
shared_examples "a guid validator" do
it "validates a well-formed guid" do
validator = validator_class.new(entity_stub(entity, property))
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "validates a well-formed guid from redmatrix" do
validator = validator_class.new(entity_stub(entity, property, "1234567890ABCDefgh_ijkl-mnopQR@example.com:3000"))
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
it "must be at least 16 chars" do
validator = validator_class.new(entity_stub(entity, property, "aaaaaa"))
expect(validator).not_to be_valid
expect(validator.errors).to include(property)
end
it "must only contain [0-9a-z-_@.:]" do
validator = validator_class.new(entity_stub(entity, property, "zzz+-#*$$"))
expect(validator).not_to be_valid
expect(validator.errors).to include(property)
end
it "must not be empty" do
validator = validator_class.new(entity_stub(entity, property, ""))
expect(validator).not_to be_valid
expect(validator.errors).to include(property)
end
end
shared_examples "a boolean validator" do
it "validates a well-formed boolean" do
[true, "true", false, "false"].each do |val|
validator = validator_class.new(entity_stub(entity, property, val))
expect(validator).to be_valid
expect(validator.errors).to be_empty
end
end
it "must not be an arbitrary string or other object" do
["asdf", Time.zone.today, 1234].each do |val|
validator = validator_class.new(entity_stub(entity, property, val))
expect(validator).not_to be_valid
expect(validator.errors).to include(property)
end
end
end