Skip to content

Commit

Permalink
BIG CHANGE TO GOOGLE AUTH: Google has discontinued their old OAuth1 s…
Browse files Browse the repository at this point in the history
…ervice. For this reason I have switched the code to use their new OAuth2 service. In order to use it though you *must* register a Google "project" for your Gate One server and add an origin and auth callback. If you configure Gate One to use Google authentication and don't have the necessary settings it will log a set of instructions to get it working. Updated documentation is forthcoming.

terminal_input.js:  Added "GateOne.Input" as a proper dependency to the `SuperSandbox()` call.  This probably isn't necessary since application plugins get loaded much later than gateone_input.js would ever be but it's good form nonetheless.
terminal.js:  Added 'term' (terminal number) to the message data that gets sent to the WebWorker inside of `GateOne.Terminal.updateTerminalAction()`.
terminal.js:  Added a `GateOne.Terminal.lastLines(n, term)` which will give you the last *n* _actual_ lines of the given *term* (e.g. the line with the shell prompt).  This is what most people want inside their plugins when performing pattern matching looking for the shell prompt (so they know when they can execute a command).
app_terminal.py:  Added a 'term' keyword argument to plugin_command_hooks and plugin_log_metadata_hooks so that value will be available to plugins when using those hooks.
  • Loading branch information
liftoff committed Jul 9, 2014
1 parent 62af456 commit 41a5463
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 26 deletions.
2 changes: 1 addition & 1 deletion gateone/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
__version_info__ = (1, 2, 0)
__license__ = "AGPLv3" # ...or proprietary (see LICENSE.txt)
__author__ = 'Dan McDougall <[email protected]>'
__commit__ = "20140609191913" # Gets replaced by git (holds the date/time)
__commit__ = "20140609214034" # Gets replaced by git (holds the date/time)

import os
GATEONE_DIR = os.path.dirname(os.path.abspath(__file__))
Expand Down
8 changes: 4 additions & 4 deletions gateone/applications/terminal/app_terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -926,22 +926,22 @@ def new_multiplex(self,
if not os.path.exists(log_dir):
mkdir_p(log_dir)
log_suffix = "-{0}.golog".format(
self.current_user['ip_address'])
self.current_user.get('ip_address', "0.0.0.0"))
log_name = datetime.now().strftime(
'%Y%m%d%H%M%S%f') + log_suffix
log_path = os.path.join(log_dir, log_name)
facility = string_to_syslog_facility(self.settings['syslog_facility'])
# This allows plugins to transform the command however they like
if self.plugin_command_hooks:
for func in self.plugin_command_hooks:
cmd = func(self, cmd)
cmd = func(self, cmd, term=term_id)
additional_log_metadata = {
'ip_address': self.current_user.get('ip_address', "")
'ip_address': self.current_user.get('ip_address', "0.0.0.0")
}
# This allows plugins to add their own metadata to .golog files:
if self.plugin_log_metadata_hooks:
for func in self.plugin_log_metadata_hooks:
metadata = func(self)
metadata = func(self, term=term_id)
additional_log_metadata.update(metadata)
terminal_emulator_kwargs = {}
if enabled_filetypes != 'all':
Expand Down
2 changes: 1 addition & 1 deletion gateone/applications/terminal/plugins/example/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def example_opt_esc_handler(self, message, term=None, multiplex=None):
"You just executed the Example plugin's optional escape sequence handler!"}
self.write_message(message)

def example_command_hook(self, command):
def example_command_hook(self, command, term=None):
"""
This demonstrates how to modify Gate One's configured 'command' before it is
executed. It will replace any occurrance of %EXAMPLE% with 'foo'. So if
Expand Down
23 changes: 22 additions & 1 deletion gateone/applications/terminal/static/terminal.js
Original file line number Diff line number Diff line change
Expand Up @@ -1443,7 +1443,8 @@ go.Base.update(GateOne.Terminal, {
'termUpdateObj': termUpdateObj,
'prefs': go.prefs,
'textTransforms': textTransforms,
'checkBackspace': checkBackspace
'checkBackspace': checkBackspace,
'term': term
};
// This event allows plugins to take actions based on the incoming message and to transform it before it is sent to the Web Worker for processing:
E.trigger("terminal:incoming_term_update", message);
Expand Down Expand Up @@ -3632,6 +3633,26 @@ go.Base.update(GateOne.Terminal, {
return shareObj['broadcast'];
}
}
},
lastLines: function(/*opt*/n, /*opt*/term) {
/**:GateOne.Terminal.lastLines([n[, term]])
Returns the last *n* non-blank (trimmed) line in the terminal. Useful for pattern matching.
If *term* is not given the ``localStorage[prefix+'selectedTerminal']`` will be used.
*/
term = term || localStorage[prefix+'selectedTerminal'];
var lastLine,
nonblankLines,
screen = go.Terminal.terminals[term].screen;
// Walk the screen to find the last non-blank line
for (var i=0; i <= screen.length-1; i++) {
if (screen[i].length && screen[i].trim().length) {
lastLine = i;
}
}
nonblankLines = screen.slice((lastLine - n)+1, lastLine+1)
return nonblankLines;
}
});

Expand Down
74 changes: 57 additions & 17 deletions gateone/auth/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"""

# Import stdlib stuff
import os, re, logging
import os, re, logging, json
try:
from urllib import quote
except ImportError: # Python 3
Expand All @@ -91,6 +91,7 @@
import tornado.auth
import tornado.escape
import tornado.httpclient
import tornado.gen

# Localization support
_ = get_translation()
Expand Down Expand Up @@ -283,21 +284,36 @@ def get(self):
self.write('unauthenticated')
self.finish()

