wolfpet / kitchen

Open Source Threaded Discussion project
6 stars 3 forks source link

NEW: Design REST API #10

Closed iamranger closed 9 years ago

iamranger commented 9 years ago

Rationale: mobile client support, dynamic HTML clients etc.

Authentication:

Basic HTTP (username/password in every request or by a token, retrieved by GET /api/profile)

API:

Returns user profile, can be used for authentication. Authentication:

Basic HTTP Auth - username, password

If credentials are valid, the response returns the cookies user2 and auth_cookie2. If the request has no Basic HTTP Auth info, but has the cookies, they will be used to determine user context.

Example of the data returned:

{
    "id": 1020,
    "name": "test",
    "new_pm": 19,
    "banned": false,
    "ban_ends": null,
    "ban_time": false,
    "prop_bold": false,
    "prop_tz": -5
}

"Collapsed threads" view, also a default view for mobile client. optional arguments - $id (max ID, inclusive), $count (how many threads to return)

Data:

{count: -1, threads: [ {msg_id:-1, subject:"", created:"ts",author : {id:-1, name:""}, answers: {count : -1}, likes:-1, dislikes:-1, flags:[], permissions:[], thread_id:-1} .... ]}

Return message data by ID, including content

Create new topic

Return answers (1 level)

Respond to a message

Like a message

Dislike a message

Optional

Message search (covers modes "By date", "Answered", "My messages")

Update user profile

"Collapsed threads"'s thread view. Returns all responses (subjects + metadata) to thread with id={id}

Enhancements

Update a message

iamranger commented 9 years ago

Changed

GET /threads?id={max_thread_id}&{count=50}

Added

GET /threads/[id]

(returns all answers)

iamranger commented 9 years ago

Added

GET /messages/[id]/answers

BGmot commented 9 years ago

Are you working on this? can I help?

iamranger commented 9 years ago

This task is next on my to-do list. All read-only methods that did not require user context are done, now we need ones that do require it to work.

The first step is to add authentication. Should not be hard - I already added a stub that gets executed before every resolved REST method (see index.php, near the top). The question is how exactly to do the authentication, as there are several alternatives. I do not mind a second opinion on this, or something completely different. So, in no particular order, with my comments:

1) Basic HTTP Auth, username/password passed every call and checked against confa_users .

Simple to implement, but I do not like this solution much, because it makes it hard to test using the browser, there is no audit trail, no "session" context and password is passed around in plain text way too much for my liking.

I could be wrong and this may be the best solution based on KISS principle.

2) Piggy-backing the existing code (auth.php) - authentication is done once on the first request (any request), a record in confa_session is created, and the session context is kept in cookies.

Username/password for the initial authentication can be passed in headers (basic http auth?) and/or as parameters (to simplify testing), then the caller is responsible for storing the auth cookie and passing it with the other requests.

Pros: can reuse the existing code including session context, audit trail (some), can test the code simply by using the browser (can't post with it anyway, though, but GET mymessages, answered and "since last time I checked"-variation of bydate mode will work.

Cons: harder to implement.

In the end, this new authentication should replace auth.php for index.php that is currently included as part of head_inc.php - we definitely need to decouple them.

The implementation of the rest of REST methods (pun intended) should be fairly straightforward - just pulling the relevant code from PHP pages, and may be making them into functions at the same time, so we don't duplicate the code.

Also, index.php is starting to get uncomfortably big, so may be it makes sense to separate the logic leaving object rendering in index.php, or split it some other way that makes sense. I have no clear plan here.

I am on vacation next week, so if you have ideas or can contribute something - feel free.

iamranger commented 9 years ago

Ability to register and authenticate via Google+/Facebook/Twitter etc could be an interesting project - not for API, for web UI.

BGmot commented 9 years ago

RE: authentication - they say: "At its heart REST is a stateless client-server relationship; this means that unlike many other approaches there is no client context being stored server side (no Sessions). To counteract that, each request contains all the information necessary for the server to authenticate the user, and any session state data that must be sent as well."

How do you test API btw? I tried using browser to no luck.

iamranger commented 9 years ago

Yeah, I am aware of this concept. You can use the browser to test API. Check if the URL e.g. /api/threads works on kirdyk forum. If it does, but you don't get the same response from your site, it means your Apache or PHP settings are incorrect. Have you followed the steps in read.me? If you get 404, make sure that mod_rewrite is enabled, then .htaccess files are enabled for root AND your website in the Apache config. If you get 500, this means phalconphp extension is not loaded by PHP. Good luck!

Sent from myMail app for Android Tuesday, 21 July 2015, 10:22AM -07:00 from EYurchenko < notifications@github.com> :

RE: authentication - they say: "At its heart REST is a stateless client-server relationship; this means that unlike many other approaches there is no client context being stored server side (no Sessions). To counteract that, each request contains all the information necessary for the server to authenticate the user, and any session state data that must be sent as well." How do you test API btw? I tried using browser to no luck. — Reply to this email directly or view it on GitHub .

BGmot commented 9 years ago

Ok. Rewrite will properly work for API calls only if I rename/remove php/api.php file. Can you please check on kirdyk forum if it is there?

iamranger commented 9 years ago

Nice catch! No luck, though - it is there. It is also there in my dev setup (EasyPHP/Windows), and I also have no issues. The .htaccess is written to redirect all requests for missing resources to index.php that implements the REST API. Looks like something else interferes and resolves /api as api.php for you.

api.php is only used by user profile editor, so if you don't need it for development, you can just rename api.php on your machine (or rename it for good... call it profileapi.php or something. it's not a perfect fix, but it'll work).

BGmot commented 9 years ago

Thanks. Found that MultiViews option was doing that for me. After applying following change there is no problem with api.php ---Options Indexes FollowSymLinks MultiViews +++Options Indexes FollowSymLinks

iamranger commented 9 years ago

Nice!

BGmot commented 9 years ago

I think we need to explicitly define Content-Type in responses to all API calls: $response->setContentType('application/json'); It's just easier to read output when you do testing using command line (I've done some, will post code later when have something more concrete) and generally it is more correct than 'plain/html'. i.e. in case of using Httpful library to test like

