Skip to content

Commit

Permalink
upgraded authentication, base modules etc..
Browse files Browse the repository at this point in the history
  • Loading branch information
xcash committed Jun 29, 2015
1 parent 8d7845f commit 0b67927
Show file tree
Hide file tree
Showing 41 changed files with 912 additions and 19,207 deletions.
2 changes: 2 additions & 0 deletions .hgignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ urls.py
templates
static
home
migrations

32 changes: 25 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@ Atuin is Scalebox's Flask web application skeleton
- Flask-SQLAlchemy (pip install flask-sqlalchemy)
- flask-cache (pip install flask-cache)
- flask-babel (pip install flask-babel)
- flask-migrate (pip install flask-migrate)
- requests (pip install requests)
- blinker (pip install blinker)
- passlib (pip install passlib)
- markdown (pip install markdown) (used for html-formatting functionalities)

Install all:

apt-get install python-dev
pip install -U flask flask-login sqlalchemy flask-sqlalchemy flask-cache flask-babel requests
pip install -U flask flask-login sqlalchemy flask-sqlalchemy flask-cache flask-babel flask-migrate requests markdown passlib blinker

## Launch devserver

./dev.py
./dev.py runserver

or, for external use

Expand Down Expand Up @@ -72,7 +76,7 @@ Installation
location /static/bootstrap {
alias /home/project/prj/static/bootstrap/;
expires 24h;
expires 24h;
}
location / {
Expand All @@ -87,7 +91,7 @@ Installation

- activate it

ln -s _/etc/nginx/sites-available/project_ _/etc/nginx/sites-enabled/project_
ln -s /etc/nginx/sites-available/project /etc/nginx/sites-enabled/project


# Other tools
Expand All @@ -100,8 +104,22 @@ with `auth.models` preloaded

./shell.py auth

## DB reset:
## DB Manage and migrations

./initdb.py

FlaskAtuin uses Flask-migrate

### First db init (used only when project is **started**)

./initdb.py db init

### Upgrade to latest migrations

./initdb.py db upgrade

### To generate new migration

./initdb.py db migrate -m "description"

*check the files* and then **apply**

./initdb.py db upgrade
105 changes: 102 additions & 3 deletions auth/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ def users_index():


@bp.route("/users/<int:userid>")
@bp.route("/users/<int:userid>/<links>")
@login_required
def users_get(userid):
def users_get(userid, links=False):
user = User.query.get_or_404(userid)
user_d = {
'id': user.id,
Expand All @@ -30,15 +31,96 @@ def users_get(userid):
'name': user.name,
'notes': user.notes,
'role': user.role,

'activeuntil': user.active_until.isoformat() if user.active_until else None,
'birthday': user.birthday.isoformat() if user.birthday else None,
'gender': user.gender,

'url_links': url_for('.users_get', userid=user.id, links='links'),
'url_visibility': url_for('.users_get_visibility', userid=user.id),
}
if user.last_login:
user_d['last_login'] = user.last_login.ctime()
else:
user_d['last_login'] = None

if links:
user_d['linked_users'] = []
for lu in user.linked_users:
user_d['linked_users'].append(
{
'id': lu.id,
'username': lu.username,
'name': lu.name,
'linked_users_num': len(lu.get_linked_users())
}
)

return jsonify(user_d)


@bp.route("/users/<int:userid>/visibility")
@login_required
def users_get_visibility(userid):
user = User.query.get_or_404(userid)

r = {
'viewedby': [],
'views': []
}

for pu in user.get_parents(10):
r['viewedby'].append(
{
'id': pu.id,
'username': pu.username,
'name': pu.name,
}
)

for lu in user.get_linked_users(10):
r['views'].append(
{
'id': lu.id,
'username': lu.username,
'name': lu.name,
}
)

return jsonify(r)


@bp.route("/users/search")
@login_required
def users_search():
q = request.args['q']
if len(q) < 3:
return 'QUERY_TOO_SHORT', 400

users = User.query.filter(User.name.like('%'+q+'%') | User.username.like('%'+q+'%')).limit(12).all()
result = {
'count': len(users),
'results': [
{
'id': user.id,
'usertype': user.usertype,
'username': user.username,
'name': user.name,
'notes': user.notes,
'role': user.role,

'activeuntil': user.active_until.isoformat() if user.active_until else None,
'birthday': user.birthday.isoformat() if user.birthday else None,
'gender': user.gender,

'linked_users_num': len(user.get_linked_users())
} for user in users
]
}

return jsonify(**result)


@bp.route("/users/save", methods=['POST'])
@bp.route("/users/<int:userid>", methods=['POST'])
@login_required
Expand All @@ -52,27 +134,44 @@ def users_save(userid=None):
else:
#new user
user = User()

db.session.add(user)

user.usertype = request.form['usertype']
user.username = request.form['username']
user.name = request.form['name']
user.notes = request.form['notes']
user.role = request.form['role']

user.gender = request.form['gender']

if ('birthday' in request.form) and (request.form['birthday']):
user.birthday = datetime.datetime.strptime(request.form['birthday'], '%d/%m/%Y')

if ('activeuntil' in request.form) and (request.form['activeuntil']):
user.active_until = datetime.datetime.strptime(request.form['activeuntil'], '%d/%m/%Y')

if request.form['password'] != '':
user.set_password(request.form['password'])

db.session.add(user)
db.session.commit()
# public ID based on generated ID
if not user.public_id:
user._generate_public_id()
db.session.commit()

flash(u'User %s saved' % user.username)
return redirect(url_for('auth.admin.users_index'))

@bp.route("/users/<int:userid>", methods=['DELETE'])
@login_required
def users_delete(userid):
if (current_user.role != 'ADMIN'):
return 'Nice try... :D', 403

user = User.query.get_or_404(userid)

db.session.delete(user)
db.session.commit()

return 'OK'

111 changes: 109 additions & 2 deletions auth/models.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,64 @@
import datetime
import random
import hashlib
import collections

from passlib.hash import sha256_crypt
from mailing import send_mail

from datastore import db, hybrid_property, backref

from datastore import db

class User(db.Model):
__tablename__ = 'users'

id = db.Column(db.Integer, primary_key=True)
public_id = db.Column(db.Integer, index=True)
usertype = db.Column(db.String)
username = db.Column(db.String, unique=True, index=True)
password = db.Column(db.String)
name = db.Column(db.String)
notes = db.Column(db.Text)
role = db.Column(db.String, index=True)

birthday = db.Column(db.Date)
gender = db.Column(db.String)

address_city = db.Column(db.String)
address_zip = db.Column(db.String)
address_country = db.Column(db.String)

otp = db.Column(db.String, default=None)
otp_expire = db.Column(db.DateTime, default=None)

active_until = db.Column(db.DateTime, default=None, index=True)

ins_timestamp = db.Column(db.DateTime, default=datetime.datetime.now)
upd_timestamp = db.Column(db.DateTime, default=datetime.datetime.now)

last_login = db.Column(db.DateTime, default=None)

policies = db.relationship('UserPolicy',
lazy='joined')

__table_args__ = ( db.Index('ix_otp_expire_otp', 'otp_expire', 'otp'),
)

usertypes_d = collections.OrderedDict([
('staff', "Staff"),
('customer', "Customer"),
])

roles_d = collections.OrderedDict([
('ADMIN', "Administrator"),
('CUSTOMER', "Customer"),
('AGENT', "Agent"),
('USER', "User"),
])

def __repr__(self):
return "<User %s %s usertype=%s>" % (self.id, self.username, self.usertype)


def get_id(self):
return self.id

Expand Down Expand Up @@ -64,6 +88,89 @@ def usertype_translated(self):
def role_translated(self):
return self.roles_d.get(self.role, self.role)

@property
def age(self):
try:
age = (datetime.date.today() - self.birthday).days / 365.2425
except:
age = 0
return int(age)

@classmethod
def check_otp(cls, otp):
user = cls.query.filter(cls.otp_expire > datetime.datetime.now(), cls.otp == otp).first()
if user:
#invalidate it
user.otp_expire = datetime.datetime.now()

return user

def generate_otp(self):
otp = hashlib.sha256(str(random.randint(99999, 999999))).hexdigest()
self.otp = otp
self.otp_expire = datetime.datetime.now() + datetime.timedelta(hours=24)

return otp

def send_email(self, subject, message):
res = send_mail(subject, message, [
{ 'email': self.username },
])
return res

def _generate_public_id(self):
self.public_id = int( str(self.id) + str(random.randint(1000, 9999)) )

def has_function(self, func):
if self.role == 'ADMIN':
return True

if not self.policies:
return False

if not hasattr(self, 'functions'):
self.functions = self.policies[0].functions.split(',')

return (func in self.functions)

def as_dict(self, show=None, hide=None, recurse=None):
""" Return a dictionary representation of this model.
"""

obj_d = {
'id' : self.id,
'public_id' : self.public_id,
'usertype' : self.usertype,
'username' : self.username,
'name' : self.name,
'notes' : self.notes,
'role' : self.role,

'birthday' : self.birthday.isoformat() if self.birthday else None,
'gender' : self.gender,

'address_city' : self.address_city,
'address_zip' : self.address_zip,
'address_country' : self.address_country,

'active_until' : self.active_until.isoformat() if self.active_until else None,
'last_login' : self.last_login.isoformat() if self.last_login else None,

'ins_timestamp' : self.ins_timestamp.isoformat() if self.ins_timestamp else None,
'upd_timestamp' : self.upd_timestamp.isoformat() if self.upd_timestamp else None,
}

return obj_d



class UserPolicy(db.Model):
__tablename__ = 'policies'

role = db.Column(db.String, db.ForeignKey('users.role'), primary_key=True)
functions = db.Column(db.Text)


#class UserSession(db.Model):
# id = db.Column(db.Integer, primary_key=True)
# user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
Expand Down
Loading

0 comments on commit 0b67927

Please sign in to comment.