You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
321 lines
8.4 KiB
C
321 lines
8.4 KiB
C
#include "oauth.h"
|
|
#include "http/server/request.h"
|
|
#include "http/server/escape.h"
|
|
|
|
#include "model/client_app.h"
|
|
#include "model/account.h"
|
|
#include "model/owner.h"
|
|
|
|
#include "form.h"
|
|
#include "json/json.h"
|
|
#include "json/layout.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
|
|
static bool handle_oauth_authorize( struct http_request* req )
|
|
{
|
|
char* client_id = NULL;
|
|
char* redirect_uri = NULL;
|
|
char* response_type = NULL;
|
|
bool result = true;
|
|
|
|
struct {
|
|
bool read;
|
|
bool write;
|
|
bool follow;
|
|
} scopes = { false, false, false };
|
|
|
|
// Parse query parameters
|
|
{
|
|
const char* key;
|
|
while(( key = http_request_route_query_key(req) )) {
|
|
if( 0 == strcmp(key,"client_id") ) {
|
|
client_id = strdup(http_request_route_query_value(req));
|
|
} else if( 0 == strcmp(key,"redirect_uri") ) {
|
|
redirect_uri = strdup(http_request_route_query_value(req));
|
|
|
|
// Validate redirection uri
|
|
bool valid = false;
|
|
if( 0 == strncmp("oauth2redirect://",redirect_uri,17) ) {
|
|
valid = true;
|
|
}
|
|
|
|
if( !valid ) {
|
|
result = false; goto cleanup;
|
|
}
|
|
} else if( 0 == strcmp(key,"response_type") ) {
|
|
if( 0 != strcmp(http_request_route_query_value(req),"code") ) {
|
|
result = false; goto cleanup;
|
|
}
|
|
} else if( 0 == strcmp(key,"scope") ) {
|
|
char* scope = strdup(http_request_route_query_value(req));
|
|
|
|
char* resume = scope;
|
|
char* item;
|
|
while(( item = strtok_r( NULL, " ", &resume ) )) {
|
|
if( 0 == strcmp(item,"read") ) {
|
|
scopes.read = true;
|
|
} else if( 0 == strcmp(item,"write") ) {
|
|
scopes.write = true;
|
|
} else if( 0 == strcmp(item,"follow") ) {
|
|
scopes.follow = true;
|
|
} else {
|
|
free(scope);
|
|
result = false; goto cleanup;
|
|
}
|
|
}
|
|
free(scope);
|
|
} else {
|
|
printf( "key=%s\n", key );
|
|
printf( "value=%s\n\n", http_request_route_query_value(req) );
|
|
}
|
|
}
|
|
}
|
|
|
|
struct client_app* app = client_app_from_id( client_id );
|
|
if( !app) {
|
|
result =false; goto cleanup;
|
|
}
|
|
|
|
// Store the redirect URI for later use
|
|
free(app->redirect_uri);
|
|
app->redirect_uri = redirect_uri;
|
|
client_app_save( app );
|
|
|
|
http_request_send_headers( req, 200, "text/html", true );
|
|
FILE* f = http_request_get_response_body( req );
|
|
#define RENDER
|
|
#include "view/authorize.html.inc"
|
|
#undef RENDER
|
|
|
|
result = true;
|
|
cleanup:
|
|
free(client_id);
|
|
free(redirect_uri);
|
|
free(response_type);
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool handle_oauth_do_authorize( struct http_request* req )
|
|
{
|
|
char* password = NULL;
|
|
char* state = NULL;
|
|
struct owner* o = owner_new();
|
|
struct client_app* app = NULL;
|
|
|
|
FILE* body = http_request_get_request_data(req);
|
|
|
|
struct form_parser* fp = form_pull_parser_new(body);
|
|
if( !fp ) {
|
|
goto invalid_request;
|
|
}
|
|
|
|
char* key;
|
|
while(( key = form_pull_parser_read_key( fp ) )) {
|
|
if( 0 == strcmp(key,"password") ) {
|
|
password = strdup(form_pull_parser_read_value(fp));
|
|
} else if( 0 == strcmp(key,"state") ) {
|
|
state = strdup(form_pull_parser_read_value(fp));
|
|
} else if( 0 == strcmp(key,"id") ) {
|
|
const char* client_id = strdup(form_pull_parser_read_value(fp));
|
|
app = client_app_from_id( client_id );
|
|
if( !app ) { goto invalid_request; }
|
|
}
|
|
}
|
|
form_pull_parser_release(fp);
|
|
|
|
if( !app ) { goto invalid_request; }
|
|
if( !password ) { goto invalid_request; }
|
|
|
|
if( !owner_check_password( o, password ) ) {
|
|
owner_free(o);
|
|
free(password);
|
|
|
|
goto access_denied;
|
|
}
|
|
|
|
client_app_gen_auth_code( app );
|
|
|
|
char location[512];
|
|
char workspace[1024];
|
|
const char* fmt = ( state ? "%s?code=%s&state=%s" : "%s?code=%s" );
|
|
snprintf( location, 512, fmt, http_escape( app->redirect_uri, workspace, 1024, ":/" ), app->auth_code, state );
|
|
|
|
printf( "redirecting to %s\n", location );
|
|
|
|
http_request_begin_send_headers( req, 302, false );
|
|
http_request_send_header( req, "Location", location );
|
|
http_request_end_send_headers( req, false );
|
|
|
|
free(password);
|
|
return false;
|
|
invalid_request:
|
|
printf( "Invalid request\n" );
|
|
http_request_send_headers( req, 400, "text/plain", false );
|
|
fprintf( http_request_get_response_body(req), "invalid_request" );
|
|
return true;
|
|
access_denied:
|
|
printf( "Access denied\n" );
|
|
http_request_send_headers( req, 400, "text/plain", false );
|
|
fprintf( http_request_get_response_body(req), "access_denied" );
|
|
return true;
|
|
}
|
|
|
|
static bool handle_oauth_get_token( struct http_request* req )
|
|
{
|
|
bool result = true;
|
|
struct account* owner_account = NULL;
|
|
struct owner* owner = NULL;
|
|
struct client_app* app = NULL;
|
|
struct data_t
|
|
{
|
|
char* code;
|
|
char* redirect_uri;
|
|
char* client_secret;
|
|
char* client_id;
|
|
char* grant_type;
|
|
char* username;
|
|
char* password;
|
|
} data;
|
|
memset(&data,0,sizeof(data));
|
|
|
|
FILE* post_data = http_request_get_request_data(req);
|
|
const char* content_type = http_request_get_header( req, "Content-Type" );
|
|
if( 0 == strcasecmp(content_type,"application/json") ) {
|
|
#define OBJ_TYPE struct data_t
|
|
static struct json_object_field layout[] = {
|
|
JSON_FIELD_STRING( code, false ),
|
|
JSON_FIELD_STRING( redirect_uri, false ),
|
|
JSON_FIELD_STRING( grant_type, false ),
|
|
JSON_FIELD_STRING( client_secret, false ),
|
|
JSON_FIELD_STRING( client_id, false ),
|
|
JSON_FIELD_STRING( username, false ),
|
|
JSON_FIELD_STRING( password, false ),
|
|
JSON_FIELD_END,
|
|
};
|
|
#undef OBJ_TYPE
|
|
if( !json_read_object_layout_from_FILE( post_data, layout, &data ) ) {
|
|
printf( "A" );
|
|
goto invalid_request;
|
|
}
|
|
|
|
} else {
|
|
|
|
struct form_parser* fp = form_pull_parser_new(post_data);
|
|
if( !fp ) {
|
|
goto invalid_request;
|
|
}
|
|
|
|
char* key;
|
|
// TODO: extend form parser to use a data layout
|
|
while(( key = form_pull_parser_read_key( fp ) )) {
|
|
if( 0 == strcmp(key,"grant_type") ) {
|
|
data.grant_type = strdup(form_pull_parser_read_value(fp));
|
|
|
|
} else if( 0 == strcmp(key,"code") ) {
|
|
data.code = strdup(form_pull_parser_read_value(fp));
|
|
|
|
} else if( 0 == strcmp(key,"client_id") ) {
|
|
data.client_id = strdup(form_pull_parser_read_value(fp));
|
|
|
|
} else if( 0 == strcmp(key,"client_secret") ) {
|
|
data.client_secret = strdup(form_pull_parser_read_value(fp));
|
|
|
|
} else if( 0 == strcmp(key,"redirect_uri") ) {
|
|
data.redirect_uri = strdup(form_pull_parser_read_value(fp));
|
|
|
|
} else {
|
|
printf( "%s=", key );
|
|
printf( "%s\n", form_pull_parser_read_value(fp) );
|
|
}
|
|
}
|
|
form_pull_parser_release(fp);
|
|
}
|
|
|
|
printf( "2" );
|
|
if( !data.redirect_uri ) { goto invalid_request; }
|
|
printf( "3" );
|
|
if( !data.client_id ) { goto invalid_request; }
|
|
printf( "4" );
|
|
|
|
app = client_app_from_id( data.client_id );
|
|
if( !app ) { goto invalid_request; }
|
|
printf( "5" );
|
|
|
|
if( !app->redirect_uri ) { goto invalid_request; }
|
|
if( 0 != strcmp( app->redirect_uri, data.redirect_uri ) ) { goto invalid_request; }
|
|
printf( "6" );
|
|
if( 0 != strcmp( app->client.secret, data.client_secret ) ) { goto access_denied; }
|
|
if( 0 == strcmp("authorization_code",data.grant_type) ) {
|
|
if( !data.code ) { goto invalid_request; }
|
|
if( 0 != strcmp( app->auth_code, data.code ) ) { goto access_denied; }
|
|
} else if( 0 == strcmp("password", data.grant_type ) ) {
|
|
owner_account = account_from_id(0);
|
|
owner = owner_new();
|
|
|
|
if( 0 != strcmp( owner_account->handle, data.username ) ) { goto access_denied; }
|
|
if( !owner_check_password( owner, data.password ) ) { goto access_denied; }
|
|
} else {
|
|
printf( "7" );
|
|
goto invalid_request;
|
|
}
|
|
|
|
// TODO: check code has not expired
|
|
|
|
client_app_generate_access_token( app );
|
|
|
|
http_request_begin_send_headers( req, 200, false );
|
|
http_request_send_header( req, "Content-Type", "application/json" );
|
|
http_request_send_header( req, "Cache-Control", "no-store" );
|
|
http_request_end_send_headers( req, false );
|
|
|
|
FILE* f = http_request_get_response_body( req );
|
|
#define RENDER
|
|
#include "view/token_grant.json.inc"
|
|
#undef RENDER
|
|
|
|
cleanup:
|
|
client_app_free(app);
|
|
free(data.code);
|
|
free(data.redirect_uri);
|
|
free(data.client_secret);
|
|
free(data.client_id);
|
|
free(data.grant_type);
|
|
free(data.password);
|
|
free(data.username);
|
|
owner_free(owner);
|
|
account_free(owner_account);
|
|
return result;
|
|
|
|
access_denied:
|
|
printf( "Access denied\n" );
|
|
result = false;
|
|
goto cleanup;
|
|
|
|
invalid_request:
|
|
printf( "Invalid request\n" );
|
|
result = false;
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
bool route_oauth( struct http_request* req )
|
|
{
|
|
if( http_request_route( req, "/authorize" ) ) {
|
|
if( http_request_route_method(req,"POST") ) {
|
|
return handle_oauth_do_authorize(req);
|
|
} else if( http_request_route( req, "?" ) ) {
|
|
return handle_oauth_authorize(req);
|
|
}
|
|
} else if( http_request_route( req, "/token" ) ) {
|
|
if( http_request_route_method(req,"POST") ) {
|
|
return handle_oauth_get_token(req);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|