summaryrefslogtreecommitdiff
path: root/src/leap/soledad/server/app.py
blob: d1a518f5f759a2a6223877276cc3d15b62785a39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# -*- coding: utf-8 -*-
# app.py
# Copyright (C) 2017 LEAP
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Soledad Server, as a Twisted Application.
"""
import os

from twisted.application import service, strports
from twisted.internet import reactor
from twisted.web import server

from leap.soledad.common.couch.check import check_schema_versions
from leap.soledad.common.log import getLogger
from leap.soledad.server import entrypoints
from leap.soledad.server import get_config


logger = getLogger(__name__)


def _deferred_shutdown(status):
    reactor.addSystemEventTrigger('after', 'shutdown',
                                  os._exit, status)
    reactor.stop()


def _exit(status):
    reactor.callWhenRunning(_deferred_shutdown, status)


def _log_and_exit(failure):
    logger.error('Error while starting up server: %r'
                 % failure.getErrorMessage())
    _exit(20)


#
# necessary checks
#

def check_env(local_port, public_port):
    if local_port == public_port:
        logger.error("LOCAL_SERVICES_PORT and HTTPS_PORT can't be the same!")
        _exit(20)

    if public_port is None and not os.getenv('DEBUG_SERVER'):
        logger.error("HTTPS_PORT env var is required to be set!")
        _exit(20)


def check_conf(conf):
    path = conf['blobs_path']
    blobs_not_empty = bool(os.path.exists(path) and os.listdir(path))
    if not conf['blobs'] and blobs_not_empty:
        message = """
**  WARNING: Blobs is disabled, but blobs directory isn't empty.          **
**  If it was previously enabled, disabling can cause data loss due blobs **
**  documents not being accessible to users.                              **
**  Blobs directory: %s
**  REFUSING TO START. Please double check your configuration.            **
    """
        logger.error(message % path)
        _exit(20)


#
# service creation functions
#

def create_local_service(port, application):
    logger.info('Starting local Services HTTP API')
    desc = 'tcp:%s:interface=127.0.0.1' % port
    site = server.Site(entrypoints.ServicesEntrypoint())
    service = strports.service(desc, site)
    service.setServiceParent(application)


def get_tls_service_description(port):
    privateKey = os.getenv('PRIVKEY_PATH', '/etc/soledad/soledad-server.key')
    certKey = os.getenv('CERT_PATH', '/etc/soledad/soledad-server.pem')
    sslmethod = os.getenv('SSL_METHOD', 'SSLv23_METHOD')
    desc = ':'.join([
        'ssl',
        'port=' + str(port),
        'privateKey=' + privateKey,
        'certKey=' + certKey,
        'sslmethod=' + sslmethod])
    return desc


def create_public_service(port, application):
    logger.info('Starting public Users HTTP API')
    if port:
        desc = get_tls_service_description(port)
    else:
        logger.warn('Using plain HTTP on public Users API.')
        desc = 'tcp:port=2424:interface=0.0.0.0'

    site = server.Site(entrypoints.UsersEntrypoint())
    service = strports.service(desc, site)
    service.setServiceParent(application)


def create_services(local_port, public_port, application):
    create_local_service(local_port, application)
    create_public_service(public_port, application)


#
# the application
#

def patch_noisy_factory():
    from twisted.internet.protocol import Factory
    Factory.noisy = False


def run(application):
    patch_noisy_factory()
    local_port = os.getenv('LOCAL_SERVICES_PORT', 2525)
    public_port = os.getenv('HTTPS_PORT', None)
    conf = get_config()
    check_env(local_port, public_port)
    check_conf(conf)
    d = check_schema_versions(conf['couch_url'])
    d.addCallback(lambda _: create_services(local_port, public_port,
                                            application))
    d.addErrback(_log_and_exit)


application = service.Application('soledad-server')