コード例 #1
0
ファイル: initializedb.py プロジェクト: pontiflex/trustme
def main(argv=sys.argv):
	if len(argv) != 2:
		usage(argv)
	config_uri = argv[1]
	setup_logging(config_uri)
	settings = get_appsettings(config_uri)
	engine = engine_from_config(settings, 'sqlalchemy.')
	DBSession.configure(bind=engine)
	Base.metadata.create_all(engine)
	with transaction.manager:
		pass
コード例 #2
0
ファイル: access.py プロジェクト: pontiflex/trustme
	def requested(cls, action):
		if isinstance(action, Action):
			return action in cls.requested(action.__class__)
		return (DBSession.query(action)
					.join(AccessRecord)
					.filter(AccessRecord.capability == None)
					.distinct())
コード例 #3
0
ファイル: check.py プロジェクト: pontiflex/trustme
def __check(action_class, serial):
    req = DBSession.query(action_class).filter(Action.serial == serial).first()
    if req is None:
        return HTTPNotFound("Invalid serial number")
    if Access.processed(req, True):
        return req.render("approved", True)
    if Access.processed(req, False):
        return req.render("denied", True)
    if Access.filtered(req):
        return req.render("pending", True)
    return req.render("rejected", True)
コード例 #4
0
ファイル: __init__.py プロジェクト: pontiflex/trustme
def main(global_config, **settings):
	""" This function returns a Pyramid WSGI application.
	"""
	# Parse the CA settings (must occur before creating the Configurator)
	Secrets.parse_config(settings)
	RevokeDB.parse_config(settings)

	engine = engine_from_config(settings, 'sqlalchemy.')
	DBSession.configure(bind=engine)

	config = Configurator(settings=settings)
	
	session_factory = session_factory_from_settings(settings)
	config.set_session_factory(session_factory)

	authn_policy = AuthTktAuthenticationPolicy(AUTH_SECRET, secure=AUTH_SECURE,
			http_only=True, include_ip=True, cookie_name=AUTH_COOKIE, wild_domain=False,
			timeout=AUTH_TIMEOUT, reissue_time=AUTH_REISSUE, callback=capability_finder)
	authz_policy = CapabilityAuthorizationPolicy()
	config.set_authentication_policy(authn_policy)
	config.set_authorization_policy(authz_policy)

	config.add_static_view('static', 'static', cache_max_age=3600)

	config.add_route('home', '/')
	config.add_route('test', '/test')

	config.add_route('login', '/login')
	config.add_route('logout', '/logout')

	config.add_route('crl', '/crl')

	config.add_route('request', '/{type}/request')
	config.add_route('check', '/{type}/check')
	config.add_route('review', '/{type}/review')
	config.add_route('revoke', '/{type}/revoke')

	config.scan()
	return config.make_wsgi_app()
コード例 #5
0
ファイル: capability.py プロジェクト: pontiflex/trustme
 def usable(cls, t=None, user=None, action_class=None, access_types=None):
     t = time() // 1 if t is None else t
     query = DBSession.query(cls)
     if user is not None:
         query = query.filter(Capability.user == user)
     if action_class is not None:
         query = query.filter(Capability.action_class == action_class)
     if access_types is not None:
         query = query.filter(Capability.access_type.in_(access_types))
     query = (
         query.filter(Capability.revoked == None)
         .filter(or_(Capability.start_time == None, Capability.start_time <= t))
         .filter(or_(Capability.end_time == None, Capability.end_time >= t))
     )
     return [cap for cap in query if cap.valid()]
コード例 #6
0
ファイル: access.py プロジェクト: pontiflex/trustme
	def processed(cls, action, success=True):
		if isinstance(action, Action):
			if action not in cls.processed(action.__class__, success=None): return False
			elif success is None: return True
			else: return success != (action in cls.processed(action.__class__, success=False))
		query = (DBSession.query(action)
					.join(AccessRecord).join(Capability)
					.filter(AccessRecord.allowed == True))
		if success is None:
			query = query.filter(Capability.access_type.in_(EXIT))
		else:
			fail_query = query.filter(Capability.access_type == EXIT[1])
			if success:
				query = query.filter(Capability.access_type == EXIT[0]).except_(fail_query)
			else: query = fail_query
		return query.distinct()
