Skip to content

Commit

Permalink
Integrate keycard as a login method and intriduce ghost accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
zoek1 committed Jul 14, 2020
1 parent 8b5e485 commit 3e759cd
Show file tree
Hide file tree
Showing 14 changed files with 302 additions and 12 deletions.
2 changes: 1 addition & 1 deletion app/app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
path('api/v1/bounty/payout/<int:fulfillment_id>', dashboard.views.payout_bounty_v1, name='payout_bounty_v1'),
re_path(r'.*api/v0.1/chat/presence$', chat.views.chat_presence, name='chat_presence'),
re_path(r'.*api/v0.1/video/presence$', townsquare.views.video_presence, name='video_presence'),

path('api/v1/signin', dashboard.views.signin, name='signin'),
# inbox
re_path(r'^inbox/?', include('inbox.urls', namespace='inbox')),

Expand Down
7 changes: 7 additions & 0 deletions app/assets/v2/js/ajax-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@
*
*/

$.ajaxSetup({
crossDomain: true,
xhrFields: {
withCredentials: true
}
});

var fetchData = function(urlRequest, methodType, data, headers) {
// Return the $.ajax promise
return $.ajax({
Expand Down
18 changes: 18 additions & 0 deletions app/dashboard/migrations/0129_profile_active_ghost_account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.2.4 on 2020-07-14 03:39

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dashboard', '0128_hackathonevent_showcase'),
]

operations = [
migrations.AddField(
model_name='profile',
name='active_ghost_account',
field=models.BooleanField(default=False),
),
]
24 changes: 24 additions & 0 deletions app/dashboard/migrations/0130_auto_20200714_0426.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 2.2.4 on 2020-07-14 04:26

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('dashboard', '0129_profile_active_ghost_account'),
]

operations = [
migrations.AddField(
model_name='profile',
name='ghost_account',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='privacy_accounts', to='dashboard.Profile'),
),
migrations.AddField(
model_name='profile',
name='is_ghost_account',
field=models.BooleanField(default=False),
),
]
18 changes: 18 additions & 0 deletions app/dashboard/migrations/0131_profile_sigin_address.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.2.4 on 2020-07-14 05:18

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dashboard', '0130_auto_20200714_0426'),
]

operations = [
migrations.AddField(
model_name='profile',
name='sigin_address',
field=models.CharField(blank=True, default='', max_length=255),
),
]
7 changes: 5 additions & 2 deletions app/dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2694,6 +2694,7 @@ class Profile(SuperModel):
max_num_issues_start_work = models.IntegerField(default=5)
etc_address = models.CharField(max_length=255, default='', blank=True)
preferred_payout_address = models.CharField(max_length=255, default='', blank=True)
sigin_address = models.CharField(max_length=255, default='', blank=True)
preferred_kudos_wallet = models.OneToOneField('kudos.Wallet', related_name='preferred_kudos_wallet', on_delete=models.SET_NULL, null=True, blank=True)
max_tip_amount_usdt_per_tx = models.DecimalField(default=2500, decimal_places=2, max_digits=50)
max_tip_amount_usdt_per_week = models.DecimalField(default=20000, decimal_places=2, max_digits=50)
Expand Down Expand Up @@ -2771,7 +2772,9 @@ class Profile(SuperModel):
last_validation_request = models.DateTimeField(blank=True, null=True, help_text=_("When the user requested a code for last time "))
encoded_number = models.CharField(max_length=255, blank=True, help_text=_('Number with the user validate the account'))
sybil_score = models.IntegerField(default=-1)

active_ghost_account = models.BooleanField(default=False)
ghost_account = models.ForeignKey('Profile', null=True, on_delete=models.SET_NULL, related_name='privacy_accounts')
is_ghost_account = models.BooleanField(default=False)
objects = ProfileManager()
objects_full = ProfileQuerySet.as_manager()

Expand Down Expand Up @@ -2806,7 +2809,7 @@ def sybil_score_str(self):
score = self.sybil_score
if score > 5:
return f'VeryX{score} High'
return _map.get(score, "Unknown")
return _map.get(score, "Unknown")

@property
def chat_num_unread_msgs(self):
Expand Down
10 changes: 10 additions & 0 deletions app/dashboard/templates/shared/nav_auth.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@
{% endif %}

