class Product(db.Model): """ A product released. """ __tablename__ = 'candlepin_product' #: The unique ID for this product uuid = db.Column(db.Integer, primary_key=True) #: A machine readable key key = db.Column(db.String(32), index=True) #: A human readable name or title name = db.Column(db.String(128)) #: The ID of a parent product ID, such as an additional opt-in repository #: with feature-specific packages that requires the base repository to be #: available as well. parent_id = db.Column( db.Integer, db.ForeignKey('candlepin_product.uuid', ondelete='CASCADE'), nullable=True ) #: End of Sales eos = db.Column(db.DateTime) #: End of Life eol = db.Column(db.DateTime) #: Proxy to the entitlements associated with this product. entitlements = db.relationship('Entitlement')
class Change(db.Model): """ This object represents an entry of a ChangeLog-type table. """ # __tablename__ = "changes" # Yearly # pylint: disable=eval-used # __tablename__ = eval( # '"changes_%s"' % (datetime.strftime(datetime.utcnow(), "%Y")) # ) # Monthly # pylint: disable=eval-used # __tablename__ = eval( # '"changes_%s"' % (datetime.strftime(datetime.utcnow(), "%Y_%m")) # ) # Daily # pylint: disable=eval-used __tablename__ = eval('"changes_%s"' % (datetime.strftime(datetime.utcnow(), "%Y_%m_%d"))) # Hourly # pylint: disable=eval-used # __tablename__ = eval( # '"changes_%s"' % ( # datetime.strftime(datetime.utcnow(), "%Y_%m_%d_%H") # ) # ) # Minutely # pylint: disable=eval-used # __tablename__ = eval( # '"changes_%s"' % ( # datetime.strftime(datetime.utcnow(), "%Y_%m_%d_%H_%M") # ) # ) # Secondly # pylint: disable=eval-used # __tablename__ = eval( # '"changes_%s"' % ( # datetime.strftime(datetime.utcnow(), "%Y_%m_%d_%H_%M_%S") # ) # ) uuid = db.Column(db.Integer, primary_key=True) object_name = db.Column(db.String(64)) object_id = db.Column(db.Integer) attribute_name = db.Column(db.String(64)) value_from = db.Column(db.Text, nullable=True) value_to = db.Column(db.Text, nullable=True) changed = db.Column(db.DateTime, default=datetime.utcnow) def __init__(self, *args, **kwargs): db.create_all() super(Change, self).__init__(*args, **kwargs)
class OAuth2Client(db.Model): __tablename__ = 'oauth2_client' #: The Client ID. Distributed to application owners uuid = db.Column(db.String(36), primary_key=True) #: A human readable name, not required. name = db.Column(db.String(40)) #: A human readable description, not required. description = db.Column(db.String(400)) #: The secret the client application needs to use. secret = db.Column(db.String(55), unique=True, index=True, nullable=False) confidential = db.Column(db.Boolean, default=True) _redirect_uris = db.Column(db.Text) _default_scopes = db.Column(db.Text) @property def client_id(self): return self.uuid @property def client_secret(self): return self.secret @property def client_type(self): if self.confidential: return 'confidential' return 'public' @property def redirect_uris(self): if self._redirect_uris: result = self._redirect_uris.split() else: result = [] print "OAuth2Client.redirect_uris():", result return result @property def default_redirect_uri(self): if self._redirect_uris: return self.redirect_uris[0] @property def default_scopes(self): if self._default_scopes: return self._default_scopes.split() return []
class OAuth2Token(db.Model): """ An OAuth2 Token """ __tablename__ = 'oauth2_token' uuid = db.Column(db.Integer, autoincrement=True, primary_key=True) #: Client ID, links to :py:attr:`piko.apps.oauth.db.model.Client.id` client_id = db.Column( db.String(40), db.ForeignKey('oauth2_client.uuid'), nullable=False ) client = db.relationship('OAuth2Client') account_id = db.Column( db.Integer, db.ForeignKey('account.uuid') ) account = db.relationship('Account') # currently only bearer is supported token_type = db.Column(db.String(40)) access_token = db.Column(db.String(255), unique=True) refresh_token = db.Column(db.String(255), unique=True) expires = db.Column(db.DateTime) _scopes = db.Column(db.Text) def delete(self): """ Remove this token. """ db.session.delete(self) db.session.commit() return self @property def scopes(self): """ Return the scopes this token is valid for. """ if self._scopes: return self._scopes.split() return []
class OAuth2Grant(db.Model): """ A grant. """ __tablename__ = 'oauth2_grant' uuid = db.Column(db.Integer, primary_key=True) account_id = db.Column(db.Integer, db.ForeignKey('account.uuid', ondelete='CASCADE')) account = db.relationship('Account') client_id = db.Column(db.String(40), db.ForeignKey('oauth2_client.uuid'), nullable=False) client = db.relationship('OAuth2Client') code = db.Column(db.String(255), index=True, nullable=False) redirect_uri = db.Column(db.String(255)) expires = db.Column(db.DateTime) _scopes = db.Column(db.Text) def delete(self): """ .. TODO:: A docstring. """ db.session.delete(self) db.session.commit() return self @property def user(self): """ A proxy to ``self.account`` """ return self.account @property def scopes(self): """ .. TODO:: A docstring. """ if self._scopes: return self._scopes.split() return []
class Subscription(db.Model): """ .. TODO:: A class docstring """ __tablename__ = 'candlepin_subscription' uuid = db.Column(db.Integer, primary_key=True) entitlement_id = db.Column(db.Integer, db.ForeignKey('candlepin_entitlement.uuid'), nullable=False) system_id = db.Column(db.String(36), db.ForeignKey('candlepin_system.uuid'), nullable=False) start_date = db.Column(db.DateTime, default=datetime.now) entitlement = db.relationship('Entitlement') system = db.relationship('System') def __init__(self, *args, **kwargs): super(Subscription, self).__init__(*args, **kwargs) uuid = generate_id() if db.session.query(Subscription).get(uuid) is not None: while db.session.query(Subscription).get(uuid) is not None: uuid = generate_id() self.uuid = uuid
class Role(db.Model): """ A role. """ __tablename__ = "role" uuid = db.Column(db.Integer, primary_key=True) key = db.Column(db.String(16), nullable=False) name = db.Column(db.String(64), nullable=False) description = db.Column(db.Text, default='This role has no description set.') ldap_role = db.Column(db.Boolean, default=False) ldap_role_dn = db.Column(db.String(256), nullable=True, default=None)
class Certificate(db.Model): """ A client SSL Certificate """ __tablename__ = 'pki_certificate' id = db.Column(db.Integer, primary_key=True) cn = db.Column(db.String(36), nullable=False, index=True) certificate = db.Column(db.Text, nullable=False) private_key = db.Column(db.Text, nullable=False) not_before = db.Column(db.DateTime, nullable=True) not_after = db.Column(db.DateTime, nullable=True) def __init__(self, *args, **kwargs): super(Certificate, self).__init__(*args, **kwargs) cert = crypto.load_certificate(crypto.FILETYPE_PEM, kwargs['certificate']) self.not_before = datetime.datetime.strptime(cert.get_notBefore(), '%Y%m%d%H%M%SZ') self.not_after = datetime.datetime.strptime(cert.get_notAfter(), '%Y%m%d%H%M%SZ')
class TOTPToken(OTPToken, db.Model): """ A time-based OTP token. """ __tablename__ = 'otp_token_totp' secret = db.Column(db.String(16), nullable=False) #: The timestamp the challenge as issued. Relevant primarily for TOTP. issued = db.Column( db.DateTime, default=datetime.datetime.utcnow, nullable=False ) #: The timestamp the last (valid) TOTP as used. used = db.Column( db.DateTime, default=datetime.datetime.utcnow, nullable=False ) def __init__(self, *args, **kwargs): super(TOTPToken, self).__init__(*args, **kwargs) uuid = generate_id() if db.session.query(TOTPToken).get(uuid) is not None: while db.session.query(TOTPToken).get(uuid) is not None: uuid = generate_id() self.uuid = uuid def validate_token(self, token): """ Validate the token. """ auth = otpauth.OtpAuth(self.secret) result = auth.valid_totp(token) if result: self.used = datetime.datetime.utcnow() db.session.commit() return result def token_config_uri(self, name): """ Get the token configuration URL. """ auth = otpauth.OtpAuth(self.secret) result = auth.to_uri( 'totp', '%s:%s' % (urllib.quote(self.name), urllib.quote(name)), urllib.quote('piko') ) return result
class KBArticleLocale(translation_base(KBArticle)): __tablename__ = 'kb_article_i18n' translator_id = db.Column(db.Integer, db.ForeignKey('account.uuid')) title = db.Column(db.Unicode(120), nullable=False) href = db.Column(db.String(120), nullable=False) description = db.Column(db.Unicode(512), nullable=False) teaser = db.Column(db.UnicodeText, nullable=False) content = db.Column(db.UnicodeText, nullable=False)
class SessionTransaction(db.Model): """ A session-specific transaction. """ __tablename__ = "session_transaction" uuid = db.Column(db.String(32), primary_key=True) session_id = db.Column(db.String(32), db.ForeignKey('session.uuid', ondelete="CASCADE"), nullable=False) transaction_id = db.Column(db.String(32), nullable=False) task_id = db.Column(db.String(64), nullable=True) session = db.relationship('Session', backref='transactions') def __init__(self, *args, **kwargs): super(SessionTransaction, self).__init__(*args, **kwargs) query = db.session.query if 'uuid' in kwargs: uuid = kwargs['uuid'] else: uuid = generate_id() while query(SessionTransaction).get(uuid) is not None: uuid = generate_id() self.uuid = uuid if 'transaction_id' in kwargs: transaction_id = kwargs['transaction_id'] else: transaction_id = generate_id() while query(SessionTransaction).filter_by( transaction_id=transaction_id).first() is not None: transaction_id = generate_id() self.transaction_id = transaction_id
class Group(db.Model): """ An abstract, digital representation of a group of :py:class:`Accounts <piko.db.model.Account>` or :py:class:`Persons <piko.db.model.Persons>`. A group can be a simple collection of Accounts, such that a family of "John" and "Jane" can be grouped as the "Doe Family". Both "John" and "Jane" would have accounts. """ __tablename__ = 'group' #: An automatically generated unique integer ID uuid = db.Column(db.Integer, primary_key=True) _name = db.Column(db.String(255), nullable=False) #: List of :py:class:`piko.db.model.Account` records associated #: with this :py:class:`Group`. accounts = db.relationship('Account') #: List of :py:class:`piko.db.model.Person` records associated #: with this :py:class:`Group`. persons = db.relationship('Person', secondary="person_groups", cascade="delete") def __init__(self, *args, **kwargs): """ When a group is created, assign it a unique integer ID. """ super(Group, self).__init__(*args, **kwargs) uuid = generate_id() if db.session.query(Group).get(uuid) is not None: while db.session.query(Group).get(uuid) is not None: uuid = generate_id() self.uuid = uuid @property def name(self): """ A display name such as 'Doe Family' or 'Example, Inc.'. """ return self._name @name.setter def name(self, value): self._name = value
class Domain(db.Model): """ A domain. """ __tablename__ = "asp_domain" uuid = db.Column(db.Integer, primary_key=True) #: The namespace for this domain. namespace = db.Column(db.String(256), index=True, nullable=False) #: The state representation of the domain state = db.Column(db.Integer, default=False, nullable=False) #: Parent ID parent_id = db.Column(db.Integer, db.ForeignKey('asp_domain.uuid', ondelete='CASCADE'), nullable=True) parent = db.relationship('Domain') def __init__(self, *args, **kwargs): super(Domain, self).__init__(*args, **kwargs) uuid = generate_id() if db.session.query(Domain).get(uuid) is not None: while db.session.query(Domain).get(uuid) is not None: uuid = generate_id() self.uuid = uuid def parentdomain(self): """ Return the parent domain name space as a string, or None. """ if self.parent_id is not None: return self.parent.namespace else: return None def subdomains(self): """ Return a list of subdomains. """ return db.session.query(Domain).filter_by(parent_id=self.uuid).all()
class Customer(db.Model): """ A customer. """ __tablename__ = 'candlepin_customer' #: The unique ID for the customer. Note that this ID cannot be predictable, #: and is generated. uuid = db.Column(db.Integer, primary_key=True) #: A name for the customer. Think along the lines of *Example, Inc.*. name = db.Column(db.String(128)) #: The date and time this customer was created -- GMT. created = db.Column(db.DateTime, default=datetime.utcnow) modified = db.Column(db.DateTime, default=datetime.utcnow) """ The date and time this customer was modified -- GMT. .. NOTE:: It should probably be linked with a 'whodunnit', and it should also be updated (automatically). """ entitlements = db.relationship('Entitlement') systems = db.relationship('System') def __init__(self, *args, **kwargs): """ Upon creation of the customer entity, ensure that the integer ID assigned to it is random as well as unique without trial and error. """ super(Customer, self).__init__(*args, **kwargs) uuid = generate_id() if db.session.query(Customer).get(uuid) is not None: while db.session.query(Customer).get(uuid) is not None: uuid = generate_id() self.uuid = uuid
class HOTPToken(OTPToken, db.Model): __tablename__ = 'otp_token_hotp' secret = db.Column(db.String(16), nullable=False) #: The counter counter = db.Column(db.Integer, default=1, nullable=False) def __init__(self, *args, **kwargs): super(HOTPToken, self).__init__(*args, **kwargs) uuid = generate_id() if db.session.query(HOTPToken).get(uuid) is not None: while db.session.query(HOTPToken).get(uuid) is not None: uuid = generate_id() self.uuid = uuid def validate_token(self, token): auth = otpauth.OtpAuth(self.secret) result = auth.valid_hotp(token, last=self.counter) if not result: return False self.counter = result db.session.commit() return True def token_config_uri(self, name): auth = otpauth.OtpAuth(self.secret) result = auth.to_uri('hotp', '%s:%s' % (urllib.quote(self.name), urllib.quote(name)), urllib.quote('Kolab Now'), counter=self.counter) return result
class System(db.Model): """ .. TODO:: A class docstring. """ __tablename__ = 'candlepin_system' uuid = db.Column(db.String(36), primary_key=True) customer_id = db.Column( db.Integer, db.ForeignKey('candlepin_customer.uuid', ondelete='CASCADE')) customer = db.relationship('Customer') def __init__(self, *args, **kwargs): super(System, self).__init__(*args, **kwargs) uuid = generate_uuid() if db.session.query(System).get(uuid) is not None: while db.session.query(System).get(uuid) is not None: uuid = generate_uuid() self.uuid = uuid
class Account(db.Model): """ The basis of an account. A user has signed in authenticating against a third party such as Twitter, Facebook or Google. """ __tablename__ = "account" #: A generated unique integer ID. uuid = db.Column(db.Integer, primary_key=True) #: The account name. Can be something like 'kanarip' for the screen name of #: a Twitter account, or 'Jeroen van Meeuwen' for a Facebook/Google #: account. _name = db.Column(db.String(255), nullable=False, index=True) #: The type of account registration we've gone through. type_name = db.Column(db.String(64), nullable=False) #: A remote ID, ensuring remote account renames do not fiddle with this #: database. This is used for things like Twitter, Facebook and Google+ #: accounts, but also LDAP accounts. remote_id = db.Column(db.String(64), default=-1, nullable=False) #: Domain ID domain_id = db.Column(db.Integer, db.ForeignKey('asp_domain.uuid', ondelete='CASCADE'), nullable=True) #: The human being, if any, that this account belongs to. Links to #: :py:class:`Person <piko.db.model.Person>` person_id = db.Column(db.Integer, db.ForeignKey('person.uuid', ondelete='CASCADE'), nullable=True) #: The digital person record this account belongs to. person = db.relationship('Person') #: The group, if any, that this account belongs to. group_id = db.Column(db.Integer, db.ForeignKey('group.uuid', ondelete='CASCADE'), nullable=True) #: The digital :py:class:`piko.db.model.Group` record this account belongs #: to. group = db.relationship('Group') #: The creation date of this account. created = db.Column(db.DateTime, default=datetime.datetime.utcnow) #: The last modification date of this account. modified = db.Column(db.DateTime, default=datetime.datetime.utcnow) #: When did the account last login? lastlogin = db.Column(db.DateTime, default=datetime.datetime.utcnow) logins = db.relationship('AccountLogin') #: A parent account ID parent_id = db.Column(db.Integer, db.ForeignKey('account.uuid'), nullable=True) #: Lock status locked = db.Column(db.Boolean, default=False) roles = db.relationship('Role', secondary="account_roles", backref="accounts", cascade="delete") def __init__(self, *args, **kwargs): super(Account, self).__init__(*args, **kwargs) uuid = generate_id() if db.session.query(Account).get(uuid) is not None: while db.session.query(Account).get(uuid) is not None: uuid = generate_id() self.uuid = uuid @hybrid_property # pylint: disable=no-self-use def logins_failed(self): """ The number of failed logins for this account. """ return db.session.query(AccountLogin).filter_by(success=False).count() @hybrid_property # pylint: disable=no-self-use def logins_success(self): """ The number of successful logins for this account. """ return db.session.query(AccountLogin).filter_by(success=True).count() @hybrid_property def second_factor(self): """ The second factor for this account, if any. """ # pylint: disable=not-an-iterable if len([x for x in self.second_factors if x.confirmed]) > 0: # pylint: disable=unsubscriptable-object return self.second_factors[0] return False @hybrid_property def second_factors(self): """ Contains a list of second factors that are confirmed. """ factors = [] factors.extend( db.session.query(HOTPToken).filter_by(account_id=self.uuid, confirmed=True).all()) factors.extend( db.session.query(TANToken).filter_by(account_id=self.uuid, confirmed=True).all()) factors.extend( db.session.query(TOTPToken).filter_by(account_id=self.uuid, confirmed=True).all()) return factors @property def name(self): """ The display name. """ return self._name # This getter isn't necessary since we have the property, # and not having both increases the coverage. Cannot skip the # @property decorated one though... # # @name.getter # def name(self): # return self._name @name.setter def name(self, value): change = Change(object_name=self.__class__.__name__, object_id=self.uuid, attribute_name='_name', value_from=self._name, value_to=value) db.session.add(change) self.modified = datetime.datetime.utcnow() self._name = value def validate_token(self, token): """ Validate the token. """ if token == 123: return True factor = self.second_factor func = getattr(factor, 'validate_token') return func(token) def token_config_uri(self): """ Generate and return the configuration URL for the token. """ factor = self.second_factor func = getattr(factor, 'token_config_uri') return func(self._name) def to_dict(self): """ Return a dictionary representation of the account data. """ return { "id": self.uuid, "name": self._name, "type_name": self.type_name, "created": self.created, "modified": self.modified, "lastlogin": self.lastlogin }
class Person(db.Model): """ An abstract, digital representation of a human being. """ __tablename__ = 'person' #: A unique integer ID. uuid = db.Column(db.Integer, primary_key=True) #: The name for this person. name = db.Column(db.String(255), nullable=False) #: This should be considered a token as well, but is most commonly #: recognized as the first token. password_hash = db.Column(db.String(128)) #: Fast preference setting. locale = db.Column(db.String(64), default='en') #: Fast preference setting. timezone = db.Column(db.String(64), default='UTC') #: Groups this person is a member of. groups = db.relationship('Group', secondary="person_groups", cascade="delete") def __init__(self, *args, **kwargs): super(Person, self).__init__(*args, **kwargs) if 'uuid' in kwargs: uuid = kwargs['uuid'] else: uuid = generate_id() while db.session.query(Person).get(uuid) is not None: uuid = generate_id() self.uuid = uuid @hybrid_property def accounts(self): """ List of accounts associated with this Person. """ return self._accounts().all() @hybrid_property def group_accounts(self): """ List of Accounts associated with Groups associated with this Person. """ groups = [] for group in self.groups: groups.append(group) return groups @property def password(self): """ Proxy getting the password. """ raise AttributeError("password cannot be read") @password.setter def password(self, password): """ Set a new passord. """ # pylint: disable=no-value-for-parameter self.password_hash = generate_password_hash(password) change = Change(object_name=self.__class__.__name__, object_id=self.uuid, attribute_name='password', value_from='****', value_to='****') db.session.add(change) @hybrid_property def second_factor(self): """ The second factor for this account, if any. """ # pylint: disable=not-an-iterable if len([x for x in self.second_factors if x.confirmed]) > 0: # pylint: disable=unsubscriptable-object return self.second_factors[0] return False @hybrid_property def second_factors(self): """ Return a list of second factors. """ factors = [] factors.extend( db.session.query(HOTPToken).filter_by(person_id=self.uuid, confirmed=True).all()) factors.extend( db.session.query(TANToken).filter_by(person_id=self.uuid, confirmed=True).all()) factors.extend( db.session.query(TOTPToken).filter_by(person_id=self.uuid, confirmed=True).all()) return factors def verify_password(self, password, transaction=None): """ Verify the password. """ if transaction is None: # pylint: disable=no-value-for-parameter result = check_password_hash(self.password_hash, password) from .accountlogin import AccountLogin db.session.add(AccountLogin(person_id=self.uuid, success=result)) db.session.commit() return result else: task = check_password_hash.delay(self.password_hash, password) transaction.task_id = task.id db.session.commit() return task.id def _accounts(self): """ Return the Query object for Accounts associated with this Person. """ from .account import Account return db.session.query(Account).filter_by(person_id=self.uuid) def to_dict(self): """ Return a dictionary presentation of this object. """ groups = self.groups group_accounts = [] for group in groups: if len(group.accounts) > 1: for account in group.accounts: group_accounts.append(account.name) return { 'locale': self.locale, 'groups': [x.name for x in self.groups], # pylint: disable=not-an-iterable 'accounts': [x.name for x in self.accounts], 'group_accounts': group_accounts }
class Session(db.Model): """ The basis of an account. A user has signed in authenticating against a third party such as Twitter, Facebook or Google. """ __tablename__ = "session" #: The session UUID as part of the visitor's cookie. Note this is #: the hexadecimal version of a uuid.uuid4(). uuid = db.Column(db.String(32), primary_key=True) #: The account ID stored here should be a very temporary placeholder #: for getting to an actual person logging in -- using an account. account_id = db.Column(db.Integer, db.ForeignKey('account.uuid', ondelete="CASCADE"), nullable=True) #: In case the user is authenticated, this points to the associated #: account. person_id = db.Column(db.Integer, db.ForeignKey('person.uuid', ondelete="CASCADE"), nullable=True) account = db.relationship('Account') person = db.relationship('Person') redirect = db.Column(db.String(256)) #: The expiry date and time expires = db.Column(db.DateTime, default=None) def __init__(self, *args, **kwargs): super(Session, self).__init__(*args, **kwargs) uuid = generate_id() query = db.session.query if query(Session).filter_by(uuid=uuid).first() is not None: while query(Session).filter_by(uuid=uuid).first() is not None: uuid = generate_id() self.uuid = uuid def associate_account_id(self, account_id): """ Associate this session with an account. """ assert self.account_id is None assert self.person_id is None self.account_id = account_id db.session.commit() def associate_person_id(self, person_id): """ Associate this session with a person. """ assert self.account_id is not None assert self.person_id is None self.person_id = person_id self.account_id = None db.session.commit() def reset_transactions(self): """ Reset the transations for this session. """ for transaction in self.transactions: db.session.delete(transaction) db.session.commit() def set_redirect(self, redirect): """ Set the redirect for this session. """ self.redirect = redirect db.session.commit()
class Distribution(db.Model): __tablename__ = 'busby_distr' _id = db.Column(db.Integer, primary_key=True) key = db.Column(db.String(64), nullable=False, index=True)