Skip to content

Commit

Permalink
Add Trust Bonus Tab With BrightID Integration (gitcoinco#7333)
Browse files Browse the repository at this point in the history
* add constant pot changing mechanism, add analytics script

* remove current designation, add a couple print test statements

* add constant pot changing mechanism, add analytics script

* remove current designation, add a couple print test statements

* add pot dealer, add analytics function & script, add additional calculation data

* update clr estimate increases using a log function, fix adityas comments

* add updated verification status

* fix up adityas 2nd round of requests

* moved analytics function to analytics_clr.py, import methods

* add brightId verfication into clr

* Add a Trust Score tab to the users profile

* Scaffold UI for connecting to BrightID

* Migrations to add, populate and require a brightid identifier on Profile

* Add BrightID UUID to Profile and display on Trust tab as proof-of-concept

* Get status of the users bright id connection when loading the Trust tab

* Display different bright id interface based on auth status & connection

* Only show Trust tab for logged in users own profile

* Get BrightID Sponsorship creation working

* Move BrightID status call into a utility function called from
  the view
* Create function for sponsorship application and import required
  crypto dependencies to do the signing, plus keys in env for signing
* If a user is detected as connected, but not sponsored, automatically
  apply a sponsorhip from Gitcoin to their user

* Create modal poppup for Connect phase of BrightID integration

* Add BrightID logo and explanatory copy
* Show app store buttons with links to download the app
* Deep link to connection URL and render the appropriate
  QR code for connection with the app

* Add modal for verification phase of BrightID Integrations

* When the user is connected by not verified, show an appropriate
  modal when the user clicks the Verify button
* Load data on upcoming BrightID verification calls from the database
  and display them on the modal
* Resolve migration conflicts and add a context tag to the UpcomingDates
  model; use tag to filter appropriate BrightID related calls

* Handle the case when the user is verified

* Clean up copy & implement full Trust Bonus UI design

* Responsive design tweaks for Trust Bonus tab

* Fix migration conflicts & add column for BrightID verification status

* Write command for fetching and updated verified users in local DB

* Schedule BrightID status updater to run at 55th minute of every hour

* Cleanup URLs in BrightID Utils

* Avoid duplicating objectifySerialized utility JS function

* Resolve javascript linting errors in BrightID modals

* Add root level package-lock.json file for dev environment commands

* Compress app store logo svg file

* Fix various spacing an indentation issues in BrightID integration

* Cleanup return condition for brightid sponsorhip assignment

* recreate migration + run pylint

* uuid migration fix

* clr tweaks

Co-authored-by: frankchen07 <frankchen07@gmail.com>
Co-authored-by: Aditya Anand M C <aditya.anandmc@gmail.com>
  • Loading branch information
3 people authored and zoek1 committed Sep 14, 2020
1 parent 2938589 commit 86886c1
Show file tree
Hide file tree
Showing 26 changed files with 5,886 additions and 77 deletions.
4 changes: 3 additions & 1 deletion app/app/local.env
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,6 @@ ETHERSCAN_API_KEY=
FORTMATIC_LIVE_KEY=
FORTMATIC_TEST_KEY=

PYPL_CLIENT_ID=
PYPL_CLIENT_ID=

BRIGHTID_PRIVATE_KEY=
2 changes: 2 additions & 0 deletions app/app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -846,3 +846,5 @@ def callback(request):
PHONE_SALT = env('PHONE_SALT', default='THIS_IS_INSECURE_CHANGE_THIS_PLEASE')

HYPERCHARGE_BOUNTIES_PROFILE_HANDLE = env('HYPERCHARGE_BOUNTIES_PROFILE', default='gitcoinbot')

BRIGHTID_PRIVATE_KEY = env('BRIGHTID_PRIVATE_KEY', default='wrong-private-key')
1 change: 1 addition & 0 deletions app/assets/v2/images/app_stores/apple_app_store.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/project_logos/brightid.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions app/assets/v2/js/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -469,3 +469,16 @@ function applyCartMenuStyles() {
}
}
}

// Turn form data pulled form page into a JS object
function objectifySerialized(data) {
let objectData = {};

for (let i = 0; i < data.length; i++) {
const item = data[i];

objectData[item.name] = item.value;
}

return objectData;
}
12 changes: 0 additions & 12 deletions app/assets/v2/js/grants/funding.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,15 +255,3 @@ function toggleSideCart() {
$('#funding-card').toggleClass('d-none');
$('#funding-card').toggleClass('d-lg-block');
}

function objectifySerialized(data) {
let objectData = {};

for (let i = 0; i < data.length; i++) {
const item = data[i];

objectData[item.name] = item.value;
}

return objectData;
}
136 changes: 136 additions & 0 deletions app/assets/v2/js/pages/profile-trust.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
let hasGeneratedBrightIdQRCode = false;

let brightIDCalls = [];

$(document).ready(function() {

$('.js-upcomingBrightIDCalls-form').each(function() {
const formData = objectifySerialized($(this).serializeArray());

brightIDCalls.push(formData);
});
});