class GoogleAuthHandler(BaseAuthHandler, tornado.auth.GoogleMixin):

class GoogleAuthHandler(BaseAuthHandler, tornado.auth.GoogleOAuth2Mixin):
"""
Google authentication handler using Tornado's built-in GoogleMixin (fairly
boilerplate).
Google authentication handler using Tornado's built-in GoogleOAuth2Mixin
(fairly boilerplate).
"""
@tornado.web.asynchronous
@tornado.gen.coroutine
def get(self):
"""
Sets the 'user' cookie with an appropriate *upn* and *session* and any
other values that might be attached to the user object given to us by
Google.
"""
self.base_url = "{protocol}://{host}:{port}{url_prefix}".format(
protocol=self.request.protocol,
host=self.request.host,
port=self.settings['port'],
url_prefix=self.settings['url_prefix'])
if 'https://' in self.base_url:
if ':443/' in self.base_url:
# Get rid of the 443 (it's assumed since https)
self.base_url = self.base_url.replace(':443', '', 1)
if 'http://' in self.base_url:
if ':80/' in self.base_url:
# Get rid of the 443 (it's assumed since https)
self.base_url = self.base_url.replace(':80', '', 1)
redirect_uri = "{base_url}auth".format(base_url=self.base_url)
check = self.get_argument("check", None)
if check:
self.set_header ('Access-Control-Allow-Origin', '*')
self.set_header('Access-Control-Allow-Origin', '*')
user = self.get_current_user()
if user:
logging.debug('GoogleAuthHandler: user is authenticated')
Expand All @@ -314,26 +330,50 @@ def get(self):
self.clear_cookie('gateone_user')
self.user_logout(user, logout_url)
return
if self.get_argument("openid.mode", None):
self.get_authenticated_user(self._on_auth)
return
self.authenticate_redirect(
ax_attrs=["name", "email", "language", "username"])
if self.get_argument('code', False):
user = yield self.get_authenticated_user(
redirect_uri=redirect_uri,
code=self.get_argument('code'))
if not user:
self.clear_all_cookies()
raise tornado.web.HTTPError(500, 'Google auth failed')
access_token = str(user['access_token'])
http_client = self.get_auth_http_client()
response = yield http_client.fetch(
'https://www.googleapis.com/oauth2/v1/userinfo?access_token='
+access_token)
if not response:
self.clear_all_cookies()
raise tornado.web.HTTPError(500, 'Google auth failed')
user = json.loads(response.body.decode('utf-8'))
self._on_auth(user)
else:
yield self.authorize_redirect(
redirect_uri=redirect_uri,
client_id=self.settings['google_oauth']['key'],
scope=['email'],
response_type='code',
extra_params={'approval_prompt': 'auto'})

def _on_auth(self, user):
"""
Just a continuation of the get() method (the final step where it
actually sets the cookie).
"""
logging.debug("GoogleAuthHandler.on_auth(%s)" % user)
if not user:
raise tornado.web.HTTPError(500, _("Google auth failed"))
# NOTE: Google auth 'user' will be a dict like so:
# user = {
# 'locale': u'en-us',
# 'first_name': u'Dan',
# 'last_name': u'McDougall',
# 'name': u'Dan McDougall',
# 'email': u'[email protected]'}
# user = {'given_name': 'Joe',
# 'verified_email': True,
# 'hd': 'example.com',
# 'gender': 'male',
# 'email': '[email protected]',
# 'name': 'Joe Schmoe',
# 'picture': 'https://lh6.googleusercontent.com/path/to/some.jpg',
# 'id': '999999999999999999999',
# 'family_name': 'Schmoe',
# 'link': 'https://plus.google.com/999999999999999999999'}
user['upn'] = user['email'] # Use the email for the upn
self.user_login(user)
next_url = self.get_argument("next", None)
Expand Down
23 changes: 22 additions & 1 deletion gateone/core/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
__version_info__ = (1, 2, 0)
__license__ = "AGPLv3" # ...or proprietary (see LICENSE.txt)
__author__ = 'Dan McDougall <[email protected]>'
__commit__ = "20140609191913" # Gets replaced by git (holds the date/time)
__commit__ = "20140609214034" # Gets replaced by git (holds the date/time)

# NOTE: Docstring includes reStructuredText markup for use with Sphinx.
__doc__ = '''\
Expand Down Expand Up @@ -3695,6 +3695,27 @@ def __init__(self, settings, **kwargs):
AuthHandler = PAMAuthHandler
elif settings['auth'] == 'google':
AuthHandler = GoogleAuthHandler
if 'google_oauth' not in tornado_settings:
logging.error(_(
'In order to use Google authentication you must create '
'a Google project for your installation and add:\n\t'
'{"google_oauth": {"key": "<YOUR CLIENT ID>", "secret":'
' "<YOUR CLIENT SECRET>"}} to your '
'20authentication.conf (under the "gateone" section).'))
logging.info(_(
'To create a Google auth client ID and secret go to: '
'https://console.developers.google.com/ and click on '
'"APIs and Auth". Then click "Create New Client ID".'
' Set the "JavaScript Origins" value to your Gate One '
'server\'s address and the "Redirect URIs" to https://'
'<your Gate One server FQDN>/auth'))
logging.info(_(
'For example, if your "JavaScript Origins" is: '
'https://gateone.example.com/'))
logging.info(_(
'Your "Redirect URIs" would be: '
'https://gateone.example.com/auth'))
sys.exit(1)
elif settings['auth'] == 'cas':
AuthHandler = CASAuthHandler
elif settings['auth'] == 'ssl':
Expand Down
2 changes: 1 addition & 1 deletion gateone/static/gateone.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 41a5463

Please sign in to comment.