def example_policies(cls): """ This function gets registered under 'example' in the :attr:`gateone.ApplicationWebSocket.security` dict and is called by the :func:`auth.require` decorator by way of the :class:`auth.policies` sub-function. It returns ``True`` or ``False`` depending on what is defined in the settings dir and what function is being called. """ instance = cls.instance # Your Application instance function = cls.function # Wrapped function f_args = cls.f_args # Wrapped function's arguments f_kwargs = cls.f_kwargs # Wrapped function's keyword arguments # This is a convenient way to map function/method names to specific policy # functions: policy_functions = { 'test_example': policy_test_example, } user = instance.current_user # The applicable_policies() function takes an application 'scope', a user # dict (must have a 'upn' key), and a dict that contains all of Gate One's # settings (always available via ApplicationWebSocket.prefs) and returns # a single dict containing the merged settings (aka policies) for that # scope. # In other words, if you have this inside a file in gateone/settings/: # { # "*": { # "example": { # "foo": "bar" # } # }, # "[email protected]": { # "example": { # "foo": "joe!" # } # } # } # # applicable_policies() would return: # # {"foo": "bar"} # # for regular users but [email protected] would get: # # {"foo": "joe!"} policy = applicable_policies('example', user, instance.ws.prefs) if not policy: # No policies found for the given scope return True # A world without limits! # Check the basics first... Is {"allow": true}? if 'allow' in policy: # Only check this if there's an "allow" somewhere if not policy['allow']: # User is DENIED! example_log.error(_( "%s denied access to the Example application by policy." % user['upn'])) return False # Here we run through our policy_functions dict and call the appropriate # policy-checking function that matches the decorated method's name: if function.__name__ in policy_functions: return policy_functions[function.__name__](cls, policy) return True # Default to permissive if we made it this far
def example_policies(cls): """ This function gets registered under 'example' in the :attr:`gateone.ApplicationWebSocket.security` dict and is called by the :func:`auth.require` decorator by way of the :class:`auth.policies` sub-function. It returns ``True`` or ``False`` depending on what is defined in the settings dir and what function is being called. """ instance = cls.instance # Your Application instance function = cls.function # Wrapped function f_args = cls.f_args # Wrapped function's arguments f_kwargs = cls.f_kwargs # Wrapped function's keyword arguments # This is a convenient way to map function/method names to specific policy # functions: policy_functions = { 'test_example': policy_test_example, } user = instance.current_user # The applicable_policies() function takes an application 'scope', a user # dict (must have a 'upn' key), and a dict that contains all of Gate One's # settings (always available via ApplicationWebSocket.prefs) and returns # a single dict containing the merged settings (aka policies) for that # scope. # In other words, if you have this inside a file in gateone/settings/: # { # "*": { # "example": { # "foo": "bar" # } # }, # "[email protected]": { # "example": { # "foo": "joe!" # } # } # } # # applicable_policies() would return: # # {"foo": "bar"} # # for regular users but [email protected] would get: # # {"foo": "joe!"} policy = applicable_policies('example', user, instance.ws.prefs) if not policy: # No policies found for the given scope return True # A world without limits! # Check the basics first... Is {"allow": true}? if 'allow' in policy: # Only check this if there's an "allow" somewhere if not policy['allow']: # User is DENIED! example_log.error( _("%s denied access to the Example application by policy." % user['upn'])) return False # Here we run through our policy_functions dict and call the appropriate # policy-checking function that matches the decorated method's name: if function.__name__ in policy_functions: return policy_functions[function.__name__](cls, policy) return True # Default to permissive if we made it this far
def authenticate(self): """ This gets called immediately after the user is authenticated successfully at the end of :meth:`ApplicationWebSocket.authenticate`. Sends all plugin JavaScript files to the client and triggers the 'example:authenticate' event. """ example_log.debug('ExampleApplication.authenticate()') # This is the log metadata that was mentioned near the top of this file. # This log_metadata will be JSON-encoded and included in all log # messages that use `self.example_log` which is super useful when # you need to parse logs at some later date and want to know the # circumstances surrounding any given message. self.log_metadata = { 'upn': self.current_user['upn'], 'ip_address': self.ws.request.remote_ip, # If your app uses the location feature make sure to include it: 'location': self.ws.location } self.example_log = go_logger("gateone.example", **self.log_metadata) # NOTE: To include even *more* metadata in a log message on a one-time # basis simply pass the metadata to the logger like so: # self.example_log("Some log message", metadata={'foo': 'bar'}) # That will ensure that {'foo': 'bar'} is included in the JSON portion # Assign our user-specific settings/policies for quick reference self.policy = applicable_policies('example', self.current_user, self.ws.prefs) # NOTE: The applicable_policies() function *is* memoized but the above # is still much faster. # Start by determining if the user can even use this app if 'allow' in self.policy: # This is the same check inside example_policies(). Why put it here # too? So we can skip sending the client JS/CSS that they won't be # able to use. if not self.policy['allow']: # User is not allowed to access this application. Don't # bother sending them any static files and whatnot... self.example_log.debug( _("User is not allowed to use the Example application. " "Skipping post-authentication functions.")) return # Render and send the client our example.css example_css = os.path.join(APPLICATION_PATH, 'templates', 'example.css') self.render_and_send_css(example_css, element_id="example.css") # NOTE: See the Gate One docs for gateone.py to see how # render_and_send_css() works. It auto-minifies and caches! # Send the client our application's static JavaScript files static_dir = os.path.join(APPLICATION_PATH, 'static') js_files = [] if os.path.isdir(static_dir): js_files = os.listdir(static_dir) # Everything in static/*.js js_files.sort() for fname in js_files: if fname.endswith('.js'): js_file_path = os.path.join(static_dir, fname) # This is notable: To ensure that all your JavaScript files # get loaded *after* example.js we add 'example.js' as a # dependency for all JS files we send to the client. if fname == 'example.js': # Adding CSS as a dependency to your app's JS is also a # good idea. You could also put 'theme.css' if you want to # ensure that the theme gets loaded before your JavaScript # init() function is called. self.send_js(js_file_path, requires=["example.css"]) else: # Send any other discovered JS files to the client with # example.js as the only dependency. self.send_js(js_file_path, requires='example.js') # If you're not using plugins you can disregard this: # The send_plugin_static_files() function will locate any JS/CSS files # in your plugins' respective static directories and send them to the # client. It is also smart enough to know which plugins are enabled # or disabled. self.ws.send_plugin_static_files(os.path.join(APPLICATION_PATH, 'plugins'), application="example", requires=["example.js"]) sess = SESSIONS[self.ws.session] # A shortcut to save some typing # Create a place to store app-specific stuff related to this session # (but not necessarily this 'location') if "example" not in sess: sess['example'] = {} # A mostly persistent place to store info # If you want to call a function whenever Gate One exits just add it # to SESSIONS[self.ws.session]["kill_session_callbacks"] like so: #if kill_session_func not in sess["kill_session_callbacks"]: #sess["kill_session_callbacks"].append(kill_session_func) # If you want to call a function whenever a user's session times out # just attach it to SESSIONS[self.ws.session]["timeout_callbacks"] # like so: #if timeout_session_func not in sess["timeout_callbacks"]: #sess["timeout_callbacks"].append(timeout_session_func) # NOTE: The user will often be authenticated before example.js is # loaded. In fact, the only time this won't be the case is when the # user is disconnected (e.g. server restart) and then reconnects. self.trigger("example:authenticate")
def authenticate(self): """ This gets called immediately after the user is authenticated successfully at the end of :meth:`ApplicationWebSocket.authenticate`. Sends all plugin JavaScript files to the client and triggers the 'example:authenticate' event. """ example_log.debug('ExampleApplication.authenticate()') # This is the log metadata that was mentioned near the top of this file. # This log_metadata will be JSON-encoded and included in all log # messages that use `self.example_log` which is super useful when # you need to parse logs at some later date and want to know the # circumstances surrounding any given message. self.log_metadata = { 'upn': self.current_user['upn'], 'ip_address': self.ws.request.remote_ip, # If your app uses the location feature make sure to include it: 'location': self.ws.location } self.example_log = go_logger("gateone.example", **self.log_metadata) # NOTE: To include even *more* metadata in a log message on a one-time # basis simply pass the metadata to the logger like so: # self.example_log("Some log message", metadata={'foo': 'bar'}) # That will ensure that {'foo': 'bar'} is included in the JSON portion # Assign our user-specific settings/policies for quick reference self.policy = applicable_policies( 'example', self.current_user, self.ws.prefs) # NOTE: The applicable_policies() function *is* memoized but the above # is still much faster. # Start by determining if the user can even use this app if 'allow' in self.policy: # This is the same check inside example_policies(). Why put it here # too? So we can skip sending the client JS/CSS that they won't be # able to use. if not self.policy['allow']: # User is not allowed to access this application. Don't # bother sending them any static files and whatnot... self.example_log.debug(_( "User is not allowed to use the Example application. " "Skipping post-authentication functions.")) return # Render and send the client our example.css example_css = os.path.join( APPLICATION_PATH, 'templates', 'example.css') self.render_and_send_css(example_css, element_id="example.css") # NOTE: See the Gate One docs for gateone.py to see how # render_and_send_css() works. It auto-minifies and caches! # Send the client our application's static JavaScript files static_dir = os.path.join(APPLICATION_PATH, 'static') js_files = os.listdir(static_dir) # Everything in static/*.js js_files.sort() for fname in js_files: if fname.endswith('.js'): js_file_path = os.path.join(static_dir, fname) # This is notable: To ensure that all your JavaScript files # get loaded *after* example.js we add 'example.js' as a # dependency for all JS files we send to the client. if fname == 'example.js': # Adding CSS as a dependency to your app's JS is also a # good idea. You could also put 'theme.css' if you want to # ensure that the theme gets loaded before your JavaScript # init() function is called. self.ws.send_js(js_file_path, requires=["example.css"]) else: # Send any other discovered JS files to the client with # example.js as the only dependency. self.ws.send_js(js_file_path, requires='example.js') # If you're not using plugins you can disregard this: # The send_plugin_static_files() function will locate any JS/CSS files # in your plugins' respective static directories and send them to the # client. It is also smart enough to know which plugins are enabled # or disabled. self.ws.send_plugin_static_files( os.path.join(APPLICATION_PATH, 'plugins'), application="example", requires=["example.js"]) sess = SESSIONS[self.ws.session] # A shortcut to save some typing # Create a place to store app-specific stuff related to this session # (but not necessarily this 'location') if "example" not in sess: sess['example'] = {} # A mostly persistent place to store info # If you want to call a function whenever Gate One exits just add it # to SESSIONS[self.ws.session]["kill_session_callbacks"] like so: #if kill_session_func not in sess["kill_session_callbacks"]: #sess["kill_session_callbacks"].append(kill_session_func) # If you want to call a function whenever a user's session times out # just attach it to SESSIONS[self.ws.session]["timeout_callbacks"] # like so: #if timeout_session_func not in sess["timeout_callbacks"]: #sess["timeout_callbacks"].append(timeout_session_func) # NOTE: The user will often be authenticated before example.js is # loaded. In fact, the only time this won't be the case is when the # user is disconnected (e.g. server restart) and then reconnects. self.trigger("example:authenticate")