Beispiel #1
0
/**
	@brief Populate the params member of an osrfMessage.
	@param msg Pointer to the osrfMessage.
	@param o Pointer to a jsonObject representing the parameter(s) to a method.

	Make a copy of a jsonObject and install it as the parameter list for a method call.

	If the osrfMessage already has any parameters, discard them.

	The @a o parameter should point to a jsonObject of type JSON_ARRAY, with each element
	of the array being a parameter.  If @a o points to any other type of jsonObject, create
	a JSON_ARRAY as a wrapper for it, and install a copy of @a o as its only element.

	Used for a REQUEST message, to pass parameters to a method.  The alternative is to call
	osrf_message_add_param() or osrf_message_add_object_param() repeatedly as needed to add
	one parameter at a time.
*/
void osrf_message_set_params( osrfMessage* msg, const jsonObject* o ) {
	if(!msg || !o) return;

	if(msg->_params)
		jsonObjectFree(msg->_params);

	if(o->type == JSON_ARRAY) {
		msg->_params = jsonObjectClone(o);
	} else {
		osrfLogDebug( OSRF_LOG_MARK, "passing non-array to osrf_message_set_params(), fixing...");
		jsonObject* clone = jsonObjectClone(o);
		msg->_params = jsonNewObjectType( JSON_ARRAY );
		jsonObjectPush(msg->_params, clone);
		return;
	}
}
Beispiel #2
0
/*
	Adds the authentication token to the user cache.  The timeout for the
	auth token is based on the type of login as well as (if type=='opac')
	the org location id.
	Returns the event that should be returned to the user.
	Event must be freed
*/
static oilsEvent* oilsAuthHandleLoginOK( jsonObject* userObj, const char* uname,
		const char* type, int orgloc, const char* workstation ) {

	oilsEvent* response;

	long timeout;
	char* wsorg = jsonObjectToSimpleString(oilsFMGetObject(userObj, "ws_ou"));
	if(wsorg) { /* if there is a workstation, use it for the timeout */
		osrfLogDebug( OSRF_LOG_MARK,
				"Auth session trying workstation id %d for auth timeout", atoi(wsorg));
		timeout = oilsAuthGetTimeout( userObj, type, atoi(wsorg) );
		free(wsorg);
	} else {
		osrfLogDebug( OSRF_LOG_MARK,
				"Auth session trying org from param [%d] for auth timeout", orgloc );
		timeout = oilsAuthGetTimeout( userObj, type, orgloc );
	}
	osrfLogDebug(OSRF_LOG_MARK, "Auth session timeout for %s: %ld", uname, timeout );

	char* string = va_list_to_string(
			"%d.%ld.%s", (long) getpid(), time(NULL), uname );
	char* authToken = md5sum(string);
	char* authKey = va_list_to_string(
			"%s%s", OILS_AUTH_CACHE_PRFX, authToken );

	const char* ws = (workstation) ? workstation : "";
	osrfLogActivity(OSRF_LOG_MARK,
		"successful login: username=%s, authtoken=%s, workstation=%s", uname, authToken, ws );

	oilsFMSetString( userObj, "passwd", "" );
	jsonObject* cacheObj = jsonParseFmt( "{\"authtime\": %ld}", timeout );
	jsonObjectSetKey( cacheObj, "userobj", jsonObjectClone(userObj));

	if( !strcmp( type, OILS_AUTH_PERSIST )) {
		// Add entries for endtime and reset_interval, so that we can gracefully
		// extend the session a bit if the user is active toward the end of the 
		// timeout originally specified.
		time_t endtime = time( NULL ) + timeout;
		jsonObjectSetKey( cacheObj, "endtime", jsonNewNumberObject( (double) endtime ) );

		// Reset interval is hard-coded for now, but if we ever want to make it
		// configurable, this is the place to do it:
		jsonObjectSetKey( cacheObj, "reset_interval",
			jsonNewNumberObject( (double) DEFAULT_RESET_INTERVAL ));
	}

	osrfCachePutObject( authKey, cacheObj, (time_t) timeout );
	jsonObjectFree(cacheObj);
	osrfLogInternal(OSRF_LOG_MARK, "oilsAuthHandleLoginOK(): Placed user object into cache");
	jsonObject* payload = jsonParseFmt(
		"{ \"authtoken\": \"%s\", \"authtime\": %ld }", authToken, timeout );

	response = oilsNewEvent2( OSRF_LOG_MARK, OILS_EVENT_SUCCESS, payload );
	free(string); free(authToken); free(authKey);
	jsonObjectFree(payload);

	return response;
}
Beispiel #3
0
/**
 * Fetches the user object from the database and updates the user object in 
 * the cache object, which then has to be re-inserted into the cache.
 * User object is retrieved inside a transaction to avoid replication issues.
 */
