Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Commit

Permalink
implement has_verified_identity
Browse files Browse the repository at this point in the history
  • Loading branch information
chadwhitacre committed May 9, 2016
1 parent a3c846b commit cd37633
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 0 deletions.
32 changes: 32 additions & 0 deletions sql/branch.sql
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,35 @@ CREATE TRIGGER enforce_email_for_participant_identity
BEFORE INSERT ON participant_identities
FOR EACH ROW
EXECUTE PROCEDURE fail_if_no_email();


-- participants.has_verified_identity

ALTER TABLE participants ADD COLUMN has_verified_identity bool NOT NULL DEFAULT false;

CREATE FUNCTION update_has_verified_identity() RETURNS trigger AS $$
BEGIN
UPDATE participants p
SET has_verified_identity=COALESCE((
SELECT is_verified
FROM participant_identities
WHERE participant_id = OLD.participant_id
AND is_verified
LIMIT 1
), false)
WHERE p.id = OLD.participant_id;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER propagate_is_verified_changes
AFTER UPDATE OF is_verified ON participant_identities
FOR EACH ROW
EXECUTE PROCEDURE update_has_verified_identity();

CREATE TRIGGER propagate_is_verified_removal
AFTER DELETE ON participant_identities
FOR EACH ROW
EXECUTE PROCEDURE update_has_verified_identity();

-- We don't need an INSERT trigger, because of the way the defaults play out.
94 changes: 94 additions & 0 deletions tests/py/test_participant_identities.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

from cryptography.fernet import InvalidToken
from gratipay.testing import Harness
from gratipay.models.participant import Participant
from gratipay.models.participant.mixins import identity, Identity
from gratipay.models.participant.mixins.identity import _validate_info, rekey
from gratipay.models.participant.mixins.identity import ParticipantIdentityInfoInvalid
from gratipay.models.participant.mixins.identity import ParticipantIdentitySchemaUnknown
from gratipay.security.crypto import EncryptingPacker, Fernet
from postgres.orm import ReadOnly
from psycopg2 import IntegrityError
from pytest import raises

Expand Down Expand Up @@ -259,6 +261,98 @@ def test_ci_still_logs_an_event_when_noop(self):
self.assert_events(crusher.id, [None], [self.TTO], ['clear identity'])


# hvi - has_verified_identity

def test_hvi_defaults_to_false(self):
crusher = self.make_participant('crusher')
assert crusher.has_verified_identity is False

def test_hvi_is_read_only(self):
crusher = self.make_participant('crusher')
with raises(ReadOnly):
crusher.has_verified_identity = True

def test_hvi_becomes_true_when_an_identity_is_verified(self):
crusher = self.make_participant('crusher')
crusher.store_identity_info(self.TTO, 'nothing-enforced', {})
crusher.set_identity_verification(self.TTO, True)
assert Participant.from_username('crusher').has_verified_identity

def test_hvi_becomes_false_when_the_identity_is_unverified(self):
crusher = self.make_participant('crusher')
crusher.store_identity_info(self.TTO, 'nothing-enforced', {})
crusher.set_identity_verification(self.TTO, True)
crusher.set_identity_verification(self.TTO, False)
assert not Participant.from_username('crusher').has_verified_identity

def test_hvi_stays_true_when_a_secondary_identity_is_verified(self):
crusher = self.make_participant('crusher')
crusher.store_identity_info(self.USA, 'nothing-enforced', {})
crusher.set_identity_verification(self.USA, True)
crusher.store_identity_info(self.TTO, 'nothing-enforced', {})
crusher.set_identity_verification(self.TTO, True)
assert Participant.from_username('crusher').has_verified_identity

def test_hvi_stays_true_when_the_secondary_identity_is_unverified(self):
crusher = self.make_participant('crusher')
crusher.store_identity_info(self.USA, 'nothing-enforced', {})
crusher.set_identity_verification(self.USA, True)
crusher.store_identity_info(self.TTO, 'nothing-enforced', {})
crusher.set_identity_verification(self.TTO, True)
crusher.set_identity_verification(self.TTO, False)
assert Participant.from_username('crusher').has_verified_identity

def test_hvi_goes_back_to_false_when_both_are_unverified(self):
crusher = self.make_participant('crusher')
crusher.store_identity_info(self.USA, 'nothing-enforced', {})
crusher.store_identity_info(self.TTO, 'nothing-enforced', {})
crusher.set_identity_verification(self.TTO, True)
crusher.set_identity_verification(self.USA, True)
crusher.set_identity_verification(self.TTO, False)
crusher.set_identity_verification(self.USA, False)
assert not Participant.from_username('crusher').has_verified_identity

def test_hvi_changes_are_scoped_to_a_participant(self):
crusher = self.make_participant('crusher')
crusher.store_identity_info(self.USA, 'nothing-enforced', {})

bruiser = self.make_participant('bruiser')
bruiser.store_identity_info(self.USA, 'nothing-enforced', {})

crusher.set_identity_verification(self.USA, True)

assert Participant.from_username('crusher').has_verified_identity
assert not Participant.from_username('bruiser').has_verified_identity

def test_hvi_resets_when_identity_is_cleared(self):
crusher = self.make_participant('crusher')
crusher.store_identity_info(self.TTO, 'nothing-enforced', {})
crusher.set_identity_verification(self.TTO, True)
crusher.clear_identity(self.TTO)
assert not Participant.from_username('crusher').has_verified_identity

def test_hvi_doesnt_reset_when_penultimate_identity_is_cleared(self):
crusher = self.make_participant('crusher')
crusher.store_identity_info(self.USA, 'nothing-enforced', {})
crusher.set_identity_verification(self.USA, True)
crusher.store_identity_info(self.TTO, 'nothing-enforced', {})
crusher.set_identity_verification(self.TTO, True)
crusher.set_identity_verification(self.TTO, False)
crusher.clear_identity(self.TTO)
assert Participant.from_username('crusher').has_verified_identity

def test_hvi_does_reset_when_both_identities_are_cleared(self):
crusher = self.make_participant('crusher')
crusher.store_identity_info(self.USA, 'nothing-enforced', {})
crusher.store_identity_info(self.TTO, 'nothing-enforced', {})
crusher.set_identity_verification(self.USA, True)
crusher.set_identity_verification(self.TTO, True)
crusher.set_identity_verification(self.TTO, False)
crusher.set_identity_verification(self.USA, False)
crusher.clear_identity(self.TTO)
assert not Participant.from_username('crusher').has_verified_identity


# fine - fail_if_no_email

def test_fine_fails_if_no_email(self):
Expand Down

0 comments on commit cd37633

Please sign in to comment.