Slide 1

Slide 1 text

Playing in the Sandbox Modularity & Security - Kindergarten Style

Slide 2

Slide 2 text

Agenda ● Concepts ● Introducing the actors ● Decomposing a kindergarten ● A closer look at the actors ● Bringing it all together

Slide 3

Slide 3 text

The Concept Sandbox Theory: In a sandbox any module can play without interfering with other modules, or the applications stability Modules could be drop-in-drop-out replaceable (yeah right...)

Slide 4

Slide 4 text

The Concept Modularity (not ruby modules) Goes beyond the sandbox theory. We want to prevent developers making 'stupid' mistakes. Should increase code coherence

Slide 5

Slide 5 text

The Concept KISS (Keep It Simple, Stupid) Provide simplification of ORM complexities DRY (Do not Repeat Yourself) Enable DRYness in the most outer layers Reduces developer skill requirements. DORY (Do Repeat Yourself) Within the Kindergarten, wax-on - wax-off

Slide 6

Slide 6 text

The Concept Sandbox Practice: (seems peaceful; war is pending)

Slide 7

Slide 7 text

Kindergarten

Slide 8

Slide 8 text

Kindergarten In order for our children to function on a kindergarten we need: ● Safety ● Guidance ● Play

Slide 9

Slide 9 text

Safety: A Perimeter

Slide 10

Slide 10 text

Guidance: A Governess

Slide 11

Slide 11 text

Play: A Sandbox

Slide 12

Slide 12 text

Kindergarten In order for our children modules to function on a kindergarten in an application we need: ● Safety - A perimeter ● Guidance - A governess ● Play - A sandbox

Slide 13

Slide 13 text

Safety ● Always a matter of (self-)discipline. ● The perimeter controls access to the ORM ● The perimeter is a mediator; ORM observers with side-effects should be moved into the perimeter ● The perimeter is governed

Slide 14

Slide 14 text

Guidance ● The governess oversees the perimeter(s) ● CanCan can help and is wrapped inside the Governess ● The Governess provides attribute scrubbing (no more ORM blacklist/whitelist non-sense)

Slide 15

Slide 15 text

Play ● The Kindergarten provides the Sandbox ● The Kindergarten governs the Sandbox ● Adding modules to the Kindergarten expands the Perimeter ● Modules should adhere to an interface (think contract, not java)

Slide 16

Slide 16 text

The Perimeter

Slide 17

Slide 17 text

Perimeter definition class SnowModule < Kindergarten::Perimeter purpose :snow govern do can :view, Snow end def shovel(snow, direction) snow.shovel(direction) end expose :shovel # like private end

Slide 18

Slide 18 text

Perimeter purpose class SnowModule < Kindergarten::Perimeter purpose :snow end ● Each module *must* have purpose. ● The purpose provides namespace. ● Modules can share a purpose.

Slide 19

Slide 19 text

Perimeter loading sandbox = Kindergarten.sandbox(child_object) sandbox.snow.shovel(snow, :down) # raises MethodNotFound sandbox.load_module(SnowModule) sandbox.snow.shovel(snow, :up) # executes the sandboxed method

Slide 20

Slide 20 text

Perimeter exposing class ProjectPerimeter < Kindergarten::Perimeter purpose :projects def internal_method # do something; module-wise end end sandbox = Kindergarten.sandbox(child_object) sandbox.load_perimeter(ProjectPerimeter) sandbox.projects.internal_method # raises MethodNotFound - was not sandboxed

Slide 21

Slide 21 text

Perimeter governing class ProjectPerimeter < Kindergarten::Perimeter # assign this perimeter with an own governess governess Kindergarten::StrictGoverness def unchecked(project) project.do_something end sandbox :unchecked end sandbox.unchecked(project) # raises Kindergarten::Perimeter::Unguarded

Slide 22

Slide 22 text

The Governess

Slide 23

Slide 23 text

Governess available ● HeadGoverness A base class for governesses, used by the Sandbox ● StrictGoverness Demands each method invocation to be guarded ● EasyGoverness Disables all guarding (testing purps only!)

Slide 24

Slide 24 text

Governess rules class ProjectPerimeter < Kindergarten::Perimeter govern do # just as inside your CanCan Ability file! can :create, Project # only an owner can add members can :create_member, Project do |project| child == project.owner end # etc, etc. end end

Slide 25

Slide 25 text

Governess hierarchy ● The sandbox has a Head Governess ● Each Perimeter can have it's own ● Makes rules perimeter specific ● The Head Governess is still aware ● Governess can be user-defined

