CiviCRM Community Forums (archive)

*

News:

Have a question about CiviCRM?
Get it answered quickly at the new
CiviCRM Stack Exchange Q+A site

This forum was archived on 25 November 2017. Learn more.
How to get involved.
What to do if you think you've found a bug.



  • CiviCRM Community Forums (archive) »
  • Old sections (read-only, deprecated) »
  • Developer Discussion »
  • APIs and Hooks (Moderator: Donald Lobo) »
  • Authentication using PHP API
Pages: [1] 2

Author Topic: Authentication using PHP API  (Read 3493 times)

Chris Burgess

  • Ask me questions
  • ****
  • Posts: 675
  • Karma: 59
Authentication using PHP API
April 24, 2012, 12:01:18 am
I'm extending the API and have run into a block of CiviCRM code which does ACL checks based on the logged-in user. But! We're not logged in, so the API functionality fails.

The REST API does authentication, but I can't see a way to authenticate when using the PHP API.

It's probably possible to use CRM_Utils_System::authenticate*() but doing that means sidestepping the API, and that feels wrong.

Can I authenticate from the PHP API? Maybe by passing in an API key as part of $params?

(The specific code is the call to CRM_Contact_BAO_Contact_Permission::cacheClause() from within CRM_Mailing_BAO_Mailing::getRecipients(), which is called to set (!) the recipients for a mailing in order to schedule its delivery.)
@xurizaemon ● www.fuzion.co.nz

Eileen

  • Forum Godess / God
  • I’m (like) Lobo ;)
  • *****
  • Posts: 4195
  • Karma: 218
    • Fuzion
Re: Authentication using PHP API
April 24, 2012, 01:26:21 am
Are you calling this via command line? 4.1? I think if you call it via drush you can pass in the username & password?
Make today the day you step up to support CiviCRM and all the amazing organisations that are using it to improve our world - http://civicrm.org/contribute

Chris Burgess

  • Ask me questions
  • ****
  • Posts: 675
  • Karma: 59
Re: Authentication using PHP API
April 24, 2012, 02:10:10 am
No, it's a Drupal callback and it's on 3.4.8. The old approach switched Drupal to the CiviCRM DB and ran a bunch of INSERT against the various tables for mailing.

I want to keep changes minimal, so I was hoping to replace just the SQL queries with civicrm_api() calls (by contributing the api methods required to do so).

The API implementation works, although getting it to match CiviCRM API standards will probably require further work - as you've noted, the lines between Form and BAO layer are a bit murky in that area :)

If the API implementation I've knocked together works, I may just be able to get away with using the REST API which should provide authentication.

More generally - what do you think about adding auth to the PHP API? Does it make sense to your mental model of how CiviCRM API works? It does to me, but I'm leaping in and stirring things up here.
« Last Edit: April 24, 2012, 02:16:30 am by grobot »
@xurizaemon ● www.fuzion.co.nz

Eileen

  • Forum Godess / God
  • I’m (like) Lobo ;)
  • *****
  • Posts: 4195
  • Karma: 218
    • Fuzion
Re: Authentication using PHP API
April 24, 2012, 02:26:26 am
The 4.1 cron jobs are run using the API & I think some authentication is going on there. Michal, Xavier & Jamie probably know more on what was put in place than I do ... or Tim
Make today the day you step up to support CiviCRM and all the amazing organisations that are using it to improve our world - http://civicrm.org/contribute

Chris Burgess

  • Ask me questions
  • ****
  • Posts: 675
  • Karma: 59
Re: Authentication using PHP API
April 24, 2012, 02:48:37 am
I might be looking in the wrong place, but http://svn.civicrm.org/civicrm/branches/v4.1/bin/cron.php doesn't look to me like it uses the API?

If it does, it's doing so after using the approach I mentioned above (calling CRM_Utils_System::authenticate*() directly) to do auth from PHP, but for API code that feels wrong ... to me, anyway!

I can see that cli.php uses CiviCRM API, maybe cron.php is using cli.php as a backend or something. I haven't dug far there.