static int _oilsAuthReloadUser(jsonObject* cacheObj) {
    int reqid, userId;
    osrfAppSession* session;
	osrfMessage* omsg;
    jsonObject *param, *userObj, *newUserObj = NULL;

    userObj = jsonObjectGetKey( cacheObj, "userobj" );
    userId = oilsFMGetObjectId( userObj );

    session = osrfAppSessionClientInit( "open-ils.cstore" );
    osrfAppSessionConnect(session);

    reqid = osrfAppSessionSendRequest(session, NULL, "open-ils.cstore.transaction.begin", 1);
	omsg = osrfAppSessionRequestRecv(session, reqid, 60);

    if(omsg) {

        osrfMessageFree(omsg);
        param = jsonNewNumberObject(userId);
        reqid = osrfAppSessionSendRequest(session, param, "open-ils.cstore.direct.actor.user.retrieve", 1);
	    omsg = osrfAppSessionRequestRecv(session, reqid, 60);
        jsonObjectFree(param);

        if(omsg) {
            newUserObj = jsonObjectClone( osrfMessageGetResult(omsg) );
            osrfMessageFree(omsg);
            reqid = osrfAppSessionSendRequest(session, NULL, "open-ils.cstore.transaction.rollback", 1);
	        omsg = osrfAppSessionRequestRecv(session, reqid, 60);
            osrfMessageFree(omsg);
        }
    }

    osrfAppSessionFree(session); // calls disconnect internally

    if(newUserObj) {

        // ws_ou and wsid are ephemeral and need to be manually propagated
        // oilsFMSetString dupe()'s internally, no need to clone the string
        oilsFMSetString(newUserObj, "wsid", oilsFMGetStringConst(userObj, "wsid"));
        oilsFMSetString(newUserObj, "ws_ou", oilsFMGetStringConst(userObj, "ws_ou"));

        jsonObjectRemoveKey(cacheObj, "userobj"); // this also frees the old user object
        jsonObjectSetKey(cacheObj, "userobj", newUserObj);
        return 1;
    } 

    osrfLogError(OSRF_LOG_MARK, "Error retrieving user %d from database", userId);
    return 0;
}
Beispiel #4
0
/**
	@brief Perform a remote procedure call.
	@param service The name of the service to invoke.
	@param method The name of the method to call.
	@param params The parameters to be passed to the method, if any.
	@return A copy of whatever the method returns as a result, or a JSON_NULL if the method
	doesn't return anything.

	If the @a params parameter points to a JSON_ARRAY, pass each element of the array
	as a separate parameter.  If it points to any other kind of jsonObject, pass it as a
	single parameter.  If it is NULL, pass no parameters.

	The calling code is responsible for freeing the returned object by calling jsonObjectFree().
*/
jsonObject* oilsUtilsQuickReq( const char* service, const char* method,
		const jsonObject* params ) {
	if(!(service && method)) return NULL;

	osrfLogDebug(OSRF_LOG_MARK, "oilsUtilsQuickReq(): %s - %s", service, method );

	// Open an application session with the service, and send the request
	osrfAppSession* session = osrfAppSessionClientInit( service );
	int reqid = osrfAppSessionSendRequest( session, params, method, 1 );

	// Get the response
	osrfMessage* omsg = osrfAppSessionRequestRecv( session, reqid, 60 );
	jsonObject* result = jsonObjectClone( osrfMessageGetResult(omsg) );

	// Clean up
	osrfMessageFree(omsg);
	osrfAppSessionFree(session);
	return result;
}
Beispiel #5
0
int osrfVersion( osrfMethodContext* ctx ) {
	if( osrfMethodVerifyContext( ctx ) ) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	/* First, see if the data is in the cache */
	char* json = jsonObjectToJSON(ctx->params);
	char* paramsmd5 = md5sum(json);
	char* cachedmd5 = osrfCacheGetString(paramsmd5);
	free(json);

	if( cachedmd5 ) {
		osrfLogDebug(OSRF_LOG_MARK,  "Found %s object in cache, returning....", cachedmd5 );
		jsonObject* resp = jsonNewObject(cachedmd5);
		osrfAppRespondComplete( ctx, resp  );
		jsonObjectFree(resp);
		free(paramsmd5);
		free(cachedmd5);
		return 0;
	}

	const jsonObject* serv = jsonObjectGetIndex(ctx->params, 0);
	const jsonObject* meth = jsonObjectGetIndex(ctx->params, 1);
	const char* service = jsonObjectGetString(serv);
	const char* methd = jsonObjectGetString(meth);

	if( service && methd ) {
		/* shove the additional params into an array */
		jsonObject* tmpArray = jsonNewObject(NULL);
		int i;
		for( i = 2; i != ctx->params->size; i++ )
			jsonObjectPush( tmpArray, jsonObjectClone(jsonObjectGetIndex(ctx->params, i)));

		osrfAppSession* ses = osrfAppSessionClientInit(service);
		int reqid = osrfAppSessionSendRequest( ses, tmpArray, methd, 1 );
		osrfMessage* omsg = osrfAppSessionRequestRecv( ses, reqid, 60 );
		jsonObjectFree(tmpArray);

		if( omsg ) {

			const jsonObject* result = osrfMessageGetResult( omsg );
			char* resultjson = jsonObjectToJSON(result);
			char* resultmd5 = md5sum(resultjson);
			free(resultjson);
			osrfMessageFree(omsg);

			if( resultmd5 ) {
				jsonObject* resp = jsonNewObject(resultmd5);
				osrfAppRespondComplete( ctx, resp );
				jsonObjectFree(resp);
				osrfAppSessionFree(ses);
				osrfLogDebug(OSRF_LOG_MARK, 
					"Found version string %s, caching and returning...", resultmd5 );
				osrfCachePutString( paramsmd5, resultmd5, OSRF_VERSION_CACHE_TIME );
				free(resultmd5);
				free(paramsmd5);
				return 0;
			}
		}
		osrfAppSessionFree(ses);
	}

	free(paramsmd5);

	return -1;
}
/**
    @brief Implement the session create method
    @param ctx The method context.
    @return -1 upon error; zero if successful, and if a STATUS message has 
    been sent to the client to indicate completion; a positive integer if 
    successful but no such STATUS message has been sent.

    Method parameters:
    - a hash with some combination of the following elements:
        - "user_id"     -- actor.usr (au) ID for the user to cache.
        - "org_unit"    -- actor.org_unit (aou) ID representing the physical 
                           location / context used for timeout, etc. settings.
        - "login_type"  -- login type (opac, staff, temp, persist)
        - "workstation" -- workstation name

*/
int oilsAuthInternalCreateSession(osrfMethodContext* ctx) {
    OSRF_METHOD_VERIFY_CONTEXT(ctx);

    const jsonObject* args  = jsonObjectGetIndex(ctx->params, 0);

    const char* user_id     = jsonObjectGetString(jsonObjectGetKeyConst(args, "user_id"));
    const char* login_type  = jsonObjectGetString(jsonObjectGetKeyConst(args, "login_type"));
    const char* workstation = jsonObjectGetString(jsonObjectGetKeyConst(args, "workstation"));
    int org_unit            = jsonObjectGetNumber(jsonObjectGetKeyConst(args, "org_unit"));

    if ( !(user_id && login_type) ) {
        return osrfAppRequestRespondException( ctx->session, ctx->request,
            "Missing parameters for method: %s", ctx->method->name );
    }

    oilsEvent* response = NULL;

    // fetch the user object
    jsonObject* idParam = jsonNewNumberStringObject(user_id);
    jsonObject* userObj = oilsUtilsCStoreReqCtx(
        ctx, "open-ils.cstore.direct.actor.user.retrieve", idParam);
    jsonObjectFree(idParam);

    if (!userObj) {
        return osrfAppRequestRespondException(ctx->session, 
            ctx->request, "No user found with ID %s", user_id);
    }

    // If a workstation is defined, add the workstation info
    if (workstation) {
        response = oilsAuthVerifyWorkstation(ctx, userObj, workstation);

        if (response) { // invalid workstation.
            jsonObjectFree(userObj);
            osrfAppRespondComplete(ctx, oilsEventToJSON(response));
            oilsEventFree(response);
            return 0;

        } else { // workstation OK.  

            // The worksation org unit supersedes any org unit value 
            // provided via the API.  oilsAuthVerifyWorkstation() sets the 
            // ws_ou value to the WS owning lib.  A value is guaranteed.
            org_unit = atoi(oilsFMGetStringConst(userObj, "ws_ou"));
        }

    } else { // no workstation

        // For backwards compatibility, when no workstation is provided, use 
        // the users's home org as its workstation org unit, regardless of 
        // any API-level org unit value provided.
        const char* orgid = oilsFMGetStringConst(userObj, "home_ou");
        oilsFMSetString(userObj, "ws_ou", orgid);

        // The context org unit defaults to the user's home library when
        // no workstation is used and no API-level value is provided.
        if (org_unit < 1) org_unit = atoi(orgid);
    }

    // determine the auth/cache timeout
    long timeout = oilsAuthGetTimeout(userObj, login_type, org_unit);

    char* string = va_list_to_string("%d.%ld.%ld", 
        (long) getpid(), time(NULL), oilsFMGetObjectId(userObj));
    char* authToken = md5sum(string);
    char* authKey = va_list_to_string(
        "%s%s", OILS_AUTH_CACHE_PRFX, authToken);

    oilsFMSetString(userObj, "passwd", "");
    jsonObject* cacheObj = jsonParseFmt("{\"authtime\": %ld}", timeout);
    jsonObjectSetKey(cacheObj, "userobj", jsonObjectClone(userObj));

    if( !strcmp(login_type, OILS_AUTH_PERSIST)) {
        // Add entries for endtime and reset_interval, so that we can gracefully
        // extend the session a bit if the user is active toward the end of the 
        // timeout originally specified.
        time_t endtime = time( NULL ) + timeout;
        jsonObjectSetKey(cacheObj, "endtime", 
            jsonNewNumberObject( (double) endtime ));

        // Reset interval is hard-coded for now, but if we ever want to make it
        // configurable, this is the place to do it:
        jsonObjectSetKey(cacheObj, "reset_interval",
            jsonNewNumberObject( (double) DEFAULT_RESET_INTERVAL));
    }

    osrfCachePutObject(authKey, cacheObj, (time_t) timeout);
    jsonObjectFree(cacheObj);
    jsonObject* payload = jsonParseFmt(
        "{\"authtoken\": \"%s\", \"authtime\": %ld}", authToken, timeout);

    response = oilsNewEvent2(OSRF_LOG_MARK, OILS_EVENT_SUCCESS, payload);
    free(string); free(authToken); free(authKey);
    jsonObjectFree(payload);

    jsonObjectFree(userObj);
    osrfAppRespondComplete(ctx, oilsEventToJSON(response));
    oilsEventFree(response);

    return 0;
}
Beispiel #7
0
/**
	@brief Either send or enqueue a response to a client, optionally with a completion notice.
	@param ctx Pointer to the method context.
	@param data Pointer to the response, in the form of a jsonObject.
	@param complete Boolean: if true, we will accompany the RESULT message with a STATUS
	message indicating that the response is complete.
	@return Zero if successful, or -1 upon error.

	For an atomic method, add a copy of the response data to a cache within the method
	context, to be sent later.  In this case the @a complete parameter has no effect,
	because we'll send the STATUS message later when we send the cached results.

	If the method is not atomic, translate the message into JSON and append it to a buffer,
	flushing the buffer as needed to avoid overflow.  If @a complete is true, append
	a STATUS message (as JSON) to the buffer and flush the buffer.
*/
static int _osrfAppRespond( osrfMethodContext* ctx, const jsonObject* data, int complete ) {
	if(!(ctx && ctx->method)) return -1;

	if( ctx->method->options & OSRF_METHOD_ATOMIC ) {
		osrfLogDebug( OSRF_LOG_MARK,
			"Adding responses to stash for atomic method %s", ctx->method->name );

		// If we don't already have one, create a JSON_ARRAY to serve as a cache.
		if( ctx->responses == NULL )
			ctx->responses = jsonNewObjectType( JSON_ARRAY );

		// Add a copy of the data object to the cache.
		if ( data != NULL )
			jsonObjectPush( ctx->responses, jsonObjectClone(data) );
	} else {
		osrfLogDebug( OSRF_LOG_MARK,
			"Adding responses to stash for method %s", ctx->method->name );

		if( data ) {
			// If you want to flush the intput buffers for every output message,
			// this is the place to do it.
			//osrf_app_session_queue_wait( ctx->session, 0, NULL );

			// Create an OSRF message
			osrfMessage* msg = osrf_message_init( RESULT, ctx->request, 1 );
			osrf_message_set_status_info( msg, NULL, "OK", OSRF_STATUS_OK );
			osrf_message_set_result( msg, data );

			// Serialize the OSRF message into JSON text
			char* json = jsonObjectToJSON( osrfMessageToJSON( msg ));
			osrfMessageFree( msg );

			// If the new message would overflow the buffer, flush the output buffer first
			int len_so_far = buffer_length( ctx->session->outbuf );
			if( len_so_far && (strlen( json ) + len_so_far + 3 >= ctx->method->bufsize )) {
				if( flush_responses( ctx->session, ctx->session->outbuf ))
					return -1;
			}

			// Append the JSON text to the output buffer
			append_msg( ctx->session->outbuf, json );
			free( json );
		}

		if(complete) {
			// Create a STATUS message
			osrfMessage* status_msg = osrf_message_init( STATUS, ctx->request, 1 );
			osrf_message_set_status_info( status_msg, "osrfConnectStatus", "Request Complete",
				OSRF_STATUS_COMPLETE );

			// Serialize the STATUS message into JSON text
			char* json = jsonObjectToJSON( osrfMessageToJSON( status_msg ));
			osrfMessageFree( status_msg );

			// Add the STATUS message to the output buffer.
			// It's short, so don't worry about avoiding overflow.
			append_msg( ctx->session->outbuf, json );
			free( json );

			// Flush the output buffer, sending any accumulated messages.
			if( flush_responses( ctx->session, ctx->session->outbuf ))
				return -1;
		}
	}

	return 0;
}
/**
	@brief Replace the jsonObject of an osrfConfig.
	@param cfg The osrfConfig to alter.
	@param obj The jsonObject to install in the osrfConfig.

	This is useful if you have a json object already, rather than an XML configuration file
	to parse.
*/
void osrfConfigReplaceConfig(osrfConfig* cfg, const jsonObject* obj) {
	if(!cfg || !obj) return;
	jsonObjectFree(cfg->config);
	cfg->config = jsonObjectClone(obj);
}