Skip to content
This repository was archived by the owner on Sep 26, 2018. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 4 additions & 0 deletions cloud/endagaweb/celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,9 @@
'task': 'endagaweb.tasks.usageevents_to_sftp',
# Run this at 15:00 UTC (10:00 PDT, 02:00 Papua time)
'schedule': crontab(minute=0, hour=17),
}, 'block-user': {
'task': 'endagaweb.tasks.block_user',
# Run this at 15:00 UTC (10:00 PDT, 02:00 Papua time)
'schedule': crontab(minute=0, hour=17),
}
})
23 changes: 23 additions & 0 deletions cloud/endagaweb/forms/dashboard_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from ccm.common.currency import CURRENCIES
from endagaweb import models
from endagaweb.templatetags import apptags
from django.contrib.auth import password_validation


class UpdateContactForm(forms.Form):
Expand Down Expand Up @@ -153,15 +154,37 @@ def __init__(self, *args, **kwargs):

class ChangePasswordForm(PasswordChangeForm):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Built in validators here: https://docs.djangoproject.com/en/dev/topics/auth/passwords/#password-validation
Do you really need to override all of these fields to show a message? The validators should print their own policy text in a ValidationError on form submission.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks,yes ,As django have four default validators and they having their own policy text.
But as per our requirement we don't need UserAttributeSimilarityValidator,CommonPasswordValidator. so only NumericPasswordValidator and MinimumLengthValidator we could use.But NumericPasswordValidator only check numeric ,it doesn't check alphanumeric and they don't have validator check for special character.For that regexValidator need to use.
For all this , made custom validator custom_password_validators, which validated min_length,alphanumeric and special character. And our custom policy provided as help text.
Removed override of all those fields , added custom validators to display our policy and validate password strength.

commitId:1f9e597e4ffae62e4e54c4447c02bc4e7f3a1232
defaultdjangovalidator

"""Change password form visible on user profile page."""
"""Updated for password validation. """

error_message = ''