<div class="dropdown-divider"></div>
{% if user.profile.privacy_accounts or user.profile.active_ghost_account and user.profile.ghost_account %}
<a class="dropdown-item" onclick="switchPrivacyMode()">
<i class="fa fa-user-secret"></i>
{% trans "Privacy mode" %}
</a>
</a>
{% endif %}
<a class="dropdown-item" href="{% url "email_settings" email_key %}">
<i class="fas fa-cog"></i>
{% trans "Manage Settings" %}
Expand Down Expand Up @@ -148,4 +155,7 @@
<a class="nav-link login interior" href="{% url 'social:begin' 'github' %}?next={{ request.get_full_path }}" onclick="dataLayer.push({'event': 'login'});">
<i class="fab fa-github"></i><span class="nav-link__text">{% trans "Log in" %}</span>
</a>
<a id="alternative-login" class="nav-link login interior" onclick="checkStatusSignIn()">
<i class="fab fa-ethereum"></i><span class="nav-link__text">{% trans "Sign in with Keycard" %}</span>
</a>
{% endif %}
64 changes: 64 additions & 0 deletions app/dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
from datetime import datetime, timedelta
from decimal import Decimal

import web3
from django.conf import settings
from django.contrib import messages
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth import login
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
Expand Down Expand Up @@ -5535,3 +5537,65 @@ def showcase(request, hackathon):
return JsonResponse({
'success': True,
})

@csrf_exempt
def signin(request):
address = request.POST.get('address')
method = request.POST.get('method')
hash = request.POST.get('hash')
signed = request.POST.get('signed')
print(address)
profile = get_object_or_404(Profile, sigin_address=address)

if method == 'request':
response = JsonResponse({
'nonce': f'Sign in as @{profile.handle}'
})

response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Methods"] = "GET, OPTIONS"
response["Access-Control-Max-Age"] = "1000"
response["Access-Control-Allow-Headers"] = "X-Requested-With, Content-Type"

return response
elif method == 'signin':
signed_address = w3.eth.account.recoverHash(hash, signature=signed)
print(signed_address)
print(profile.sigin_address)
if signed_address == profile.sigin_address:
login(request, profile.user, backend='django.contrib.auth.backends.ModelBackend')

return JsonResponse({
'refresh': True
})
response = JsonResponse({
'error': True,
'msg': 'Failed compare message'
}, status=422)

response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Methods"] = "GET, OPTIONS"
response["Access-Control-Max-Age"] = "1000"
response["Access-Control-Allow-Headers"] = "X-Requested-With, Content-Type"

return response
elif method == 'switch':
signed_address = w3.eth.account.recoverHash(hash, signature=signed)

if signed_address == profile.sigin_address:
if request.user == profile.user:
login(request, profile.ghost_account.user, backend='django.contrib.auth.backends.ModelBackend')
else:
login(request, profile.user, backend='django.contrib.auth.backends.ModelBackend')

return JsonResponse({
'refresh': True
})
return JsonResponse({
'error': True,
'msg': 'Failed compare message'
}, status=422)

return JsonResponse({
'error': True
}, status=422)
53 changes: 44 additions & 9 deletions app/marketing/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils import timezone, translation
from django.utils.crypto import get_random_string
from django.utils.translation import LANGUAGE_SESSION_KEY
from django.utils.translation import gettext_lazy as _

Expand Down Expand Up @@ -154,6 +155,8 @@ def settings_helper_get_auth(request, key=None):


def privacy_settings(request):
from coolname import generate_slug

# setup
profile, __, __, is_logged_in = settings_helper_get_auth(request)
if not profile:
Expand All @@ -162,20 +165,44 @@ def privacy_settings(request):

msg = ''
if request.POST and request.POST.get('submit'):
if profile:
if profile and not profile.is_ghost_account:
profile.dont_autofollow_earnings = bool(request.POST.get('dont_autofollow_earnings', False))
profile.suppress_leaderboard = bool(request.POST.get('suppress_leaderboard', False))
profile.hide_profile = bool(request.POST.get('hide_profile', False))
profile.pref_do_not_track = bool(request.POST.get('pref_do_not_track', False))
profile.hide_wallet_address = bool(request.POST.get('hide_wallet_address', False))
profile = record_form_submission(request, profile, 'privacy')
profile.active_ghost_account = bool(request.POST.get('active_ghost_account', False))
if profile.alumni and profile.alumni.exists():
alumni = profile.alumni.first()
alumni.public = bool(not request.POST.get('hide_alumni', False))
alumni.save()

profile.save()