コード例 #7
0
ファイル: review.py プロジェクト: pontiflex/trustme
def review_page(request, action_class, **kwargs):
	access = Access(request)
	allowable = access.allowable(action_class)
	if allowable is False:
		simple = action_class.readable()
		raise HTTPForbidden("You don't have sufficient permissions to review %s requests" % simple)

	serial_field = 'SERIAL'

	answer = ''
	POST = request.POST
	if serial_field in POST and (EXIT[0] in POST or EXIT[1] in POST):
		serial = POST[serial_field]
		action = DBSession.query(action_class).filter(Action.serial == serial).first()
		if action is None:
			raise HTTPNotFound('Invalid serial number')
		if action not in allowable:
			raise HTTPForbidden('Action not available for processing')
		if EXIT[0] in POST and EXIT[1] in POST:
			raise ValueError('Both "%s" and "%s" specified in form' % EXIT)
		choice = EXIT[1] if EXIT[1] in POST else EXIT[0]
		caps = [c for c in allowable[action] if c.access_type == choice]
		try:
			answer = access.perform_with_one(action, caps)
		except HTTPException as e:
			answer = e.detail
		else:
			del allowable[action]

	forms = []
	form_params = dict(serial_field=serial_field)
	button_options = {EXIT[0]:'Allow', EXIT[1]:'Deny'}
	for action, caps in allowable.iteritems():
		render_template, render_params = action.render('pending')
		form_params['info'] = HTML(render(render_template, render_params, request))
		form_params['serial'] = action.serial
		form_params['credentials'] = offer_creds(request, caps)
		choices = set((c.access_type for c in caps))
		form_params['buttons'] = ((c, button_options[c]) for c in choices)
		forms.append(HTML(render(FORM_TEMPLATE, form_params, request)))
	if not forms:
		forms.append('No requests are available for processing')
	
	return dict(forms=forms, answer=HTML(answer), **kwargs)
コード例 #8
0
ファイル: revoke.py プロジェクト: pontiflex/trustme
def revoke_page(request, action_class, **kwargs):
	access = Access(request)
	revocable = access.revocable(action_class)
	if revocable is False:
		simple = action_class.readable()
		raise HTTPForbidden("You don't have sufficient permissions to revoke %s requests" % simple)

	serial_field = 'SERIAL'

	answer = ''
	POST = request.POST
	if serial_field in POST:
		serial = POST[serial_field]
		action = DBSession.query(action_class).filter(Action.serial == serial).first()
		if action is None:
			raise HTTPNotFound('Invalid serial number')
		if action not in revocable:
			raise HTTPForbidden('Action not available for revocation')
		try:
			answer = access.perform_with_one(action, revocable[action])
		except HTTPException as e:
			answer = e.detail
		else:
			del revocable[action]

	forms = []
	form_params = dict(serial_field=serial_field, button='Revoke')
	button_options = {EXIT[0]:'Allow', EXIT[1]:'Deny'}
	for action, caps in revocable.iteritems():
		render_template, render_params = action.render('approved')
		form_params['info'] = HTML(render(render_template, render_params, request))
		form_params['serial'] = action.serial
		form_params['credentials'] = offer_creds(request, caps)
		forms.append(HTML(render(FORM_TEMPLATE, form_params, request)))
	if not forms:
		forms.append('No requests are available for revocation')
	
	return dict(forms=forms, answer=HTML(answer), **kwargs)