I can totally use the same approach in a Drupal callback (stuff auth details in $_REQUEST, call CRM_Utils_System::authenticateWhatever()), but wanted to check first if there was a "correct" method to auth API requests.

Will ask the others in IRC if I catch them. Thanks again!
« Last Edit: April 24, 2012, 02:51:27 am by grobot »
@xurizaemon ● www.fuzion.co.nz

Eileen

  • Forum Godess / God
  • I’m (like) Lobo ;)
  • *****
  • Posts: 4195
  • Karma: 218
    • Fuzion
Re: Authentication using PHP API
April 24, 2012, 02:54:37 am
what about this - I presume drush uses it to call the API?

http://svn.civicrm.org/civicrm/branches/v4.1/bin/cli.php
Make today the day you step up to support CiviCRM and all the amazing organisations that are using it to improve our world - http://civicrm.org/contribute

Chris Burgess

  • Ask me questions
  • ****
  • Posts: 675
  • Karma: 59
Re: Authentication using PHP API
April 24, 2012, 02:06:06 pm
Yes, cli.php uses API calls, but its auth is still using internal methods.

Code: [Select]
$cms->authenticate( $this->_user, $this->_password, false, $civicrm_root )
What I was looking for was something of the form,

Code: [Select]
civicrm_api('Session', 'get', $params) (returns PHPSESSID which can be used in further civicrm_api() calls)

or

Code: [Select]
civicrm_api('Contact', 'get', array('version' => 1, 'id' => 1, 'api_key' => 'x', 'site_key' => 'x')) (permits auth in API request, preventing need for a separate authentication request as is req'd for REST API).

To me it seems like the API approach is there so developers do not have to use internal methods, so it makes sense for auth to be something done (if required) via civicrm_api() rather than via CRM_Utils_System::authenticate*() or $cms->authenticate().
@xurizaemon ● www.fuzion.co.nz

totten

  • Administrator
  • Ask me questions
  • *****
  • Posts: 695
  • Karma: 64
Re: Authentication using PHP API
April 24, 2012, 11:21:36 pm
grobot, you are correct that explicit authentication is not supported in the PHP API -- you'd have to go through CRM_Utils_System, etal. This generally isn't an issue because most API users are writing code in a context where authentication is implicitly handled by some other system, e.g.

  • For web UI use-cases, the end-user has already logged into the CMS via web form, and we can piggy-back off the CMS's session.
  • For CLI use-cases, one would typically use drush (eg "drush -l example.com -u exampleuser scr /path/to/example/script.php").
  • For web-service use-cases, the REST adapter has authentication support.
 

Those are only "typical" cases, and one can imagine situations where those aren't good enough. For example, if you wanted to implement a new API medium (JSON-RPC, SOAP, etc) or a new authentication mechanism (OAUTH tokens, DIGEST-MD5, etc), then the PHP developer would need to explicitly handle authentication. But that's speculation -- what actual use-case have you encountered which requires explicit authentication in PHP?

With that said, if you wanted to extend the API to integrate explicit authentication, then you certainly could use CRM_Utils_System, etal, as a building-block for the new API calls, and I think we could provide more helpful ideas if we understood the use-case.

xavier

  • Forum Godess / God
  • I’m (like) Lobo ;)
  • *****
  • Posts: 4453
  • Karma: 161
    • Tech To The People
  • CiviCRM version: yes probably
  • CMS version: drupal
Re: Authentication using PHP API
April 24, 2012, 11:44:00 pm
Hi,

the php api doesn't *need* authentication (as you can call BAO/DAO directly too) if you can include the civicrm.settings.php.

The rationale being that if you can run php code on the server and that it can read the file that contains the key & login info to the db, checking if this user has a key doesn't help a lot securing.

The OO version of the php api has a mode where you can simply
require ('/path/to/civicrm/api/class.api.php');
$api = new civicrm_api3 (array('conf_path' => '/path/to/drupal/sites/default/'));

and you can then $api->Contact->Create(array ("first_name"=>"look, no pwd",last_name=>"given"));

