Slide 1

Slide 1 text

SOA SSO & @MattKetmo PHPTour Lyon 2014

Slide 2

Slide 2 text

Service Oriented Architecture “Developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms” #EventDriven #DistributedSystem

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

/me Matthieu Moquet @MattKetmo Developer at …for almost 3 years

Slide 5

Slide 5 text

Back to 2011…

Slide 6

Slide 6 text

Covoiturage.fr 1 monolithic PHP app o Core website o Web mobile o B2B platform o Backoffice tools o Web views (mobile apps) o External widgets

Slide 7

Slide 7 text

Plain Old PHP No Dependency Injection No functional tests High coupling …hard to maintain

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

# wc -l lib.trip.php 3678 …longest method: 1000+ lines

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

get rid of the TECHNICAL DEBT

Slide 12

Slide 12 text

started from scratch since 2012

Slide 13

Slide 13 text

2.0

Slide 14

Slide 14 text

Let’s embrace best practices

Slide 15

Slide 15 text

1 bundle BlablacarAppBundle BlablacarBlogBundle BlablacarFaqBundle BlablacarTripBundle BlablacarUserBundle ... N bundles OR

Slide 16

Slide 16 text

Services & Controllers §  Should controllers access the repository? §  How should I split my services? §  Is ContainerAware that bad? §  How to organize my Business Layer?

Slide 17

Slide 17 text

Where should I put my Model & Doctrine entities?

Slide 18

Slide 18 text

Forms Should I map my entities with my forms?

Slide 19

Slide 19 text

Validation §  Should I map validation with entities? §  Should I use groups?

Slide 20

Slide 20 text

Translations §  Catalogue message? §  Should I use key identifiers or phrases? §  How to name the keys?

Slide 21

Slide 21 text

Route naming homepage blablacar_homepage blablacar_main_homepage

Slide 22

Slide 22 text

Events §  Listen on Doctrine events? §  Create your own? ACTION / PRE_ACTION / POST_ACTION ? §  Subscribers VS. Listeners

Slide 23

Slide 23 text

Design When you don’t have full-time designer, then Bootstrap(2) FTW

Slide 24

Slide 24 text

With a few developers… At the beginning, you need to start quickly, (but you take time to write conventions). Legacy App New App

Slide 25

Slide 25 text

Then you hire new people

Slide 26

Slide 26 text

How many developers to be more efficient?

Slide 27

Slide 27 text

More code means…

Slide 28

Slide 28 text

Heavy Container

Slide 29

Slide 29 text

$ app/console cache:warmup Warming up the cache for the dev env with debug true

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

$ app/console assetic:dump Dumping all dev assets. Debug mode is on. 23:00:32 [file+] /path/to/web/css/0e781b6.css 23:00:35 [file+] /path/to/web/css/0e781b6_part_1_bootstrap_1.css 23:00:35 [file+] /path/to/web/css/0e781b6_part_2_buttons_1.css 23:00:35 [file+] /path/to/web/css/0e781b6_part_2_card_2.css 23:00:35 [file+] /path/to/web/css/0e781b6_part_2_form_3.css 23:00:35 [file+] /path/to/web/css/0e781b6_part_2_grid_4.css 23:00:36 [file+] /path/to/web/css/0e781b6_part_2_list_5.css 23:00:36 [file+] /path/to/web/css/0e781b6_part_2_navbar_6.css 23:00:36 [file+] /path/to/web/css/0e781b6_part_2_panel_7.css 23:00:37 [file+] /path/to/web/css/0e781b6_part_2_vcard_9.css 23:00:37 [file+] /path/to/web/js/e66598b.js 23:00:37 [file+] /path/to/web/js/e66598b_jquery_1.js 23:00:37 [file+] /path/to/web/js/e66598b_bootstrap_2.js 23:00:37 [file+] /path/to/web/js/e66598b_script_3.js

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

$ phpunit -c app ........................... .............. (63/975) .......................... ............... (126/975) .............. ........................... (189/975) .......F................ ................. (252/975) .............F....

Slide 34

Slide 34 text

Changed few lines of code? Run the full test suite!

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

Optimize test execution time Invest in parallelization processes Split TestSuite in VMs (on AWS) Run 1h tests in 10min

Slide 37

Slide 37 text

One big projet makes your team less reactive How often do you deploy your main project?

Slide 38

Slide 38 text

https://code.facebook.com/posts/218678814984400/scaling-mercurial-at-facebook/

Slide 39

Slide 39 text

Changing foundations is expensive What if we want to change…

Slide 40

Slide 40 text

What if we want to change The backend framework? We don’t plan to change it, and we couldn’t

Slide 41

Slide 41 text