コード例 #9
0
ファイル: access.py プロジェクト: pontiflex/trustme
	def __perform(self, action, capability, vetted=False, value=None):
		# Don't perform the access again
		if self.__performed:
			raise HTTPForbidden('Cannot perform access again')
		allowed = True

		try:

			if capability is None:
				at = ENTER
			else:
				at = capability.access_type
				# Verify that the action is allowed by the capability
				if not vetted:
					cons = capability.constraint
					if cons is not None and not cons.allows(action, self):
						raise HTTPForbidden('Invoked capability does not allow this access')

			if at in EXIT:
				# Make sure that the action has been filtered before making any attempt to
				# process its execution
				if not vetted and not self.filtered(action):
					raise HTTPForbidden('Action not available for processing')
				# If the access is an approval for processing, check that the action has not
				# been processed before, then perform it
				if at == EXIT[0]:
					if self.processed(action, None):
						raise HTTPForbidden('Action not available for processing')
					return action.perform(self.request)
				# Otherwise, if this action has already been approved, this is a revocation
				if self.processed(action):
					return action.revoke(self.request)
				# Otherwise, logging this access is enough to mark the action as denied
				return HTTPForbidden(FILTER_DENY) if vetted else 'Request marked as denied'
			elif at in FILTER:
				# Filtering is only done automatically
				if not vetted:
					raise RuntimeError('Request filtering must be automatic')
				if at == FILTER[0]:
					# If this access is letting the action through the filter, failure to
					# take further automatic action should simply stop execution. Possible
					# automatic accesses are drawn from the EXIT list
					types = EXIT
					filters = []
					# If this is an authenticated request, try proceeding with the requestor's
					# positive access capabilities. If none exist, then move on to auto-filters
					if self.user is not None:
						filters = self.own_processes(action)
					if not filters:
						filters = self.processes(action, True)
					def fail(msg): return value
				else:
					# Otherwise, logging this access is enough to mark the action as rejected
					return HTTPForbidden(FILTER_REJECT)
			elif at == ENTER:
				# If this access is a new request, failure should raise an unauthorized
				# exception and store the failed access. Possible automatic accesses are
				# drawn from the FILTER list
				types = FILTER
				filters = []
				# If this is an authenticated request, try proceeding with the requestor's
				# positive access capabilities. If none exist, then move on to auto-filters
				if self.user is not None:
					filters = self.own_filters(action)
				if not filters:
					filters = self.filters(action, True)
				value = HTTPAccepted(action.serial)
				def fail(msg): raise HTTPForbidden(msg)
			else: raise RuntimeError('%s is not a valid access type' % str(at))

			# If no filters actually match the action, then the attempt at automatic
			# access should fail closed immediately (lack of a no does not mean yes)
			if not filters: return fail(NO_FILTERS)

			pos = [cap for cap in filters if cap.access_type == types[0]]
			neg = [cap for cap in filters if cap.access_type == types[1]]
			if neg:
				# If any negative filters were triggered, perform a negative access
				value = self.__perform(action, neg[0], True, value)
				DBSession.add_all((self.__record(action, cap, True) for cap in neg[1:]))
				DBSession.add_all((self.__record(action, cap, False) for cap in pos))
			else:
				# Otherwise, perform a positive access
				value = self.__perform(action, pos[0], True, value)
				DBSession.add_all((self.__record(action, cap, True) for cap in pos[1:]))

		except:
			allowed = False
			raise
		finally:
			self.__save(action, capability, allowed)

		if value is None:
			raise RuntimeException('Illegal return state')
		return value
コード例 #10
0
ファイル: access.py プロジェクト: pontiflex/trustme
	def _perform(self, action, capability):
		DBSession.add(action)
		ret = self.__perform(action, capability)
		self.__performed = True
		return ret
コード例 #11
0
ファイル: access.py プロジェクト: pontiflex/trustme
	def own_processes(self, action):
		q1 = ((lambda(a): True) if isinstance(action, Action)
								else (lambda(a): DBSession.query(a)))
		filtered = (q1, lambda(a): self.processed(a, None))
		return self.__acceptable(action, (EXIT[0],), filtered)
