class _SiteConfig: __borg_state = {} def __init__(self): self.__dict__ = self.__borg_state try: self._setup except AttributeError: self._setup = False if not self._setup: self.event_log = EventLog() self._setup = True def add(self, version, name=None, email=None): """ Adds an entry to the site config database. """ sc = SiteConfig(version) sc.version = version if name: sc.site_name = name self.event_log.add('update_sitename', -1, False, '', name) if email: sc.admin_email = email mainDB.session.add(sc) mainDB.session.commit() self.event_log.add('update_siteconf', -1, False, '', str(version)) def getCurrent(self): return SiteConfig.query.order_by(SiteConfig.id.desc()).first()
def static_page(): """ Render the administrative static page """ user_db = UserDB() role_db = RoleDB() event_log = EventLog() icebox = Icebox() if current_user.is_authenticated() and current_user.is_active(): is_admin = user_db.in_group(current_user, mainApp.config['ADMIN_GROUP']) all_activities = set() for m in role_db.get_roles(current_user): acts = role_db.get_activities(m.role_id) for act in acts: if acts[act]: all_activities.add(act) can_admin_static = 'make_static' in all_activities can_admin_static_now = 'make_static_now' in all_activities if is_admin or can_admin_static: if request.method == 'POST': if 'sched_rebuild' in request.form: event_log.add('rebuild_static', current_user.id) flash(_('Full site rebuild scheduled.')) elif 'incr_build' in request.form: icebox.generate_pages() flash(_('New/updated pages generated.')) elif 'rebuild_now' in request.form: if can_admin_static_now: icebox.generate_pages(True) flash(_('Full site rebuilt')) else: flash(_('Not authorized for full site rebuild', 'error')) return render_template('admin_static.html', state=get_state(), can_admin_static=can_admin_static, can_admin_static_now=can_admin_static_now, title=_('Administrate Static Site'), sched_rebuild_text=_('Schedule a full site rebuild'), sched_rebuild_button=_('Schedule'), incr_build_text=_('Perform an incremental build'), incr_build_button=_('Incremental'), rebuild_now_text=_('Perform a full rebuild now'), rebuild_now_button=_('Rebuild')) else: return _not_auth() else: return _not_auth()
def setup_DB(): """ Called when the DB is to be initialized. Should only be called once. """ event_log = EventLog() sc = _SiteConfig() userDB = UserDB() roleDB = RoleDB() mainDB.create_all() event_log.add('db_setup', -1, True) default_group = userDB.add_group(mainApp.config['DEFAULT_GROUP'], mainApp.config['DEFAULT_GROUP_DESC']) default_acts = get_activity_dict(False) default_acts.update(mainApp.config['DEFAULT_ROLE_ACTIVITIES']) dummy = roleDB.add_role( mainApp.config['DEFAULT_ROLE_NAME'], mainApp.config['DEFAULT_ROLE_DESC'], default_acts) user = userDB.add(mainApp.config['ADMIN_USER'], mainApp.config["ADMIN_PASSWD"], mainApp.config['ADMIN_FULLNAME']) admin_group = userDB.add_group(mainApp.config['ADMIN_GROUP'], mainApp.config['ADMIN_GROUP_DESC'], user.id) if not userDB.update_primary(user, admin_group): raise SetupError( 'Could not assign the admin user "{0}" primary group to the ' \ 'admin group "{1}"!'.format(user, admin_group)) # By default, the admin is part of the top level group as well as default dummy = userDB.add_group(mainApp.config['TOP_LEVEL_GROUP'], mainApp.config['TOP_LEVEL_GROUP_DESC'], user.id) userDB.add_to_group(user, default_group) admin_role = get_activity_dict(True) role = roleDB.add_role( mainApp.config['ADMIN_ROLE_NAME'], mainApp.config['ADMIN_ROLE_DESC'], admin_role) roleDB.assign_role(user, mainApp.config['ADMIN_GROUP'], role) roleDB.assign_role(user, mainApp.config['TOP_LEVEL_GROUP'], role) roleDB.assign_role(user, mainApp.config['DEFAULT_GROUP'], role) sc.add(mainApp.noink_version, mainApp.config['SITE_NAME'], mainApp.config['SITE_ADMIN_EMAIL']) event_log.add('db_finish', -1, True)
class UserDB: __borg_state = {} def __init__(self): self.__dict__ = self.__borg_state try: self._setup except AttributeError: self._setup = False if not self._setup: self.eventLog = EventLog() self._setup = True def find_user_by_name(self, username): ''' Finds a user by their username @param username: The username to find. @return The user objects found ''' return User.query.filter_by(name=username).all() def find_user_by_id(self, uid): ''' Finds a user by their user ID. @param uid: The user's ID to find. @return The user object found ''' return User.query.get(uid) def get_users(self, u): ''' Given user information, returns the user object @param u: The user to find. Can be an integer uid, username string, or even a user object. @return the user objects found ''' users = [u] if isinstance(u, IntType): users = [self.find_user_by_id(u)] elif isinstance(u, string_types): users = self.find_user_by_name(u) return users def get_all_users(self, g=None): """ Get all users. Given an optional group, only find users in that primary group. """ if g is None: return User.query.all() else: group = self.get_group(g) return User.query.filter_by(primary_group=group).all() def get_user(self, u): ''' Given user information, returns the user object @param u: The user to find. Can be an integer uid, username string, or even a user object. @return the user object found ''' user = u if isinstance(u, IntType): user = self.find_user_by_id(u) elif isinstance(u, string_types): tu = self.find_user_by_name(u) if len(tu) > 0: user = tu[0] else: user = None return user def add(self, username, password, fullname, bio="", group=None, active=False): ''' Adds a user to the database. @param username: The username to add, must be unique. @param password: The password to use. @param fullname: The user's full name @param bio: The user's bio (optional) @param group: The user's primary group (optional) @param active: Whether the user is active or not @return The user object for the user crated. ''' try: exists = self.find_user_by_name(username) except: exists = False if exists: raise DuplicateUser("{0} already exists in database with id '{1}'". format(username, str(exists))) else: from noink.role_db import RoleDB role_db = RoleDB() passHash = mainCrypt.generate_password_hash(password) u = User(username, fullname, bio, passHash) if group is None: group = self.get_group(mainApp.config['DEFAULT_GROUP']) u.primary_group = group u.active = active mainDB.session.add(u) mainDB.session.commit() self.eventLog.add('add_user', u.id, True, None, username) self.add_to_group(u, group) role_db.assign_role(u, group, mainApp.config['DEFAULT_ROLE_NAME']) return u def create_temp_empty_user(self): """ Returns a temporary, empty user object. """ return User(None, None, None, None) def create_temp_empty_group(self): """ Returns a temporary, empty group object. """ return Group(None, None) def add_group(self, group_name, description='', user_id=None): ''' Adds a new group to the database. @param group_name: The group name to add, must be unique. @param description: (Optional) The group description. @param user_id: (Optional) Single or multiple user IDs to associate with this group. @return The group object created ''' exists = Group.query.filter_by(name=group_name).first() if exists: raise DuplicateGroup("{0} already exists in database with id '{0}'". format(group_name, exists.id)) else: g = Group(group_name, description) mainDB.session.add(g) mainDB.session.flush() if user_id: exists = User.query.filter_by(id=user_id).first() if exists: gm = GroupMapping(g, exists) mainDB.session.add(gm) else: raise UserNotFound( "%s not found in database to match with new group". format(user_id)) mainDB.session.commit() self.eventLog.add('add_group', 0, True, None, group_name) return g def add_to_group(self, u, g): ''' Adds a user to a group. @param u: The user to link. Can be an integer for the uid or a user object @param g: The group to link. Can be an integer for the gid, a string for group name, or a group object ''' user = self.get_user(u) group = self.get_group(g) exist = GroupMapping.query.filter_by(user=user).filter_by( group=group).all() if exist == []: gm = GroupMapping(group, user) mainDB.session.add(gm) mainDB.session.commit() def get_group(self, g): ''' Given a group identifier, return the group object. @param g: The group to return. Can be an integer for the gid or a string of the group's name. @return The group object found, or None. ''' group = g if isinstance(g, IntType): group = Group.query.filter_by(id=g).first() elif isinstance(g, string_types): group = Group.query.filter_by(name=g).first() return group def get_all_groups(self): ''' Return all possible groups. ''' return Group.query.all() def get_users_groups(self, u): ''' Given a user identifier, return the groups it is a member of. @param u: The user. Can be an integer for the uid, a string for the username, or a user object @return A list of groups the user is a member of. ''' user = self.get_user(u) gms = GroupMapping.query.filter_by(user=user) if gms is None: raise UserHasNoGroups( "{0} does not have any group mappings! Every user should be "\ "a member of at least one group!".format(user)) groups = [] for m in gms: groups.append(m.group) return groups def update_primary(self, u, g): ''' Updates the primary group a user belongs to. If the user is not already in that group, they are added to it. @param u: The user to update. Can be an integer for the uid, a username, or a user object @param g: The group to use. Can be an integer for the gid, a group name, or a group object. @return True on success, False on failure ''' user = self.get_user(u) group = self.get_group(g) if isinstance(user, User) and isinstance(group, Group): exists = GroupMapping.query.filter_by( user=user).filter_by(group=group).first() if exists == []: self.add_to_group(u, g) user.primary_group = group mainDB.session.commit() return True else: return False def remove_from_group(self, u, g): """ Removes a group association from a user. """ user = self.get_user(u) group = self.get_group(g) if isinstance(user, User) and isinstance(group, Group): if user.primary_group == group: raise CannotRemovePrimaryGroup( 'Cannot remove {0} from their primary group {1}'.format( user, group)) gm = GroupMapping.query.filter_by(user=user).filter_by( group=group).first() if gm == []: raise UserNotInGroup( 'User {0} was not a member of the group {1}'.format( user, group)) mainDB.session.delete(gm) mainDB.session.commit() self.eventLog.add('rm_from_group', user.id, True, None, user, group) return True return False def update_password(self, u, newpass): ''' Updates the user's password. Assumes that the password has been validated. @param u: The user to update. Can be an integer for the uid, a username, or a user object. @param newpass: The new password. ''' user = self.get_user(u) if user is not None: user.passhash = mainCrypt.generate_password_hash(newpass) mainDB.session.commit() else: raise UserNotFound("User not found in database") def update_user(self, u): ''' Given a user object, update the database with whatever changes it contains. @param u: A user object. ''' if isinstance(u, User): exists = User.query.get(u.id) if exists == []: mainDB.session.add(u) mainDB.session.commit() def update_group(self, g): """ Given a group object, update the database with whatever changes it contains. """ if isinstance(g, Group): exists = Group.query.get(g.id) if exists == []: mainDB.session.add(g) mainDB.session.commit() def in_group(self, u, g): ''' Checks if a user is in a group @param u: The user to link. Can be an integer for the uid or a user object @param g: The group to link. Can be an integer for the gid, a string for group name, or a group object ''' user = self.get_user(u) group = self.get_group(g) exist = GroupMapping.query.filter_by(user=user).filter_by( group=group).first() if exist is None: return False return True def delete(self, u): ''' Deletes a user from the database. @param u: A user to delete. Can be an integer for the uid or a user object ''' user = self.get_user(u) if user is not None: uid = int(user.id) uname = str(user.name) mainDB.session.delete(user) mainDB.session.commit() self.eventLog.add('del_user', uid, True, None, uname) else: raise UserNotFound("User not found in database") def delete_group(self, g): """ Deletes a group from the database. Group can be integer, string, or group object. """ group = self.get_group(g) if group is not None: gid = int(group.id) gname = group.name mainDB.session.delete(group) mainDB.session.commit() self.eventLog.add('del_group', gid, True, None, gname) else: raise GroupNotFound('Group not found in database') def authenticate(self, username, passwd, remember): ''' Authenticates a user. ''' try: users = self.find_user_by_name(username) if len(users) < 1: return False u = users[0] if mainCrypt.check_password_hash(u.passhash, passwd): u.authenticated = True u.active = True mainDB.session.commit() return login_user(u, remember=remember) else: u.authenticated = False u.active = False mainDB.session.commit() return False except: raise return False def logout(self, u): ''' Given a user, will log them out. Returns True on success, False on failure. @param u: A user to logout. Can be a uid or a user object. ''' user = self.get_user(u) if user.authenticated or user.active: user.authenticated = False user.active = False mainDB.session.commit() logout_user() return True else: logout_user() return False
class EntryDB: __borg_state = {} def __init__(self): self.__dict__ = self.__borg_state try: self._setup except AttributeError: self._setup = False if not self._setup: self.event_log = EventLog() self._setup = True def add(self, title, entry, author, group=None, weight=0, url=None, html=False, parent=None, static=False, postdate=datetime.datetime.now()): """ Adds an entry to the system. Will not perform any checks, it will just add this entry. It's not this method's responsibility to check whether or not your entry is a duplicate. The one check it does do is to verify whether a URL is unique or not. :param title: The title of the post. :param entry: The entry of the post. :param author: The user object for the post's author :param group: The (optional) group this post will belong to. If None, use the author's primary group :param url: The (optional) URL for this post. :param html: Flag detailing whether this post is in HTML or not :param parent: The (optional) parent for this post. :param static: (Optional) Whether or not the post is static. :param postdate: (Optional) The date of the post, current date default. :returns: New entry object just added """ if group is None: group = author.primary_group e = Entry(title, author, group, postdate, entry, weight, url, html, parent, static) if type(url) is StringType: if self.find_by_URL(url): raise DuplicateURL( 'The URL "%s" was already found in the UrlDB!' % url) else: mainDB.session.add(e) mainDB.session.commit() else: mainDB.session.add(e) mainDB.session.commit() # FIXME # When a child is added, we need to also trigger page refreshes for # each member of the parent's children pe = PEntry(e) self.event_log.add('add_entry', author.id, False, pickle(pe), repr(e.title)) return e def add_entry_object(self, entry): """ Given an entry object, add it to the system. :param entry: The entry object to add. """ mainDB.session.add(entry) mainDB.session.commit() pe = PEntry(entry) self.event_log.add('add_entry', entry.author.id, False, pickle(pe), repr(entry.title)) def update_entry(self, entry): """ Given an entry object, update it. :param entry: The entry object to update. """ mainDB.session.commit() pe = PEntry(entry) self.event_log.add('update_entry', entry.author.id, False, pickle(pe), repr(entry.title)) def create_temp_entry(self, title, entry, author, group=None, weight=0, url=None, html=False, parent=None, static=False, postdate=datetime.datetime.now()): ''' Create a temporary entry object. Will not add it to the database. Will not perform any checks, it will just add this entry. It's not this method's responsibility to check whether or not your entry is a duplicate. The one check it does do is to verify whether a URL is unique or not. :param title: The title of the post. :param entry: The entry of the post. :param author: The user object for the post's author :param group: The (optional) group this post will belong to. If None, use the author's primary group :param url: The (optional) URL for this post. :param html: Flag detailing whether this post is in HTML or not :param parent: The (optional) parent for this post. :param static: (Optional) Whether or not the post is static. :returns: New entry object ''' if group is None: group = author.primary_group e = Entry(title, author, group, postdate, entry, weight, url, html, parent, static) if type(url) is StringType: if self.find_by_URL(url): raise DuplicateURL( 'The URL "%s" was already found in the UrlDB!' % url) return e def add_tag(self, tags, entry): """ Adds one or more tags to be associated with an entry. Or, if tags already exist, updates them to also point to the entry. :param tags: Array of one or more tags. :param entry: An entry to associate with the tags. """ for tag in tags: if tag != '': t = Tag.query.filter_by(tag=tag).first() if t == None: t = Tag(tag) mainDB.session.add(t) self.event_log.add('add_tag', entry.author_id, False, tag, tag) exist = TagMapping.query.filter_by(tag_id=t.id).filter_by( entry_id=entry.id).all() if exist == []: tm = TagMapping(t, entry) mainDB.session.add(tm) mainDB.session.commit() tags = self.find_tags_by_entry(entry) def get_tags(self): """ Obtain the tags currently in the system. """ return Tag.query.order_by(Tag.id).all() def update_editor(self, u, e): """ Given a user and an entry, update that entry's editors with that user. :param u: The user who edited the entry. Can be a user object, a uid, or a string representing the username. :param e: The entry. Can be an entry id or an entry object. """ now = datetime.datetime.now() entry = e if type(e) is IntType: entry = Entry.query.filter_by(id=e).first() users = UserDB().get_users(u) if len(users) > 0: user = users[0] editor = Editor.query.filter_by(entry_id=e.id).filter_by( user_id=user.id).first() if editor is None: editor = Editor(user, entry, now) mainDB.session.add(editor) else: editor.date = now mainDB.session.commit() pe = PEntry(entry) self.event_log.add('update_entry', user.id, False, pickle( pe), entry.title) def find_editors_by_entry(self, entry): """ Given an entry, find all editors associated with it. :param entry: The entry. Either an Entry object or entry id. :return Array containing zero or more editors. """ eid = entry if type(entry) is not IntType: eid = entry.id return Editor.query.filter_by(entry_id=eid).all() def find_by_URL(self, url): """ Given a URL, find all entries associated with it. :param url: The URL string. :returns: Array containing one or more entries. """ return Entry.query.filter_by(url=url).all() def find_tags_by_entry(self, entry): """ Given an entry, find all tags associated with it. :param entry: The entry. Either an Entry object or entry id. :returns: Array containing one or more tag objects. """ e = entry if type(entry) is IntType: e = self.find_by_id(entry) tags = [] for tm in TagMapping.query.filter_by(entry_id=e.id).all(): tags.append(tm.tag) return tags def find_by_tags(self, tags): """ Given one or more tags, find all entries tagged with them. :param tags: One or more tags. Tags can be tag ids, tag objects, or tag strings. Tags must be iterable. :returns: Array contating the entry objects. """ # FIXME - The following should order them by weight and date, instead # it will cluster them first by tags. e = [] clauses = [] for tag in tags: tagObj = tag if type(tag) is IntType: tagObj = Tag.query.filter_by(id=tag).first() elif type(tag) is StringType: tagObj = Tag.query.filter_by(tag=tag).first() if tagObj != None: clauses.append(TagMapping.tag == tagObj) # Yeah, double loops is bad if len(clauses) > 0: where = mainDB.or_(*clauses) # XXX - Is this the best way to do this? for mapping in TagMapping.query.filter(where).join(Entry).order_by( Entry.weight).order_by(Entry.date).all(): e.append(Entry.query.get(mapping.entry_id)) return e def find_recent_by_num(self, num, offset=0, weight=True): """ Finds the most recent entries with a maximum of 'num'. :param num: The number of entries to find :param offset: The offset for the entries to find. :param weight: If the weight should be taken into account (defaults to true. :returns: Array containing the entry objects. """ if type(num) is IntType: if weight: return Entry.query.order_by(Entry.date.desc(), Entry.weight).offset(offset).limit(num).all() else: return Entry.query.order_by(Entry.date.desc()).offset( offset).limit(num).all() else: raise TypeError("Expected integer for num") def count(self): """ Returns the number of possible entries. """ return Entry.query.order_by(Entry.date.desc(), Entry.weight).count() def find_by_title(self, title): """ Finds entries based upon the title. Can search using sub-strings. :param title: The title of the post (or sub-string of title). :returns: Array containing one or more entry objects, or None. """ return Entry.query.filter(Entry.Entry.title.like("%%%s%%" % title)).all() def find_by_id(self, num): """ Finds entries based upon the ID. :param num: The numerical ID of the post. :returns: The entry objects, or None. """ return Entry.query.get(num) def delete(self, e): """ Deletes an entry from the database. :param e: An entry to delete. Can be an integer for the entry id or an entry object. """ # FIXME # Should deal with parents and children so as not to leave orphans entry = e if type(e) is IntType: entry = Entry.query.filter_by(id=e).first() pe = PEntry(e) mainDB.session.delete(entry) mainDB.session.commit() self.event_log.add('del_entry', 0, False, pickle(pe), entry.title)
class RoleDB: __borg_state = {} def __init__(self): self.__dict__ = self.__borg_state try: self._setup except AttributeError: self._setup = False if not self._setup: self.eventLog = EventLog() self._setup = True def find_role_by_name(self, rolename): """ Finds a role by it's name. @param rolename: The name of the role. @return The role object found. """ return Role.query.filter_by(name=rolename).first() def find_role_by_id(self, rid): """ Find a role by its role ID. @param rid: The role ID to find. @return The role object found. """ return Role.query.get(rid) def get_role(self, role): """ Given a role identifier, return the role object. @param role: The role. Can be role object, rid, or string name. """ r = role if isinstance(role, IntType): r = self.find_role_by_id(role) elif isinstance(role, string_types): r = self.find_role_by_name(role) return r def get_rolemapping(self, user, group, role): """ Given a user, group and role, will return the rolemap of the three, if it exists. Otherwise will return None. FIXME - Docstring """ r = self.get_role(role) user_db = UserDB() u = user_db.get_user(user) g = user_db.get_group(group) return RoleMapping.query.filter_by(user=u).filter_by(group=g).filter_by(role=r).first() def add_role(self, name, description, activities=None): """ Add a new role to the DB. @param name: Short, descriptive name of the role. Must be unique. @param description: Longer description of the role. @param activities: An activity dict defining the role's activities. If parameter is omitted, then a default dict is used. """ try: exists = self.find_role_by_name(name) except: exists = False if exists: raise DuplicateRole("{0} already exists as a role with id " "'{1}'".format(name, str(exists))) if activities is None: activities = get_activity_dict(False) now = datetime.datetime.now() pact = pickle(activities) role = Role(name, description, pact, now) mainDB.session.add(role) mainDB.session.commit() blob = pickle({"id": role.id}) # XXX - Do we want to use the user ID of the person adding this role? self.eventLog.add("add_role", -1, True, blob, role.name) return role def update_role(self, role): """ Given a role object, update the database with whatever changes it contains. """ if isinstance(role, Role): exists = Role.query.get(role.id) if exists == []: mainDB.session.add(role) mainDB.session.commit() def create_temp_empty_role(self): """ Returns a temporary, empty role object. """ pact = pickle(get_activity_dict(False)) return Role(None, None, pact, None) def update_temp_role_activities(self, role, acts): """ Given a temportary role and updated activies for it, update it. Retuns updated role. """ pact = pickle(acts) role.activities = pact return role def get_activities(self, role): """ Given a role, return the activities that role can do. @param role: The role to use. Can be a role object, a role.id, or a role name. @return Decoded/decoupled activity dictionary """ r = self.get_role(role) if r is not None: return depickle(r.activities) else: return None def assign_role(self, user, group, role): """ Given a user, group and role, assign the user as the role when part of the group. @param user: The user. Can be user object, uid, or string name of the user. @param group: The group. Can be group object, gid, or string name. @param role: The role. Can be role object, rid, or string name. """ userDB = UserDB() u = userDB.get_user(user) g = userDB.get_group(group) r = self.get_role(role) exist = RoleMapping.query.filter_by(user=u).filter_by(group=g).filter_by(role=r).all() if exist == []: rm = RoleMapping(r, u, g) mainDB.session.add(rm) mainDB.session.commit() def revoke_role(self, user, group, role): """ Given a user, group and role, revoke the user's rights to that role when part of the group. @param user: The user. Can be a user object, uid, or string name of the user. @param group: The group. Can be a group object, gid, or string name. @param role: The role. Can be role object, rid, or string name. """ user_db = UserDB() u = user_db.get_user(user) g = user_db.get_group(group) r = self.get_role(role) rmaps = RoleMapping.query.filter_by(user=u).filter_by(group=g).filter_by(role=r).all() for rm in rmaps: mainDB.session.delete(rm) mainDB.session.commit() def delete_role(self, role): """ Given a role, delete it from the database. Role can be integer, string or role object. """ r = self.get_role(role) if role is not None: rid = int(r.id) rname = r.name mainDB.session.delete(r) mainDB.session.commit() self.eventLog.add("del_role", rid, True, None, rname) else: raise RoleNotFound("Role not found in database") def get_roles(self, user, group=None): """ Get the roles a given user has. Optionally, limit by group. @param user: The user. Can be user object, uid, or string name. @param group: Group to limit by. Can be group object, gid, or string name. @return A list of role mappings. """ userDB = UserDB() u = userDB.get_user(user) rm = RoleMapping.query.filter_by(user=u) if group is not None: g = userDB.get_group(group) rm = rm.filter_by(group=g) return rm.all() def get_all_roles(self): """ Get all the available roles """ return Role.query.all()