What if we want to change The testing framework? We won’t rewrite the whole test suite. But we can use several frameworks at the same time (eg. Behat)

Slide 42

Slide 42 text

What if we want to change The frontend framework? Well, we are stuck with Bootstrap2. Updating to Bootstrap3 or rewriting our own will take time.

Slide 43

Slide 43 text

What if we want to change The assets builder? Assetic took too long to compile all assets. We moved to asset management with Grunt.

Slide 44

Slide 44 text

What if we want to change The data layer? Actually frontend servers make MySQL queries. But in the long term, it’s not a good practice (see coming slides).

Slide 45

Slide 45 text

We’ll ALWAYS have technical debt. We must LIMIT it as much as possible.

Slide 46

Slide 46 text

Think as small as possible µServices The secret to building large apps is never build large apps. Break your application into small pieces. Then, assemble those testable, bite-sized pieces into your big application — Justin Meyer

Slide 47

Slide 47 text

3 patterns to build a better software architecture…

Slide 48

Slide 48 text

Request-Response

Slide 49

Slide 49 text

Request-Response GET your resources synchronously Data Layer

Slide 50

Slide 50 text

Request-Response $user = $this ->get('my.repository.user') ->find(1337);

Slide 51

Slide 51 text

Request-Response $mysql->query('...')

Slide 52

Slide 52 text

Request-Response Id   Firstname   Lastname   Pseudo   Email   Birthday   1337   Ma'hieu   Moquet   Ma'Ketmo   ma'[email protected]   1988-­‐12-­‐17  

Slide 53

Slide 53 text

Request-Response However front-end servers should NOT access the database directly It should fetch normalized data from an internal service API for the win!

Slide 54

Slide 54 text

Request-Response GET /users/123 Do you speak REST ? Better versioning & normalization

Slide 55

Slide 55 text

Request-Response …or you may speak: § SOAP § XML-RPC § Protobuffer § Thrift § etc.

Slide 56

Slide 56 text

Request-Response Even RabbitMQ can be used for synchronous requests scrutinizer-ci/rabbitmq src/Scrutinizer/RabbitMQ/Rpc http://www.rabbitmq.com/tutorials/tutorial-six-python.html

Slide 57

Slide 57 text

Request-Response

Slide 58

Slide 58 text

Command(Handler)

Slide 59

Slide 59 text

Command $cmd = FooCommand('bar'); $handler = FooCommandHandler(); $handler->execute($cmd); Do not expect a return value

Slide 60

Slide 60 text

Command Perfect for asynchronous jobs §  Send e-mails / SMS / PUSH notifications §  Image processing §  Data indexation §  Saving complex data §  etc.

Slide 61

Slide 61 text

Command publish consume RabbitMQ   DIRECT  rou

Slide 62

Slide 62 text

Command publish consume Easy  scaling  

Slide 63

Slide 63 text

Command Service is accessible via a queueing system only (not REST) §  RabbitMQ §  ActiveMQ §  Beanstalkd §  Gearman §  … More at http://queues.io

Slide 64

Slide 64 text

Sending Newsletters publish 100k messages Create an army of workers in AWS consume send mails message payload = emails address + content App Scheduler (Java + Quartz) Worker NL @10am segment X Get users of segment X (Scroll ElasticSearch)

Slide 65

Slide 65 text

PubSub Obverser / Event Dispatcher

Slide 66

Slide 66 text

PubSub $dispatcher = new EventDispatcher();! ! // Listen using an object or a callback! $listener = new AcmeListener();! $dispatcher->addListener('foobar', array($listener, 'onFoobar'));! ! $dispatcher->addListener('foobar', function (Event $event) {! // do something else with the events! });! ! // Dispatch event! $dispatcher->dispatch('foobar', $event);! Example with the Symfony EventDispatcher

Slide 67

Slide 67 text

PubSub Notify your infrastructure of every business events Publish events without knowing who is listening Create services without any core changes

Slide 68

Slide 68 text

PubSub pub RabbitMQ   TOPIC  rou

Slide 69

Slide 69 text

PubSub Use Case BI

Slide 70

Slide 70 text

user.register user.edit_bio user.left_rating user.post_trip ... Data Warehouse Log every business events in Hadoop

Slide 71

Slide 71 text

user.register user.edit_bio user.left_rating user.post_trip ... user.register user.post_trip ... Meanwhile… RealTime Dashboard Log every business events in ElasticSearch

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

ReqRes — Command — PubSub Message Broker RabbitMQ removes hard link between services

Slide 74

Slide 74 text

Now we have the keys to start a distributed architecture let’s start decoupling our application… Front-end desktop + mobile Hard to split in several projects (need to delegate jobs) API For mobile apps & partners Backoffice Set of administration tools Workers Already decoupled from the core app

