def create_file(session, cls, tid, raw_cert): config = ConfigFactory(session, tid, 'node') db_cfg = load_tls_dict(session, tid) db_cfg['ssl_cert'] = raw_cert cv = cls.validator() ok, _ = cv.validate(db_cfg) if ok: config.set_val(u'https_cert', raw_cert) State.tenant_cache[tid].https_cert = raw_cert return ok
def db_serialize_node(session, tid, language): """ Serialize node info. """ # Contexts and Receivers relationship configured = session.query(models.ReceiverContext).filter(models.ReceiverContext.context_id == models.Context.id, models.Context.tid == tid).count() > 0 node = ConfigFactory(session, tid, 'public_node').serialize() ret_dict = { 'root_tenant': tid == 1, 'languages_enabled': models.EnabledLanguage.list(session, tid) if node['wizard_done'] else list(LANGUAGES_SUPPORTED_CODES), 'languages_supported': LANGUAGES_SUPPORTED, 'configured': configured, 'accept_submissions': State.accept_submissions, 'logo': db_get_file(session, tid, u'logo') } l10n_dict = NodeL10NFactory(session, tid).localized_dict(language) if tid != 1: root_tenant_node = ConfigFactory(session, 1, 'public_node') ret_dict['enable_footer_customization'] = root_tenant_node.get_val(u'enable_footer_customization') if language not in models.EnabledLanguage.list(session, tid): language = root_tenant_node.get_val(u'default_language') root_tenant_l10n = NodeL10NFactory(session, tid) l10n_dict['footer'] = root_tenant_l10n.get_val(u'footer', language) ret_dict['favicon'] = ret_dict['css'] = ret_dict['script'] = '' if tid == 1 or node['enable_graphic_customization']: ret_dict['css'] = db_get_file(session, tid, u'css') ret_dict['favicon'] = db_get_file(session, tid, u'favicon') ret_dict['script'] = db_get_file(session, tid, u'script') return merge_dicts(node, ret_dict, l10n_dict)
def load_tls_dict(session, tid): """ A quick and dirty function to grab all of the tls config for use in subprocesses """ node = ConfigFactory(session, tid) return { 'ssl_key': node.get_val(u'https_priv_key'), 'ssl_cert': node.get_val(u'https_cert'), 'ssl_intermediate': node.get_val(u'https_chain'), 'ssl_dh': node.get_val(u'https_dh_params'), 'https_enabled': node.get_val(u'https_enabled'), 'hostname': node.get_val(u'hostname'), }
def serialize_https_config_summary(session, tid): config = ConfigFactory(session, tid) file_summaries = {} for key, file_res_cls in FileHandler.mapped_file_resources.items(): file_summaries[key] = file_res_cls.db_serialize(session, tid) return { 'enabled': config.get_val(u'https_enabled'), 'running': State.process_supervisor.is_running(), 'status': State.process_supervisor.get_status(), 'files': file_summaries, 'acme': config.get_val(u'acme') }
def db_serialize(session, tid): c = ConfigFactory(session, tid).get_val('https_chain') if len(c) == 0: return {'name': 'chain', 'set': False} x509 = crypto.load_certificate(crypto.FILETYPE_PEM, c) expr_date = letsencrypt.convert_asn1_date(x509.get_notAfter()) return { 'name': 'chain', 'issuer': tls.parse_issuer_name(x509), 'expiration_date': expr_date, 'set': True, }
def try_to_enable_https(session, tid): config = ConfigFactory(session, tid) cv = tls.ChainValidator() tls_config = load_tls_dict(session, tid) tls_config['https_enabled'] = False ok, _ = cv.validate(tls_config) if not ok: raise errors.InputValidationError() config.set_val('https_enabled', True) State.tenant_cache[tid].https_enabled = True State.snimap.load(tid, tls_config)
def db_update_node(session, tid, request, language): """ Update and serialize the node infos :param tid: :param request: :param session: the session on which perform queries. :param language: the language in which to localize data :return: a dictionary representing the serialization of the node """ config = ConfigFactory(session, tid) config.update('node', request) if 'basic_auth' in request and request['basic_auth'] and request[ 'basic_auth_username'] and request['basic_auth_password']: config.set_val('basic_auth', True) config.set_val('basic_auth_username', request['basic_auth_username']) config.set_val('basic_auth_password', request['basic_auth_password']) else: config.set_val('basic_auth', False) config.set_val('basic_auth_username', '') config.set_val('basic_auth_password', '') if request['enable_ricochet_panel'] and not request['ricochet_address']: request['enable_ricochet_panel'] = False # Validate that IP addresses/ranges we're getting are goo if 'ip_filter_admin' in request and request[ 'ip_filter_admin_enable'] and request['ip_filter_admin']: parse_csv_ip_ranges_to_ip_networks(request['ip_filter_admin']) if 'ip_filter_whistleblower_enable' in request and request[ 'ip_filter_whistleblower_enable'] and request[ 'ip_filter_whistleblower']: parse_csv_ip_ranges_to_ip_networks(request['ip_filter_whistleblower']) if 'languages_enabled' in request and 'default_language' in request: db_update_enabled_languages(session, tid, request['languages_enabled'], request['default_language']) if language in models.EnabledLanguage.list(session, tid): ConfigL10NFactory(session, tid).update('node', request, language) db_refresh_memory_variables(session, [tid]) if tid == 1: log.setloglevel(config.get_val('log_level')) return db_admin_serialize_node(session, tid, language)
def db_serialize(session, tid): c = ConfigFactory(session, tid).get_val(u'https_chain') if len(c) == 0: return {'name': 'chain', 'set': False} x509 = load_certificate(FILETYPE_PEM, c) expr_date = format_cert_expr_date(x509.get_notAfter()) return { 'name': 'chain', 'issuer': tls.parse_issuer_name(x509), 'expiration_date': datetime_to_ISO8601(expr_date), 'set': True, }
def signup_activation(session, state, tid, token, language): config = ConfigFactory(session, 1) if not config.get_val(u'enable_signup'): raise errors.ForbiddenOperation signup = session.query(models.Signup).filter(models.Signup.activation_token == token).one_or_none() if signup is None: return {} if not session.query(models.Config).filter(models.Config.tid == signup.tid).count(): tenant = session.query(models.Tenant).filter(models.Tenant.id == signup.tid).one() mode = config.get_val('mode') db_initialize_tenant(session, tenant, mode) password_admin = generateRandomKey(16) password_recipient = generateRandomKey(16) node_name = signup.organization_name if signup.organization_name else signup.subdomain wizard = { 'node_language': signup.language, 'node_name': node_name, 'admin_name': signup.name + ' ' + signup.surname, 'admin_password': password_admin, 'admin_mail_address': signup.email, 'receiver_name': signup.name + ' ' + signup.surname, 'receiver_password': password_recipient, 'receiver_mail_address': signup.email, 'profile': 'default', 'enable_developers_exception_notification': True } db_wizard(session, signup.tid, wizard, False, language) template_vars = { 'type': 'activation', 'node': db_admin_serialize_node(session, 1, language), 'notification': db_get_notification(session, 1, language), 'signup': serialize_signup(signup), 'password_admin': password_admin, 'password_recipient': password_recipient } state.format_and_send_mail(session, 1, {'mail_address': signup.email}, template_vars) db_refresh_memory_variables(session, [1])
def db_admin_serialize_node(session, tid, language, config_node='admin_node'): config = ConfigFactory(session, tid, config_node).serialize() # Contexts and Receivers relationship configured = session.query(models.ReceiverContext).filter(models.ReceiverContext.context_id == models.Context.id, models.Context.tid).count() > 0 misc_dict = { 'languages_supported': LANGUAGES_SUPPORTED, 'languages_enabled': models.EnabledLanguage.list(session, tid), 'configured': configured, 'root_tenant': tid == 1, 'https_possible': tid == 1 or State.tenant_cache[1].reachable_via_web, } if tid != 1: root_tenant_node = ConfigFactory(session, 1, 'node') misc_dict['version'] = root_tenant_node.get_val(u'version') misc_dict['latest_version'] = root_tenant_node.get_val(u'latest_version') misc_dict['enable_footer_customization'] = root_tenant_node.get_val(u'enable_footer_customization') l10n_dict = NodeL10NFactory(session, tid).localized_dict(language) return utils.sets.merge_dicts(config, misc_dict, l10n_dict)
def signup(session, state, tid, request, language): config = ConfigFactory(session, 1) if not config.get_val(u'enable_signup'): raise errors.ForbiddenOperation request['activation_token'] = generateRandomKey(32) request['language'] = language tenant_id = db_preallocate_tenant(session, {'label': request['subdomain'], 'subdomain': request['subdomain']}).id signup = models.Signup(request) signup.tid = tenant_id session.add(signup) # We need to send two emails # # The first one is sent to the platform owner with the activation email. # # The second goes to the instance administrators notifying them that a new # platform has been added. signup_dict = serialize_signup(signup) # Email 1 - Activation Link template_vars = { 'type': 'signup', 'node': db_admin_serialize_node(session, 1, language), 'notification': db_get_notification(session, 1, language), 'signup': signup_dict } state.format_and_send_mail(session, 1, {'mail_address': signup.email}, template_vars) # Email 2 - Admin Notification for user_desc in db_get_admin_users(session, 1): template_vars = { 'type': 'admin_signup_alert', 'node': db_admin_serialize_node(session, 1, user_desc['language']), 'notification': db_get_notification(session, 1, user_desc['language']), 'user': user_desc, 'signup': signup_dict } state.format_and_send_mail(session, 1, user_desc, template_vars)
def update_notification(session, tid, request, language): notif = ConfigFactory(session, tid, 'notification') if request['smtp_password'] == '': del request['smtp_password'] notif.update(request) notif_l10n = NotificationL10NFactory(session, tid) notif_l10n.update(request, language) if request.pop('reset_templates'): notif_l10n.reset_templates(load_appdata()) db_refresh_memory_variables(session, [tid]) return admin_serialize_notification(session, tid, language)
def update_notification(session, tid, request, language): config = ConfigFactory(session, tid) if request['smtp_password'] == '': del request['smtp_password'] config.update('notification', request) config_l10n = ConfigL10NFactory(session, tid) config_l10n.update('notification', request, language) if request.pop('reset_templates'): config_l10n.reset('notification', load_appdata()) db_refresh_memory_variables(session, [tid]) return db_get_notification(session, tid, language)
def serialize_tenant(session, tenant): ret = { 'id': tenant.id, 'creation_date': tenant.creation_date, 'active': tenant.active } ret.update(ConfigFactory(session, tenant.id).serialize('tenant')) signup = session.query(models.Subscriber).filter( models.Subscriber.tid == tenant.id).one_or_none() if signup is not None: from globaleaks.handlers.signup import serialize_signup ret['signup'] = serialize_signup(signup) return ret
def db_create_acme_key(session, tid): priv_fact = ConfigFactory(session, tid) key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) key = key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption(), ) priv_fact.set_val(u'acme', True) priv_fact.set_val(u'acme_accnt_key', key) return key
def db_get_notification(session, tid, language): """ Transaction to get the notification settings for the specified tenant :param session: An ORM session :param tid: A tenant ID :param language: The language to be used in the serialization :return: the serialization of notification settings for the specified tenant """ ret = ConfigFactory(session, tid).serialize('notification') ret.update( ConfigL10NFactory(session, tid).serialize('notification', language)) ret['templates'] = ConfigL10NFilters['notification'] return ret
def migrate_Tenant(self): for old_obj in self.session_old.query(self.model_from['Tenant']): self.entries_count['Config'] += 1 node = ConfigFactory(self.session_new, old_obj.id) new_obj = self.model_to['Tenant']() for key in new_obj.__table__.columns._data.keys(): setattr(new_obj, key, getattr(old_obj, key)) for key in ['subdomain']: x = self.model_to['Config']() x.tid = old_obj.id x.var_name = key x.value = getattr(old_obj, key) self.session_new.add(x) self.session_new.add(new_obj)
def load_tls_dict(session, tid): """ Transaction for loading the TLS configuration of a tenant :param session: An ORM session :param tid: A tenant ID :return: The serialized TLS configuration for the specified tenant """ node = ConfigFactory(session, tid) return { 'tid': tid, 'ssl_key': node.get_val('https_key'), 'ssl_cert': node.get_val('https_cert'), 'ssl_intermediate': node.get_val('https_chain'), 'https_enabled': node.get_val('https_enabled'), 'hostname': node.get_val('hostname'), }
def perform_data_update(db_file): session = get_session(make_db_uri(db_file), foreign_keys=False) enabled_languages = [ lang.name for lang in session.query(models.EnabledLanguage) ] removed_languages = list( set(enabled_languages) - set(LANGUAGES_SUPPORTED_CODES)) if removed_languages: removed_languages.sort() removed_languages = ', '.join(removed_languages) raise Exception( "FATAL: cannot complete the upgrade because the support for some of the enabled languages is currently incomplete (%s)\n" "Read about how to handle this condition at: https://github.com/globaleaks/GlobaLeaks/wiki/Upgrade-Guide#lang-drop" % removed_languages) try: cfg = ConfigFactory(session, 1) stored_ver = cfg.get_val(u'version') if stored_ver != __version__: # The below commands can change the current store based on the what is # currently stored in the DB. for tid in [t[0] for t in session.query(models.Tenant.id)]: appdata = load_appdata() config.update_defaults(session, tid, appdata) db_update_defaults(session) db_fix(session) cfg.set_val(u'version', __version__) cfg.set_val(u'latest_version', __version__) cfg.set_val(u'version_db', DATABASE_VERSION) session.commit() except: session.rollback() raise finally: session.close()
def db_serialize(session, tid): c = ConfigFactory(session, tid).get_val('https_cert') if not c: return { 'name': 'cert', 'set': False } log.err(c) c = """{}""".format(c) x509 = crypto.load_certificate(crypto.FILETYPE_PEM, c) expr_date = letsencrypt.convert_asn1_date(x509.get_notAfter()) return { 'name': 'cert', 'issuer': tls.parse_issuer_name(x509), 'expiration_date': expr_date, 'set': True, }
def db_update_node(session, tid, request, language, config_node): """ Update and serialize the node infos :param session: the session on which perform queries. :param language: the language in which to localize data :return: a dictionary representing the serialization of the node """ node = ConfigFactory(session, tid, config_node) node.update(request) if 'basic_auth' in request: if request['basic_auth'] and request[ 'basic_auth_username'] and request['basic_auth_password']: node.set_val(u'basic_auth', True) node.set_val(u'basic_auth_username', request['basic_auth_username']) node.set_val(u'basic_auth_password', request['basic_auth_password']) else: node.set_val(u'basic_auth', False) # Validate that IP addresses/ranges we're getting are goo if 'ip_filter_authenticated' in request: if request['ip_filter_authenticated_enable'] and request[ 'ip_filter_authenticated']: # Make sure we can validate and parse the whole thing parse_csv_ip_ranges_to_ip_networks( request['ip_filter_authenticated']) if 'languages_enabled' in request and 'default_language' in request: db_update_enabled_languages(session, tid, request['languages_enabled'], request['default_language']) if language in models.EnabledLanguage.list(session, tid): node_l10n = NodeL10NFactory(session, tid) node_l10n.update(request, language) db_refresh_memory_variables(session, [tid]) return db_admin_serialize_node(session, tid, language)
def serialize_tenant(session, tenant, signup=None): from globaleaks.handlers.signup import serialize_signup node = ConfigFactory(session, tenant.id) ret = { 'id': tenant.id, 'label': tenant.label, 'active': tenant.active, 'subdomain': tenant.subdomain, 'hostname': node.get_val('hostname'), 'onionservice': node.get_val('onionservice'), 'mode': node.get_val('mode'), 'creation_date': tenant.creation_date } if signup is not None: ret['signup'] = serialize_signup(signup) return ret
def db_get_notification(session, tid, language): """ Transaction to get the notification settings for the specified tenant :param session: An ORM session :param tid: A tenant ID :param language: The language to be used in the serialization :return: the serialization of notification settings for the specified tenant """ config_dict = ConfigFactory(session, tid).serialize('admin_notification') conf_l10n_dict = ConfigL10NFactory(session, tid).serialize('notification', language) additional_dict = { 'smtp_password': '', 'templates': ConfigL10NFilters['notification'] } return merge_dicts(config_dict, conf_l10n_dict, additional_dict)
def get_l10n(session, tid, lang): if tid != 1: config = ConfigFactory(session, 1) if config.get_val(u'mode') == u'whistleblowing.it': tid = 1 path = langfile_path(lang) directory_traversal_check(Settings.client_path, path) if not os.path.exists(path): raise errors.ResourceNotFound() texts = read_json_file(path) custom_texts = session.query(models.CustomTexts).filter(models.CustomTexts.lang == lang, models.CustomTexts.tid == tid).one_or_none() custom_texts = custom_texts.texts if custom_texts is not None else {} texts.update(custom_texts) return texts
def db_get_notification(session, tid, language): """ Transaction to get the notification settings for the specified tenant :param session: An ORM session :param tid: A tenant ID :param language: The language to be used in the serialization :return: the serialization of notification settings for the specified tenant """ config_dict = ConfigFactory(session, tid).serialize('admin_notification') conf_l10n_dict = ConfigL10NFactory(session, tid).serialize('notification', language) cmd_flags = { 'reset_templates': False, 'exception_email_pgp_key_remove': False, 'smtp_password': '', } return merge_dicts(config_dict, cmd_flags, conf_l10n_dict)
def check_hostname(session, tid, input_hostname): """ Ensure the hostname does not collide across tenants or include an origin that it shouldn't. """ root_hostname = ConfigFactory(session, 1).get_val(u'hostname') forbidden_endings = ['onion', 'localhost'] if tid != 1 and root_hostname != '': forbidden_endings.append(root_hostname) for v in forbidden_endings: if input_hostname.endswith(v): raise errors.InputValidationError('Hostname contains a forbidden origin') existing_hostnames = {h.value for h in session.query(Config) .filter(Config.tid != tid, Config.var_name == u'hostname')} if input_hostname in existing_hostnames: raise errors.InputValidationError('Hostname already reserved')
def evaluate_update_notification(session, state, latest_version): priv_fact = ConfigFactory(session, 1, 'node') stored_latest = priv_fact.get_val(u'latest_version') if V(stored_latest) < V(latest_version): priv_fact.set_val(u'latest_version', latest_version) if V(__version__) == V(latest_version): return for user_desc in db_get_admin_users(session, 1): lang = user_desc['language'] template_vars = { 'type': 'software_update_available', 'latest_version': latest_version, 'node': db_admin_serialize_node(session, 1, lang), 'notification': db_get_notification(session, 1, lang), 'user': user_desc, } state.format_and_send_mail(session, 1, user_desc, template_vars)
def db_acme_cert_issuance(session, tid): priv_fact = ConfigFactory(session, tid, 'node') hostname = State.tenant_cache[tid].hostname raw_accnt_key = priv_fact.get_val(u'acme_accnt_key') accnt_key = serialization.load_pem_private_key(raw_accnt_key.encode(), password=None, backend=default_backend()) priv_key = priv_fact.get_val(u'https_priv_key') tmp_chall_dict = State.tenant_state[tid].acme_tmp_chall_dict # Run ACME registration all the way to resolution cert_str, chain_str = letsencrypt.run_acme_reg_to_finish( hostname, accnt_key, priv_key, hostname, tmp_chall_dict, Settings.acme_directory_url) priv_fact.set_val(u'https_cert', cert_str) priv_fact.set_val(u'https_chain', chain_str) State.tenant_cache[tid].https_cert = cert_str State.tenant_cache[tid].https_chain = chain_str
def perform_data_update(db_file): engine = get_engine('sqlite+pysqlite:////' + db_file, foreign_keys=False) session = sessionmaker(bind=engine)() enabled_languages = [lang.name for lang in session.query(models.EnabledLanguage)] removed_languages = list(set(enabled_languages) - set(LANGUAGES_SUPPORTED_CODES)) if removed_languages: removed_languages.sort() removed_languages = ', '.join(removed_languages) raise Exception("FATAL: cannot complete the upgrade because the support for some of the enabled languages is currently incomplete (%s)\n" "Read about how to handle this condition at: https://github.com/globaleaks/GlobaLeaks/wiki/Upgrade-Guide#lang-drop" % removed_languages) try: prv = ConfigFactory(session, 1, 'node') stored_ver = prv.get_val(u'version') if stored_ver != __version__: prv.set_val(u'version', __version__) # The below commands can change the current store based on the what is # currently stored in the DB. for tid in [t[0] for t in session.query(models.Tenant.id)]: appdata = load_appdata() config.update_defaults(session, tid) l10n.update_defaults(session, tid, appdata) db_update_defaults(session) db_fix_fields_attrs(session) session.commit() except: session.rollback() raise finally: session.close()
def create_file(session, cls, tid): log.info("Generating an ACME account key with %d bits" % Settings.key_bits) priv_fact = ConfigFactory(session, tid, 'node') # NOTE key size is hard coded to align with minimum CA requirements # TODO change format to OpenSSL key to normalize types of keys used priv_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) log.debug("Saving the ACME key") b = priv_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption(), ) priv_fact.set_val(u'acme', True) priv_fact.set_val(u'acme_accnt_key', b) return priv_key