if profile.active_ghost_account and not profile.ghost_account:
slug = generate_slug()
domain = '.'.join(generate_slug(2).split('-'))
anonuser = User.objects.create_user(username=slug,
email='{}@{}.com'.format(slug, domain),
password=None)
anonuser.set_unusable_password()
anonuser.save()
anonprofile = Profile.objects.create(data={},
user=anonuser,
handle=anonuser.username,
email=anonuser.email,
dont_autofollow_earnings=True,
suppress_leaderboard=True,
hide_profile=True,
pref_do_not_track=True,
hide_wallet_address=True,
)

profile.ghost_account = anonprofile

profile.save()

context = {
'profile': profile,
'nav': 'home',
Expand Down Expand Up @@ -531,6 +558,14 @@ def account_settings(request):
profile.persona_is_hunter = bool(request.POST.get('persona_is_hunter', False))
profile.save()

if 'sigin_address' in request.POST.keys():
eth_address = request.POST.get('sigin_address', '')
if not is_valid_eth_address(eth_address):
eth_address = profile.sigin_address
profile.sigin_address = eth_address
profile.save()
msg = _('Updated your sign in Address')

if 'preferred_payout_address' in request.POST.keys():
eth_address = request.POST.get('preferred_payout_address', '')
if not is_valid_eth_address(eth_address):
Expand All @@ -552,7 +587,7 @@ def account_settings(request):
earnings = earnings.filter(network='mainnet').order_by('-created_on')
for earning in earnings:
writer.writerow([earning.pk,
earning.created_on.strftime("%Y-%m-%dT%H:00:00"),
earning.created_on.strftime("%Y-%m-%dT%H:00:00"),
earning.from_profile.handle if earning.from_profile else '*',
earning.from_profile.data.get('location', 'Unknown') if earning.from_profile else 'Unknown',
earning.to_profile.handle if earning.to_profile else '*',
Expand Down Expand Up @@ -743,7 +778,7 @@ def tax_settings(request):
if not user or not profile or not is_logged_in:
login_redirect = redirect('/login/github?next=' + request.get_full_path())
return login_redirect

# location dict is not empty
location = ''
if profile.location:
Expand Down Expand Up @@ -783,15 +818,15 @@ def tax_settings(request):
location += ', ' + country_code
else:
location += country_code

#address is not empty
if profile.address:
address = profile.address
address = profile.address
else:
address = ''

g_maps_api_key = "AIzaSyBaJ6gEXMqjw0Y7d5Ps9VvelzOOvfV6BvQ"

context = {
'is_logged_in': is_logged_in,
'nav': 'home',
Expand Down Expand Up @@ -924,9 +959,9 @@ def leaderboard(request, key=''):
'type': 'line',
'stacking': False
},
'terms':
'terms':
['amount']

}],
chart_options =
{'legend': {
Expand Down
4 changes: 4 additions & 0 deletions app/retail/templates/settings/account.html
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ <h5>{% trans "ETH Account Preferences" %}</h5>
<input type="text" name="preferred_payout_address" class="form__input" placeholder='0x0' value="{{ profile.preferred_payout_address }}">
<small id="preferred_payout_addresshelp" class="form__input-help">{% trans "so we know where to send tips to you." %}</small>
</div>
<div class="form-group">
<label class="form__label" for="sigin_address">{% trans "Alternative Sign in with Ethereum Address" %}:</label>
<input type="text" name="sigin_address" class="form__input" placeholder='0x0' value="{{ profile.sigin_address }}">
</div>
{% csrf_token %}
<input class='button button--primary' type='submit' name='submit' value="{% trans "Save" %}">
</form>
Expand Down
6 changes: 6 additions & 0 deletions app/retail/templates/settings/privacy.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ <h5>{% trans "Privacy Preferences" %}</h5>
<br>
<small id="hide_profile_help" class="form__input-help">{% trans "If this option is chosen, we will remove your profile altogether" %}</small>
</div>
<div>
<input type="checkbox" name="active_ghost_account" id=active_ghost_account value="1" {% if profile.active_ghost_account %} checked="checked" {% endif %} {% if not is_logged_in %} disabled {% endif %} >
<label class="form__label" for="active_ghost_account">{% trans "Activate ghost account" %}</label>
<br>
<small id="hide_profile_help" class="form__input-help">{% trans "If this option is chosen, the whost account wil be activated" %}</small>
</div>
<div>
<input type="checkbox" name="pref_do_not_track" id=pref_do_not_track value="1" {% if profile.pref_do_not_track %} checked="checked" {% endif %} {% if not is_logged_in %} disabled {% endif %} >
<label class="form__label" for="pref_do_not_track">{% trans "Do not Track" %}</label>
Expand Down
Loading

0 comments on commit 3e759cd

Please sign in to comment.