Slide 75

Slide 75 text

Backoffice A set of independent tools Easy to split

Slide 76

Slide 76 text

Backoffice — CRUD Some tools are just data manipulation: – User Management – Blog – FAQ GET /users PUT /users/123 Data Layer

Slide 77

Slide 77 text

Backoffice — Moderation Manage user « data » which need to be moderated user.upload_avatar user.edit_bio user.left_rating UI to check data manually Auto detect spam & non compliant data Machine Learning data.received data.treated send mails

Slide 78

Slide 78 text

Backoffice — URL Shortener Some tools are completely independent https://www.blablacar.com/register SHORTEN No data shared with core business

Slide 79

Slide 79 text

Authentication Splitting our backoffice in many apps should not be a pain for the UX

Slide 80

Slide 80 text

Let’s build a Single Sign-On service

Slide 81

Slide 81 text

Single Sign On “SSO is a method allowing a user to access multiple applications, making only a single authentication.”

Slide 82

Slide 82 text

STARTING POINT LDAP

Slide 83

Slide 83 text

Single Sign ONCE example.org/app app.example.org Simple Case: Shared Host / Shared Domain

Slide 84

Slide 84 text

No content

Slide 85

Slide 85 text

« OAuth is an open standard for authorization […] to access server resources on behalf of a resource owner » — Wikipedia

Slide 86

Slide 86 text

OAuth2 Grant Types Authorization Code Connect-like workflow Implicit Grant (Direct Token) Usefull for JS app Password flow Trusted app (client credentials) Client Credentials Basic (use of client id + secret)

Slide 87

Slide 87 text

OAuth2

Slide 88

Slide 88 text

Do It Yourself with

Slide 89

Slide 89 text

From LDAP to OAuth Given I am on the login page Then I should login with my LDAP credentials App LDAP Login

Slide 90

Slide 90 text

There is a bundle for that

Slide 91

Slide 91 text

FR3DLdapBundle

Slide 92

Slide 92 text

FR3DLdapBundle/config.yml # LDAP Configuration! fr3d_ldap:! driver:! host: "%ldap_host%"! username: "%ldap_username%"! password: "%ldap_password%"! baseDn: "%ldap_username%"! user:! baseDn: "%ldap_user_dn%"! filter: "%ldap_user_filter%"! attributes:! - { ldap_attr: "uid", user_method: "setUsername" }! - { ldap_attr: "cn", user_method: "setName" }! - { ldap_attr: "mail", user_method: "setEmail" }! service:! user_manager: "acme.user_manager"!

Slide 93

Slide 93 text

From LDAP to OAuth Given I am on "/me" with "user" access token Then I should get "user" resources in JSON App LDAP OAuth2 API

Slide 94

Slide 94 text

There is a bundle for that

Slide 95

Slide 95 text

FOSOAuthServerBundle

Slide 96

Slide 96 text

FOSOAuthServerBundle/ security.yml firewalls:! api:! pattern: ^/api! fos_oauth: true! stateless: true  

Slide 97

Slide 97 text

From LDAP to OAuth App LDAP OAuth2 API Login

Slide 98

Slide 98 text

Enforce security PO said, this SSO entry-point should be a top-notch secure app

Slide 99

Slide 99 text

There ARE bundles for THAT

Slide 100

Slide 100 text

SchebTwoFactorBundle SpomkyIpFilterBundle CCDNUserSecurityBundle NelmioSecurityBundle …

Slide 101

Slide 101 text

App LDAP OAuth2 2FA FW

Slide 102

Slide 102 text

What about the client?

Slide 103

Slide 103 text

Of course, there is a bundle for that

Slide 104

Slide 104 text

HWIOAuthBundle

Slide 105

Slide 105 text

No content

Slide 106

Slide 106 text

# HWI OAuth Configuration! hwi_oauth:! firewall_name: "main"! resource_owners:! acme_sso:! type: "oauth2"! client_id: "%client_id%"! client_secret: "%client_secret%"! access_token_url: "%base_url%/oauth/v2/token"! authorization_url: "%base_url%/oauth/v2/auth"! infos_url: "%base_url%/api/me”! paths:! identifier: "id"! nickname: "username"! realname: "name"! email: "email"!

Slide 107

Slide 107 text

UX — We don’t really need this login form

Slide 108

Slide 108 text

UX — We don’t really need a one-button page

Slide 109

Slide 109 text

302 Found UX — Get transparent login process

Slide 110

Slide 110 text

OAuth is all about authorization, not authentication Watch out for weaknesses & attacks

Slide 111

Slide 111 text

