Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Trust Bonus Tab With BrightID Integration #7333

Merged
merged 39 commits into from
Sep 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
afafe38
add constant pot changing mechanism, add analytics script
frankchen07 Aug 24, 2020
d1593f9
remove current designation, add a couple print test statements
frankchen07 Aug 25, 2020
69bd515
add constant pot changing mechanism, add analytics script
frankchen07 Aug 24, 2020
5f2a4f0
remove current designation, add a couple print test statements
frankchen07 Aug 25, 2020
5f598c4
Merge branch 'frank-master' of https://github.com/frankchen07/web int…
frankchen07 Aug 25, 2020
b12fe94
add pot dealer, add analytics function & script, add additional calcu…
frankchen07 Aug 26, 2020
79f4c01
update clr estimate increases using a log function, fix adityas comments
frankchen07 Aug 27, 2020
44307b4
add updated verification status
frankchen07 Aug 31, 2020
92dd30b
fix up adityas 2nd round of requests
frankchen07 Sep 2, 2020
6fd6f85
moved analytics function to analytics_clr.py, import methods
frankchen07 Sep 3, 2020
e3220a6
Merge remote-tracking branch 'upstream/master' into clr
thelostone-mc Sep 4, 2020
ff8e515
add brightId verfication into clr
thelostone-mc Sep 4, 2020
c48493c
Add a Trust Score tab to the users profile
apbendi Aug 17, 2020
f8f6028
Scaffold UI for connecting to BrightID
apbendi Aug 17, 2020
e1919f3
Migrations to add, populate and require a brightid identifier on Profile
apbendi Aug 17, 2020
e26b3c9
Add BrightID UUID to Profile and display on Trust tab as proof-of-con…
apbendi Aug 17, 2020
a0cf0e6
Get status of the users bright id connection when loading the Trust tab
apbendi Aug 18, 2020
1cf5495
Display different bright id interface based on auth status & connection
apbendi Aug 18, 2020
72c552d
Only show Trust tab for logged in users own profile
apbendi Aug 19, 2020
0b59c28
Get BrightID Sponsorship creation working
apbendi Aug 19, 2020
515c552
Create modal poppup for Connect phase of BrightID integration
apbendi Aug 28, 2020
3f6ed76
Add modal for verification phase of BrightID Integrations
apbendi Sep 1, 2020
a407d25
Handle the case when the user is verified
apbendi Sep 3, 2020
ec30a52
Clean up copy & implement full Trust Bonus UI design
apbendi Sep 3, 2020
8611a19
Responsive design tweaks for Trust Bonus tab
apbendi Sep 3, 2020
de0265c
Fix migration conflicts & add column for BrightID verification status
apbendi Sep 4, 2020
0d54c77
Write command for fetching and updated verified users in local DB
apbendi Sep 4, 2020
776c5f7
Schedule BrightID status updater to run at 55th minute of every hour
apbendi Sep 4, 2020
3b7e315
Cleanup URLs in BrightID Utils
apbendi Sep 4, 2020
edcab71
Avoid duplicating objectifySerialized utility JS function
apbendi Sep 4, 2020
5c2d610
Resolve javascript linting errors in BrightID modals
apbendi Sep 4, 2020
84384ff
Add root level package-lock.json file for dev environment commands
apbendi Sep 4, 2020
ce57735
Compress app store logo svg file
apbendi Sep 7, 2020
19654b5
Fix various spacing an indentation issues in BrightID integration
apbendi Sep 7, 2020
59e5d5a
Cleanup return condition for brightid sponsorhip assignment
apbendi Sep 7, 2020
a2b6cae
recreate migration + run pylint
thelostone-mc Sep 8, 2020
4a95483
uuid migration fix
thelostone-mc Sep 8, 2020
bbe2f1a
Merge pull request #86 from frankchen07/frank-master
thelostone-mc Sep 9, 2020
55b084c
clr tweaks
thelostone-mc Sep 9, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
apbendi marked this conversation as resolved.
Show resolved Hide resolved
</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]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hardcoded limit is gonna get us in trtouble in prod where there are 40k profiles

also whats the plan for new profiles, how are they going to get a brightid uuid?

Copy link
Member

@thelostone-mc thelostone-mc Sep 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume it would be auto-assigned one on creation which is unique (as defined in the model)
Is my understanding right @apbendi ?

  • turn the uuid field as blank=True
  • write up a mgmt command which duplicates the logic for assigning uuid & run every 3 hours or so

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if I've done this right, a new UUID should be generated when a user is created, per this migration:

https://github.com/gitcoinco/web/pull/7333/files#diff-f2afa72d3c42b2cac75651dd47a6c961R14

Can you sanity check that looks correct.

I'm not sure about the 1K limit @thelostone-mc, why did you have that in there? Did it take 2 minutes to run for only 1K records or for the full 40K?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 min for the whole 40k !

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',
Copy link
Member

@thelostone-mc thelostone-mc Sep 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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