コード例 #12
0
ファイル: admin.py プロジェクト: pontiflex/trustme
def setup_admin(request):
    """Provide a form view for configuring initial admin account settings"""
    # Name and maxLength of the email field
    mail_field = "email", User.email.property.columns[0].type.length
    # Names of the password/confirmation fields
    pass_fields = "pass1", "pass2", "pass3", "pass4"
    # Name of the submitted field
    submitted = "newuser.submitted"

    # Set the inputs and error message to empty strings
    email, passwords, message = "", ("", "", "", ""), ""
    # FIXME: Set defaults for easy testing
    email = "*****@*****.**"
    passwords = ("password", "password", "password1", "password1")

    # If the form was submitted, process the input
    if submitted in request.params:
        # Retrieve the input values
        email = request.POST[mail_field[0]]
        passwords = (
            request.POST[pass_fields[0]].encode("utf-8"),
            request.POST[pass_fields[1]].encode("utf-8"),
            request.POST[pass_fields[2]].encode("utf-8"),
            request.POST[pass_fields[3]].encode("utf-8"),
        )
        # Validate the email, and passwords
        message = validate_email(email)
        if not message:
            message = validate_passwords((passwords[0], passwords[1]))
        if not message:
            message = validate_passwords((passwords[2], passwords[3]))
        if not message and passwords[0] == passwords[2]:
            message = "ROOT and USERS passwords must be different"
            # If no error occurred, create the configured accounts
        if not message:
            # Create the ROOT user
            priv_root = User("ROOT", email, passwords[0])
            DBSession.add(priv_root)
            # Give it an admin capability (can grant any capability)
            DBSession.add(AdminCapability(priv_root))

            # Create the USERS user
            user_root = User("USERS", email, passwords[2])
            DBSession.add(user_root)
            # Give it every capability related to NewUser requests
            for access_type in FILTER_ACCESS + PROCESS_ACCESS:
                grant = GrantCapability(user_root, NewUser, access_type)
                DBSession.add(grant)
                access = grant.grant(user_root)
                DBSession.add(access)

                # Redirect to the home page, logged in as USERS
            return HTTPFound(location=request.route_url("home"), headers=remember(request, "USERS"))

            # Return the render dictionary
    return dict(
        mail_field=mail_field,
        pass_fields=pass_fields,
        message=message,
        email=email,
        passwords=passwords,
        submitted=submitted,
    )
コード例 #13
0
ファイル: admin.py プロジェクト: pontiflex/trustme
def _needs_admin(info, request):
    """Custom predicate which checks if an admin needs to be created"""
    return DBSession.query(User).count() == 0
コード例 #14
0
ファイル: credentials.py プロジェクト: pontiflex/trustme
def validate_username(username, allow_existing=False):
	if len(username) < 3:
		return 'Username must be at least 3 characters long'
	if not allow_existing and DBSession.query(User).filter(User.login == username).count() > 0:
		return 'Username already taken'
	return ''
コード例 #15
0
ファイル: access.py プロジェクト: pontiflex/trustme
	def __save(self, *args, **kwargs):
		DBSession.add(self.__record(*args, **kwargs))
コード例 #16
0
ファイル: access.py プロジェクト: pontiflex/trustme
	def own_filters(self, action):
		q1 = ((lambda(a): True) if isinstance(action, Action)
								else (lambda(a): DBSession.query(a)))
		requested = (q1, lambda(a): self.filtered(a, None))
		return self.__acceptable(action, (FILTER[0],), requested)
コード例 #17
0
ファイル: user.py プロジェクト: pontiflex/trustme
	def is_admin(self):
		"""Check if this user is the ROOT admin account"""
		return DBSession.query(User).get(1) is self
コード例 #18
0
ファイル: constraint.py プロジェクト: pontiflex/trustme
	def query(self, access_info, action_class=None):
		action_class = self._action_class(action_class)
		return DBSession.query(action_class).filter(self.condition(access_info, action_class))
コード例 #19
0
ファイル: user.py プロジェクト: pontiflex/trustme
	def get(cls, userid):
		"""Take a username and return the corresponding User, if it exists"""
		if userid:
			return DBSession.query(cls).filter(cls.login==userid).first()
		return None