<?php
include('./httpful.phar');
$response = \Httpful\Request::get("http://<host>/api/threads/4")->send();
print $response->body->id ."\n";
print $response->body->message->subject ."\n";
?>

instead of

PHP Notice:  Trying to get property of non-object in ...

we'd have

4
Тест редактирования сообщения
iamranger commented 9 years ago

Sure. It makes perfect sense.

iamranger commented 9 years ago

Added three things:

1) authentication support (basic http or cookies). 2) new method GET /profile, returns 403. 3) explicit content type.

Check it out!

iamranger commented 9 years ago

Implemented like and dislike:

PUT /messages/$id/like DELETE /messages/$id/like

Both methods require authentication

iamranger commented 9 years ago

Implemented

POST /threads POST /messages/id/answers PUT /messages/id

Body must be JSON, with fields: subject, body and, optionally, ticket (a unique string, to prevent duplicates) and nsfw flag (boolean)

PUT /messages/id/bookmark DELETE /messages/id/bookmark

BGmot commented 9 years ago

Sorry but I still don't get it how you do PUT/POST testing with HTTP authentication. PHP_AUTH_USER and PHP_AUTH_PW are set up only in response to user's input into pop-up Username/Password window which in turn pops up by a browser in response to "WWW-Authenticate:" header. In other words I do not see where you send this header and in what way a native app (let's forget about browsers) should response to it / or provide PHP_AUTH_USER and PHP_AUTH_PW in initial PUT/POST request. Can you share your testing strategy please?

iamranger commented 9 years ago

just set the header for basic http authentication, that's all.

see http://kirdyk.radier.ca/restsample.html (the example doesn't actually set the header, as SendRequest is called with null, null as username/password - but the code is there)

iamranger commented 9 years ago

PHP_* are server-side things, you need not worry about them.

iamranger commented 9 years ago

The easiest way to test is probably with Chrome App called Postman. You can easily compose requests right in its UI and it supports all kinds of HTTP methods, headers, formats and other things including, naturally, Basic HTTP Authentication.

BGmot commented 9 years ago

I am a Linux guy -((( prefer testing from command line. Following seems to work: <?php include('./httpful.phar');

$request = \Httpful\Request::post("http://kitchen.bgmot.com/api/threads") ->sendsJson() ->authenticateWith('test','xxx') ->body('{"subject":"subject goes here"}'); $response = $request->send(); if ($response->hasErrors() ){ print_r( $response->_parseHeaders($response->raw_headers)); exit -1; } print($response->raw_body);

BGmot commented 9 years ago

oh... by the way body is marked on Wiki as mandatory parameter but validate() actionally does not check that on "" and passes requests with only 'subject' specified. We need either to modify Wiki or add a fix to validate().

iamranger commented 9 years ago

Nice work, thanks!

wolfpet commented 9 years ago

Got the auth test working in mobile app. Getting ready to implement PostReply(). Fingers crossed.

wolfpet commented 9 years ago

Actually, Ranger, if you could update app.js and index.html on Kyrdyk/mobile it would be greatly appreciated. :-) I was unable to configure my own instance and I gave up on this idea for now

BGmot commented 9 years ago

Feel free to use my test-bed at http://kitchen.bgmot.com It's has the latest code from github at the moment.

iamranger commented 9 years ago

The latest code was promoted to "production" :)

wolfpet commented 9 years ago

@BGmot : Thanks! I tried to check http://kitchen.bgmot.com/Mobile/index.html and it's not there. I guess you only managed to configure the desktop portion.

BGmot commented 9 years ago

Try now. I did not realize you were going to work with Mobile/ section of this site... sorry.

wolfpet commented 9 years ago

No problem. And for now you gotta change the root URL in the app.js in order for it to work. We'll keep it as is here for convenience, but eventually it should query a setting or simply detect the forum root URL

Here, in the 1st line: https://github.com/wolfpet/kitchen/blob/master/mobile/www/js/app.js

BGmot commented 9 years ago

Now changed to correct URL and the interface looks nice on my BlackBerry -) Unfortunately I don't know javascript and don't know how to get this variable from settings.php or detect on the fly.

wolfpet commented 9 years ago

We'll figure this out, no worries.

iamranger commented 9 years ago

both "mymessages" and "answered" modes should be working now.

Outstanding is PM access. I am thinking:

GET /api/inbox{?id=<max_id>&count=<how_many>}
GET /api/inbox/{id}
GET /api/sent (same parameters)
GET /api/sent{id}
POST /api/sent

A number of new messages in Inbox can be read from /api/profile

What do you think?

wolfpet commented 9 years ago

How do I specify who is the message for in POST /api/sent ? In the body?

iamranger commented 9 years ago

A property in the payload called 'recipient'. Messages returned by /inbox method will have 'author' property in addition to usual subject and body.

wolfpet commented 9 years ago

sounds good!

iamranger commented 9 years ago

Implemented POST /api/sent

'recipient' property is a string (name of the recipient)

I think that's it, folks! I make a motion to close this issue, possible additions can be handled as enhancements

wolfpet commented 9 years ago

I'm thinking of implementing Pmail to look like a typical email mobile app. Any suggestions on the UI? I personally love Outlook on my phone,

iamranger commented 9 years ago

Seems reasonable.