function simple_form($form_state) {
$form['item'] = array('#type' => 'hidden', '#value' => 'some_value');
$form['#action'] = 'http://external-website.com/';
$form['submit'] = array(
'#type' => 'submit',
);
return $form;
}
Simple Drupal form, gets called with drupal_get_form(simple_form).
Unfortunately, it is really easy to change the value of 'item' to something else in the form, and then send that value to the external site.
As far as I tried, there is no way to check the form before it leaves my site.
function simple_form_validate() and submit never get called.
How can I prevent that? Set the action to an internal function and then submit it after validating? How would I go about that?
Unfortunately, setting
$form['item'] = array('#type' => 'value', '#value' => 'some_value');
doesn't work? The external site doesn't receive the value for 'item'.
Any advice?
It is either/or. and that has nothing to do with Drupal, but with the nature of HTTP POST.
Either you use #type=>value and Drupal maintains its storage outside of the form, inside of Drupal, trough a session,
Or you post to external, in which case that internal session storage cannot help.
Drupals security system in the Form API relies on the fact that Drupal "knows" the exact form itself and therefore can avoid incorrect values, by comparing them to the original form.
An external site knows nothing about the original form, and therefore knows nothing about how to validate it.
You will need to perform validation on the end of the receiver of the POST (that external site), nothing else will be secure (enough).
That validation could be a local script running on the remote site, wich simply validates against whitelists or regular expressions.
Alternatively, that remote site could request (e.g. over HTTP-SOAP, or XMLRPC) the form on the original Drupal-site, but that is going to be pretty hard to achieve.
A third alternative, and IMHO the simplest, is to let Drupal handle the entire form locally, and if validated process it in the submit hook. In there, you can either post the values to the remote site in a secure way, or you can simply stick it in the database or some spool-system of that remote site. In any case: in the submit hook, after Drupal validated the form, you push it on to the remote site.
Related
I have a settings_view in which there is a textField and I would like to give the user the option to modify application/config/email.php and more precisely the $config['smtp_user'] so the latter would receive the string entered by the user.
I already know how to get the controller to receive the value by creating a form in the view and then send it to another view or use the value in a db query but I'm wondering whether I can actually make it so the config/email.php can be modified.
I would appreciate it if someone could point me in the right direction or tell me to stop searching if this is not doable.
You can place your settings inside config/email.php, but you can also modify them inside your controller. It overwrites variables that you are only interested and will also use the rest from config file.
$new_config_values = array(
'new_key' => 'new value'
);
$this->email->initialize($new_config_values);
I have recently been asked to create an API that can process data using PHP. I am not that accustomed to PHP so I am not quite sure how to proceed.
Basically what I would like to achieve is create an API that processes a form post that the user can call like this:
<form METHOD="POST" ACTION="https://MyURL/index.php" id=aForm name=aForm>
<input type="hidden" id="Lite_Merchant_ApplicationID" name="Lite_Merchant_ApplicationID" value="Your Application Id">
(various other fields to be processed)
</form>
I might be wrong in calling this an API, because it's supposed to handle a form post. But I need to compile documentation for users to be able to integrate with our system and post the form to our URL which will then process the info in the form.
Are there any good tutorials that I can have a look at? I am not sure if the ones I am looking at are applicable as they mention nothing about using a form to call the API? e.g.
https://docs.phalconphp.com/en/latest/reference/tutorial-rest.html
and
http://coreymaynard.com/blog/creating-a-restful-api-with-php/
Or do I just process the form as normal in PHP and accesss the values using:
$_POST["name"];
If that is the case will users be able to call the API using the language of their choice?
An additional question I have would be if there is anything I would need to look at or consider due to the fact that it will be "https"?
Thanks in advance and my apologies if this is not very specific, any advise/pointers will be appreciated.
Additional info:
The system needs to be able to perform redirects and login credentials will be sent within the hidden form inputs
Your question is a little wide ranging, and you may be using words in a way that isn't consistent my understanding.
An API typically is more than a single method, whereas handling a form POST event is just - well, a form handler. The difference is more than semantic - for an API, you probably need to consider versioning (how do you upgrade your API without breaking client applications), abstraction (how can you make your API easy to use), documentation, and security (how can you ensure that only authorised users/applications consume your API?). An API often has more than one user, and often needs to support the scalability requirements of the client applications.
REST is a great way to design an API - it's easy to understand for clients, and lots of smart people have solved problems like authentication/authorisation, versioning and abstraction.
It's important to note that REST uses existing HTTP concepts, so a RESTful API would expose POST requests to create new entities. That POST request can be called from a web page with a <form> element, or from a REST client.
If you write a RESTful API, clients can be written in any language that supports HTTP.
There are a bunch of frameworks which make building RESTful web APIs easier in PHP. I haven't used any, so can't make a recommendation.
If, however, all you have to do is handle a POST request, from a web page that won't change - well, I'd not build a RESTful API, I'd just write a PHP "POST" handler. In this case, the client can be anything that understands your POST parameters (in practice, pretty much any application that can make an HTTP request).
However, the difference between "POST Handler" and "API" in my view is that when you create an API, you make certain promises that your clients depend on. "I won't change the field names without telling you". "I won't change the location without telling you". "You can depend on what my documentation says". When you create a POST handler, you only promise the maker of the HTML form that it works, and that you will tell that team of any changes.
The only challenge with HTTPS is that you must make sure that the calling application can handle it, and that the keys work.
Just process the form as normal in PHP and accesss the values using:
$_POST["name"];
The API user just has to send a POST request, by html form, AJAX, or whatever.
You should add a field for the response format html, xml, json, then use that to format the response.
Check below links (restful services)... Its very simple and meets your requirement.
http://rest.elkstein.org/2008/02/what-is-rest.html
http://www.9lessons.info/2012/05/create-restful-services-api-in-php.html
Going along with Neville K's answer here is an example of how my company handles RESTful api calls.
First we have a php file that handles the calls with a switch statement. Routing the different actions to said functions and classes.
/* Class file that is called on this page */
include_once "$_SERVER[DOCUMENT_ROOT]/classes/class.myclass.php";
/**
* This function makes it simpler to stop it from working for debugging purposes.
* All we have to do is comment out the one line of code apiCall($_REQUEST);
* You could have this outside of the function and it would work just as well.
* #param type $REQUEST
*/
function apiCall($REQUEST) {
$con = new MyClass();
switch ($REQUEST['action']) {
case 'getList':
/* Setting the content type to json means that the developer can
* expect a response in the form of parseable json.
*/
header('Content-Type: application/json');
echo json_encode($con->getList($REQUEST));
case 'setValue':
header('Content-Type: application/json');
echo json_encode($con->setValue($REQUEST));
case 'login':
if ($con->login($REQUEST)) {
header('Location: /index.php');
} else {
header('Content-Type: /login.php?status=Failed+Login');
}
default:
header('Content-Type: application/json');
/* If an invalid action was sent in, then this error message will be sent
* back to the user
*/
echo json_encode(['status' => 'Invalid API Call']);
}
}
/* Using $_REQUEST allows developers to access the api via GET or POST */
apiCall($_REQUEST);
Then we handle all the logic in the different classes we called.
class MyClass {
public function getList($REQUEST) {
$id = $REQUEST['id'];
/* code */
return ['status' => 'ok', 'results' => $array];
}
public function setList($REQUEST) {
/* code */
return ['status' => 'ok'];
}
public function login($REQUEST) {
/* code */
$_SESSION['user_id'] = $user_id;
return $login_successful;
}
}
Using JSON is good for applications that send information via AJAX calls. Using the header('Location:') are good for form submissions without ajax.
You can then use JavaScript ajax calls or for submissions based on how you handle the submission of data.
Example of using jQuery.getJSON
$.getJSON('/switch.php', $.param({id: id, action: 'getList'}), function (json) {
if (json) {
/*code*/
}
});
Then you would pass a hidden input with action in it to the switch page for regular form submissions.
<form action="/switch.php" method="post">
<!--hidden input named action to direct which switch to use-->
<input name="action" value="login" type="hidden"/>
<input name="username"/>
<input name="password" type="password"/>
<input type="submit"/>
</form>
These examples are for html/JavaScript web applications. If you are using JAVA, Python, .NET, or some other language, it would be as simple as using the REST API and parsing out the JSON to figure out how to handle your application logic.
You can even run a php to php api call using file_get_contents or curl.
$data = [
'action' => 'setValue',
'information' => 'More'
];
$json = json_decode(file_get_contents('/switch.php?' . http_build_query($data)),true);
if(!empty($json)){
/*code*/
}
You could create a seperate page for each call and not have to worry about passing in an action to every request. But then your filetree starts to look like this.
/api/loginSubmit.php
/api/login.php
/api/getListFromId.php
/api/getList.php
/api/setValues.php
/api/getValues.php
It gets really tedious to traverse all these files to figure out where the problem is.
I created API Framework, its very light weight, simple, fast.
Github
Clickme
OR
Link : https://github.com/mackraja/mackApi
I'm implementing the Rabo OmniKassa (which is a bit like Paypal) into a CakePHP application. I've read the Rabo manual, I tested it on an 'empty' project (without Security) and it all worked perfectly.
Now I'm implementing it in the actual application, and there are some problems with the data the Rabo OmniKassa sends back to my application -- which are caused by the Security component.
The Security component blocks the POST data the RaboKassa sends me, because it might be a threat to the application (external server inserting data etc...). It's returning a black hole.
When I just load the page without the redirect from the RaboKassa, it obviously loads, but since it has no POST data, it doesn't really do anything.
I've tried quite a few options I found on the internet like
$this->Components->disable('Security');
and
$this->Security->csrfCheck = false;
and
$this->Security->unlockedActions = array(
'kassareturn' // which is the function the RaboKassa has to return to
);
but none of them seem to work.
I can't seem to solve this problem, so is there anyone who's tried this or something similar before?
I can't alter the RaboKassa, so I have to receive the POST data...
I'm using CakePHP 2.1.3.
I'd recommend to always check the official docs first before you start trying stuff you find somewhere on the internet.
POST data validation needs to be disabled too
Besides the CSRF check there's also POST data validation. When receiving POST data from external sources you'll have to disable both checks.
$this->Security->csrfCheck = false;
$this->Security->validatePost = false;
And don't forget to make sure that you're disabling this only for your specific action!
public function beforeFilter() {
parent::beforeFilter();
if($this->request->params['action'] === 'kassareturn') {
$this->Security->csrfCheck = false;
$this->Security->validatePost = false;
}
}
See also
http://book.cakephp.org/.../core-libraries/components/security-component.html#usage
http://book.cakephp.org/.../security-component.html#SecurityComponent::$csrfCheck
http://book.cakephp.org/.../security-component.html#SecurityComponent::$validatePost
SecurityComponent::$unlockedActions as of CakePHP 2.3
I think you'll notice that using SecurityComponent::$unlockedActions will magically start working once you've updated your CakePHP installation to at least 2.3.x, as this feature is only available as of CakePHP 2.3
See also http://book.cakephp.org/...disabling-csrf-and-post-data-validation-for-specific-actions
The actual questions
How to "map" access restrictions so it can be used from php and javasript?
What kind of method should I use to share access restrictions / rules between php and javascript?
Explanation
I have created a RESTful backend using php which will use context-aware access control to limit data access and modification. For example, person can modify address information that belongs to him and can view (but not modify) address information of all other persons who are in the same groups. And of course, group admin can modify address details of all the persons in that group.
Now, php side is quite "simple" as that is all just a bunch of checks. Javascript side is also quite "simple" as that as well is just a bunch of checks. The real issue here is how to make those checks come from the same place?
Javascript uses checks to show/hide edit/save buttons.
PHP uses checks to make the actual changes.
and yes,
I know this would be much more simpler situation if I ran javascript (NodeJS or the like) on server, but the backend has already been made and changing ways at this point would cause major setbacks.
Maybe someone has already deviced a method to model access checks in "passive" way, then just use some sort of "compiler" to run the actual checks?
Edit:
Im case it helps to mention, the front-end (js) part is built with AngularJS...
Edit2
This is some pseudo-code to clarify what I think I am searching for, but am not at all certain that this is possible in large scale. On the plus side, all access restrictions would be in single place and easy to amend if needed. On the darkside, I would have to write AccessCheck and canAct functions in both languages, or come up with a way to JIT compile some pseudo code to javascript and php :)
AccessRestrictions = {
Address: {
View: [
OWNER, MEMBER_OF_OWNER_PRIMARY_GROUP
],
Edit: [
OWNER, ADMIN_OF_OWNER_PRIMARY_GROUP
]
}
}
AccessCheck = {
OWNER: function(Owner) {
return Session.Person.Id == Owner.Id;
},
MEMBER_OF_OWNER_PRIMARY_GROUP: function(Owner) {
return Session.Person.inGroup(Owner.PrimaryGroup)
}
}
canAct('Owner', 'Address', 'View') {
var result;
AccessRestrictions.Address.View.map(function(role) {
return AccessCheck[role](Owner);
});
}
First things first.
You can't "run JavaScript on the server" because Javascript is always run on the client, at the same way PHP is always run on the server and never on the client.
Next, here's my idea.
Define a small library of functions you need to perform the checks. This can be as simple as a single function that returns a boolean or whatever format for your permissions. Make sure that the returned value is meaningful for both PHP and Javascript (this means, return JSON strings more often than not)
In your main PHP scripts, include the library when you need to check permissions and use the function(s) you defined to determine if the user is allowed.
Your front-end is the one that requires the most updates: when you need to determine user's permission, fire an AJAX request to your server (you may need to write a new script similar to #2 to handle AJAX requests if your current script isn't flexible enough) which will simply reuse your permissions library. Since the return values are in a format that's easily readable to JavaScript, when you get the response you'll be able to check what to show to the user
There are some solutions to this problem. I assume you store session variables, like the name of the authorized user in the PHP's session. Let's assume all you need to share is the $authenticated_user variable. I assume i'ts just a string, but it can also be an array with permissions etc.
If the $authenticated_user is known before loading the AngularJS app you may prepare a small PHP file whish mimics a JS file like this:
config.js.php:
<?php
session_start();
$authenticated_user = $_SESSION['authenticated_user'];
echo "var authenticated_user = '$authenticated_user';";
?>
If you include it in the header of your application it will tell you who is logged in on the server side. The client side will just see this JS code:
var authenticated_user = 'johndoe';
You may also load this file with ajax, or even better JSONP if you wrap it in a function:
<?php
session_start();
$authenticated_user = $_SESSION['authenticated_user'];
echo <<<EOD;
function set_authenticated_user() {
window.authenticated_user = '$authenticated_user';
}
EOD;
?>
I'm trying to login into a website using PHPQuery's WebBrowser plugin. I'm able to successfully login but I'm not sure how to reuse cookies from a previous call to the next.
$client = phpQuery::browserGet('https://website.com/login', 'success1');
function success1($browser) {
$handle = $browser
->WebBrowser('success2');
$handle
->find('input[name=name]')
->val('username');
$handle
->find('input[name=pass]')
->val('password')
->parents('form')
->submit();
}
function success2($browser) {
print $browser; // prints page showing I'm logged in
// make authenticated requests here
}
How do I make other requests with session/login cookies?
I've taken a look at the source code to aid you with this problem. My first impression was that the code was very poorly written. Debugging code commented out, typos all over the place, mile-long functions, etc. You really might want to consider switching to a different solution in the long run because if the author changes something in this code, you might end up having your own code broken with an upgrade.
That being said, the WebBrowser plugin gives you access to the browser object itself, which contains a function called getLastResponse(). This returns a Zend_Http_Response object, which you can theoretically use to get the cookies.
The problem is that you don't have any way of setting those cookies. You would have to patch the web browser plugin somewhere around line 102 to include your own HTTP request object (parameter 2 for phpQuery::ajax()) with your cookies set, around here:
$xhr = phpQuery::ajax(array(
'type' => 'GET',
'url' => $url,
'dataType' => 'html',
));
Alternatively you could also patch phpQuery.php line 691 to include a global cookie jar you could define as a singleton or so. (Right where it says $client->setCookieJar();).
Again, this code is very poorly written, you are probably MUCH better off using raw curl calls, even if it lacks a bit of functionality.