let show_brightid_connect_modal = function(brightid_uuid) {
const brightIdLink = `https://app.brightid.org/link-verification/http:%2f%2fnode.brightid.org/Gitcoin/${brightid_uuid}`;
const brightIdAppLink = `brightid://link-verification/http:%2f%2fnode.brightid.org/Gitcoin/${brightid_uuid}`;

const content = $.parseHTML(
`<div id="connect_brightid_modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content px-4 py-3">
<div class="col-12">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="col-12 pt-2 pb-2 text-center">
<img src="/static/v2/images/project_logos/brightid.png" alt="BrightID Logo" width="100">
<h2 class="font-title mt-2">Connect With BrightID</h2>
</div>
<div class="col-12 pt-2">
<p>
BrightID is a digital identity solution that ensures accounts in any application are created by real humans; each user is unique and only has one account.
<a href="https://www.brightid.org/" target="_blank">Learn More</a>.
</p>
<p>
To increase your Trust Bonus using BrightID, you must first get connected. Follow these steps:
</p>
<p>
<strong>Step 1</strong>: Download the BrightID App on your mobile device<br />
<a href="https://apps.apple.com/us/app/brightid/id1428946820">
<img src="/static/v2/images/app_stores/apple_app_store.svg" width="100">
</a>
<a href="https://play.google.com/store/apps/details?id=org.brightid">
<img src="/static/v2/images/app_stores/google_play_store.png" width="125">
</a>
</p>
<p>
<strong>Step 2</strong>: Connect BrightID to Gitcoin by scanning this QR code
from the BrightID app, or <a href="${brightIdLink}">clicking here</a> from your mobile device.
<div style="display: flex; justify-content: center; text-align: center;" id="qrcode"></div>
</p>
<div class="col-12 my-4 text-center">
<a href="" class="btn btn-gc-blue px-5 mb-2 mx-2">Done Connecting</a>
</div>
</div>
</div>
</div>
</div>`);

$(content).appendTo('body');
$('#connect_brightid_modal').bootstrapModal('show');

// avoid duplicate QR Codes if user presses button multiple times
if (!hasGeneratedBrightIdQRCode) {
const element = document.getElementById('qrcode');
const qrCodeData = {
text: brightIdAppLink,
width: 100,
height: 100
};

new QRCode(element, qrCodeData); // eslint-disable-line

hasGeneratedBrightIdQRCode = true;
}
};

let show_brightid_verify_modal = function(brightid_uuid) {
let callsMarkup = '';

for (let index = 0; index < brightIDCalls.length; index++) {
const call = brightIDCalls[index];
const callDate = new Date(parseFloat(call.date) * 1000);

callsMarkup = `${callsMarkup}
<div class="row mb-3">
<div class="col-md-7">
<strong>${call.title}</strong><br />
${callDate.toLocaleString()}
</div>
<div class="col-md-5">
<a href="${call.url}" target="_blank" class="btn btn-gc-blue px-5 mb-2 mx-1">Register</a>
</div>
</div>
`;
}

const content = $.parseHTML(
`<div id="verify_brightid_modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content px-4 py-3">
<div class="col-12">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="col-12 pt-2 pb-2 text-center">
<img src="/static/v2/images/project_logos/brightid.png" alt="BrightID Logo" width="100">
<h2 class="font-title mt-2">Verify Your BrightID</h2>
</div>
<div class="col-12 pt-2">
<p>
BrightID is a digital identity solution that ensures accounts in any application are created by real humans; each user is unique and only has one account.
<a href="https://www.brightid.org/" target="_blank">Learn More</a>.
</p>
<p>
Now that you've connected your BrightID, you need to get verified by
by connecting with other real humans.
</p>
<p>
<strong>Join a Gitcoin + BrightID Zoom Community Call</strong><br />
<font size="2" color="grey">
You can learn more about how BrightID works and make connections that will help you get verified on the Zooom Community Call.
Register for one of the events.
</font>
${callsMarkup}
</p>
</div>
</div>
</div>
</div>`);

$(content).appendTo('body');
$('#verify_brightid_modal').bootstrapModal('show');
};
69 changes: 69 additions & 0 deletions app/dashboard/brightid_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import base64
import json
import time

from django.conf import settings

import ed25519
import requests


def get_brightid_status(brightid_uuid):
brightIDUrl = 'http://node.brightid.org/brightid/v4/verifications/Gitcoin/' + str(brightid_uuid)

try:
response = requests.get(brightIDUrl)
responseData = response.json()
isVerified = responseData.get('data', {}).get('unique', False) and responseData.get('data', {}).get('context', '') == 'Gitcoin'

if isVerified:
return 'verified'
# NOT CONNECTED
elif responseData['errorNum'] == 2:
return 'not_connected'
# CONNECTED NOT SPONSORED
elif responseData['errorNum'] == 4:
sponsor_success = assign_brightid_sponsorship(brightid_uuid)

if sponsor_success:
return 'not_verified'
else:
return 'unknown'
# CONNECTED AND SPONSORED, NOT VERIFIED
elif responseData['errorNum'] == 3:
return 'not_verified'
else:
return 'unknown'
except:
return 'unknown'