It puts the contact as the modifier on the log (as it would if you called directly the BAO)

As for implementing oAuth: yes! ;)

X+

-Hackathon and data journalism about the European parliament 24-26 jan. Watch out the result

Chris Burgess

  • Ask me questions
  • ****
  • Posts: 675
  • Karma: 59
Re: Authentication using PHP API
April 25, 2012, 01:31:20 am
Thanks for the feedback!

Quote from: totten on April 24, 2012, 11:21:36 pm
(...) most API users are writing code in a context where authentication is implicitly handled by some other system, e.g. (...)
what actual use-case have you encountered which requires explicit authentication in PHP? I think we could provide more helpful ideas if we understood the use-case.

This use case is as described here:
  • I'm implementing a new API method, Mailing->create
  • The client's existing code uses an (unauthenticated!) Drupal callback to create a Mailing
  • The API implementation works if authenticated, but fails when adding recipients due to access perms
The correct approach then is to authenticate to Drupal/CiviCRM before creating the mailing. The old methodology (built for 3.2.x series) simply inserted SQL to a couple of tables, but in 3.4.x onwards we need to fire CRM_Mailing_BAO_Mailing::getRecipients() (to set recipients), and contact access controls apply here. As a result of all the above, I'm probably dredging up some edgy stuff :)

Quote from: xavier on April 24, 2012, 11:44:00 pm
the php api doesn't *need* authentication (as you can call BAO/DAO directly too) if you can include the civicrm.settings.php.

The rationale being that if you can run php code on the server and that it can read the file that contains the key & login info to the db, checking if this user has a key doesn't help a lot securing.

No, the PHP API doesn't need authentication, but some of CiviCRM's internal methods apply logic based on authentication. (I'm intrigued that you say Contact->create is permitted from an API call without auth; CiviCRM's default permissions in Drupal don't grant "add contacts" to the anonymous role. One day when I have too much time ...)

Anyway, agreed: if we're in the PHP API, there's not much left to *secure*. For this use case, my aim was more to perform actions as a specific contact than it is to provide a layer of security.

I'll stop pontificating and go either make REST work or do auth the ugly way. (But I still think it's ugly to have to prefix API methods with internal method calls for auth!)
@xurizaemon ● www.fuzion.co.nz

totten

  • Administrator
  • Ask me questions
  • *****
  • Posts: 695
  • Karma: 64
Re: Authentication using PHP API
April 25, 2012, 02:35:49 am
Quote from: grobot on April 25, 2012, 01:31:20 am
Thanks for the feedback!

Quote from: totten on April 24, 2012, 11:21:36 pm
(...) most API users are writing code in a context where authentication is implicitly handled by some other system, e.g. (...)
what actual use-case have you encountered which requires explicit authentication in PHP? I think we could provide more helpful ideas if we understood the use-case.

This use case is as described here:
  • I'm implementing a new API method, Mailing->create
  • The client's existing code uses an (unauthenticated!) Drupal callback to create a Mailing
  • The API implementation works if authenticated, but fails when adding recipients due to access perms
The correct approach then is to authenticate to Drupal/CiviCRM before creating the mailing. The old methodology (built for 3.2.x series) simply inserted SQL to a couple of tables, but in 3.4.x onwards we need to fire CRM_Mailing_BAO_Mailing::getRecipients() (to set recipients), and contact access controls apply here. As a result of all the above, I'm probably dredging up some edgy stuff :)

OK, that makes sense. (Just to restate to indicate that I'm following) So you need to escalate the current user's privileges in a temporary, controlled way. The API has an option, "check_permissions=>FALSE", which sounds appropriate but which doesn't work because the mailer's ACL check doesn't respect it. Fixing the ACL check probably sounds scarier than tackling authentication.

If I were you, I'd probably go with the REST approach rather than revising the PHP API -- using the REST API can be as easy as the PHP API (see Xavier's class-based API wrapper which hides a lot of REST mechanics), and it's less prone to novel security bugs. For example, to achieve the temporary privilege escalation within the same process, you would need to account for:

 * Both escalating and restoring/de-escalating privileges
 * Resetting any caches or session-variables that depend on the user's identity
 * Processing (or skipping) hooks that tie into the login process
 

