Extending the Zend Server UI and WebAPI Adding your own functionality to Zend Server Presented by Yonni Mendes with cats, spartans, pirates and Obama! Before we start, kindly visit avast.yonman.net and install the listed applications & stuff! Presented by Yonni Mendes, ZS6 UI Tech Leader Setup: what you should have ● ● ● ● Zend Studio, or other IDE of choice Zend Server AhoyWorld zpk deployable Tutorial package ○ ○ ○ Skeletons Exercise snippets Complete code ● Recommended ○ ○ Chrome: Dev HTTP Client or similar Firefox: Firebug, live HTTP Headers or similar Who and What is a Yonni ● A restaurant in Los Angeles, California. Sadly, closed. ● The UI Technological Leader for Zend Server ● A semi-famous male model Stuff you need: Also IDE Zend Server ● A father Ahoy World ● A gamer Chrome: Dev HTTP Firefox: Firebug, live HTTP Headers ● A technologist Sqlite database manager Ask me about the extra thing of choice But first… Some exercises to get the blood flowing Stand up please! Stuff you need: IDE Zend Server Ahoy World Chrome: Dev HTTP Firefox: Firebug, live HTTP Headers Sqlite database manager of choice • • Topics Create a basic 3rd party module Understanding ZS6 o o • o Under the Zend Server 6 hood Beasts and common practices of ZS6 Lets make our module do somethingStuff you need: Some more modules that are out there IDE Zend Server Ahoy World Chrome: Dev HTTP Firefox: Firebug, live HTTP Headers Sqlite database manager of choice Why extend ZS6? ● Zend Server’s unique position ● Zend Server’s existing platform & services And, for us, most importantly: B.W.C Stuff you need: IDE Zend Server Ahoy World Chrome: Dev HTTP Firefox: Firebug, live HTTP Headers Sqlite database manager of choice The first steps: 3rd party module So you want to add some functionality... A few ZF2 buzzwords These are a few devices we will need ● ServiceManager, Factories ● Merged config arrays ● Modules, Module manager Our scenario ● We have an application, “Ahoy World!” ○ Deploy it (https://github.com/YonmaN/AhoyWorld) ● This application can be turned on and off ● We want to allow a user logged into Zend Server to control this toggle directly from the ● We also want to control this toggle from other remote sources via WebAPI UI Andy the admin’s requirements • Access to toggle the application state o Anywhere, Anytime, not on a production port 80 o No access to those Creative Type People over at marketing • Human UI and API access • Must be secure o But: Do not create a new user authentication system, nor modify the existing one at Ahoy • In fact, do not modify AhoyWorld at all • Show some general app information too Our Module: Avast ● WebAPI will allow a client to: ○ ○ ○ Retrieve the current status of the site (on or off) Toggle the current status of the site Rely on external code that’s included into the module ● UI will display the deployment information about our application ● UI will only show a status indicator to the developer, nothing else ● UI will show a toggle button that allows the admin to turn the site on or off Functional requirements ● ZF2 module, use ZF2 features, be ZF2 compliant ● WebAPI will return both json and xml output ● UI page will be integrated in the ZS6 ACL ● UI page will show up in the navigation page ● UI will be accessible only to a logged in user Preparing to develop for ZS6 • Switch off opcode cache in Zend Server o Linux/mac: /usr/local/zend/gui/lighttpd/etc/conf.d/optimizerplus.ini • o the UI (Configuration|Components) Restart Zend Server o o • Windows: Through Throught the UI (localhost:10081) Command line (<install_dir>/bin/zendctl.sh restart) Recommended: open <install_dir>/gui as a php project in the IDE Warning! Confusion ahead! ● Zend Studio copies files ● Changes may not apply ● You may have to make some changes manually ● Particularly, the vendor library changes Creating a new module, the hard way ● ● ● ● Create a new php project zendframework/ZendSkeletonModule Unzip into your workspace folder Rename everything that needs renaming Rename gotta rename ● Folder names ○ in src ● File names ○ ○ Controller filename view scripts ● Controller class name Create a new module, the easy way ● Create a new php project ● Unzip avast.yonman.net/Avast-00-Initial.zip Important: can you create a symbolic link? Recommended: add the Zend GUI project as a library to the new module project, in ‘include path’ Make a connection ● In the Zend Server UI directory link to ./vendor o Add ‘Avast’ to application.config.php o • Open localhost:10081/ZendServer/Avast Note case sensitive URL Result: error :( Necessary changes ● Remove the entire router entry ● Replace controllers entry 'invokables' => array( 'Avast' => 'Avast\Controller\AvastController', ), Result: Still error! ACL entries and overrides • Pitfall: GUI will now return 404! o o • This is because of a bug in the ACL system There is no resource called “Avast" and therefore no controller is found ... ergo 404 ACL requires SQL entries o sqlite: var/db/gui.db or data/db/gui.db INSERT OR IGNORE INTO GUI_ACL_RESOURCES VALUES(NULL,'route:Avast'); INSERT OR IGNORE INTO GUI_ACL_PRIVILEGES VALUES(3, last_insert_rowid(), ''); (Place in sqls in a dedicated sql file - acl.sqlite.entries.sql) Result: localhost:10081/ZendServer/Avast should respond correctly Exercise Avast-01-acl Installation process of your module • Unpack / link module into ./vendor • Add module name to application.config.php • Run SQLs • Copy any config.php files into • ./config/autoload Copy any public directory assets to the public direcotry (js, images, css) Bask in the glory of your Module! Our module can now: ● Display an empty page ● Override navigation bar ● Display app info & status indicator ● Provide WebAPI control and monitor of Ahoy ● Provide a UI button to control Ahoy Avast ye landlubbers! Lets make things interesting Lets grease those elbows! From here on: 1. Describe a tool or ZS6 subsystem and its use 2. Show its usage in the ZS6 codebase 3. Integrate it to some extent into your module Our goal: 1. Create a functioning, usable module for ZS6 2. Module will provide functionality in webapi 3. Module will also provide a UI interface to use the above functionality 4. Have fun! Files you might want to watch Linux ● gui/lighttpd/logs/zend_server_ui.log ● gui/lighttpd/logs/php.log Windows ● logs/zend_server_ui.log ● logs/php.log You can access both through the Zend Server UI (Overview|Logs) • • • ZF2: Navigation override navigation.global.config.php We wish to add a new Avast tab Tab should use Avast controller Exercise: Override the navigation array in Avast/module.config.php Ask me about ZS6.2! Result: Navigation bar should Exercise Avast-02-navigation Calling the ZS databases • • You’ll want to do this through our mappers If you want to build a new mapper ... o ServiceManager and delegated Di DbConnector::factory Driver Connection Adapter TableGateway Mapper o Can also call ServiceManager->get('PDO') Spotlight: Deployment\FilteredAccessMapper ● Wraps another mapper, which wrapper yet another, foreign mapper. Good times. ● Things of interest ○ ○ FilteredAccessMapper::getMasterApplicationsByIds Deployment\Application\Container Ask me about the big dirty lie! Foreign code is a cruel mistress ● Next we will implement ○ ○ Avast UI has to show deployment information about “Ahoy World” Use Deployment\FilteredAccessMapper class to retrieve and display data ● Use foreign code carefully to avoid errors ● Aggregate, don’t inherit ● Avoid directly referring to foreign code directly Ask me why aggregate and not extend! An aside: Integration in good practice Recommended approach: ● Create an aggregating class for FilteredAccessMapper ● Implement findAhoyApplication() code in class ● Integrate your class into the controller Can go the extra mile - wrap the application container that is returned Calling the database ● Avast UI has to show deployment information about “Ahoy World” ● Use Deployment\FilteredAccessMapper class to retrieve and display data Exercise: Integrate a call to retrieve all applications. Find ‘Ahoy World’ application data. Show this data in your view script. Ask me why there’s no native Exercise Avast-03-applications Add a visual status indicator Requirement: ● UI will show a toggle status indicator for Ahoy World Thoughts on how to integrate with the AhoyWorld Application? Meanwhile, in AhoyWorld <ahoyworld-url>/Status A restfull api returns the current application status JSON output: {"status":true} Add a visual status indicator... Requirement: ● UI will show a toggle status indicator for Ahoy World Exercise: Add a visual indicator for the current state. Retrieve state from AhoyAPI Status Result: Visual indicator will display correct Exercise Avast-04-StatusIndicator application state Bask in the glory of your Module! Our module can now: ● Display an empty page ● Override navigation bar ● Display app info & status indicator ● Provide WebAPI control and monitor of Ahoy ● Provide a UI button to control Ahoy WebAPI ho! It’s not Zend Server if there’s no WebAPI WebAPI: How does one use it • Create a request using Zend\Http\Client • Add an Accept header o • o application/vnd.zend.serverapi+json Can be X-Accept too (Ask me why!) Sign the request using SignatureGenerator Date should be a GMT with specific format o Use key obtained in the WebAPI section of UI o Machines may have to have their clocks synched o Good news: We don’t need any of the above Tools: Request simulation How’s about we sees us an example? ● URL: localhost:10081/ZendServer/Api/getSystemInfo ● Header: Accept: …. ○ To version or not to version? ■ application/vnd.zend.serverapi+json;version=1.5 ○ Output type? ● Get or post? ● Signed request or session hijacking? ● Inspecting the response WebAPI: avastAhoyStatus ● WebAPI action to return the current state of Ahoy’s master directive ● No parameters ● Output: json and xml We want to create the avastAhoyStatus route & ACL … but how do we do that? Create a WebAPI route Create a Literal route in module.config.php 'webapi_routes' => array( 'AvastWebAPI' => array( 'type' => 'Zend\Mvc\Router\Http\Literal', 'options' => array( 'route' => '/Api/example', 'defaults' => array( 'controller' => 'AvastWebAPI', 'action' 'versions' => 'avastAhoyStatus', => array('1.5'), ), )) ) ... Ask me about more route options! Tell me: what did I forget? More ACL entries and overrides • ACL requires SQL entries o sqlite, db/gui.db INSERT OR IGNORE INTO GUI_ACL_RESOURCES VALUES(NULL,'route:AvastWebAPI'); INSERT OR IGNORE INTO GUI_ACL_PRIVILEGES VALUES(3, last_insert_rowid(), ''); • • These queries have to be reapplied after upgrades Controller names are case sensitive Ask me about exceptions! avastAhoyStatus, cont’d Reminder: ● WebAPI action to return the current state of Ahoy’s master directive ● No parameters ● Output: json and xml We want to create a controller and view scripts … but how do we do that? Lets add a WebAPI controller • Class WebAPIController o o • o Extends WebAPIActionController class Add to controllers in module.config (AvastWebAPI_1-5) Controller names are case sensitive Action should be named as indicated by the route’s action parameter Lets add a WebAPI view script • Actions need View scripts - one for each format • o avast/web-api/1x5/<action>.pjson.phtml o avast/web-api/1x5/<action>.pxml.phtml o <action> has dashed inflection: avastahoy-status o Wrapped by layout.p<output>.phtml automatically o Create a string that “nestles” within the layout View script has to include “responseData” wrapper element in json and xml avastAhoyStatus, cont’d ● WebAPI action to return the current state of Ahoy’s master directive ● No parameters ● Output: json and xml Exercise: Create the avastAhoyStatus webapi controller & action Exercise: Create json and xml view scripts Use dummy data for this exercise Result: avastAhoyStatus responds to requests avastAhoyStatus functionality ● Add functionality to our webapi action ● Retrieve a boolean status ● Pass it out to the view script Exercise: Use Integration Stub to retrieve status Replace the dummy data from the previous exercise Result: avastAhoyStatus should respond to Exercise Avast-05-avastAhoyStatus requests with meaningful information WebAPI: avastAhoyToggle ● WebAPI action to return the updated state of Ahoy’s master directive ● Parameter: state ● Output: json and xml Exercise: Create the avastAhoyToggle route Exercise: Create the avastAhoyToggle webapi Exercise: Create json and xml output, similar or identical to avastAhoyStatus Result: avastAhoyToggle responds to requests avastAhoyToggle functionality ● Add functionality to our webapi action ● Collect a parameter and validate it ● Pass the parameter to a toggle method Exercise: Use <ahoyworld-url>/Status/Toggle to change AhoyWorld’s status Result: calls to avastAhoyToggle affect AhoyWorld Exercise Avast-06-avastAhoyToggle Bask in the glory of your Module! Our module can now: ● Display an empty page ● Override navigation bar ● Display app info & status indicator ● Provide WebAPI control and monitor of Ahoy ● Provide a UI button to control Ahoy Lets make our UI do a jig Shiver me timbers! Javascript! ● WebAPI is everywhere in the UI ● Different usage scenarios ● Some tools we provide zswebapi.js ○ zgridPolling.js ○ FragmentManager.js ○ Ask me about the exceptions to the rule! Integrate WebAPI into the UI ● Status indicator ● Button that will call our toggle webapi ● Use toggle response to update indicator Exercise: use Request.WebAPI to call the toggle Result: We have a button that lets the user affect AhoyWorld directly Exercise Avast-07-webapiButton Requirements, reminder ● avastAhoyStatus should be accessible to admin and developer alike ● avastAhoyToggle should be accessible to the admin only ACL in Zend Server • • ACL is consulted for every request Two ACLs: Role/Identity, Edition/License o • o Identity ACL initialized from the database GUI_ACL_ROLES, GUI_ACL_RESOURCES, GUI_ACL_PRIVILEGES Roles: administrator, developer, developerLimited License ACL initialized within the application ACL Resources are prefixed: o o route - to indicate this is an MVC controller resource data - to indicate this is a logical resource • • • ACL in Zend Server, cont'd AclQuery class, registered services o ZendServerAcl (AclQuery class) o ZendServerEditionAcl (Acl class) o ZendServerIdentityAcl (Acl class) AclQuery is used many places AclQuerierInterface sets ZendServerAcl ZendServer\..\PhpRenderer - for view usage o ZendServer\..\ActionController - inherited by every controller in the ZS6 UI application o ZendServer\..\DefaultNavigationFactory for navigation rendering o and others... o ACL: querying in MVC ● Acl Queries can be performed in view script ○ ○ ○ PhpRenderer::isAllowed() PhpRenderer::isAllowedIdentity() PhpRenderer::isAllowedEdition() ● Acl Queries can also be used in controllers ○ ○ ○ ActionController::isAclAllowed() ActionController::isAclAllowedIdentity( ) ActionController::isAclAllowedEdition() Parameters for isAllowed* For all of the previous methods isAllowed(‘route:<controller>’, ‘<action>’) ACL: limit access to WebAPI ● avastAhoyStatus should be accessible to admin and developer alike ● avastAhoyToggle should be accessible to the admin only Exercise: Add an isAclAllowed check in Controller action If fails, throw a WebAPI\Exception Exception Code: INSUFFICIENT… Result: action doesn’t fail… what did we forget? ACL: limit access, cont’d ● avastAhoyStatus ... admin and developer ● avastAhoyToggle should be accessible to the admin only Exercise: Modify SQLs to map ACL directives. Apply the new directives immediately to your gui.db Result: Developer user gets an error for toggle Ask me about the neat trick! Exercise Avast-08-webapiAclLimit ACL: affecting presentation ● Ahoy status indicator should be visible to anyone ● Ahoy toggle button should be available only to an administrator Exercise: Add ACL queries to the view script Affect button availability according to acl Result: Ahoy toggle button does not get displayed at all to the developer Exercise Avast-09-uiAclLimits Bask in the glory of your Module! Our module can now: ● Display an empty page ● Override navigation bar ● Display app info & status indicator ● Provide WebAPI control and monitor of Ahoy ● Provide a UI button to control Ahoy Extras Should we have the time & spirit... Add polling to the Avast page ● zgridPolling, Request.WebAPI ○ ○ Poll avastAhoyStatus Polling populates button label and indicator label ● Polling removes the need to refresh the page after toggle button is pressed Calling the database: extended ● Clusters require we show the application status for every server, not just its aggregate. ● Show each server’s state for AhoyWorld ● Servers\Db\Mapper methods will assist Exercise: Use the application’s servers list to cross reference with the available servers list Create an avastAppInfo WebAPI ● Move the functionality of retrieving application data form the UI into a webapi action ● This can be used for page display by polling avastAppInfo from the page Practices Complex and silly things Logging • Zend Server UI log: zend_server_ui.log o o • • o Linux: <zend-server>/gui/lighttpd/logs Windows: <zend-server>\logs Written to by class ZendServer\Log\Log Verbosity change be changed in the Settings o Default value is notice and up Logs can be viewed in the Logs page Relies on GUI_AVAILABLE_LOGS table o You can add logs, table is cleared during upgrade o Debugging the ZS UI When debugging your module inside ZS6: ● Log verbosity should stay on notice ○ ○ Throw out messages in high levels like “alert” Debug level throws out LOTS of information ● UI is constantly polling, you may face lots of log entries that are unrelated to your own code ○ ○ zend_gui.debugModeEnabled = true adds the request URI to the log entry Allows you to filter the entries IdentityFilter Authentication • Simple Authentication • Extended Authentication o o Login Group2 ... App 2 Group3 o LDAP List of Apps Authenticate user credentials o Create session Identity object o Determine allowed applications o End result: Identity has a list of allowed applications App 1 Group1 User Groups • o Identity Groups Groups to Apps mapping CustomAuth module Zend Server Session storage IdentityFilter - Use and usage • Classes implement IdentityFilterInterface interface IdentityFilterInterface { public function filterAppIds($applicationIds, $emptyIsAll); public function setAddGlobalAppId($addGlobalAppId); } • filterAppIds o • o Intersect identity applications with $applicationIds If empty applications, check $emptyIsAll setAddGlobalAppId o Global application (Ask me what that is!) UI compounded services • • Compound services o Controllers call other controllers using forward>dispatch plugin o MVC Failures are handled by dispatch.error event and event handlers o Nested WebAPI calls in clusters (An example?) View scripts o Some initial data is rendered by calling a relevant view script directly o Using WebAPI from the Zend Server UI client is easy! (ask me why!) Nested webapi requests Should I respond to this request? Somewhere in the internet... WebAPI Request Used by: • Logs display • Server info • Codetracing details view Zend Server Webapi ZS6 cluster db Zend Server Webapi WebAPI validation Set of validation functions that throw exceptions Integrated into all existing webapi actions • • • ZendServer\Mvc\Controller\WebAPIActionController ZendServer\Mvc\Controller\ActionController Existing ZS6 Modules ● Populated with code written by Zend developers ● No warranty and no SLA cover ● Highlights ZendServer-TokenAuthentication ○ ZendServer-CustomAuth ○ ZendServerNagiosPlugin ○ ● External and peripheral integration ○ ○ ○ ZendServerWebApiModule ZendServerSDK ZendServerDeploymentHelper /usr/local/zend/gui/vendor/ThankYou/src/HadFun/Cya.php Give me a wonderful review: https://joind.in/9049 Feedback: yonni.m@zend.com, @zendsui, https://joind.in/9049, linkedin.com/in/yonman