-
Notifications
You must be signed in to change notification settings - Fork 0
Beginnings of user accounts, sqlalchemy etc. #85
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
Changes from all commits
11eec8a
f93dc78
e60dce4
7499a87
d5317d6
f05976b
5744d5d
22fa34b
f2eb401
ac402f6
a89147a
cf087e5
ecd4105
7e747cb
14e309c
5edbf53
d0bb78d
6935f93
a2abdf7
de9d56c
683afaf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # User role | ||
| ADMIN = 0 | ||
| STAFF = 1 | ||
| USER = 2 | ||
| ROLE = { | ||
| ADMIN: 'admin', | ||
| STAFF: 'staff', | ||
| USER: 'user', | ||
| } | ||
|
|
||
| # user status | ||
| INACTIVE = 0 | ||
| NEW = 1 | ||
| ACTIVE = 2 | ||
| STATUS = { | ||
| INACTIVE: 'inactive', | ||
| NEW: 'new', | ||
| ACTIVE: 'active', | ||
| } | ||
|
|
||
| #OAUTH keys supported: | ||
| OAUTHS = ['github'] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| from functools import wraps | ||
|
|
||
| from flask import g, flash, redirect, url_for, request | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
|
|
||
| def requires_login(f): | ||
| @wraps(f) | ||
| def decorated_function(*args, **kwargs): | ||
| if g.user is None: | ||
| flash(u'You need to be signed in for this page.') | ||
| return redirect(url_for('users.login', next=request.path)) | ||
| return f(*args, **kwargs) | ||
| return decorated_function | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| from flask.ext.wtf import ( | ||
| Form, TextField, PasswordField, BooleanField, RecaptchaField | ||
| ) | ||
| from flask.ext.wtf import Required, Email, EqualTo | ||
|
|
||
| from .models import User | ||
|
|
||
|
|
||
| class LoginForm(Form): | ||
| username = TextField('Username', [Required()]) | ||
| password = PasswordField('Password', [Required()]) | ||
|
|
||
|
|
||
| class RegisterForm(Form): | ||
| username = TextField('Username', [Required()]) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am getting SQL Alchemy exceptions now.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now I dropped and re-created but there is probably a better approach.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't used it, but I've seen this https://code.google.com/p/sqlalchemy-migrate/ exists. Here's an example for using it with flask: http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-iv-database At the moment, I'm doing the same thing as you - deleting and restoring. |
||
| email = TextField('Email address', [Email()]) | ||
| password = PasswordField('Password', [Required()]) | ||
| confirm = PasswordField('Repeat Password', [ | ||
| Required(), | ||
| EqualTo('password', message='Passwords must match') | ||
| ]) | ||
| accept_tos = BooleanField('I accept the TOS', [Required()]) | ||
| #recaptcha = RecaptchaField() | ||
|
|
||
| def validate(self): | ||
| rv = Form.validate(self) | ||
| if not rv: | ||
| return False | ||
|
|
||
| uname_clash = User.query.filter_by(username=self.username.data).first() | ||
| if uname_clash: | ||
| self.username.errors.append("This username is already in use.") | ||
| return False | ||
|
|
||
| return True | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| def load_user_to_session(session, user): | ||
| session['user_id'] = user.id | ||
| session['user_name'] = user.username | ||
|
|
||
|
|
||
| def get_user_id_from_session(session): | ||
| try: | ||
| return session['user_id'] | ||
| except KeyError: | ||
| return None |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| from datetime import datetime | ||
|
|
||
| from deploystream import db | ||
| from deploystream.apps.users import constants as USERS | ||
|
|
||
|
|
||
| class User(db.Model): | ||
|
|
||
| __tablename__ = 'users_user' | ||
| id = db.Column(db.Integer, primary_key=True) | ||
| username = db.Column(db.String(50), unique=True) | ||
| email = db.Column(db.String(120)) | ||
| password = db.Column(db.String(20)) | ||
| role = db.Column(db.SmallInteger, default=USERS.USER) | ||
| status = db.Column(db.SmallInteger, default=USERS.NEW) | ||
| created = db.Column(db.DateTime, default=datetime.now) | ||
|
|
||
| oauth_keys = db.relationship('OAuth', backref='user') | ||
|
|
||
| def __init__(self, username=None, email=None, password=None): | ||
| self.username = username | ||
| self.email = email | ||
| self.password = password | ||
|
|
||
| def getStatus(self): | ||
| return USERS.STATUS[self.status] | ||
|
|
||
| def getRole(self): | ||
| return USERS.ROLE[self.role] | ||
|
|
||
| def __repr__(self): | ||
| return '<User %r>' % (self.username) | ||
|
|
||
|
|
||
| class OAuth(db.Model): | ||
|
|
||
| __tablename__ = 'users_oauth' | ||
| id = db.Column(db.Integer, primary_key=True) | ||
| user_id = db.Column(db.Integer, db.ForeignKey('users_user.id')) | ||
| service = db.Column(db.String(20)) | ||
| service_user_id = db.Column(db.String(120)) | ||
| service_username = db.Column(db.String(50)) | ||
|
|
||
| # A user on an external service should only exist once in deploystream. | ||
| db.UniqueConstraint('service', 'service_user_id', name='service_user') | ||
|
|
||
| # Each user in deploystream should only have each service oauth'd once. | ||
| db.UniqueConstraint('user_id', 'service', name='user_service') |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| from . import constants | ||
| from .models import OAuth | ||
| from deploystream import app | ||
|
|
||
|
|
||
| @app.template_filter('all_oauths') | ||
| def all_oauths(user): | ||
| """Return all available oauths for the User. | ||
|
|
||
| Returns all oauth information including those that the user hasn't | ||
| connected to yet. | ||
| """ | ||
| all_oauth = [] | ||
| for oauth_service in constants.OAUTHS: | ||
| user_oauth = OAuth.query.filter_by(user_id=user.id, | ||
| service=oauth_service).first() | ||
| if user_oauth: | ||
| all_oauth.append((oauth_service, user_oauth.service_username)) | ||
| else: | ||
| all_oauth.append((oauth_service, None)) | ||
|
|
||
| return all_oauth | ||
|
|
||
|
|
||
| @app.template_filter('humanize_time') | ||
| def humanize_time(datetime): | ||
| return datetime.strftime("%B %d, %Y") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a bit unclear about how we really want to handle user registration and linking external accounts / oauth tokens. In particular how much user management we want in the core "open-source" project (in particular, self-registration).
E.g. now, any non-registered user can log in via github Oauth, and have a user account created with no email. Whereas for self-registered users, email is a required field.
Up for a discussion elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about it some more I'd be happy with username+password for non oauth accounts as opposed to email+password - although I'm not sure that's your concern.
Happy to have a chat about this.