xavier

  • Forum Godess / God
  • I’m (like) Lobo ;)
  • *****
  • Posts: 4453
  • Karma: 161
    • Tech To The People
  • CiviCRM version: yes probably
  • CMS version: drupal
Re: Authentication using PHP API
April 25, 2012, 03:59:08 am
Quote from: grobot link=topic=24404.msg102732#msg102732
but in 3.4.x onwards we need to fire CRM_Mailing_BAO_Mailing::getRecipients() (to set recipients), and contact access controls apply here.

Never thought about it. Now I'm wondering what's really going on when calling the api without authentication (other methods).
Wondering why api.Contact.Get works but not this one, where both applies ACL/permission.

Did you check what's going on in getRecipients that needs the auth?

Quote from: grobot link=topic=24404.msg102732#msg102732
I'll stop pontificating and go either make REST work or do auth the ugly way. (But I still think it's ugly to have to prefix API methods with internal method calls for auth!)

Ping me if you go REST.

Anyway, if I understood you, that's anonymous that is running your code and that's when anonymous tries to run that it fails.
If you run it from the cli directly (without any authentication), does it work?

And so I'm sure I understand, the existing implementation is letting anyone creating a mailing, and you are trying to keep it that way (but using the API obviously) ?
« Last Edit: April 25, 2012, 07:57:55 am by xavier »
-Hackathon and data journalism about the European parliament 24-26 jan. Watch out the result

Eileen

  • Forum Godess / God
  • I’m (like) Lobo ;)
  • *****
  • Posts: 4195
  • Karma: 218
    • Fuzion
Re: Authentication using PHP API
April 25, 2012, 04:29:04 am
When a mailing is created the recipient list is created (from 3.3? 3.4?) by resolving who in the chosen group the logged in user has permissions to access - that's probably the reason for the authentication.

ie. I could choose to email 'all individuals' but it will only go to the members of the group that I can view & that list is generated when I create the mailing
Make today the day you step up to support CiviCRM and all the amazing organisations that are using it to improve our world - http://civicrm.org/contribute

xavier

  • Forum Godess / God
  • I’m (like) Lobo ;)
  • *****
  • Posts: 4453
  • Karma: 161
    • Tech To The People
  • CiviCRM version: yes probably
  • CMS version: drupal
Re: Authentication using PHP API
April 25, 2012, 08:15:05 am
Ok, and is there a mode to force to skip this test & do and consider it has view all contacts rights?

I thought that WordPress doesn't handle permissions as finely as drupal, how does it work on WP?

X+
-Hackathon and data journalism about the European parliament 24-26 jan. Watch out the result

Chris Burgess

  • Ask me questions
  • ****
  • Posts: 675
  • Karma: 59
Re: Authentication using PHP API
April 25, 2012, 06:33:06 pm
Crude workaround for this - it does appear to work to precede the CiviCRM API code with impersonation of a Drupal user with correct perms.

CiviCRM then uses that user's ACL restrictions.

So,

Code: [Select]
  // NB: See "Safely impersonating another user" on Drupal.org - https://drupal.org/node/218104
  $civicrm_uid = 1; // user.uid to perform action as
  $original_user = $user;
  $old_state = session_save_session();
  session_save_session(FALSE);
  $user = user_load($civicrm_uid);

  /* CiviCRM API code here */

  // switch user back
  $user = $original_user;
  session_save_session($old_state);

Not ideal, but worked for this specific issue. Will try REST when I have more time to play with this.
@xurizaemon ● www.fuzion.co.nz

Pages: [1] 2
  • CiviCRM Community Forums (archive) »
  • Old sections (read-only, deprecated) »
  • Developer Discussion »
  • APIs and Hooks (Moderator: Donald Lobo) »
  • Authentication using PHP API

This forum was archived on 2017-11-26.