Slide 26

Slide 26 text

Governess definition class LogGoverness < Kindergarten::HeadGoverness # the sandbox wraps all methods in the governed method # of the perimeters governess # def governed(method, &block) logger.info "#{method} performed" super # a chain of governesses (poor kid!) end end class ProjectPerimeter < Kindergarten::Perimeter governess LogGoverness # ... end

Slide 27

Slide 27 text

Governess head governess # in self governed perimeter-1 govern do can :view, Project end # in self governed perimeter-2 govern do can :update, Project end guard(:view, Project) # => false - not in this perimeter # sandbox (both true) sandbox.allows?(:update, Project) sandbox.allows?(:view, Project)

Slide 28

Slide 28 text

Governess scrubbing class ProjectPerimeter < Kindergarten::Perimeter def create_project(attributes) guard(:create, project) # remove any attributes not specified safe = scrub(attributes, :name, :description) Project.create(safe) end end

Slide 29

Slide 29 text

Governess rinsing class ProjectPerimeter < Kindergarten::Perimeter def create_project(attributes) guard(:create, project) # remove any attributes like scrub, or those that do # not untaint with the given Regexp safer = rinse(attributes, :name => /^([a-zA-Z0-9\s]+)$/i, :description => /^(.*)$/ ) Project.create(safer) end end

Slide 30

Slide 30 text

Governess orm class Project < ActiveRecord::Base # updates :create, :new & :update_attributes so that # they will only accept scrubbed attributes # include Kindergarten::ORM::Governess # or even stricter - only rinsed attributes force_rinsing end

Slide 31

Slide 31 text

The Sandbox

Slide 32

Slide 32 text

Sandbox mediation ● Not truly a mediator, but a publish/subscribe pattern ● Mediation could be done by keeping track of module purpose

Slide 33

Slide 33 text

Sandbox mediation class UserPerimeter < Kindergarten::Perimeter subscribe :projects, create, ->(event) { event.target.request_invitation } subscribe :follows_user, :follow_back def follow_back(event) child.follow(event.target) end end

Slide 34

Slide 34 text

Sandbox mediation cont'd class UserPerimeter < Kindergarten::Perimeter purpose :users # subscribe :purpose, :event subscribe :projects, create, ->(event) { event.target.request_invitation } subscribe :users, :follows, :follow_back def follow_back(event) child.follow(event.target) end end

Slide 35

Slide 35 text

Sandbox mediation cont'd class UserPerimeter < Kindergarten::Perimeter purpose :users def follow(user) guard :view, user child.follow(user) # notify the other players in the sandbox of the # :follows event in the :users purpose publish :follows, :target => user end end

Slide 36

Slide 36 text

Sandbox mediation cont'd sandbox.perimeters(purpose: :users) >> [UserPerimeter, ..., ...] # subscribe all project perimeters to do something on # task creation sandbox.perimeter(purpose: :projects).each do |perim| perim.subscribe(:tasks, :create) do |event| # do something end end # end all subscriptions on the :users purpose sandbox.unsubscribe(purpose: :users)

Slide 37

Slide 37 text

Sandbox mediation broadcast # provide a self-written broadcast - could be Faye, # AMQP, Stomp, whatever # sandbox.broadcast_publish do |event| @faye_client.publish( "/#{event.purpose}/#{event.name}", event.to_json ) end

Slide 38

Slide 38 text

Sandbox mediation broadcast cont'd # same goes for subscribe # stomp.subscribe("/users/create") do |msg| # using 'broadcasted' prevents broadcasting sandbox.broadcasted_publish(msg) # equal sandbox.publish( :users, :create, msg, broadcast: false ) end

Slide 39

Slide 39 text

All together now

Slide 40

Slide 40 text

All together now Rails example class ApplicationController < ActionController::Base before_filter { @sandbox = Kindergarten.sandbox(current_user) } def sandbox @sandbox end helper :sandbox end

Slide 41

Slide 41 text

All together now Rails example class ProjectsController < ApplicationController before_filter { @sandbox.extend_perimeter(Projects) } respond_to :html def index respond_with sandbox.projects.all end def show respond_with sandbox.projects.find(params[:id]) end end

Slide 42

Slide 42 text

class ProjectsModule < Kindergarten::Perimeter purpose :projects govern do can :manage, Project do |project| project.owner == child end can [:view, :create], Project end def all child.projects end def find(id) guard(:view, Project.find(id)) end expose :all, :find end All together now Rails example