summaryrefslogtreecommitdiff
path: root/service/src/pixelated/application.py
diff options
context:
space:
mode:
authorKali Kaneko <kali@leap.se>2017-07-25 11:40:11 -0400
committerKali Kaneko <kali@leap.se>2017-07-25 11:40:29 -0400
commit91e4481c450eb7eb928debc1cb7fa59bdb63dd7b (patch)
tree8fd7e6e77b6df669c33d96b7edad6db3cbe14dfe /service/src/pixelated/application.py
parente4f755309d4cf5cfb6b0bcc62ed73d6070956ab5 (diff)
[pkg] packaging and path changes
- move all the pixelated python package under src/ - move the pixelated_www package under the leap namespace - allow to set globally the static folder - add hours and minutes to the timestamp in package version, to allow for several releases a day.
Diffstat (limited to 'service/src/pixelated/application.py')
-rw-r--r--service/src/pixelated/application.py219
1 files changed, 219 insertions, 0 deletions
diff --git a/service/src/pixelated/application.py b/service/src/pixelated/application.py
new file mode 100644
index 00000000..ef99d47c
--- /dev/null
+++ b/service/src/pixelated/application.py
@@ -0,0 +1,219 @@
+#
+# Copyright (c) 2015 ThoughtWorks, Inc.
+#
+# Pixelated is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pixelated is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+
+from OpenSSL import SSL
+from OpenSSL import crypto
+from leap.common.events import (server as events_server,
+ register, catalog as events)
+from leap.soledad.common.errors import InvalidAuthTokenError
+from twisted.logger import Logger
+from twisted.conch import manhole_tap
+from twisted.cred import portal
+from twisted.cred.checkers import AllowAnonymousAccess
+from twisted.internet import defer
+from twisted.internet import reactor
+from twisted.internet import ssl
+
+from pixelated.adapter.welcome_mail import add_welcome_mail
+from pixelated.authentication import Authenticator
+from pixelated.config import arguments
+from pixelated.config import logger
+from pixelated.config import services
+from pixelated.config.leap import initialize_leap_single_user, init_monkeypatches, initialize_leap_provider
+from pixelated.config.services import ServicesFactory, SingleUserServicesFactory
+from pixelated.config.site import PixelatedSite
+from pixelated.resources.auth import PixelatedRealm, PixelatedAuthSessionWrapper, SessionChecker
+from pixelated.resources.login_resource import LoginResource
+from pixelated.resources.root_resource import RootResource
+
+log = Logger()
+
+
+class UserAgentMode(object):
+ def __init__(self, is_single_user):
+ self.is_single_user = is_single_user
+
+
+@defer.inlineCallbacks
+def start_user_agent_in_single_user_mode(root_resource, services_factory, leap_home, leap_session):
+ log.info('Bootstrap done, loading services for user %s' % leap_session.user_auth.username)
+
+ _services = services.Services(leap_session)
+ yield _services.setup()
+
+ if leap_session.fresh_account:
+ yield add_welcome_mail(leap_session.mail_store)
+
+ services_factory.add_session(leap_session.user_auth.uuid, _services)
+
+ authenticator = Authenticator(leap_session.provider)
+ root_resource.initialize(provider=leap_session.provider, authenticator=authenticator)
+
+ # soledad needs lots of threads
+ reactor.getThreadPool().adjustPoolsize(5, 15)
+ log.info('Done, the user agent is ready to be used')
+
+
+def _ssl_options(sslkey, sslcert):
+ with open(sslkey) as keyfile:
+ pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, keyfile.read())
+ with open(sslcert) as certfile:
+ cert = crypto.load_certificate(crypto.FILETYPE_PEM, certfile.read())
+
+ acceptable = ssl.AcceptableCiphers.fromOpenSSLCipherString(
+ u'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:!RC4:HIGH:!MD5:!aNULL:!EDH')
+ options = ssl.CertificateOptions(privateKey=pkey,
+ certificate=cert,
+ method=SSL.TLSv1_2_METHOD,
+ acceptableCiphers=acceptable)
+ return options
+
+
+def _create_service_factory(args):
+ if args.single_user:
+ return SingleUserServicesFactory(UserAgentMode(is_single_user=True))
+ else:
+ return ServicesFactory(UserAgentMode(is_single_user=False))
+
+
+def initialize():
+ log.info('Starting the Pixelated user agent')
+ args = arguments.parse_user_agent_args()
+ logger.init(debug=args.debug)
+ services_factory = _create_service_factory(args)
+ resource = RootResource(services_factory)
+
+ def start():
+ start_async = _start_mode(args, resource, services_factory)
+ add_top_level_system_callbacks(start_async, services_factory)
+
+ log.info('Running the reactor')
+ reactor.callWhenRunning(start)
+ reactor.run()
+
+
+def add_top_level_system_callbacks(deferred, services_factory):
+
+ def _quit_on_error(failure):
+ failure.printTraceback()
+ reactor.stop()
+
+ def _log_user_out(event, user_data):
+ log.info('Invalid soledad token, logging out %s' % user_data)
+ user_data = {'user_id': user_data['uuid']} if 'uuid' in user_data else {'user_id': user_data, 'using_email': True}
+ services_factory.destroy_session(**user_data)
+
+ def _log_user_out_on_token_expire(leap_session):
+ register(events.SOLEDAD_INVALID_AUTH_TOKEN, _log_user_out)
+ return leap_session
+
+ deferred.addCallback(_log_user_out_on_token_expire)
+ deferred.addErrback(_quit_on_error)
+
+
+def _start_mode(args, resource, services_factory):
+ if services_factory.mode.is_single_user:
+ deferred = _start_in_single_user_mode(args, resource, services_factory)
+ else:
+ deferred = _start_in_multi_user_mode(args, resource, services_factory)
+ return deferred
+
+
+def _start_in_multi_user_mode(args, root_resource, services_factory):
+ try:
+ protected_resources = _setup_multi_user(args, root_resource, services_factory)
+ start_site(args, protected_resources)
+ reactor.getThreadPool().adjustPoolsize(5, 15)
+ return defer.succeed(None)
+ except Exception as e:
+ return defer.fail(e)
+
+
+def _setup_multi_user(args, root_resource, services_factory):
+ if args.provider is None:
+ raise ValueError('Multi-user mode: provider name is required')
+ init_monkeypatches()
+ events_server.ensure_server()
+ provider = initialize_leap_provider(args.provider, args.leap_provider_cert, args.leap_provider_cert_fingerprint, args.leap_home)
+ protected_resource = set_up_protected_resources(root_resource, provider, services_factory, banner=args.banner)
+ return protected_resource
+
+
+def set_up_protected_resources(root_resource, provider, services_factory, banner=None, authenticator=None):
+ auth = authenticator or Authenticator(provider)
+ session_checker = SessionChecker(services_factory)
+
+ realm = PixelatedRealm()
+ _portal = portal.Portal(realm, [session_checker, AllowAnonymousAccess()])
+
+ anonymous_resource = LoginResource(services_factory, provider, disclaimer_banner=banner, authenticator=auth)
+ protected_resource = PixelatedAuthSessionWrapper(_portal, root_resource, anonymous_resource, [])
+ root_resource.initialize(provider, disclaimer_banner=banner, authenticator=auth)
+ return protected_resource
+
+
+def _start_in_single_user_mode(args, resource, services_factory):
+ start_site(args, resource)
+ deferred = initialize_leap_single_user(args.leap_provider_cert,
+ args.leap_provider_cert_fingerprint,
+ args.credentials_file,
+ args.leap_home)
+
+ def _handle_error(exception):
+ if(exception.type is InvalidAuthTokenError):
+ log.critical('Got an invalid soledad token, the user agent can\'t synchronize data, exiting')
+ os._exit(1)
+ else:
+ exception.raiseException()
+
+ deferred.addCallbacks(
+ lambda leap_session: start_user_agent_in_single_user_mode(
+ resource,
+ services_factory,
+ args.leap_home,
+ leap_session), _handle_error)
+ return deferred
+
+
+def start_site(config, resource):
+ log.info('Starting the API on port %s' % config.port)
+
+ if config.manhole:
+ log.info('Starting the manhole on port 8008')
+
+ multiService = manhole_tap.makeService(dict(namespace=globals(),
+ telnetPort='8008',
+ sshPort='8009',
+ sshKeyDir='sshKeyDir',
+ sshKeyName='id_rsa',
+ sshKeySize=4096,
+ passwd='passwd'))
+ telnetService, sshService = multiService.services
+ telnetFactory = telnetService.factory
+ sshFactory = sshService.factory
+
+ reactor.listenTCP(8008, telnetFactory, interface='localhost')
+ reactor.listenTCP(8009, sshFactory, interface='localhost')
+
+ site = PixelatedSite(resource)
+ site.displayTracebacks = False
+ if config.sslkey and config.sslcert:
+ reactor.listenSSL(config.port, site, _ssl_options(config.sslkey, config.sslcert),
+ interface=config.host)
+ else:
+ reactor.listenTCP(config.port, site, interface=config.host)