AmfphpAuthentication.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <?php
  2. /**
  3. * This file is part of amfPHP
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the license that is bundled
  8. * with this package in the file license.txt.
  9. */
  10. /**
  11. * Authentication for Amfphp.
  12. * This plugin can be deactivated if the project doesn't need to protect access to its services.
  13. *
  14. * On a service object, the plugin looks for a method called _getMethodRoles. If the method exists, the plugin will look for a role in the session that matches the role.
  15. * If the roles don't match, an Exception is thrown.
  16. * The _getMethodRoles takes a parameter $methodName, and must return an array of strings containing acceptable roles for the method. If the return value is null,
  17. * it is considered that that particular method is not protected.
  18. *
  19. * For example:
  20. * <code>
  21. * public function _getMethodRoles($methodName){
  22. * if($methodName == 'adminMethod'){
  23. * return array('admin');
  24. * }else{
  25. * return null;
  26. * }
  27. * }
  28. *
  29. * </code>
  30. *
  31. * To authenticate a user, the plugin looks for a 'login' method. This method can either be called
  32. * explicitly, or by setting a header with the name 'Credentials', containing {userid: userid, password: password}, as defined by the AS2
  33. * NetConnection.setCredentials method. It is considered good practise to have a 'logout' method, though this is optional
  34. * The login method returns a role in a 'string'. It takes 2 parameters, the user id and the password.
  35. * The logout method should call AmfphpAuthentication::clearSessionInfo();
  36. *
  37. * See the AuthenticationService class in the test data for an example of an implementation.
  38. *
  39. * Roles are stored in an associative array in $_SESSION[self::SESSION_FIELD_ROLES], using the role as key for easy access
  40. *
  41. * @link https://github.com/silexlabs/amfphp-2.0/blob/master/Tests/TestData/Services/AuthenticationService.php
  42. * @package Amfphp_Plugins_Authentication
  43. * @author Ariel Sommeria-klein
  44. */
  45. class AmfphpAuthentication {
  46. /**
  47. * the field in the session where the roles array is stored
  48. */
  49. const SESSION_FIELD_ROLES = 'amfphp_roles';
  50. /**
  51. * the name of the method on the service where the method roles are given
  52. */
  53. const METHOD_GET_METHOD_ROLES = '_getMethodRoles';
  54. /**
  55. * the name of the login method
  56. */
  57. const METHOD_LOGIN = 'login';
  58. /**
  59. * the user id passed in the credentials header
  60. * @var String
  61. */
  62. public $headerUserId;
  63. /**
  64. * the password passed in the credentials header
  65. * @var String
  66. */
  67. protected $headerPassword;
  68. /**
  69. * constructor.
  70. * @param array $config optional key/value pairs in an associative array. Used to override default configuration values.
  71. */
  72. public function __construct(array $config = null) {
  73. $filterManager = Amfphp_Core_FilterManager::getInstance();
  74. $filterManager->addFilter(Amfphp_Core_Common_ServiceRouter::FILTER_SERVICE_OBJECT, $this, 'filterServiceObject');
  75. $filterManager->addFilter(Amfphp_Core_Amf_Handler::FILTER_AMF_REQUEST_HEADER_HANDLER, $this, 'filterAmfRequestHeaderHandler');
  76. $this->headerUserId = null;
  77. $this->headerPassword = null;
  78. }
  79. /**
  80. * filter amf request header handler
  81. * @param Object $handler
  82. * @param Amfphp_Core_Amf_Header $header the request header
  83. * @return AmfphpAuthentication
  84. */
  85. public function filterAmfRequestHeaderHandler($handler, Amfphp_Core_Amf_Header $header) {
  86. if ($header->name == Amfphp_Core_Amf_Constants::CREDENTIALS_HEADER_NAME) {
  87. return $this;
  88. }
  89. }
  90. /**
  91. * called when the service object is created, just before the method call.
  92. * Tries to authenticate if a credentials header was sent in the packet.
  93. * Throws an exception if the roles don't match
  94. *
  95. * @param <Object> $serviceObject
  96. * @param <String> $serviceName
  97. * @param <String> $methodName
  98. * @return <array>
  99. */
  100. public function filterServiceObject($serviceObject, $serviceName, $methodName) {
  101. if (!method_exists($serviceObject, self::METHOD_GET_METHOD_ROLES)) {
  102. return;
  103. }
  104. if ($methodName == self::METHOD_GET_METHOD_ROLES) {
  105. throw new Exception('_getMethodRoles method access forbidden');
  106. }
  107. //the service object has a '_getMethodRoles' method. role checking is necessary if the returned value is not null
  108. $methodRoles = call_user_func(array($serviceObject, self::METHOD_GET_METHOD_ROLES), $methodName);
  109. if (!$methodRoles) {
  110. return;
  111. }
  112. //try to authenticate using header info if available
  113. if ($this->headerUserId && $this->headerPassword) {
  114. call_user_func(array($serviceObject, self::METHOD_LOGIN), $this->headerUserId, $this->headerPassword);
  115. }
  116. if (session_id() == '') {
  117. session_start();
  118. }
  119. if (!isset($_SESSION[self::SESSION_FIELD_ROLES])) {
  120. throw new Amfphp_Core_Exception('User not authenticated');
  121. }
  122. $userRoles = $_SESSION[self::SESSION_FIELD_ROLES];
  123. foreach ($methodRoles as $methodRole) {
  124. if (isset($userRoles[$methodRole])) {
  125. //a match is found
  126. return;
  127. }
  128. }
  129. throw new Amfphp_Core_Exception('Access denied.');
  130. }
  131. /**
  132. * clears the session info set by the plugin. Use to logout
  133. */
  134. public static function clearSessionInfo() {
  135. if (session_id() == '') {
  136. session_start();
  137. }
  138. if (isset($_SESSION[self::SESSION_FIELD_ROLES])) {
  139. unset($_SESSION[self::SESSION_FIELD_ROLES]);
  140. }
  141. }
  142. /**
  143. * add role
  144. * @param String $roleToAdd
  145. */
  146. public static function addRole($roleToAdd) {
  147. if (session_id() == '') {
  148. session_start();
  149. }
  150. if (!isset($_SESSION[self::SESSION_FIELD_ROLES])) {
  151. $_SESSION[self::SESSION_FIELD_ROLES] = array();
  152. }
  153. //role isn't already available. Add it.
  154. $_SESSION[self::SESSION_FIELD_ROLES][$roleToAdd] = true;
  155. }
  156. /**
  157. * looks for a 'Credentials' request header. If there is one, uses it to try to authentify the user.
  158. * @param Amfphp_Core_Amf_Header $header the request header
  159. * @return void
  160. */
  161. public function handleRequestHeader(Amfphp_Core_Amf_Header $header) {
  162. if ($header->name != Amfphp_Core_Amf_Constants::CREDENTIALS_HEADER_NAME) {
  163. throw new Amfphp_Core_Exception('not an authentication amf header. type: ' . $header->name);
  164. }
  165. $userIdField = Amfphp_Core_Amf_Constants::CREDENTIALS_FIELD_USERID;
  166. $passwordField = Amfphp_Core_Amf_Constants::CREDENTIALS_FIELD_PASSWORD;
  167. $userId = $header->data->$userIdField;
  168. $password = $header->data->$passwordField;
  169. if (session_id() == '') {
  170. session_start();
  171. }
  172. $this->headerUserId = $userId;
  173. $this->headerPassword = $password;
  174. }
  175. }
  176. ?>