Fatih Degirmenci | c2a4601 | 2019-09-30 12:07:45 +0200 | [diff] [blame] | 1 | import base64 |
| 2 | from datetime import datetime, timedelta |
| 3 | import os |
Fatih Degirmenci | c2a4601 | 2019-09-30 12:07:45 +0200 | [diff] [blame] | 4 | |
| 5 | from werkzeug.security import generate_password_hash, check_password_hash |
| 6 | from flask_login import UserMixin |
| 7 | |
| 8 | from app import db, login |
| 9 | |
Fatih Degirmenci | c2a4601 | 2019-09-30 12:07:45 +0200 | [diff] [blame] | 10 | @login.user_loader |
| 11 | def load_user(id): |
| 12 | return User.query.get(int(id)) |
| 13 | |
| 14 | class PaginatedAPIMixin(object): |
| 15 | @staticmethod |
| 16 | def to_collection_dict(query, page, per_page, endpoint, **kwargs): |
| 17 | resources = query.paginate(page, per_page, False) |
| 18 | data = { |
| 19 | 'items': [item.to_dict() for item in resources.items], |
| 20 | '_meta': { |
| 21 | 'page': page, |
| 22 | 'per_page': per_page, |
| 23 | 'total_pages': resources.pages, |
| 24 | 'total_items': resources.total |
| 25 | }, |
| 26 | } |
| 27 | return data |
| 28 | |
| 29 | class UserRole(db.Model): |
| 30 | id = db.Column(db.Integer, primary_key=True) |
| 31 | role_name = db.Column(db.String(32), index=True, unique=True, default='regular') |
| 32 | role_description = db.Column(db.String(128), default='Regular user with access to only base stacks') |
| 33 | |
| 34 | users = db.relationship('User', backref='user_role', lazy='dynamic') |
| 35 | |
| 36 | def __repr__(self): |
| 37 | return ('\tID: {}\n' |
| 38 | '\tRole name: {}\n' |
| 39 | '\tRole description: {}\n' |
| 40 | .format(self.id, self.role_name, self.role_description)) |
| 41 | |
| 42 | class UserOrganization(db.Model): |
| 43 | id = db.Column(db.Integer, primary_key=True) |
| 44 | org_name = db.Column(db.String(32), index=True, unique=True, default='nordix') |
| 45 | org_description = db.Column(db.String(128), default='Nordix Community users') |
| 46 | |
| 47 | users = db.relationship('User', backref='user_organization', lazy='dynamic') |
| 48 | |
| 49 | def __repr__(self): |
| 50 | return ('\tID: {}\n' |
| 51 | '\tOrganization name: {}\n' |
| 52 | '\tOrganization description: {}\n' |
| 53 | .format(self.id, self.org_name, self.org_description)) |
| 54 | |
| 55 | class User(PaginatedAPIMixin, UserMixin, db.Model): |
| 56 | id = db.Column(db.Integer, primary_key=True) |
| 57 | role_id = db.Column(db.Integer, db.ForeignKey('user_role.id')) |
| 58 | org_id = db.Column(db.Integer, db.ForeignKey('user_organization.id')) |
| 59 | username = db.Column(db.String(64), index=True, unique=True) |
| 60 | email = db.Column(db.String(128), index=True, unique=True) |
Fatih Degirmenci | 0eb007f | 2019-09-30 14:16:48 +0000 | [diff] [blame] | 61 | fullname = db.Column(db.String(128)) |
Fatih Degirmenci | c2a4601 | 2019-09-30 12:07:45 +0200 | [diff] [blame] | 62 | password_hash = db.Column(db.String(128)) |
| 63 | ssh_public_key = db.Column(db.String(1024)) |
| 64 | registered_on = db.Column(db.DateTime, index=True, default=datetime.utcnow) |
| 65 | confirmed = db.Column(db.Integer, default=0) |
| 66 | last_logged_in = db.Column(db.DateTime, index=True) |
| 67 | confirmed_on = db.Column(db.DateTime, index=True) |
| 68 | updated_on = db.Column(db.DateTime, index=True, default=datetime.utcnow) |
| 69 | token = db.Column(db.String(32), index=True, unique=True) |
| 70 | token_expiration = db.Column(db.DateTime) |
| 71 | |
| 72 | bookings = db.relationship('Booking', backref='user', lazy='dynamic') |
| 73 | |
| 74 | def __repr__(self): |
| 75 | return ('\tID: {}\n' |
| 76 | '\tUsername: {}\n' |
| 77 | '\tFullname: {}\n' |
| 78 | '\tEmail: {}\n' |
Fatih Degirmenci | c2a4601 | 2019-09-30 12:07:45 +0200 | [diff] [blame] | 79 | '\tPassword Hash: {}\n' |
| 80 | '\tSSH Public Key: {}\n' |
| 81 | '\tRegistered On: {}\n' |
| 82 | '\tConfirmed: {}\n' |
| 83 | '\tConfirmed On: {}\n' |
| 84 | '\tUpdated On: {}\n' |
Fatih Degirmenci | 89e4ac9 | 2019-10-01 07:41:29 +0000 | [diff] [blame] | 85 | '\tRole ID: {}\n' |
| 86 | .format(self.id, self.username, self.fullname, self.email, |
| 87 | self.password_hash, self.ssh_public_key, self.registered_on, |
| 88 | self.confirmed, self.confirmed_on, self.updated_on, self.role_id)) |
Fatih Degirmenci | c2a4601 | 2019-09-30 12:07:45 +0200 | [diff] [blame] | 89 | |
| 90 | def set_password(self, password): |
| 91 | self.password_hash = generate_password_hash(password) |
| 92 | |
| 93 | def check_password(self, password): |
| 94 | return check_password_hash(self.password_hash, password) |
| 95 | |
| 96 | def to_dict(self): |
| 97 | data = { |
| 98 | 'id': self.id, |
| 99 | 'role_id': self.role_id, |
| 100 | 'role_name': UserRole.query.filter_by(id=self.role_id).first().role_name, |
| 101 | 'org_id': self.org_id, |
| 102 | 'org_name': UserOrganization.query.filter_by(id=self.org_id).first().org_name, |
| 103 | 'username': self.username, |
| 104 | 'email': self.email, |
| 105 | 'ssh_public_key': self.ssh_public_key, |
| 106 | 'registered_on': self.registered_on.isoformat(), |
| 107 | 'confirmed': self.confirmed |
| 108 | } |
| 109 | return data |
| 110 | |
| 111 | def from_dict(self, data, new_user=False): |
| 112 | for field in ['confirmed', 'org_id', 'role_id']: |
| 113 | if field in data: |
| 114 | setattr(self, field, data[field]) |
| 115 | if new_user and 'password' in data: |
| 116 | self.set_password(data['password']) |
| 117 | |
| 118 | def get_token(self, expires_in=3600): |
| 119 | now = datetime.utcnow() |
| 120 | if self.token and self.token_expiration > now + timedelta(seconds=60): |
| 121 | return self.token |
| 122 | self.token = base64.b64encode(os.urandom(24)).decode('utf-8') |
| 123 | self.token_expiration = now + timedelta(seconds=expires_in) |
| 124 | db.session.add(self) |
| 125 | return self.token |
| 126 | |
| 127 | def revoke_token(self): |
| 128 | self.token_expiration = datetime.utcnow() - timedelta(seconds=1) |
| 129 | |
| 130 | @staticmethod |
| 131 | def check_token(token): |
| 132 | user = User.query.filter_by(token=token).first() |
| 133 | if user is None or user.token_expiration < datetime.utcnow(): |
| 134 | return None |
| 135 | return user |
| 136 | |
| 137 | @staticmethod |
| 138 | def check_none_regular_user(token): |
| 139 | user = User.query.filter_by(token=token).first() |
| 140 | current_user_role_id = user.role_id |
| 141 | regular_user_role_id = UserRole.query.filter_by(role_name='regular').first().id |
| 142 | if current_user_role_id == regular_user_role_id: |
| 143 | return None |
| 144 | return user |
| 145 | |
| 146 | class Booking(PaginatedAPIMixin, db.Model): |
| 147 | id = db.Column(db.Integer, primary_key=True) |
| 148 | user_id = db.Column(db.Integer, db.ForeignKey('user.id')) |
| 149 | status_id = db.Column(db.Integer, db.ForeignKey('booking_status.id')) |
| 150 | booking_length = db.Column(db.Integer) |
| 151 | created_on = db.Column(db.DateTime, index=True, default=datetime.utcnow) |
| 152 | updated_on = db.Column(db.DateTime, index=True, default=datetime.utcnow) |
| 153 | expires_on = db.Column(db.DateTime, index=True, default=datetime.utcnow) |
| 154 | heat_stack_name = db.Column(db.String(64)) |
| 155 | floating_ip = db.Column(db.String(64)) |
| 156 | engine_version = db.Column(db.String(64)) |
| 157 | stack = db.Column(db.String(64)) |
| 158 | stack_version = db.Column(db.String(64)) |
| 159 | scenario = db.Column(db.String(64)) |
| 160 | scenario_version = db.Column(db.String(64)) |
| 161 | scenario_deploy_log_url = db.Column(db.String(256)) |
| 162 | |
| 163 | def __repr__(self): |
| 164 | return ('\tID: {}\n' |
| 165 | '\tUser ID: {}\n' |
| 166 | '\tStatus: {}\n' |
| 167 | '\tCreated: {}\n' |
| 168 | '\tUpdated: {}\n' |
| 169 | '\tExpires: {}\n' |
| 170 | '\tHeat Stack Name: {}\n' |
| 171 | '\tFloating IP: {}\n' |
| 172 | '\tStack Name: {}\n' |
| 173 | '\tStack Version: {}\n' |
| 174 | '\tScenario: {}\n' |
| 175 | '\tScenario Version: {}\n' |
| 176 | .format(self.id, self.user_id, self.status, self.created, |
| 177 | self.updated, self.expires, self.heat_stack_name, self.floating_ip, |
| 178 | self.engine_version, self.stack, self.stack_version, self.scenario, self.scenario_version)) |
| 179 | |
| 180 | def to_dict(self): |
| 181 | data = { |
| 182 | 'id': self.id, |
| 183 | 'user_id': self.user_id, |
| 184 | 'booking_length': self.booking_length, |
| 185 | 'heat_stack_name': self.heat_stack_name, |
| 186 | 'scenario_deploy_log_url': self.scenario_deploy_log_url, |
| 187 | 'engine_version': self.engine_version, |
| 188 | 'scenario_version': self.scenario_version, |
| 189 | 'floating_ip': self.floating_ip, |
| 190 | 'created_on': self.created_on.isoformat(), |
| 191 | 'stack': self.stack, |
| 192 | 'status_text': BookingStatus.query.filter_by(id=self.status_id).first().status_text, |
| 193 | 'status_id': self.status_id, |
| 194 | 'scenario': self.scenario |
| 195 | } |
| 196 | return data |
| 197 | |
| 198 | def from_dict(self, data, new_booking=False): |
| 199 | for field in ['status_text', 'floating_ip', 'heat_stack_name', \ |
| 200 | 'scenario_deploy_log_url', 'engine_version', \ |
| 201 | 'scenario_version']: |
| 202 | if field in data: |
| 203 | setattr(self, field, data[field]) |
| 204 | |
| 205 | class BookingStatus(db.Model): |
| 206 | id = db.Column(db.Integer, primary_key=True) |
| 207 | status_text = db.Column(db.String(64), default='new') |
| 208 | status_description = db.Column(db.String(128), default='New booking request') |
| 209 | |
| 210 | bookings = db.relationship('Booking', backref='booking_status', lazy='dynamic') |
| 211 | |
| 212 | def __repr__(self): |
| 213 | return ('\tID: {}\n' |
| 214 | '\tStatus: {}\n' |
| 215 | .format(self.id, self.status)) |
| 216 | |
| 217 | class Stack(db.Model): |
| 218 | id = db.Column(db.Integer, primary_key=True) |
| 219 | stack_name = db.Column(db.String(64), index=True, unique=True) |
| 220 | stack_description = db.Column(db.String(128), unique=True) |
| 221 | is_sandbox_enabled = db.Column(db.Boolean(8), default=False) |
| 222 | |
| 223 | scenarios = db.relationship('Scenario', backref='stack', lazy='dynamic') |
| 224 | |
| 225 | def __repr__(self): |
| 226 | return ('\tID: {}\n' |
| 227 | '\tStack name: {}\n' |
| 228 | '\tStack description: {}\n' |
| 229 | '\tIs sandbox enabled: {}\n' |
| 230 | .format(self.id, self.stack_name, self.stack_description, self.is_sandbox_enabled)) |
| 231 | |
| 232 | class Scenario(db.Model): |
| 233 | id = db.Column(db.Integer, primary_key=True) |
| 234 | stack_id = db.Column(db.Integer, db.ForeignKey('stack.id')) |
| 235 | scenario_name = db.Column(db.String(64), index=True, unique=True) |
| 236 | scenario_description = db.Column(db.String(128), unique=True) |
| 237 | is_sandbox_enabled = db.Column(db.Boolean(8), default=False) |
| 238 | |
| 239 | def __repr__(self): |
| 240 | return ('\tID: {}\n' |
| 241 | '\tScenario name: {}\n' |
| 242 | '\tScenario description: {}\n' |
| 243 | '\tIs sandbox enabled: {}\n' |
| 244 | .format(self.id, self.scenario_name, self.scenario_description, self.is_sandbox_enabled)) |