Beispiel #1
0
def get_connect_string(self, term):
    """
    Attached to the (server-side) `terminal:ssh_get_connect_string` WebSocket
    action; writes the connection string associated with *term* to the WebSocket
    like so::

        {'terminal:sshjs_reconnect': {*term*: <connection string>}}

    In ssh.js we attach a WebSocket action to 'terminal:sshjs_reconnect'
    that assigns the connection string sent by this function to
    `GateOne.Terminal.terminals[*term*]['sshConnectString']`.
    """
    # This is the first function that normally gets called when a user uses SSH
    # so it's a good time to update the logger with extra metadata
    self.ssh_log = go_logger("gateone.terminal.ssh",
                             plugin='ssh',
                             **self.log_metadata)
    term = int(term)
    if term not in self.loc_terms:
        return  # Nothing to do (already closed)
    self.ssh_log.debug("get_connect_string()", metadata={'term': term})
    connect_string = self.loc_terms[term].get('ssh_connect_string', None)
    if connect_string:
        message = {
            'terminal:sshjs_reconnect': {
                'term': term,
                'connect_string': connect_string
            }
        }
        self.write_message(message)
Beispiel #2
0
def get_connect_string(self, term):
    """
    Attached to the (server-side) `terminal:ssh_get_connect_string` WebSocket
    action; writes the connection string associated with *term* to the WebSocket
    like so::

        {'terminal:sshjs_reconnect': {*term*: <connection string>}}

    In ssh.js we attach a WebSocket action to 'terminal:sshjs_reconnect'
    that assigns the connection string sent by this function to
    `GateOne.Terminal.terminals[*term*]['sshConnectString']`.
    """
    # This is the first function that normally gets called when a user uses SSH
    # so it's a good time to update the logger with extra metadata
    self.ssh_log = go_logger(
        "gateone.terminal.ssh", plugin='ssh', **self.log_metadata)
    term = int(term)
    if term not in self.loc_terms:
        return # Nothing to do (already closed)
    self.ssh_log.debug("get_connect_string()", metadata={'term': term})
    connect_string = self.loc_terms[term].get('ssh_connect_string', None)
    if connect_string:
        message = {
            'terminal:sshjs_reconnect': {
                'term': term,
                'connect_string': connect_string
            }
        }
        self.write_message(message)
Beispiel #3
0
def notice_esc_seq_handler(self, message, term=None, multiplex=None):
    """
    Handles text passed from the special optional escape sequance handler to
    display a *message* to the connected client (browser).  It can be invoked
    like so:

    .. ansi-block::

        $ echo -e "\033]_;notice|Text passed to some_function()\007"

    .. seealso::

        :class:`app_terminal.TerminalApplication.opt_esc_handler` and
        :func:`terminal.Terminal._opt_handler`
    """
    if not hasattr(self, 'notice_log'):
        self.notice_log = go_logger('gateone.terminal.notice',
                                    plugin='notice',
                                    **self.log_metadata)
    self.notice_log.info("Notice Plugin: %s" % message,
                         metadata={
                             'term': term,
                             'text': message
                         })
    message = "Term {term}: {message}".format(term=term, message=message)
    message = {'go:notice': message}
    self.write_message(message)
Beispiel #4
0
def initialize(self):
    """
    Called inside of :meth:`TerminalApplication.initialize` shortly after the
    WebSocket is instantiated.
    """
    self.ssh_log = go_logger("gateone.terminal.ssh", plugin='ssh')
    # NOTE:  Why not use the 'Events' hook for these?  You can't attach two
    # functions to the same event via that mechanism because it's a dict
    # (one would override the other).
    # An alternative would be to write a single function say, on_auth() that
    # calls both of these functions then assign it to 'terminal:authenticate' in
    # the 'Events' hook.  I think this way is better since it is more explicit.
    self.on('terminal:authenticate', bind(send_ssh_css_template, self))
    self.on('terminal:authenticate', bind(create_user_ssh_dir, self))
Beispiel #5
0
def initialize(self):
    """
    Called inside of :meth:`TerminalApplication.initialize` shortly after the
    WebSocket is instantiated.
    """
    self.ssh_log = go_logger("gateone.terminal.ssh", plugin='ssh')
    # NOTE:  Why not use the 'Events' hook for these?  You can't attach two
    # functions to the same event via that mechanism because it's a dict
    # (one would override the other).
    # An alternative would be to write a single function say, on_auth() that
    # calls both of these functions then assign it to 'terminal:authenticate' in
    # the 'Events' hook.  I think this way is better since it is more explicit.
    self.on('terminal:authenticate', bind(send_ssh_css_template, self))
    self.on('terminal:authenticate', bind(create_user_ssh_dir, self))