def __init__(self, *args, **kwargs):
super(ChangePasswordForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = 'id-updateAccountForm'
self.helper.form_method = 'post'
self.helper.form_action = '/account/password/change'
self.helper.form_class = 'profile-form'
self.error_message = ''
self.helper.add_input(Submit('submit', 'Save'))

def clean_password1(self):
old_password = self.cleaned_data.get("old_password")
new_password1 = self.cleaned_data.get("new_password1")
if new_password1 and old_password and old_password == new_password1:
self.error_message = 'Error: new password must not be old password.'
raise forms.ValidationError(self.error_message)
password_validation.validate_password(new_password1)
return new_password1

def clean_new_password2(self):
password1 = self.cleaned_data.get('new_password1')
password2 = self.cleaned_data.get('new_password2')
if password1 and password2:
if password1 != password2:
self.error_message = 'Error:conform password does not match.'
raise forms.ValidationError(self.error_message)
return password2

class NotifyEmailsForm(forms.Form):
notify_emails = forms.CharField(required=False, label="",
Expand Down
8 changes: 5 additions & 3 deletions cloud/endagaweb/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ class UserProfile(models.Model):
# because a user may have permissions on other Network instances.
# For example to get a list of networks the user can view:
# >>> get_objects_for_user(user_profile.user, 'view_network', klass=Network)
network = models.ForeignKey('Network', null=True, on_delete=models.SET_NULL)
network = models.ForeignKey('Network', null=True,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please revert this accidental change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

reverted.

on_delete=models.SET_NULL)
# Added for Password Expiry
last_pwd_update = models.DateTimeField(auto_now=True)

def __str__(self):
return "%s's profile" % self.user
Expand Down Expand Up @@ -1466,8 +1469,7 @@ class ConfigurationKey(models.Model):
Can be associated with many things.
"""
bts = models.ForeignKey(BTS, null=True, blank=True, on_delete=models.CASCADE)
network = models.ForeignKey(Network, null=True, blank=True,
on_delete=models.CASCADE)
network = models.ForeignKey(Network, null=True, blank=True, on_delete=models.CASCADE)
category = models.TextField() # "endaga", "openbts", etc..
key = models.TextField()
value = models.TextField()
Expand Down
12 changes: 12 additions & 0 deletions cloud/endagaweb/settings/prod.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,10 @@
# Enable/disable billing for networks. If false, we ignore what's in the
# network's account balance.
'NW_BILLING': os.environ.get("NW_BILLING", "True").lower() == "true",
# Password Expiration day
'PASSWORD_EXPIRED_DAY': 90,
# Password Expiry Alert
'PASSSWORD_EXPIRED_LAST_SEVEN_DAYS': 83,
}

STRIPE_API_KEY = os.environ.get("STRIPE_API_KEY",
Expand Down Expand Up @@ -317,3 +321,11 @@

# Security middleware settings
SECURE_CONTENT_TYPE_NOSNIFF = True

# Password validation
AUTH_PASSWORD_VALIDATORS = [

{ 'NAME': 'endagaweb.util.custom_password_validators.CustomPasswortValidator',
},

]
16 changes: 15 additions & 1 deletion cloud/endagaweb/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
from endagaweb.models import Subscriber
from endagaweb.models import UsageEvent
from endagaweb.models import SystemEvent
from endagaweb.models import TimeseriesStat
from endagaweb.models import TimeseriesStat, UserProfile
from endagaweb.ic_providers.nexmo import NexmoProvider


Expand Down Expand Up @@ -439,3 +439,17 @@ def req_bts_log(self, obj, retry_delay=60*10, max_retries=432):
raise
finally:
obj.save()

@app.task(bind=True)
def block_user(self):
""" Block User if User password is not updated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please make the comment reflect the fact that the number of days is configured in a setting.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

comments incorporated in commit id 1703300.

for last number of days which is configured in a settings .
"""
password_expired_duration = (django.utils.timezone.now() -
datetime.timedelta(
days=settings.ENDAGA['PASSWORD_EXPIRED_DAY']))
user_profiles = UserProfile.objects.filter(last_pwd_update__lte=password_expired_duration)
for user_profile in user_profiles:
user_profile.user.is_active = False
print '%s user is Blocked!' % user_profile.user.username
user_profile.user.save()
50 changes: 50 additions & 0 deletions cloud/endagaweb/templates/dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,56 @@
display: none;
}
</style>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>

$(document).ready(function(){
$("#dialog").modal('show');
});

</script>
</head>
<body>
{% if messages %}
<div class='modal fade' id='dialog'>
<div class='modal-dialog'>
<div class='modal-content'>
<div class='modal-header'>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class='modal-title'>
Password Expiry Status
</h4>
</div>
<div class='modal-body'>
{% for message in messages %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this really a loop? I assume we're only going to prompt a single user, the one that is logged in, to change their password.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

thanks,yes, there is one message need to prompt.But as per django message framework, message it store in FallbackStorage is the default storage.As per its definition This class first uses CookieStorage for all messages, falling back to using SessionStorage for the messages that could not fit in a single cookie. Since it is uses SessionStorage, it also requires Django's contrib.sessions application.
so it seems need to iterate in for loop for displaying message in template.If we not iterate and directly shows messages, it show like
Hi <django.contrib.messages.storage.fallback. at 0x7f1b7cb75590> Please change your password.

<p>
Hi {{ message }}
</p>
{% endfor %}
<p>
<strong>
Please change your password.
</strong>
</p>
</div>
<div class='modal-footer'>
<button type='button' class='btn btn-default' data-dismiss='modal'>Cancel</button>
<a href='/password/change' class='btn btn-primary' type='button'>Change Password</a>

</div>
</div>
</div>
</div>


{% endif %}
</div>
</body>

{% endblock %}


Expand Down
37 changes: 37 additions & 0 deletions cloud/endagaweb/templates/dashboard/password_change.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{% extends "dashboard/layout.html" %}
{% comment %}
Copyright (c) 2016-present, Facebook, Inc.
All rights reserved.

This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree. An additional grant
of patent rights can be found in the PATENTS file in the same directory.
{% endcomment %}
{% load apptags %}
{% load crispy_forms_tags %}

{% block title %} {% tmpl_const "SITENAME" %} | Monitor Usage, Pay Bills {% endblock %}

{% block content %}

<div class="row">
<div class="col-xs-12 page-header">
<h3>Hi {{ user_profile.display_name }}!</h3>
</div>
</div>

<div class="row">

<div class='col-xs-12 col-sm-6 col-md-4 col-xl-2'>
<h4>Change Password</h4>
{% crispy change_pass_form %}
{% for message in messages %}
{% if 'password' in message.tags %}
<div class="{{ message.tags }} message">{{ message }}</div>
{% endif %}
{% endfor %}
</div>
</div>


{% endblock %}
Loading