def assign_brightid_sponsorship(brightid_uuid):
brightIDv5OpUrl = 'http://node.brightid.org/brightid/v5/operations'

op = {
'name': 'Sponsor',
'app': 'Gitcoin',
'contextId': str(brightid_uuid),
'timestamp': int(time.time()*1000),
'v': 5
}

signing_key = ed25519.SigningKey(base64.b64decode(settings.BRIGHTID_PRIVATE_KEY))
message = json.dumps(op, sort_keys=True, separators=(',', ':')).encode('ascii')
sig = signing_key.sign(message)
op['sig'] = base64.b64encode(sig).decode('ascii')

response = requests.post(brightIDv5OpUrl, json.dumps(op))
return 200 == response.status_code

def get_verified_uuids():
endpointURL = 'https://app.brightid.org/node/v5/verifications/Gitcoin'

try:
response = requests.get(endpointURL)
responseData = response.json()
approved_uuids = responseData.get('data', {}).get('contextIds', [])

return approved_uuids
except:
return []
16 changes: 16 additions & 0 deletions app/dashboard/migrations/0145_brightid_identifier_column.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.db import migrations, models
import uuid

class Migration(migrations.Migration):

dependencies = [
('dashboard', '0144_auto_20200903_1904'),
]

operations = [
migrations.AddField(
model_name='profile',
name='brightid_uuid',
field=models.UUIDField(null=True),
),
]
43 changes: 43 additions & 0 deletions app/dashboard/migrations/0146_populate_brightid_identifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from django.db import migrations, models, transaction
import uuid


def gen_uuid_0(apps, schema_editor):
Profile = apps.get_model('dashboard', 'Profile')

existing_uuid = []

profiles_with_uuid = Profile.objects.filter(brightid_uuid__isnull=False)
for profile in profiles_with_uuid:
brightid_uuid = profile.brightid_uuid
print(f"UUID for profile -> {profile.pk} -> {brightid_uuid}")
while brightid_uuid in existing_uuid:
brightid_uuid = uuid.uuid4()
existing_uuid.append(brightid_uuid)
profile.save()


while Profile.objects.filter(brightid_uuid__isnull=True).exists():
with transaction.atomic():

for profile in Profile.objects.filter(brightid_uuid__isnull=True)[:1000]:
brightid_uuid = uuid.uuid4()
print(f"NO UUID for profile -> {profile.pk} -> {brightid_uuid}")
while brightid_uuid in existing_uuid:
brightid_uuid = uuid.uuid4()
existing_uuid.append(brightid_uuid)

profile.brightid_uuid = brightid_uuid
profile.save()


class Migration(migrations.Migration):
atomic = False

dependencies = [
('dashboard', '0145_brightid_identifier_column'),
]

operations = [
migrations.RunPython(gen_uuid_0, reverse_code=migrations.RunPython.noop),
]
16 changes: 16 additions & 0 deletions app/dashboard/migrations/0147_require_brightid_identifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.db import migrations, models
import uuid

class Migration(migrations.Migration):

dependencies = [
('dashboard', '0146_populate_brightid_identifier'),
]

operations = [
migrations.AlterField(
model_name='profile',
name='brightid_uuid',
field=models.UUIDField(default=uuid.uuid4, unique=True),
),
]
15 changes: 15 additions & 0 deletions app/dashboard/migrations/0148_add_brightid_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.db import migrations, models

class Migration(migrations.Migration):

dependencies = [
('dashboard', '0147_require_brightid_identifier'),
]

operations = [
migrations.AddField(
model_name='profile',
name='is_brightid_verified',
field=models.BooleanField(default=False)
),
]
3 changes: 3 additions & 0 deletions app/dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import collections
import json
import logging
import uuid
from datetime import datetime, timedelta
from decimal import Decimal
from functools import reduce
Expand Down Expand Up @@ -2854,6 +2855,8 @@ class Profile(SuperModel):
ignore_tribes = models.ManyToManyField('dashboard.Profile', related_name='ignore', blank=True)
objects = ProfileManager()
objects_full = ProfileQuerySet.as_manager()
brightid_uuid=models.UUIDField(default=uuid.uuid4, unique=True)
is_brightid_verified=models.BooleanField(default=False)

@property
def is_blocked(self):
Expand Down
2 changes: 2 additions & 0 deletions app/dashboard/templates/profiles/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@
<script src="{% static "v2/js/pages/profile.js" %}"></script>
<script src="{% static "v2/js/pages/tribe_title.js" %}"></script>
<script src="{% static "v2/js/pages/profile-edit.js" %}"></script>
<script src="{% static "v2/js/pages/profile-trust.js" %}"></script>
<script src="{% static "v2/js/lib/qrcode.js" %}"></script>
<script src="{% static "v2/js/rating.js" %}"></script>
<script src="{% static "v2/js/status.js" %}"></script>
<script src="{% static "v2/js/pages/tribe-edit.js" %}"></script>
Expand Down
Loading

0 comments on commit 86886c1

Please sign in to comment.