Beispiel #6
0
def notice_esc_seq_handler(self, message):
    """
    Handles text passed from the special optional escape sequance handler to
    display a *message* to the connected client (browser).  It can be invoked
    like so:

    .. ansi-block::

        $ echo -e "\033]_;notice|Text passed to some_function()\007"

    .. seealso::

        :class:`app_terminal.TerminalApplication.opt_esc_handler` and
        :func:`terminal.Terminal._opt_handler`
    """
    if not hasattr(self, 'notice_log'):
        self.notice_log = go_logger('gateone.notice', **self.log_metadata)
    self.notice_log.info("Notice Plugin: %s" % message)
    message = {'go:notice': message}
    self.write_message(message)
Beispiel #7
0
__license__ = "AGPLv3 or Proprietary (see LICENSE.txt)"
__version_info__ = (1, 0)
__author__ = 'Dan McDougall <*****@*****.**>'

# Standard library imports
import os, io

# Gate One imports
from utils import RUDict, json_decode, json_encode, get_translation
from golog import go_logger

# 3rd party imports
from tornado.options import options

APPLICATION_PATH = os.path.split(__file__)[0] # Path to our application
term_log = go_logger("gateone.terminal")

# Localization support
_ = get_translation()

def save_term_settings(term, location, session, settings):
    """
    Saves the *settings* associated with the given *term*, *location*, and
    *session* in the 'term_settings.json' file inside the user's session
    directory.

    When complete the given *callback* will be called (if given).
    """
    term = str(term) # JSON wants strings as keys
    term_settings = RUDict()
    term_settings[location] = {term: settings}
Beispiel #8
0
from functools import partial

# Our stuff
from gateone import BaseHandler
from utils import get_translation, mkdir_p, shell_command, which
from utils import noop, bind
from golog import go_logger

_ = get_translation()

# Tornado stuff
import tornado.web
import tornado.ioloop

# Globals
ssh_log = go_logger("gateone.terminal.ssh", plugin='ssh')
OPENSSH_VERSION = None
DROPBEAR_VERSION = None
PLUGIN_PATH = os.path.split(__file__)[0]  # Path to this plugin's directory
OPEN_SUBCHANNELS = {}
SUBCHANNEL_TIMEOUT = timedelta(
    minutes=5)  # How long to wait before auto-closing
READY_STRING = "GATEONE_SSH_EXEC_CMD_CHANNEL_READY"
READY_MATCH = re.compile("^%s$" % READY_STRING, re.MULTILINE)
OUTPUT_MATCH = re.compile("^{rs}.+^{rs}$".format(rs=READY_STRING),
                          re.MULTILINE | re.DOTALL)
VALID_PRIVATE_KEY = valid = re.compile(
    r'^-----BEGIN [A-Z]+ PRIVATE KEY-----.*-----END [A-Z]+ PRIVATE KEY-----$',
    re.MULTILINE | re.DOTALL)
TIMER = None  # Used to store temporary, cancellable timeouts
Beispiel #9
0
 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")
Beispiel #10
0
from functools import partial

# Our stuff
from gateone import BaseHandler
from utils import get_translation, mkdir_p, shell_command, which
from utils import noop, bind
from golog import go_logger

_ = get_translation()

# Tornado stuff
import tornado.web
import tornado.ioloop

# Globals
ssh_log = go_logger("gateone.terminal.ssh", plugin='ssh')
OPENSSH_VERSION = None
DROPBEAR_VERSION = None
PLUGIN_PATH = os.path.split(__file__)[0] # Path to this plugin's directory
OPEN_SUBCHANNELS = {}
SUBCHANNEL_TIMEOUT = timedelta(minutes=5) # How long to wait before auto-closing
READY_STRING = "GATEONE_SSH_EXEC_CMD_CHANNEL_READY"
READY_MATCH = re.compile("^%s$" % READY_STRING, re.MULTILINE)
OUTPUT_MATCH = re.compile(
    "^{rs}.+^{rs}$".format(rs=READY_STRING), re.MULTILINE|re.DOTALL)
VALID_PRIVATE_KEY = valid = re.compile(
    r'^-----BEGIN [A-Z]+ PRIVATE KEY-----.*-----END [A-Z]+ PRIVATE KEY-----$',
    re.MULTILINE|re.DOTALL)
TIMER = None # Used to store temporary, cancellable timeouts
# TODO: make execute_command() a user-configurable option...  So it will automatically run whatever command(s) the user likes whenever they connect to a given server.  Differentiate between when they connect and when they start up a master or slave channel.
# TODO: Make it so that equivalent KnownHostsHandler functionality works over the WebSocket.
Beispiel #11
0
from golog import go_logger

# 3rd party imports
import tornado.web
import tornado.auth
import tornado.escape

# Localization support
_ = get_translation()

# Globals
GATEONE_DIR = os.path.dirname(os.path.abspath(__file__))
SETTINGS_CACHE = {}  # Lists of settings files and their modification times
# The security stuff below is a work-in-progress.  Likely to change all around.

auth_log = go_logger('gateone.auth')


# Authorization stuff
@memoize
def applicable_policies(application, user, policies):
    """
    Given an *application* and a *user* object, returns the merged/resolved
    policies from the given *policies* :class:`RUDict`.

    .. note:: Policy settings always start with '*', 'user', or 'group'.
    """
    # Start with the default policy
    try:
        policy = RUDict(policies['*'][application].copy())
    except KeyError:
Beispiel #12
0
 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")
Beispiel #13
0
# gettext stuff if you want:
from utils import get_translation


# 3rd party imports
# You can add command line options to Gate One with define():
from tornado.options import define, options
# You need 'options' to get define()'d values

# Globals
SESSIONS = {} # This will get replaced with gateone.py's SESSIONS dict
# NOTE: The overwriting of SESSIONS happens inside of gateone.py as part of
# the application initialization process.
APPLICATION_PATH = os.path.split(__file__)[0] # Path to our application
web_handlers = [] # Populated at the bottom of this file
example_log = go_logger("gateone.example") # Our app's logger
# NOTE: You can pass additional metadata to logs which will be JSON-encoded
# when your messages are logged.  Examples of how to do this are further along
# in this file...

# Localization support
_ = get_translation() # You don't *have* to do this but it is a good idea

# This is how you add command-line options to Gate One:
define(
    "example_option", # NOTE: underscores are the preferred word separator here
    default=True,
    help=_("Doesn't do anything (just an example from the example app).")
)
# You could then reference this option like so:
# print(options.example_option)
Beispiel #14
0
from golog import go_logger

# 3rd party imports
import tornado.web
import tornado.auth
import tornado.escape

# Localization support
_ = get_translation()

# Globals
GATEONE_DIR = os.path.dirname(os.path.abspath(__file__))
SETTINGS_CACHE = {} # Lists of settings files and their modification times
# The security stuff below is a work-in-progress.  Likely to change all around.

auth_log = go_logger('gateone.auth')

# Authorization stuff
@memoize
def applicable_policies(application, user, policies):
    """
    Given an *application* and a *user* object, returns the merged/resolved
    policies from the given *policies* :class:`RUDict`.

    .. note:: Policy settings always start with '*', 'user', or 'group'.
    """
    # Start with the default policy
    try:
        policy = RUDict(policies['*'][application].copy())
    except KeyError:
        # No default policy--not good but not mandatory
Beispiel #15
0
# You can use this for providing localization but you could just use the stdlib
# gettext stuff if you want:
from utils import get_translation

# 3rd party imports
# You can add command line options to Gate One with define():
from tornado.options import define, options
# You need 'options' to get define()'d values

# Globals
SESSIONS = {}  # This will get replaced with gateone.py's SESSIONS dict
# NOTE: The overwriting of SESSIONS happens inside of gateone.py as part of
# the application initialization process.
APPLICATION_PATH = os.path.split(__file__)[0]  # Path to our application
web_handlers = []  # Populated at the bottom of this file
example_log = go_logger("gateone.example")  # Our app's logger
# NOTE: You can pass additional metadata to logs which will be JSON-encoded
# when your messages are logged.  Examples of how to do this are further along
# in this file...

# Localization support
_ = get_translation()  # You don't *have* to do this but it is a good idea

# This is how you add command-line options to Gate One:
define(
    "example_option",  # NOTE: underscores are the preferred word separator here
    default=True,
    help=_("Doesn't do anything (just an example from the example app)."))
# You could then reference this option like so:
# print(options.example_option)
Beispiel #16
0
from golog import go_logger

# 3rd party imports
import tornado.web
import tornado.auth
import tornado.escape

# Localization support
_ = get_translation()

# Globals
GATEONE_DIR = os.path.dirname(os.path.abspath(__file__))
SETTINGS_CACHE = {}  # Lists of settings files and their modification times
# The security stuff below is a work-in-progress.  Likely to change all around.

auth_log = go_logger("gateone.auth")

# Authorization stuff
@memoize
def applicable_policies(application, user, policies):
    """
    Given an *application* and a *user* object, returns the merged/resolved
    policies from the given *policies* :class:`RUDict`.

    .. note:: Policy settings always start with '*', 'user', or 'group'.
    """
    # Start with the default policy
    try:
        policy = RUDict(policies["*"][application].copy())
    except KeyError:
        # No default policy--not good but not mandatory
Beispiel #17
0
__license__ = "AGPLv3 or Proprietary (see LICENSE.txt)"
__version_info__ = (1, 0)
__author__ = 'Dan McDougall <*****@*****.**>'

# Standard library imports
import os, io

# Gate One imports
from utils import RUDict, json_decode, json_encode, get_translation
from golog import go_logger

# 3rd party imports
from tornado.options import options

APPLICATION_PATH = os.path.split(__file__)[0]  # Path to our application
term_log = go_logger("gateone.terminal")

# Localization support
_ = get_translation()


def save_term_settings(term, location, session, settings):
    """
    Saves the *settings* associated with the given *term*, *location*, and
    *session* in the 'term_settings.json' file inside the user's session
    directory.

    When complete the given *callback* will be called (if given).
    """
    term = str(term)  # JSON wants strings as keys
    term_settings = RUDict()