GET /api/me { "id": 1337, "firstname": "Matthieu", "lastname": "Moquet", "nickname": "MattKetmo" } (the cheat)

Slide 112

Slide 112 text

CSRF http://example.org?redirect_uri=xxxx&state=yyyy session[state] === params[state]. http://tools.ietf.org/html/draft-ietf-oauth-v2-27#section-10.12

Slide 113

Slide 113 text

Cover Redirect (is a joke) http://homakov.blogspot.fr/2014/05/covert-redirect-faq.html

Slide 114

Slide 114 text

id_token Multiple Response Type Encoding Practices. Provides an assertion of the identity of the Resource Owner. http://openid.bitbucket.org/oauth-v2-multiple-response-types-1_0.html

Slide 115

Slide 115 text

However if OAuth2 is not designed to be an SSO protocol, what should I use?

Slide 116

Slide 116 text

OAuth1.a

Slide 117

Slide 117 text

It seems Symfony have a bundle for everything…

Slide 118

Slide 118 text

BazingaOAuthServerBundle

Slide 119

Slide 119 text

No content

Slide 120

Slide 120 text

SAML (Security Assertion Markup Language) SAML is an XML-based protocol that uses security tokens containing assertions to pass information about a principal between an identity provider, and a consumer. There are bundles/lib for that (but not maintained, see impl.) §  pdias/FOSSamlBundle §  aerialship/SamlSPBundle §  chtitux/sfSAMLPlugin (symfony1)

Slide 121

Slide 121 text

CAS (Central Authentication Service) Example of client implementation BeSimple/BeSimpleSsoAuthBundle

Slide 122

Slide 122 text

h"ps://github.com/BeSimple/BeSimpleSsoAuthBundle/blob/master/Resources/doc/protocols.md  

Slide 123

Slide 123 text

JWT (JSON Web Token) Payload signed server-side with a JSON Web Signature (JWS). auth.   signed token request

Slide 124

Slide 124 text

JWT (JSON Web Token) Sign payload using a secret key auth.   signed token request

Slide 125

Slide 125 text

firebase/php-jwt curl –H "Authorization: Bearer eyJ0eXAiOiJKV..." example.org $key = "s3cr3t_key"; $token = array( "sub" => "mattketmo", "aud" => "http://example.com", "exp" => 1356999524, ); $jwt = JWT::encode($token, $key);  // eyJ0eXAiOiJKV...

Slide 126

Slide 126 text

namshi/jose OpenSSL + cookies // Auth (SSO) $privateKey = openssl_pkey_get_private("file://private.key"); $jws = new JWS('RS256'); $jws->setPayload([$userId]); $jws->sign($privateKey) setcookie('identity', $jws->getTokenString()); // Client App $jws = JWS::load($_COOKIE['identity']); $publicKey = openssl_pkey_get_public("/public.key"); if ($jws->isValid($publicKey)) { $payload = $jws->getPayload(); $userId = $payload['id']; }

Slide 127

Slide 127 text

OAuth2 + JWT http://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-08

Slide 128

Slide 128 text

OpenID Connect Feb. 2014 — openid.net/connect/

Slide 129

Slide 129 text

No content

Slide 130

Slide 130 text

Core app is now lightweight o Tiny container (less services/listeners) o Test suite is smaller o Forget about technical debt

Slide 131

Slide 131 text

Choose the right tools for the task Backend / Frontend / Datastore / …

Slide 132

Slide 132 text

Make experiments

Slide 133

Slide 133 text

Work faster No more bottleneck & dependencies with other dev teams

Slide 134

Slide 134 text

BE EFFICIENT AVOID REPETITIVE PROCESSES

Slide 135

Slide 135 text

Install dev environment in less than 1h

Slide 136

Slide 136 text

Bootstrap everything composer create-project blablacar/backoffice-app git clone [email protected]/worker-skeleton.git via git or via composer

Slide 137

Slide 137 text

Enforce reusable components { "require": { "blablacar/monolog": "~1.0", "blablacar/scheduler-client": "~1.1", "blablacar/redis-client": "~1.2", "blablacar/rabbit-mq-admin-toolkit": "dev-master" } }

Slide 138

Slide 138 text

Be ready for production Don’t loose time in configuration setup Parameters for $ENV Config template for $PROJECT Config file for $PROJECT/$ENV Centralized build tool to generate a project configuration file for any environment (local / dev / staging / prod)

Slide 139

Slide 139 text

Never miss a log

Slide 140

Slide 140 text

Don’t be lost in translations Open-sourced a tool to manage your project translations easily More information tomorrow, 9:45

Slide 141

Slide 141 text

Thank you Slides available at moquet.net/talks/phptour-2014-sso-soa Leave feedbacks @MattKetmo