def view(self, access_id): level = access.get(access_id) auditlog.log(auditlog.CODE_CONTENT_VIEW, target=level) level_includes = lambda have, want: (have & want) != 0 # This is needed because jinja is whining about the '&' in the conditional return render('access/view.html', {'level': level, 'level_includes': level_includes, 'acl_labels': acl.ACCESS_LEVEL_LABELS})
def getPasswordForResource(self, username, resource_id, include_history=False): """ Looks up a password matching specified username @ specified resource name (e.g. hostname). :param username: The username associated with the password. :type username: str :param resource_id: The resource ID or name that we are looking up. :type resource_id: int or str :param include_history: Whether to include history (previous passwords) for this password. :type include_history: bool :return: The matching password, or None if none found. """ try: try: resource_id = int(resource_id) except ValueError: resource = resources.get_by_name(resource_id, assert_single=True) else: resource = resources.get(resource_id) pw = passwords.get_for_resource(username=username, resource_id=resource.id, assert_exists=True) auditlog.log(auditlog.CODE_CONTENT_VIEW, target=pw) return pw.to_dict(decrypt=True, include_history=include_history) except exc.NoSuchEntity: log.info("Unable to find password matching user@resource: {0}@{1}".format(username, resource_id)) raise except: log.exception("Unable to find password for resource.") raise RuntimeError("Unhandled error trying to lookup password for user@resource: {0}@{1}".format(username, resource_id))
def checkpassword(realm, username, password): auth_providers = get_configured_providers() try: for auth_provider in auth_providers: try: auth_provider.authenticate(username, password) except exc.InsufficientPrivileges: # Fail fast in this case; we don't want to continue on to try other authenticators. raise _LoginFailed() except exc.AuthError: # Swallow other auth errors so it goes onto next authenticator in the list. pass except: # Other exceptions needs to get logged at least. log.exception("Unexpected error authenticating user using {0!r}".format(auth_provider)) else: log.info("Authentication succeeded for username {0} using provider {1}".format(username, auth_provider)) break else: log.debug("Authenticators exhausted; login failed.") raise _LoginFailed() except _LoginFailed: auditlog.log(auditlog.CODE_AUTH_FAILED, comment=username) return False else: # Resolve the user using the *current value* for auth_provider (as that is the one that passed the auth. user = auth_provider.resolve_user(username) log.debug("Setting up cherrypy session with username={0}, user_id={1}".format(username, user.id)) cherrypy.session['username'] = username # @UndefinedVariable cherrypy.session['user_id'] = user.id # @UndefinedVariable auditlog.log(auditlog.CODE_AUTH_LOGIN) return True
def process_add(self, **kwargs): # We don't specify the args explicitly since we are using wtforms here. form = GroupAddForm(request_params()) if form.validate(): group = groups.create(name=form.name.data) auditlog.log(auditlog.CODE_CONTENT_ADD, target=group) notify_entity_activity(group, 'created') raise cherrypy.HTTPRedirect('/group/list') else: return render("group/add.html", {'form': form})
def getResource(self, resource_id): try: resource_id = int(resource_id) except ValueError: resource = resources.get_by_name(resource_id, assert_single=True) else: resource = resources.get(resource_id) auditlog.log(auditlog.CODE_CONTENT_VIEW, target=resource) return resource.to_dict(decrypt=True, include_passwords=True)
def getGroup(self, group_id): try: group_id = int(group_id) except ValueError: group = groups.get_by_name(group_id) else: group = groups.get(group_id) auditlog.log(auditlog.CODE_CONTENT_VIEW, target=group) return group.to_dict(include_resources=True)
def process_login(self, **kwargs): form = LoginForm(request_params()) # TODO: Refactor to combine with the ensconce.server:checkpassword method. Lots of duplicate # logic here. AT MINIMUM MAKE SURE THAT ANY CHANGES HERE ARE REFLECTED THERE # This is a "flow-control" exception. ... You'll see. :) class _LoginFailed(Exception): pass try: if not form.validate(): raise _LoginFailed() username = form.username.data password = form.password.data for auth_provider in get_configured_providers(): try: auth_provider.authenticate(username, password) except exc.InsufficientPrivileges: form.username.errors.append(ValidationError("Insufficient privileges to log in.")) # Fail fast in this case; we don't want to continue on to try other authenticators. raise _LoginFailed() except exc.AuthError: # Swallow other auth errors so it goes onto next authenticator in the list. pass except: # Other exceptions needs to get logged at least. log.exception("Unexpected error authenticating user using {0!r}".format(auth_provider)) else: log.info("Authentication succeeded for username {0} using provider {1}".format(username, auth_provider)) break else: log.debug("Authenticators exhausted; login failed.") form.password.errors.append(ValidationError("Invalid username/password.")) raise _LoginFailed() except _LoginFailed: auditlog.log(auditlog.CODE_AUTH_FAILED, comment=username) return render("login.html", {'auth_provider': config['auth.provider'], 'form': form}) else: # Resolve the user using the *current value* for auth_provider (as that is the one that passed the auth. user = auth_provider.resolve_user(username) log.debug("Setting up cherrypy session with username={0}, user_id={1}".format(username, user.id)) cherrypy.session['username'] = username # @UndefinedVariable cherrypy.session['user_id'] = user.id # @UndefinedVariable auditlog.log(auditlog.CODE_AUTH_LOGIN) if form.redirect.data: raise cherrypy.HTTPRedirect(form.redirect.data) else: raise cherrypy.HTTPRedirect("/")
def view(self, group_id): try: group_id = int(group_id) except ValueError: group = groups.get_by_name(group_id) else: group = groups.get(group_id) auditlog.log(auditlog.CODE_CONTENT_VIEW, target=group) return render("group/view.html", {'group': group})
def view(self, resource_id): try: resource_id = int(resource_id) except ValueError: resource = resources.get_by_name(resource_id, assert_single=True) else: resource = resources.get(resource_id) auditlog.log(auditlog.CODE_CONTENT_VIEW, target=resource) return render('resource/view.html', {'resource': resource})
def getPassword(self, password_id, include_history=False): """ Get the specified password record (decrypted) and optionally password history. :param password_id: The ID of password to lookup. :type password_id: int :param include_history: Whether to include history (previous passwords) for this password. :type include_history: bool """ pw = passwords.get(password_id) auditlog.log(auditlog.CODE_CONTENT_VIEW, target=pw) return pw.to_dict(decrypt=True, include_history=include_history)
def deleteResource(self, resource_id): """ Delete a resource (resource must be empty or error will be thrown). :param resource_id: The numeric ID of resource (cannot use resource name here). :type resource_id: int :return: The deleted resource object. :rtype: dict """ r = resources.delete(resource_id) auditlog.log(auditlog.CODE_CONTENT_DEL, target=r) return r.to_dict(decrypt=False)
def process_add(self, **kwargs): form = OperatorAddForm(request_params()) form.access_id.choices = [(l.id, l.description) for l in access.list()] if form.validate(): operator = operators.create(username=form.username.data, password=form.password.data, access_id=form.access_id.data) auditlog.log(auditlog.CODE_CONTENT_ADD, target=operator) notify_entity_activity(operator, 'created') raise cherrypy.HTTPRedirect('/user/list') else: return render('user/add.html', {'form': form })
def reveal(self, password_id, _=None): """ (AJAX) Returns the specified password. (This is a separate action for the purposes of auditing.) (jquery will pass an additional _ arg for no-caching purposes.) """ pw = passwords.get(password_id) if not pw: raise ValueError("Invalid password specified: {0}".format(password_id)) auditlog.log(auditlog.CODE_CONTENT_VIEW, target=pw) return pw.password_decrypted
def deletePassword(self, password_id): """ Delete a password (and all password history). :param password_id: The numeric ID of password. :type password_id: int :return: The deleted password object. :rtype: dict """ pw = passwords.delete(password_id) auditlog.log(auditlog.CODE_CONTENT_DEL, target=pw) return pw.to_dict(decrypt=False)
def createGroup(self, name): """ Create a new group with specified name. :param name: The new group name. :type name: str :return: The created group object. :rtype: dict """ group = groups.create(name) auditlog.log(auditlog.CODE_CONTENT_ADD, target=group) return group.to_dict()
def process_add(self, **kwargs): form = PasswordAddForm(request_params()) if form.validate(): pw = passwords.create(username=form.username.data, resource_id=form.resource_id.data, password=form.password_decrypted.data, description=form.description.data, tags=form.tags.data) auditlog.log(auditlog.CODE_CONTENT_ADD, target=pw) notify_entity_activity(pw, 'created') raise cherrypy.HTTPRedirect('/resource/view/%d' % pw.resource_id) else: return render('password/add.html', {'form': form})
def process_edit(self, **kwargs): """ Updates a group (changes name). """ form = GroupEditForm(request_params()) if form.validate(): (group, modified) = groups.modify(form.group_id.data, name=form.name.data) auditlog.log(auditlog.CODE_CONTENT_MOD, target=group, attributes_modified=modified) notify_entity_activity(group, 'updated') raise cherrypy.HTTPRedirect('/group/list') else: return render('group/edit.html', {'form': form})
def deleteGroup(self, group_id): """ Delete a group (group must be empty or error will be thrown). :param group_id: The numeric ID of group (cannot use group name here). :type group_id: int :return: The deleted group object. :rtype: dict """ g = groups.delete(group_id) auditlog.log(auditlog.CODE_CONTENT_DEL, target=g) return g.to_dict()
def process_add(self, **kwargs): form = AccessAddForm(request_params()) if form.validate(): level_mask = 0 for i in form.levels.data: level_mask |= int(i) level = access.create(level_mask, form.description.data) auditlog.log(auditlog.CODE_CONTENT_ADD, target=level) notify_entity_activity(level, 'created') raise cherrypy.HTTPRedirect('/access/list') else: return render('access/add.html', {'form': form})
def process_merge(self, **kwargs): form = MergeForm(request_params()) group_tuples = [(g.id, g.name) for g in groups.list()] form.from_group_id.choices = [(0, '[From Group]')] + group_tuples form.to_group_id.choices = [(0, '[To Group]')] + group_tuples if form.validate(): log.info("Passed validation, somehow.") (moved_resources, from_group, to_group) = groups.merge(form.from_group_id.data, form.to_group_id.data) for r in moved_resources: auditlog.log(auditlog.CODE_CONTENT_MOD, target=r, attributes_modified=['group_id']) auditlog.log(auditlog.CODE_CONTENT_DEL, target=from_group) raise cherrypy.HTTPRedirect('/group/view/{0}'.format(to_group.id)) else: return render("group/merge.html", {'form': form})
def tagsearch(self, tags): """ Perform a search for specified tags (only). :param tags: A list of tags to search for (and). :type tags: list :returns: A dict like {'resources': [r1,r2,...], 'passwords': [p1,p2,...]} :rtype: dict """ results = search.tagsearch(tags) auditlog.log(auditlog.CODE_SEARCH, comment=repr(tags)) return { 'resources': [r.to_dict(decrypt=False) for r in results.resource_matches], 'passwords': [r.to_dict(decrypt=False) for r in results.password_matches], }
def process_add(self, **kwargs): form = ResourceAddForm(request_params()) form.group_ids.choices = [(g.id, g.label) for g in groups.list()] if form.validate(): resource = resources.create(name=form.name.data, group_ids=form.group_ids.data, addr=form.addr.data, description=form.description.data, notes=form.notes_decrypted.data, tags=form.tags.data) # XXX: process auditlog.log(auditlog.CODE_CONTENT_ADD, target=resource) notify_entity_activity(resource, 'created') raise cherrypy.HTTPRedirect('/resource/view/{0}'.format(resource.id)) else: return render('resource/add.html', {'form': form })
def process_edit(self, **kwargs): form = PasswordEditForm(request_params()) if form.validate(): (pw, modified) = passwords.modify(form.password_id.data, username=form.username.data, password=form.password_decrypted.data, description=form.description.data, tags=form.tags.data) auditlog.log(auditlog.CODE_CONTENT_MOD, target=pw, attributes_modified=modified) notify_entity_activity(pw, 'updated') raise cherrypy.HTTPRedirect('/resource/view/{0}'.format(pw.resource_id)) else: log.warning("Form failed validation: {0}".format(form.errors)) return render('password/edit.html', {'form': form})
def modifyGroup(self, group_id, name): """ Modify (rename) a group. :param group_id: The ID of group to modify. :type group_id: int :keyword name: The new name of the group. :type name: str :return: The updated group object. :rtype: dict """ (group, modified) = groups.modify(group_id, name=name) auditlog.log(auditlog.CODE_CONTENT_MOD, target=group, attributes_modified=modified) return group.to_dict()
def search(self, searchstr): """ Perform a search for specified search string. :param searchstr: A string to match (exactly). :type searchstr: str :returns: A dict like {'resources': [r1,r2,...], 'groups': [g1,g2,...], 'passwords': [p1,p2,...]} :rtype: dict """ results = search.search(searchstr) auditlog.log(auditlog.CODE_SEARCH, comment=searchstr) return { 'resources': [r.to_dict(decrypt=False) for r in results.resource_matches], 'groups': [r.to_dict() for r in results.group_matches], 'passwords': [r.to_dict(decrypt=False) for r in results.password_matches], }
def process_edit(self, **kwargs): form = ResourceEditForm(request_params()) form.group_ids.choices = [(g.id, g.label) for g in groups.list()] if form.validate(): (resource, modified) = resources.modify(form.resource_id.data, name=form.name.data, addr=form.addr.data, group_ids=form.group_ids.data, notes=form.notes_decrypted.data, description=form.description.data, tags=form.tags.data) # XXX: process auditlog.log(auditlog.CODE_CONTENT_MOD, target=resource, attributes_modified=modified) notify_entity_activity(resource, 'updated') raise cherrypy.HTTPRedirect('/resource/view/{0}'.format(resource.id)) else: log.warning("Form validation failed.") log.warning(form.errors) return render('resource/edit.html', {'form': form})
def process_edit(self, **kwargs): log.debug("params = %r" % request_params()) form = OperatorEditForm(request_params()) form.access_id.choices = [(l.id, l.description) for l in access.list()] if form.validate(): params = dict(operator_id=form.operator_id.data, username=form.username.data, access_id=form.access_id.data) # If password is blank, let's just not change it. if form.password.data: params['password'] = form.password.data (operator, modified) = operators.modify(**params) auditlog.log(auditlog.CODE_CONTENT_MOD, target=operator, attributes_modified=modified) notify_entity_activity(operator, 'updated') raise cherrypy.HTTPRedirect('/user/list') else: return render('user/edit.html', {'form': form, 'externally_managed': operator.externally_managed})
def createResource(self, group_ids, name, addr=None, description=None, notes=None): """ Creates a new resource, adding to specified groups. :param group_ids: The group IDs associated with this password. :type group_ids: list :keyword name: :keyword addr: :keyword description: :keyword notes: The notes field (will be encrypted). :return: The created resource object. :rtype: dict """ resource = resources.create(name=name, group_ids=group_ids, addr=addr, description=description, notes=notes) auditlog.log(auditlog.CODE_CONTENT_ADD, target=resource) return resource.to_dict()
def createPassword(self, resource_id, username, password, description=None, expire_months=None): """ Creates a new password for specified resource. :param resource_id: The resource with which to associate this password. :type resource_id: int :keyword: username: :keyword: password: :keyword: description: :keyword: expire_months: :return: The created password object. :rtype: dict """ pw = passwords.create(username, resource_id=resource_id, description=description, password=password, expire_months=expire_months) auditlog.log(auditlog.CODE_CONTENT_ADD, target=pw) return pw.to_dict()
def modifyPassword(self, password_id, **kwargs): """ Modify an existing password. This method requires keyword arguments and so only works with JSON-RPC2 protocol. :param password_id: The ID of the password to modify. :type password_id: int :keyword username: :keyword password: :keyword description: :return: The modified password object. :rtype: dict """ (pw, modified) = passwords.modify(password_id, **kwargs) auditlog.log(auditlog.CODE_CONTENT_MOD, target=pw, attributes_modified=modified) return pw.to_dict()