Pragmantic SaaS Architecture

Pragmantic SaaS Architecture

A presentation I gave at wearedevelopers about pragmatic SaaS architectures.

181de1fb11dffe39774f3e2e23cda3b6?s=128

Armin Ronacher

May 11, 2017
Tweet

Transcript

  1. 4.
  2. 5.

    800°C 36° 2' 0.4662" N 118° 15' 38.7792" W 795°C

    789°C 797°C 793°C 805°C 782°C
  3. 7.
  4. 11.
  5. 20.

    Tenant Isolation from framework import get_request def get_tenant_from_request(): request =

    get_request() auth = validate_auth(request.headers.get('Authorization')) return Tenant.query.get(auth.tenant_id) def get_current_tenant(): rv = tls.current_tenant if rv is None: rv = get_tenant_from_request() tls.current_tenant = rv return rv
  6. 21.

    Automatic Tenant Scoping def batch_update_projects(ids, changes): projects = Project.query.filter( Project.id.in_(ids)

    & Project.status != ProjectStatus.INVISIBLE ) for project in projects: update_project(project, changes) DANGER!
  7. 22.

    Automatic Tenant Scoping class Project(db.Model): id = db.Column(db.Integer, primary_key=True) status

    = db.Column(db.Integer) tenant_id = db.Column(db.Integer, db.ForeignKey('tenants.id')) tenant = db.relationship(Tenant) @classproperty def query(cls): return db.Query(self).filter( Project.tenant == get_current_tenant() )
  8. 24.

    User Scope & Request Scope def get_current_scopes(): current_user = get_current_user()

    if current_user is None: all_scopes = set(['anonymous']) else: all_scopes = current_user.get_roles() return all_scopes & scopes_from_request_authorization()
  9. 26.

    Log Security Related Actions def log(action, message=None): data = {

    'action': action, 'timestamp': datetime.utcnow() } if message is not None: data['message'] = message if request: data['ip'] = get_request().remote_addr user = get_current_user() if user is not None: data['user'] = User db.session.add(LogMessage(**data))
  10. 28.

    Language from User or Request def get_current_language(): user = get_current_user()

    if user is not None: return user.language request = get_current_request() if request and request.accept_languages: return request.accept_languages[0] return 'en_US'
  11. 34.

    custom linters on commit mitsuhiko at herzog in ~/Development/sentry on

    git:master+? workon sentry $ git ci -am 'Performance improvements to the data scrubber.' src/sentry/utils/data_scrubber.py:147:1: F401 'unused' imported but unused
  12. 42.