Пример #1
0
/**
	@brief Register an application.
	@param appName Name of the application.
	@param soFile Name of the shared object file to be loaded for this application.
	@return Zero if successful, or -1 upon error.

	Open the shared object file and call its osrfAppInitialize() function, if it has one.
	Register the standard system methods for it.  Arrange for the application name to
	appear in subsequent log messages.
*/
int osrfAppRegisterApplication( const char* appName, const char* soFile ) {
	if( !appName || ! soFile ) return -1;
	char* error;

	osrfLogSetAppname( appName );

	if( !_osrfAppHash ) {
		_osrfAppHash = osrfNewHash();
		osrfHashSetCallback( _osrfAppHash, osrfAppFree );
	}

	osrfLogInfo( OSRF_LOG_MARK, "Registering application %s with file %s", appName, soFile );

	// Open the shared object.
	void* handle = dlopen( soFile, RTLD_NOW );
	if( ! handle ) {
		const char* msg = dlerror();
		osrfLogError( OSRF_LOG_MARK, "Failed to dlopen library file %s: %s", soFile, msg );
		return -1;
	}

	// Construct the osrfApplication.
	osrfApplication* app = safe_malloc(sizeof(osrfApplication));
	app->handle = handle;
	app->methods = osrfNewHash();
	osrfHashSetCallback( app->methods, osrfMethodFree );
	app->onExit = NULL;

	// Add the newly-constructed app to the list.
	osrfHashSet( _osrfAppHash, app, appName );

	// Try to run the initialize method.  Typically it will register one or more
	// methods of the application.
	int (*init) (void);
	*(void **) (&init) = dlsym( handle, "osrfAppInitialize" );

	if( (error = dlerror()) != NULL ) {
		osrfLogWarning( OSRF_LOG_MARK,
			"! Unable to locate method symbol [osrfAppInitialize] for app %s: %s",
			appName, error );

	} else {

		/* run the method */
		int ret;
		if( (ret = (*init)()) ) {
			osrfLogWarning( OSRF_LOG_MARK, "Application %s returned non-zero value from "
				"'osrfAppInitialize', not registering...", appName );
			osrfHashRemove( _osrfAppHash, appName );
			return ret;
		}
	}

	register_system_methods( app );
	osrfLogInfo( OSRF_LOG_MARK, "Application %s registered successfully", appName );
	osrfAppSetOnExit( app, appName );

	return 0;
}
Пример #2
0
/**
	@brief Load a specified query from the database query tables.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Method parameters:
	- query id (key of query.stored_query table)

	Returns: a hash with two entries:
	- "token": A character string serving as a token for future references to the query.
	- "bind_variables" A hash of bind variables; see notes for doParamList().
*/
int doPrepare( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query id from a method parameter
	const jsonObject* query_id_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( query_id_obj->type != JSON_NUMBER ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query id must be a number" );
		return -1;
	}

	int query_id = atoi( jsonObjectGetString( query_id_obj ));
	if( query_id <= 0 ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter: query id must be greater than zero" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Loading query for id # %d", query_id );

	BuildSQLState* state = buildSQLStateNew( dbhandle );
	state->defaults_usable = 1;
	state->values_required = 0;
	StoredQ* query = getStoredQuery( state, query_id );
	if( state->error ) {
		osrfLogWarning( OSRF_LOG_MARK, "Unable to load stored query # %d", query_id );
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Unable to load stored query" );
		if( state->panic ) {
			osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, 
				"Database connection isn't working" ));
			osrfAppSessionPanic( ctx->session );
		}
		return -1;
	}

	const char* token = save_query( ctx, state, query );

	osrfLogInfo( OSRF_LOG_MARK, "Token for query id # %d is \"%s\"", query_id, token );

	// Build an object to return.  It will be a hash containing the query token and a
	// list of bind variables.
	jsonObject* returned_obj = jsonNewObjectType( JSON_HASH );
	jsonObjectSetKey( returned_obj, "token", jsonNewObject( token ));
	jsonObjectSetKey( returned_obj, "bind_variables",
		oilsBindVarList( state->bindvar_list ));

	osrfAppRespondComplete( ctx, returned_obj );
	return 0;
}
Пример #3
0
/**
    @brief Initialize the application by registering functions for method calls.
    @return Zero on success, 1 on error.
*/
int osrfAppInitialize() {

    osrfLogInfo(OSRF_LOG_MARK, "Initializing Auth Internal Server...");

    /* load and parse the IDL */
    /* return non-zero to indicate error */
    if (!oilsInitIDL(NULL)) return 1; 

    osrfAppRegisterMethod(
        MODULENAME,
        "open-ils.auth_internal.session.create",
        "oilsAuthInternalCreateSession",
        "Adds a user to the authentication cache to indicate "
        "the user is authenticated", 1, 0 
    );

    osrfAppRegisterMethod(
        MODULENAME,
        "open-ils.auth_internal.user.validate",
        "oilsAuthInternalValidate",
        "Determines whether a user should be allowed to login.  " 
        "Returns SUCCESS oilsEvent when the user is valid, otherwise "
        "returns a non-SUCCESS oilsEvent object", 1, 0
    );

    return 0;
}
Пример #4
0
/**
	@brief Execute an SQL query and return a result set.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Method parameters:
	- query token, as previously returned by the .prepare method.

	Returns: A series of responses, each of them a row represented as an array of column values.
*/
int doExecute( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query token
	const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( token_obj->type != JSON_STRING ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query token must be a string" );
		return -1;
	}
	const char* token = jsonObjectGetString( token_obj );

	// Look up the query token in the session-level userData
	CachedQuery* query = search_token( ctx, token );
	if( !query ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid query token" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Executing query for token \"%s\"", token );
	if( query->state->error ) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
			"No valid prepared query available for query id # %d", query->query->id ));
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
							  ctx->request, "No valid prepared query available" );
		return -1;
	} else if( buildSQL( query->state, query->query )) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
			"Unable to build SQL statement for query id # %d", query->query->id ));
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Unable to build SQL statement" );
		return -1;
	}

	jsonObject* row = oilsFirstRow( query->state );
	while( row ) {
		osrfAppRespond( ctx, row );
		row = oilsNextRow( query->state );
	}

	if( query->state->error ) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
			"Unable to execute SQL statement for query id # %d", query->query->id ));
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Unable to execute SQL statement" );
		if( query->state->panic ) {
			osrfLogError( OSRF_LOG_MARK, sqlAddMsg( query->state,
				"Database connection isn't working" ));
			osrfAppSessionPanic( ctx->session );
		}
		return -1;
	}

	osrfAppRespondComplete( ctx, NULL );
	return 0;
}
Пример #5
0
osrfHash* oilsInitIDL(const char* idl_filename) {

	char* freeable_filename = NULL;
	const char* filename;

	if(idl_filename)
		filename = idl_filename;
	else {
		freeable_filename = osrf_settings_host_value("/IDL");
		filename = freeable_filename;
	}

	if (!filename) {
		osrfLogError(OSRF_LOG_MARK, "No settings config for '/IDL'");
		return NULL;
	}

	osrfLogInfo(OSRF_LOG_MARK, "Parsing IDL %s", filename);

	if (!oilsIDLInit( filename )) {
		osrfLogError(OSRF_LOG_MARK, "Problem loading IDL file [%s]!", filename);
		if(freeable_filename)
			free(freeable_filename);
		return NULL;
	}

	if(freeable_filename)
		free(freeable_filename);
	return oilsIDL();
}
Пример #6
0
/**
	@brief Given a barcode, fetch the corresponding row from the actor.usr table, if any.
	@param name The barcode for which to search.
	@return A Fieldmapper object for the relevant row in the actor.usr table, if it exists;
	or a JSON_NULL if it doesn't.

	Look up the barcode in actor.card.  Follow a foreign key from there to get a row in
	actor.usr.

	The calling code is responsible for freeing the returned object by calling jsonObjectFree().
*/
jsonObject* oilsUtilsFetchUserByBarcode(const char* barcode) {
	if(!barcode) return NULL;

	osrfLogInfo(OSRF_LOG_MARK, "Fetching user by barcode %s", barcode);

	jsonObject* params = jsonParseFmt("{\"barcode\":\"%s\"}", barcode);
	jsonObject* card = oilsUtilsQuickReq(
		"open-ils.cstore", "open-ils.cstore.direct.actor.card.search", params );
	jsonObjectFree(params);

	if(!card)
		return NULL;   // No such card

	// Get the user's id as a double
	char* usr = oilsFMGetString(card, "usr");
	jsonObjectFree(card);
	if(!usr)
		return NULL;   // No user id (shouldn't happen)
	double iusr = strtod(usr, NULL);
	free(usr);

	// Look up the user in actor.usr
	params = jsonParseFmt("[%f]", iusr);
	jsonObject* user = oilsUtilsQuickReq(
		"open-ils.cstore", "open-ils.cstore.direct.actor.user.retrieve", params);

	jsonObjectFree(params);
	return user;
}
Пример #7
0
/**
	@brief Implement the param_list method.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Provide a list of bind variables for a specified query, along with their various
	attributes.

	Method parameters:
	- query token, as previously returned by the .prepare method.

	Returns: A (possibly empty) JSON_HASH, keyed on the names of the bind variables.
	The data for each is another level of JSON_HASH with a fixed set of tags:
	- "label"
	- "type"
	- "description"
	- "default_value" (as a jsonObject)
	- "actual_value" (as a jsonObject)

	Any non-existent values are represented as JSON_NULLs.
*/
int doParamList( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query token from a method parameter
	const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( token_obj->type != JSON_STRING ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query token must be a string" );
		return -1;
	}
	const char* token = jsonObjectGetString( token_obj );

	// Look up the query token in the session-level userData
	CachedQuery* query = search_token( ctx, token );
	if( !query ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid query token" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Returning list of bind variables for token %s", token );

	osrfAppRespondComplete( ctx, oilsBindVarList( query->state->bindvar_list ) );
	return 0;
}
Пример #8
0
static int handler(request_rec *r) {
    int stat = OK;
	if(strcmp(r->handler, MODULE_NAME)) return DECLINED;
    if(r->header_only) return stat;

	r->allowed |= (AP_METHOD_BIT << M_GET);
	r->allowed |= (AP_METHOD_BIT << M_POST);

	osrfLogSetAppname("osrf_http_translator");
	osrfAppSessionSetIngress(TRANSLATOR_INGRESS);
    testConnection(r);
    crossOriginHeaders(r, allowedOrigins);

	osrfLogMkXid();
    osrfHttpTranslator* trans = osrfNewHttpTranslator(r);
    if(trans->body) {
        stat = osrfHttpTranslatorProcess(trans);
        //osrfHttpTranslatorDebug(trans);
        osrfLogInfo(OSRF_LOG_MARK, "translator resulted in status %d", stat);
    } else {
        osrfLogWarning(OSRF_LOG_MARK, "no message body to process");
    }
    osrfHttpTranslatorFree(trans);
	return stat;
}
Пример #9
0
/**
	@brief Implement the bind_param method.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Apply values to bind variables, overriding the defaults, if any.

	Method parameters:
	- query token, as previously returned by the .prepare method.
	- hash of bind variable values, keyed on bind variable names.

	Returns: Nothing.
*/
int doBindParam( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query token from a method parameter
	const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( token_obj->type != JSON_STRING ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query token must be a string" );
		return -1;
	}
	const char* token = jsonObjectGetString( token_obj );

	// Look up the query token in the session-level userData
	CachedQuery* query = search_token( ctx, token );
	if( !query ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid query token" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Binding parameter(s) for token %s", token );

	jsonObject* bindings = jsonObjectGetIndex( ctx->params, 1 );
	if( !bindings ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "No parameter provided for bind variable values" );
		return -1;
	} else if( bindings->type != JSON_HASH ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter for bind variable values: not a hash" );
		return -1;
	}

	if( 0 == bindings->size ) {
		// No values to assign; we're done.
		osrfAppRespondComplete( ctx, NULL );
		return 0;
	}

	osrfHash* bindvar_list = query->state->bindvar_list;
	if( !bindvar_list || osrfHashGetCount( bindvar_list ) == 0 ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "There are no bind variables to which to assign values" );
		return -1;
	}

	if( oilsApplyBindValues( query->state, bindings )) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Unable to apply values to bind variables" );
		return -1;
	} else {
		osrfAppRespondComplete( ctx, NULL );
		return 0;
	}
}
Пример #10
0
/**
	Resets the auth login timeout
	@return The event object, OILS_EVENT_SUCCESS, or OILS_EVENT_NO_SESSION
*/
static oilsEvent*  _oilsAuthResetTimeout( const char* authToken ) {
	if(!authToken) return NULL;

	oilsEvent* evt = NULL;
	time_t timeout;

	osrfLogDebug(OSRF_LOG_MARK, "Resetting auth timeout for session %s", authToken);
	char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken );
	jsonObject* cacheObj = osrfCacheGetObject( key );

	if(!cacheObj) {
		osrfLogInfo(OSRF_LOG_MARK, "No user in the cache exists with key %s", key);
		evt = oilsNewEvent(OSRF_LOG_MARK, OILS_EVENT_NO_SESSION);

	} else {
		// Determine a new timeout value
		jsonObject* endtime_obj = jsonObjectGetKey( cacheObj, "endtime" );
		if( endtime_obj ) {
			// Extend the current endtime by a fixed amount
			time_t endtime = (time_t) jsonObjectGetNumber( endtime_obj );
			int reset_interval = DEFAULT_RESET_INTERVAL;
			const jsonObject* reset_interval_obj = jsonObjectGetKeyConst(
				cacheObj, "reset_interval" );
			if( reset_interval_obj ) {
				reset_interval = (int) jsonObjectGetNumber( reset_interval_obj );
				if( reset_interval <= 0 )
					reset_interval = DEFAULT_RESET_INTERVAL;
			}

			time_t now = time( NULL );
			time_t new_endtime = now + reset_interval;
			if( new_endtime > endtime ) {
				// Keep the session alive a little longer
				jsonObjectSetNumber( endtime_obj, (double) new_endtime );
				timeout = reset_interval;
				osrfCachePutObject( key, cacheObj, timeout );
			} else {
				// The session isn't close to expiring, so don't reset anything.
				// Just report the time remaining.
				timeout = endtime - now;
			}
		} else {
			// Reapply the existing timeout from the current time
			timeout = (time_t) jsonObjectGetNumber( jsonObjectGetKeyConst( cacheObj, "authtime"));
			osrfCachePutObject( key, cacheObj, timeout );
		}

		jsonObject* payload = jsonNewNumberObject( (double) timeout );
		evt = oilsNewEvent2(OSRF_LOG_MARK, OILS_EVENT_SUCCESS, payload);
		jsonObjectFree(payload);
		jsonObjectFree(cacheObj);
	}

	free(key);
	return evt;
}
Пример #11
0
/**
	@brief Initialize the application by registering functions for method calls.
	@return Zero in all cases.
*/
int osrfAppInitialize() {

	osrfLogInfo(OSRF_LOG_MARK, "Initializing Auth Server...");

	/* load and parse the IDL */
	if (!oilsInitIDL(NULL)) return 1; /* return non-zero to indicate error */

	osrfAppRegisterMethod(
		MODULENAME,
		"open-ils.auth.authenticate.init",
		"oilsAuthInit",
		"Start the authentication process and returns the intermediate authentication seed"
		" PARAMS( username )", 1, 0 );

	osrfAppRegisterMethod(
		MODULENAME,
		"open-ils.auth.authenticate.complete",
		"oilsAuthComplete",
		"Completes the authentication process.  Returns an object like so: "
		"{authtoken : <token>, authtime:<time>}, where authtoken is the login "
		"token and authtime is the number of seconds the session will be active"
		"PARAMS(username, md5sum( seed + md5sum( password ) ), type, org_id ) "
		"type can be one of 'opac','staff', or 'temp' and it defaults to 'staff' "
		"org_id is the location at which the login should be considered "
		"active for login timeout purposes", 1, 0 );

	osrfAppRegisterMethod(
		MODULENAME,
		"open-ils.auth.session.retrieve",
		"oilsAuthSessionRetrieve",
		"Pass in the auth token and this retrieves the user object.  The auth "
		"timeout is reset when this call is made "
		"Returns the user object (password blanked) for the given login session "
		"PARAMS( authToken )", 1, 0 );

	osrfAppRegisterMethod(
		MODULENAME,
		"open-ils.auth.session.delete",
		"oilsAuthSessionDelete",
		"Destroys the given login session "
		"PARAMS( authToken )",  1, 0 );

	osrfAppRegisterMethod(
		MODULENAME,
		"open-ils.auth.session.reset_timeout",
		"oilsAuthResetTimeout",
		"Resets the login timeout for the given session "
		"Returns an ILS Event with payload = session_timeout of session "
		"if found, otherwise returns the NO_SESSION event"
		"PARAMS( authToken )", 1, 0 );

	return 0;
}
Пример #12
0
/**
	@brief Call the exit handler for every application that has one.

	Normally a server's child process (a so-called "drone") calls this function just before
	shutting down.
*/
void osrfAppRunExitCode( void ) {
	osrfHashIterator* itr = osrfNewHashIterator(_osrfAppHash);
	osrfApplication* app;
	while( (app = osrfHashIteratorNext(itr)) ) {
		if( app->onExit ) {
			osrfLogInfo(OSRF_LOG_MARK, "Running onExit handler for app %s",
				osrfHashIteratorKey(itr) );
			app->onExit();
		}
	}
	osrfHashIteratorFree(itr);
}
Пример #13
0
static int oilsAuthLoginVerifyPassword(const osrfMethodContext* ctx, 
    int user_id, const char* username, const char* password) {

    // build the cache key
    growing_buffer* gb = buffer_init(64); // free me
    buffer_add(gb, OILS_AUTH_CACHE_PRFX);
    buffer_add(gb, username);
    buffer_add(gb, OILS_AUTH_COUNT_SFFX);
    char* countkey = buffer_release(gb); // free me

    jsonObject* countobject = osrfCacheGetObject(countkey); // free me

    long failcount = 0;
    if (countobject) {
        failcount = (long) jsonObjectGetNumber(countobject);

        if (failcount >= _oilsAuthBlockCount) {
            // User is blocked.  Don't waste any more CPU cycles on them.

            osrfLogInfo(OSRF_LOG_MARK, 
                "oilsAuth found too many recent failures for '%s' : %i, "
                "forcing failure state.", username, failcount);

            jsonObjectFree(countobject);
            free(countkey);   
            return 0;
        }
    }

    int verified = oilsAuthLoginCheckPassword(user_id, password);

    if (!verified) { // login failed.  increment failure counter.
        failcount++;

        if (countobject) {
            // append to existing counter
            jsonObjectSetNumber(countobject, failcount);

        } else { 
            // first failure, create a new counter
            countobject = jsonNewNumberObject((double) failcount);
        }

        osrfCachePutObject(countkey, countobject, _oilsAuthBlockTimeout);
    }

    jsonObjectFree(countobject); // NULL OK
    free(countkey);

    return verified;
}
Пример #14
0
/**
	@brief Verify the password received from the client.
	@param ctx The method context.
	@param userObj An object from the database, representing the user.
	@param password An obfuscated password received from the client.
	@return 1 if the password is valid; 0 if it isn't; or -1 upon error.

	(None of the so-called "passwords" used here are in plaintext.  All have been passed
	through at least one layer of hashing to obfuscate them.)

	Take the password from the user object.  Append it to the username seed from memcache,
	as stored previously by a call to the init method.  Take an md5 hash of the result.
	Then compare this hash to the password received from the client.

	In order for the two to match, other than by dumb luck, the client had to construct
	the password it passed in the same way.  That means it neded to know not only the
	original password (either hashed or plaintext), but also the seed.  The latter requirement
	means that the client process needs either to be the same process that called the init
	method or to receive the seed from the process that did so.
*/
static int oilsAuthVerifyPassword( const osrfMethodContext* ctx, int user_id, 
        const char* identifier, const char* password, const char* nonce) {

    int verified = 0;

    // We won't be needing the seed again, remove it
    osrfCacheRemove("%s%s%s", OILS_AUTH_CACHE_PRFX, identifier, nonce);

    // Ask the DB to verify the user's password.
    // Here, the password is md5(md5(password) + salt)

    jsonObject* params = jsonParseFmt( // free
        "{\"from\":[\"actor.verify_passwd\",%d,\"main\",\"%s\"]}", 
        user_id, password);

    jsonObject* verify_obj = // free 
        oilsUtilsCStoreReq("open-ils.cstore.json_query", params);

    jsonObjectFree(params);

    if (verify_obj) {
        verified = oilsUtilsIsDBTrue(
            jsonObjectGetString(
                jsonObjectGetKeyConst(
                    verify_obj, "actor.verify_passwd")));

        jsonObjectFree(verify_obj);
    }

    char* countkey = va_list_to_string("%s%s%s", 
        OILS_AUTH_CACHE_PRFX, identifier, OILS_AUTH_COUNT_SFFX );
    jsonObject* countobject = osrfCacheGetObject( countkey );
    if(countobject) {
        long failcount = (long) jsonObjectGetNumber( countobject );
        if(failcount >= _oilsAuthBlockCount) {
            verified = 0;
            osrfLogInfo(OSRF_LOG_MARK, 
                "oilsAuth found too many recent failures for '%s' : %i, "
                "forcing failure state.", identifier, failcount);
        }
        if(verified == 0) {
            failcount += 1;
        }
        jsonObjectSetNumber( countobject, failcount );
        osrfCachePutObject( countkey, countobject, _oilsAuthBlockTimeout );
        jsonObjectFree(countobject);
    }
    free(countkey);

    return verified;
}
Пример #15
0
/**
	@brief Run the application-specific child initialization function for a given application.
	@param appname Name of the application.
	@return Zero if successful, or if the application has no child initialization function; -1
	if the application is not registered, or if the function returns non-zero.

	The child initialization function must be named "osrfAppChildInit" within the shared
	object library.  It initializes a drone process of a server.
*/
int osrfAppRunChildInit(const char* appname) {
	osrfApplication* app = _osrfAppFindApplication(appname);
	if(!app) return -1;

	char* error;
	int ret;
	int (*childInit) (void);

	*(void**) (&childInit) = dlsym(app->handle, "osrfAppChildInit");

	if( (error = dlerror()) != NULL ) {
		osrfLogInfo( OSRF_LOG_MARK, "No child init defined for app %s : %s", appname, error);
		return 0;
	}

	if( (ret = (*childInit)()) ) {
		osrfLogError(OSRF_LOG_MARK, "App %s child init failed", appname);
		return -1;
	}

	osrfLogInfo(OSRF_LOG_MARK, "%s child init succeeded", appname);
	return 0;
}
Пример #16
0
/**
	@brief Set the effective output buffer size for a given method.
	@param appName Name of the application.
	@param methodName Name of the method.
	@param bufsize Desired size of the output buffer, in bytes.
	@return Zero if successful, or -1 if the specified method cannot be found.

	A smaller buffer size may result in a lower latency for the first response, since we don't
	wait for as many messages to accumulate before flushing the output buffer.  On the other
	hand a larger buffer size may result in higher throughput due to lower network overhead.

	Since the buffer size is not an absolute limit, it may be set to zero, in which case each
	output transport message will contain no more than one RESULT message.

	This function has no effect on atomic methods, because all responses are sent in a single
	message anyway.  Likewise it has no effect on a method that returns only a single response.
*/
int osrfMethodSetBufferSize( const char* appName, const char* methodName, size_t bufsize ) {
	osrfMethod* method = _osrfAppFindMethod( appName, methodName );
	if( method ) {
		osrfLogInfo( OSRF_LOG_MARK,
			"Setting outbuf buffer size to %lu for method %s of application %s",
			(unsigned long) bufsize, methodName, appName );
		method->bufsize = bufsize;
		return 0;
	} else {
		osrfLogWarning( OSRF_LOG_MARK,
			"Unable to set outbuf buffer size to %lu for method %s of application %s",
			(unsigned long) bufsize, methodName, appName );
		return -1;
	}
}
Пример #17
0
/**
	@brief Save a pointer to the application's exit function.
	@param app Pointer to the osrfApplication.
	@param appName Application name (used only for log messages).

	Look in the shared object for a symbol named "osrfAppChildExit".  If you find one, save
	it as a pointer to the application's exit function.  If present, this function will be
	called when a server's child process (a so-called "drone") is shutting down.
*/
static void osrfAppSetOnExit(osrfApplication* app, const char* appName) {
	if(!(app && appName)) return;

	/* see if we can run the initialize method */
	char* error;
	void (*onExit) (void);
	*(void **) (&onExit) = dlsym(app->handle, "osrfAppChildExit");

	if( (error = dlerror()) != NULL ) {
		osrfLogDebug(OSRF_LOG_MARK, "No exit handler defined for %s", appName);
		return;
	}

	osrfLogInfo(OSRF_LOG_MARK, "registering exit handler for %s", appName);
	app->onExit = (*onExit);
}
Пример #18
0
static oilsEvent* oilsAuthVerifyWorkstation(
		const osrfMethodContext* ctx, jsonObject* userObj, const char* ws ) {
	osrfLogInfo(OSRF_LOG_MARK, "Attaching workstation to user at login: %s", ws);
	jsonObject* workstation = oilsUtilsFetchWorkstationByName(ws);
	if(!workstation || workstation->type == JSON_NULL) {
		jsonObjectFree(workstation);
		return oilsNewEvent(OSRF_LOG_MARK, "WORKSTATION_NOT_FOUND");
	}
	long wsid = oilsFMGetObjectId(workstation);
	LONG_TO_STRING(wsid);
	char* orgid = oilsFMGetString(workstation, "owning_lib");
	oilsFMSetString(userObj, "wsid", LONGSTR);
	oilsFMSetString(userObj, "ws_ou", orgid);
	free(orgid);
	jsonObjectFree(workstation);
	return NULL;
}
/**
 * Clear the session cache, release the session pool
 */
void CALLBACK on_disconnect_handler(
    void *data, const WebSocketServer *server) {

    // if the threads wake up during disconnect, this tells 
    // them to go back to sleep.
    trans->client_connected = 0;

    request_rec *r = server->request(server);
    osrfLogInfo(OSRF_LOG_MARK, "WS disconnect from %s", get_client_ip(r)); 

    // Clear any lingering session data
    // NOTE: we could apr_pool_destroy the stateful_session_pool to truly free
    // the memory, but since there is a limit to the size of the pool
    // (max_concurrent_sessions), the memory cannot grow unbounded, 
    // so there's no need.
    apr_hash_clear(trans->stateful_session_cache);
    apr_pool_clear(trans->stateful_session_pool);
}
Пример #20
0
/**
	@brief Initialize a server drone.
	@return Zero if successful, -1 if not.

	Connect to the database.  For each non-virtual class in the IDL, execute a dummy "SELECT * "
	query to get the datatype of each column.  Record the datatypes in the loaded IDL.

	This function is called by a server drone shortly after it is spawned by the listener.
*/
int osrfAppChildInit( void ) {

	dbhandle = oilsConnectDB( modulename );
	if( !dbhandle )
		return -1;
	else {
		oilsSetDBConnection( dbhandle );
		osrfLogInfo( OSRF_LOG_MARK, "%s successfully connected to the database", modulename );

		// Apply datatypes from database to the fields in the IDL
		//if( oilsExtendIDL() ) {
		//	osrfLogError( OSRF_LOG_MARK, "Error extending the IDL" );
		//	return -1;
		//}
		//else
		return 0;
	}
}
Пример #21
0
/**
	@brief Construct an SQL query, but without executing it.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Method parameters:
	- query token, as previously returned by the .prepare method.

	Returns: A string containing an SQL query..
*/
int doSql( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query token
	const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( token_obj->type != JSON_STRING ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query token must be a string" );
		return -1;
	}
	const char* token = jsonObjectGetString( token_obj );

	// Look up the query token in the session-level userData
	CachedQuery* query = search_token( ctx, token );
	if( !query ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid query token" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Returning SQL for token \"%s\"", token );
	if( query->state->error ) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
			"No valid prepared query available for query id # %d", query->query->id ));
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "No valid prepared query available" );
		return -1;
	} else if( buildSQL( query->state, query->query )) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
			"Unable to build SQL statement for query id # %d", query->query->id ));
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Unable to build SQL statement" );
		return -1;
	}

	osrfAppRespondComplete( ctx, jsonNewObject( OSRF_BUFFER_C_STR( query->state->sql )));
	return 0;
}
Пример #22
0
static void osrf_json_gateway_child_init(apr_pool_t *p, server_rec *s) {

	char* cfg = osrf_json_gateway_config_file;
	char buf[32];
	int t = time(NULL);
	snprintf(buf, sizeof(buf), "%d", t);

	if( ! osrfSystemBootstrapClientResc( cfg, CONFIG_CONTEXT, buf ) ) {
		ap_log_error( APLOG_MARK, APLOG_ERR, 0, s,
			"Unable to Bootstrap OpenSRF Client with config %s..", cfg);
		return;
	}

	bootstrapped = 1;
	osrfLogInfo(OSRF_LOG_MARK, "Bootstrapping gateway child for requests");

	// when this pool is cleaned up, it means the child
	// process is going away.  register some cleanup code
	// XXX causes us to disconnect even for clone()'d process cleanup (as in mod_cgi)
	//apr_pool_cleanup_register(p, NULL, child_exit, apr_pool_cleanup_null);
}
/**
 * Create the per-client translator
 */
void* CALLBACK on_connect_handler(const WebSocketServer *server) {
    request_rec *r = server->request(server);

    if (!trans) { // first connection

        // connect to opensrf
        if (child_init(server) != APR_SUCCESS)
            return NULL;

        // build pools, thread data, and the translator
        if (build_startup_data(server) != APR_SUCCESS)
            return NULL;
    }

    const char* client_ip = get_client_ip(r);
    osrfLogInfo(OSRF_LOG_MARK, "WS connect from %s", client_ip);

    last_activity_time = time(NULL);
    trans->client_connected = 1;
    return trans;
}
Пример #24
0
/**
	@brief Return a list of column names for the SELECT list.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Method parameters:
	- query token, as previously returned by the .prepare method.

	Returns: An array of column names; unavailable names are represented as nulls.
*/
int doColumns( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query token from a method parameter
	const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( token_obj->type != JSON_STRING ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query token must be a string" );
		return -1;
	}
	const char* token = jsonObjectGetString( token_obj );

	// Look up the query token in the session-level userData
	CachedQuery* query = search_token( ctx, token );
	if( !query ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid query token" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Listing column names for token %s", token );

	jsonObject* col_list = oilsGetColNames( query->state, query->query );
	if( query->state->error ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Unable to get column names" );
		if( query->state->panic ) {
			osrfLogError( OSRF_LOG_MARK, sqlAddMsg( query->state,
				"Database connection isn't working" ));
			osrfAppSessionPanic( ctx->session );
		}
		return -1;
	} else {
		osrfAppRespondComplete( ctx, col_list );
		return 0;
	}
}
Пример #25
0
static int oilsAuthInitBarcodeHandler(
    osrfMethodContext* ctx, const char* barcode, const char* nonce) {

    osrfLogInfo(OSRF_LOG_MARK, 
        "User logging in with barcode %s", barcode);

    int user_id = -1;
    jsonObject* resp = NULL; // free
    jsonObject* user_obj = oilsUtilsFetchUserByBarcode(ctx, barcode); // free

    if (user_obj && user_obj->type != JSON_NULL) 
        user_id = oilsFMGetObjectId(user_obj);

    jsonObjectFree(user_obj); // NULL OK

    char* seed = oilsAuthBuildInitCache(user_id, barcode, "barcode", nonce);
    resp = jsonNewObject(seed);
    free(seed);

    osrfAppRespondComplete(ctx, resp);
    jsonObjectFree(resp);
    return 0;
}
Пример #26
0
/**
	@brief Return a list of previously generated error messages for a specified query.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Method parameters:
	- query token, as previously returned by the .prepare method.

	Returns: A (possibly empty) array of strings, each one an error message generated during
	previous operations in connection with the specified query.
*/
int doMessages( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query token from a method parameter
	const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( token_obj->type != JSON_STRING ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query token must be a string" );
		return -1;
	}
	const char* token = jsonObjectGetString( token_obj );

	// Look up the query token in the session-level userData
	CachedQuery* query = search_token( ctx, token );
	if( !query ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid query token" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Returning messages for token %s", token );

	jsonObject* msgs = jsonNewObjectType( JSON_ARRAY );
	const osrfStringArray* error_msgs = query->state->error_msgs;
	int i;
	for( i = 0; i < error_msgs->size; ++i ) {
		jsonObject* msg = jsonNewObject( osrfStringArrayGetString( error_msgs, i ));
		jsonObjectPush( msgs, msg );
	}

	osrfAppRespondComplete( ctx, msgs );
	return 0;
}
Пример #27
0
osrfHash* oilsIDLInit( const char* idl_filename ) {

	if (idlHash) return idlHash;

	char* prop_str = NULL;

	idlHash = osrfNewHash();
	osrfHash* class_def_hash = NULL;

	osrfLogInfo(OSRF_LOG_MARK, "Parsing the IDL XML...");
	idlDoc = xmlReadFile( idl_filename, NULL, XML_PARSE_XINCLUDE );
	
	if (!idlDoc) {
		osrfLogError(OSRF_LOG_MARK, "Could not load or parse the IDL XML file!");
		return NULL;
	}

	osrfLogDebug(OSRF_LOG_MARK, "Initializing the Fieldmapper IDL...");

	xmlNodePtr docRoot = xmlDocGetRootElement(idlDoc);
	xmlNodePtr kid = docRoot->children;
	while (kid) {
		if (!strcmp( (char*)kid->name, "class" )) {

			class_def_hash = osrfNewHash();
			char* current_class_name = (char*) xmlGetProp(kid, BAD_CAST "id");
			
			osrfHashSet( class_def_hash, current_class_name, "classname" );
			osrfHashSet( class_def_hash, xmlGetNsProp(kid, BAD_CAST "fieldmapper", BAD_CAST OBJECT_NS), "fieldmapper" );
			osrfHashSet( class_def_hash, xmlGetNsProp(kid, BAD_CAST "readonly", BAD_CAST PERSIST_NS), "readonly" );

			osrfHashSet( idlHash, class_def_hash, current_class_name );

			if ((prop_str = (char*)xmlGetNsProp(kid, BAD_CAST "tablename", BAD_CAST PERSIST_NS))) {
				osrfLogDebug(OSRF_LOG_MARK, "Using table '%s' for class %s", prop_str, current_class_name );
				osrfHashSet(
					class_def_hash,
					prop_str,
					"tablename"
				);
			}

			if ((prop_str = (char*)xmlGetNsProp(kid, BAD_CAST "restrict_primary", BAD_CAST PERSIST_NS))) {
				osrfLogDebug(OSRF_LOG_MARK, "Delete restriction policy set at '%s' for pkey of class %s", prop_str, current_class_name );
				osrfHashSet(
					class_def_hash,
					prop_str,
					"restrict_primary"
				);
			}

			if ((prop_str = (char*)xmlGetNsProp(kid, BAD_CAST "virtual", BAD_CAST PERSIST_NS))) {
				osrfHashSet(
					class_def_hash,
					prop_str,
					"virtual"
				);
			}

			// Tokenize controller attribute into an osrfStringArray
			prop_str = (char*) xmlGetProp(kid, BAD_CAST "controller");
			if( prop_str )
				osrfLogDebug(OSRF_LOG_MARK, "Controller list is %s", prop_str );
			osrfStringArray* controller = osrfStringArrayTokenize( prop_str, ' ' );
			xmlFree( prop_str );
			osrfHashSet( class_def_hash, controller, "controller");

			osrfHash* current_links_hash = osrfNewHash();
			osrfHash* current_fields_hash = osrfNewHash();

			osrfHashSet( class_def_hash, current_fields_hash, "fields" );
			osrfHashSet( class_def_hash, current_links_hash, "links" );

			xmlNodePtr _cur = kid->children;

			while (_cur) {

				if (!strcmp( (char*)_cur->name, "fields" )) {

					if( (prop_str = (char*)xmlGetNsProp(_cur, BAD_CAST "primary", BAD_CAST PERSIST_NS)) ) {
						osrfHashSet(
							class_def_hash,
							prop_str,
							"primarykey"
						);
					}

					if( (prop_str = (char*)xmlGetNsProp(_cur, BAD_CAST "sequence", BAD_CAST PERSIST_NS)) ) {
						osrfHashSet(
							class_def_hash,
							prop_str,
							"sequence"
						);
					}

					unsigned int array_pos = 0;
					char array_pos_buf[ 7 ];  // For up to 1,000,000 fields per class

					xmlNodePtr _f = _cur->children;
					while(_f) {
						if (strcmp( (char*)_f->name, "field" )) {
							_f = _f->next;
							continue;
						}

						// Get the field name.  If it's one of the three standard
						// fields that we always generate, ignore it.
						char* field_name = (char*)xmlGetProp(_f, BAD_CAST "name");
						if( field_name ) {
							osrfLogDebug(OSRF_LOG_MARK, 
									"Found field %s for class %s", field_name, current_class_name );
							if(    !strcmp( field_name, "isnew" )
								|| !strcmp( field_name, "ischanged" )
								|| !strcmp( field_name, "isdeleted" ) ) {
								free( field_name );
								_f = _f->next;
								continue;
							}
						} else {
							osrfLogDebug(OSRF_LOG_MARK,
									"Found field with no name for class %s", current_class_name );
							_f = _f->next;
							continue;
						}
 
						osrfHash* field_def_hash = osrfNewHash();

						// Insert array_position
						snprintf( array_pos_buf, sizeof( array_pos_buf ), "%u", array_pos++ );
						osrfHashSet( field_def_hash, strdup( array_pos_buf ), "array_position" );

						if( (prop_str = (char*)xmlGetNsProp(_f, BAD_CAST "i18n", BAD_CAST PERSIST_NS)) ) {
							osrfHashSet(
								field_def_hash,
								prop_str,
								"i18n"
							);
						}

						if( (prop_str = (char*)xmlGetNsProp(_f, BAD_CAST "virtual", BAD_CAST PERSIST_NS)) ) {
							osrfHashSet(
								field_def_hash,
								prop_str,
								"virtual"
							);
						} else {   // default to virtual
							osrfHashSet(
								field_def_hash,
								"false",
								"virtual"
							);
						}

						if( (prop_str = (char*)xmlGetNsProp(_f, BAD_CAST "primitive", BAD_CAST PERSIST_NS)) ) {
							osrfHashSet(
								field_def_hash,
								prop_str,
								"primitive"
							);
						}

						osrfHashSet( field_def_hash, field_name, "name" );
						osrfHashSet(
							current_fields_hash,
							field_def_hash,
							field_name
						);
						_f = _f->next;
					}

					// Create three standard, stereotyped virtual fields for every class
					add_std_fld( current_fields_hash, "isnew",     array_pos++ );
					add_std_fld( current_fields_hash, "ischanged", array_pos++ );
					add_std_fld( current_fields_hash, "isdeleted", array_pos   );

				}

				if (!strcmp( (char*)_cur->name, "links" )) {
					xmlNodePtr _l = _cur->children;

					while(_l) {
						if (strcmp( (char*)_l->name, "link" )) {
							_l = _l->next;
							continue;
						}

						osrfHash* link_def_hash = osrfNewHash();

						if( (prop_str = (char*)xmlGetProp(_l, BAD_CAST "reltype")) ) {
							osrfHashSet(
								link_def_hash,
								prop_str,
								"reltype"
							);
							osrfLogDebug(OSRF_LOG_MARK, "Adding link with reltype %s", prop_str );
						} else
							osrfLogDebug(OSRF_LOG_MARK, "Adding link with no reltype" );

						if( (prop_str = (char*)xmlGetProp(_l, BAD_CAST "key")) ) {
							osrfHashSet(
								link_def_hash,
								prop_str,
								"key"
							);
							osrfLogDebug(OSRF_LOG_MARK, "Link fkey is %s", prop_str );
						} else
							osrfLogDebug(OSRF_LOG_MARK, "Link with no fkey" );

						if( (prop_str = (char*)xmlGetProp(_l, BAD_CAST "class")) ) {
							osrfHashSet(
								link_def_hash,
								prop_str,
								"class"
							);
							osrfLogDebug(OSRF_LOG_MARK, "Link fclass is %s", prop_str );
						} else
							osrfLogDebug(OSRF_LOG_MARK, "Link with no fclass" );

						// Tokenize map attribute into an osrfStringArray
						prop_str = (char*) xmlGetProp(_l, BAD_CAST "map");
						if( prop_str )
							osrfLogDebug(OSRF_LOG_MARK, "Link mapping list is %s", prop_str );
						osrfStringArray* map = osrfStringArrayTokenize( prop_str, ' ' );
						osrfHashSet( link_def_hash, map, "map");
						xmlFree( prop_str );

						if( (prop_str = (char*)xmlGetProp(_l, BAD_CAST "field")) ) {
							osrfHashSet(
								link_def_hash,
								prop_str,
								"field"
							);
							osrfLogDebug(OSRF_LOG_MARK, "Link fclass is %s", prop_str );
						} else
							osrfLogDebug(OSRF_LOG_MARK, "Link with no fclass" );

						osrfHashSet(
							current_links_hash,
							link_def_hash,
							prop_str
						);

						_l = _l->next;
					}
				}
/**** Structure of permacrud in memory ****

{ create :
    { permission : [ x, y, z ],
      global_required : "true", -- anything else, or missing, is false
      local_context : [ f1, f2 ],
      foreign_context : { class1 : { fkey : local_class_key, field : class1_field, context : [ a, b, c ] }, ...}
    },
  retrieve : null, -- no perm check, or structure similar to the others
  update : -- like create
    ...
  delete : -- like create
    ...
}   

**** Structure of permacrud in memory ****/

				if (!strcmp( (char*)_cur->name, "permacrud" )) {
					osrfHash* pcrud = osrfNewHash();
					osrfHashSet( class_def_hash, pcrud, "permacrud" );
					xmlNodePtr _l = _cur->children;

					while(_l) {
						if (strcmp( (char*)_l->name, "actions" )) {
							_l = _l->next;
							continue;
						}

						xmlNodePtr _a = _l->children;

						while(_a) {
							const char* action_name = (const char*) _a->name;
							if (
								strcmp( action_name, "create" ) &&
								strcmp( action_name, "retrieve" ) &&
								strcmp( action_name, "update" ) &&
								strcmp( action_name, "delete" )
							) {
								_a = _a->next;
								continue;
							}

							osrfLogDebug(OSRF_LOG_MARK, "Found Permacrud action %s for class %s",
								action_name, current_class_name );

							osrfHash* action_def_hash = osrfNewHash();
							osrfHashSet( pcrud, action_def_hash, action_name );

							// Tokenize permission attribute into an osrfStringArray
							prop_str = (char*) xmlGetProp(_a, BAD_CAST "permission");
							if( prop_str )
								osrfLogDebug(OSRF_LOG_MARK,
									"Permacrud permission list is %s", prop_str );
							osrfStringArray* map = osrfStringArrayTokenize( prop_str, ' ' );
							osrfHashSet( action_def_hash, map, "permission");
							xmlFree( prop_str );

					    	osrfHashSet( action_def_hash,
								(char*)xmlGetNoNsProp(_a, BAD_CAST "global_required"), "global_required");

							// Tokenize context_field attribute into an osrfStringArray
							prop_str = (char*) xmlGetProp(_a, BAD_CAST "context_field");
							if( prop_str )
								osrfLogDebug(OSRF_LOG_MARK,
									"Permacrud context_field list is %s", prop_str );
							map = osrfStringArrayTokenize( prop_str, ' ' );
							osrfHashSet( action_def_hash, map, "local_context");
							xmlFree( prop_str );

							osrfHash* foreign_context = osrfNewHash();
							osrfHashSet( action_def_hash, foreign_context, "foreign_context");

							xmlNodePtr _f = _a->children;

							while(_f) {
								if ( strcmp( (char*)_f->name, "context" ) ) {
									_f = _f->next;
									continue;
								}

								if( (prop_str = (char*)xmlGetNoNsProp(_f, BAD_CAST "link")) ) {
									osrfLogDebug(OSRF_LOG_MARK,
										"Permacrud context link definition is %s", prop_str );

									osrfHash* _tmp_fcontext = osrfNewHash();

									// Store pointers to elements already stored
									// from the <link> aggregate
									osrfHash* _flink = osrfHashGet( current_links_hash, prop_str );
									osrfHashSet( _tmp_fcontext, osrfHashGet(_flink, "field"), "fkey" );
									osrfHashSet( _tmp_fcontext, osrfHashGet(_flink, "key"), "field" );
									xmlFree( prop_str );

								    if( (prop_str = (char*)xmlGetNoNsProp(_f, BAD_CAST "jump")) )
									    osrfHashSet( _tmp_fcontext, osrfStringArrayTokenize( prop_str, '.' ), "jump" );
									xmlFree( prop_str );

									// Tokenize field attribute into an osrfStringArray
									char * field_list = (char*) xmlGetProp(_f, BAD_CAST "field");
									if( field_list )
										osrfLogDebug(OSRF_LOG_MARK,
											"Permacrud foreign context field list is %s", field_list );
									map = osrfStringArrayTokenize( field_list, ' ' );
									osrfHashSet( _tmp_fcontext, map, "context");
									xmlFree( field_list );

									// Insert the new hash into a hash attached to the parent node
									osrfHashSet( foreign_context, _tmp_fcontext, osrfHashGet( _flink, "class" ) );

								} else {

									if( (prop_str = (char*)xmlGetNoNsProp(_f, BAD_CAST "field") )) {
										char* map_list = prop_str;
										osrfLogDebug(OSRF_LOG_MARK,
											"Permacrud local context field list is %s", prop_str );
			
										if (strlen( map_list ) > 0) {
											char* st_tmp = NULL;
											char* _map_class = strtok_r(map_list, " ", &st_tmp);
											osrfStringArrayAdd(
												osrfHashGet( action_def_hash, "local_context"), _map_class);
									
											while ((_map_class = strtok_r(NULL, " ", &st_tmp))) {
												osrfStringArrayAdd(
													osrfHashGet( action_def_hash, "local_context"), _map_class);
											}
										}
										xmlFree(map_list);
									}

								}
								_f = _f->next;
							}
							_a = _a->next;
						}
						_l = _l->next;
					}
				}

				if (!strcmp( (char*)_cur->name, "source_definition" )) {
					char* content_str;
					if( (content_str = (char*)xmlNodeGetContent(_cur)) ) {
						osrfLogDebug(OSRF_LOG_MARK, "Using source definition '%s' for class %s",
							content_str, current_class_name );
						osrfHashSet(
							class_def_hash,
							content_str,
							"source_definition"
						);
					}

				}

				_cur = _cur->next;
			} // end while
		}

		kid = kid->next;
	} // end while

	osrfLogInfo(OSRF_LOG_MARK, "...IDL XML parsed");

	return idlHash;
}
Пример #28
0
/**
	@brief Respond to the beginning of an XML element.
	@param session Pointer to the transport_session, cast to a void pointer.
	@param name Name of the XML element.
	@param atts Pointer to a ragged array containing attributes and values.

	The XML parser calls this when it sees the beginning of an XML element.  We note what
	element it is by setting the corresponding switch in the state machine, and grab whatever
	attributes we expect to find.
*/
static void startElementHandler(
	void *session, const xmlChar *name, const xmlChar **atts) {

	transport_session* ses = (transport_session*) session;
	if( ! ses ) { return; }


	if( strcmp( (char*) name, "message" ) == 0 ) {
		ses->state_machine->in_message = 1;
		buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
		buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
		buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
		buffer_add( ses->osrf_xid_buffer, get_xml_attr( atts, "osrf_xid" ) );
		buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
		buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
		buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
		const char* broadcast = get_xml_attr( atts, "broadcast" );
		if( broadcast )
			ses->router_broadcast = atoi( broadcast );

		return;
	}

	if( ses->state_machine->in_message ) {

		if( strcmp( (char*) name, "body" ) == 0 ) {
			ses->state_machine->in_message_body = 1;
			return;
		}

		if( strcmp( (char*) name, "subject" ) == 0 ) {
			ses->state_machine->in_subject = 1;
			return;
		}

		if( strcmp( (char*) name, "thread" ) == 0 ) {
			ses->state_machine->in_thread = 1;
			return;
		}

	}

	if( strcmp( (char*) name, "presence" ) == 0 ) {
		ses->state_machine->in_presence = 1;
		buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
		buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
		return;
	}

	if( strcmp( (char*) name, "status" ) == 0 ) {
		ses->state_machine->in_status = 1;
		return;
	}


	if( strcmp( (char*) name, "stream:error" ) == 0 ) {
		ses->state_machine->in_error = 1;
		ses->state_machine->connected = 0;
		osrfLogWarning(  OSRF_LOG_MARK, "Received <stream:error> message from Jabber server" );
		return;
	}


	/* first server response from a connect attempt */
	if( strcmp( (char*) name, "stream:stream" ) == 0 ) {
		if( ses->state_machine->connecting == CONNECTING_1 ) {
			ses->state_machine->connecting = CONNECTING_2;
			buffer_add( ses->session_id, get_xml_attr(atts, "id") );
		}
		return;
	}

	if( strcmp( (char*) name, "handshake" ) == 0 ) {
		ses->state_machine->connected = 1;
		ses->state_machine->connecting = 0;
		return;
	}


	if( strcmp( (char*) name, "error" ) == 0 ) {
		ses->state_machine->in_message_error = 1;
		buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
		ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
		osrfLogInfo( OSRF_LOG_MARK, "Received <error> message with type %s and code %d",
			OSRF_BUFFER_C_STR( ses->message_error_type ), ses->message_error_code );
		return;
	}

	if( strcmp( (char*) name, "iq" ) == 0 ) {
		ses->state_machine->in_iq = 1;

		const char* type = get_xml_attr(atts, "type");

		if( strcmp( type, "result") == 0
				&& ses->state_machine->connecting == CONNECTING_2 ) {
			ses->state_machine->connected = 1;
			ses->state_machine->connecting = 0;
			return;
		}

		if( strcmp( type, "error") == 0 ) {
			osrfLogWarning( OSRF_LOG_MARK,  "Error connecting to jabber" );
			return;
		}
	}
}
Пример #29
0
/**
	@brief Implement the "complete" 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:
		- "username"
		- "barcode"
		- "password" (hashed with the cached seed; not plaintext)
		- "type"
		- "org"
		- "workstation"

	The password is required.  Either a username or a barcode must also be present.

	Return to client: Intermediate authentication seed.

	Validate the password, using the username if available, or the barcode if not.  The
	user must be active, and not barred from logging on.  The barcode, if used for
	authentication, must be active as well.  The workstation, if specified, must be valid.

	Upon deciding whether to allow the logon, return a corresponding event to the client.
*/
int oilsAuthComplete( osrfMethodContext* ctx ) {
	OSRF_METHOD_VERIFY_CONTEXT(ctx);

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

	const char* uname       = jsonObjectGetString(jsonObjectGetKeyConst(args, "username"));
	const char* password    = jsonObjectGetString(jsonObjectGetKeyConst(args, "password"));
	const char* type        = jsonObjectGetString(jsonObjectGetKeyConst(args, "type"));
	int orgloc        = (int) jsonObjectGetNumber(jsonObjectGetKeyConst(args, "org"));
	const char* workstation = jsonObjectGetString(jsonObjectGetKeyConst(args, "workstation"));
	const char* barcode     = jsonObjectGetString(jsonObjectGetKeyConst(args, "barcode"));

	const char* ws = (workstation) ? workstation : "";

	if( !type )
		 type = OILS_AUTH_STAFF;

	if( !( (uname || barcode) && password) ) {
		return osrfAppRequestRespondException( ctx->session, ctx->request,
			"username/barcode and password required for method: %s", ctx->method->name );
	}

	oilsEvent* response = NULL;
	jsonObject* userObj = NULL;
	int card_active     = 1;      // boolean; assume active until proven otherwise

	// Fetch a row from the actor.usr table, by username if available,
	// or by barcode if not.
	if(uname) {
		userObj = oilsUtilsFetchUserByUsername( uname );
		if( userObj && JSON_NULL == userObj->type ) {
			jsonObjectFree( userObj );
			userObj = NULL;         // username not found
		}
	}
	else if(barcode) {
		// Read from actor.card by barcode

		osrfLogInfo( OSRF_LOG_MARK, "Fetching user by barcode %s", barcode );

		jsonObject* params = jsonParseFmt("{\"barcode\":\"%s\"}", barcode);
		jsonObject* card = oilsUtilsQuickReq(
			"open-ils.cstore", "open-ils.cstore.direct.actor.card.search", params );
		jsonObjectFree( params );

		if( card && card->type != JSON_NULL ) {
			// Determine whether the card is active
			char* card_active_str = oilsFMGetString( card, "active" );
			card_active = oilsUtilsIsDBTrue( card_active_str );
			free( card_active_str );

			// Look up the user who owns the card
			char* userid = oilsFMGetString( card, "usr" );
			jsonObjectFree( card );
			params = jsonParseFmt( "[%s]", userid );
			free( userid );
			userObj = oilsUtilsQuickReq(
					"open-ils.cstore", "open-ils.cstore.direct.actor.user.retrieve", params );
			jsonObjectFree( params );
			if( userObj && JSON_NULL == userObj->type ) {
				// user not found (shouldn't happen, due to foreign key)
				jsonObjectFree( userObj );
				userObj = NULL;
			}
		}
	}

	if(!userObj) {
		response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_AUTH_FAILED );
		osrfLogInfo(OSRF_LOG_MARK,  "failed login: username=%s, barcode=%s, workstation=%s",
				uname, (barcode ? barcode : "(none)"), ws );
		osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
		oilsEventFree(response);
		return 0;           // No such user
	}

	// Such a user exists.  Now see if he or she has the right credentials.
	int passOK = -1;
	if(uname)
		passOK = oilsAuthVerifyPassword( ctx, userObj, uname, password );
	else if (barcode)
		passOK = oilsAuthVerifyPassword( ctx, userObj, barcode, password );

	if( passOK < 0 ) {
		jsonObjectFree(userObj);
		return passOK;
	}

	// See if the account is active
	char* active = oilsFMGetString(userObj, "active");
	if( !oilsUtilsIsDBTrue(active) ) {
		if( passOK )
			response = oilsNewEvent( OSRF_LOG_MARK, "PATRON_INACTIVE" );
		else
			response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_AUTH_FAILED );

		osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
		oilsEventFree(response);
		jsonObjectFree(userObj);
		free(active);
		return 0;
	}
	free(active);

	osrfLogInfo( OSRF_LOG_MARK, "Fetching card by barcode %s", barcode );

	if( !card_active ) {
		osrfLogInfo( OSRF_LOG_MARK, "barcode %s is not active, returning event", barcode );
		response = oilsNewEvent( OSRF_LOG_MARK, "PATRON_CARD_INACTIVE" );
		osrfAppRespondComplete( ctx, oilsEventToJSON( response ) );
		oilsEventFree( response );
		jsonObjectFree( userObj );
		return 0;
	}


	// See if the user is even allowed to log in
	if( oilsAuthCheckLoginPerm( ctx, userObj, type ) == -1 ) {
		jsonObjectFree(userObj);
		return 0;
	}

	// If a workstation is defined, add the workstation info
	if( workstation != NULL ) {
		osrfLogDebug(OSRF_LOG_MARK, "Workstation is %s", workstation);
		response = oilsAuthVerifyWorkstation( ctx, userObj, workstation );
		if(response) {
			jsonObjectFree(userObj);
			osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
			oilsEventFree(response);
			return 0;
		}

	} else {
		// Otherwise, use the home org as the workstation org on the user
		char* orgid = oilsFMGetString(userObj, "home_ou");
		oilsFMSetString(userObj, "ws_ou", orgid);
		free(orgid);
	}

	char* freeable_uname = NULL;
	if(!uname) {
		uname = freeable_uname = oilsFMGetString( userObj, "usrname" );
	}

	if( passOK ) {
		response = oilsAuthHandleLoginOK( userObj, uname, type, orgloc, workstation );

	} else {
		response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_AUTH_FAILED );
		osrfLogInfo(OSRF_LOG_MARK,  "failed login: username=%s, barcode=%s, workstation=%s",
				uname, (barcode ? barcode : "(none)"), ws );
	}

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

	if(freeable_uname)
		free(freeable_uname);

	return 0;
}
Пример #30
0
/**
	@brief Determine the login timeout.
	@param userObj Pointer to an object describing the user.
	@param type Pointer to one of four possible character strings identifying the login type.
	@param orgloc Org unit to use for settings lookups (negative or zero means unspecified)
	@return The length of the timeout, in seconds.

	The default timeout value comes from the configuration file, and depends on the
	login type.

	The default may be overridden by a corresponding org unit setting.  The @a orgloc
	parameter says what org unit to use for the lookup.  If @a orgloc <= 0, or if the
	lookup for @a orgloc yields no result, we look up the setting for the user's home org unit
	instead (except that if it's the same as @a orgloc we don't bother repeating the lookup).

	Whether defined in the config file or in an org unit setting, a timeout value may be
	expressed as a raw number (i.e. all digits, possibly with leading and/or trailing white
	space) or as an interval string to be translated into seconds by PostgreSQL.
*/
static long oilsAuthGetTimeout( const jsonObject* userObj, const char* type, int orgloc ) {

	if(!_oilsAuthOPACTimeout) { /* Load the default timeouts */

		jsonObject* value_obj;

		value_obj = osrf_settings_host_value_object(
			"/apps/open-ils.auth/app_settings/default_timeout/opac" );
		_oilsAuthOPACTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj ));
		jsonObjectFree(value_obj);
		if( -1 == _oilsAuthOPACTimeout ) {
			osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for OPAC logins" );
			_oilsAuthOPACTimeout = 0;
		}

		value_obj = osrf_settings_host_value_object(
			"/apps/open-ils.auth/app_settings/default_timeout/staff" );
		_oilsAuthStaffTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj ));
		jsonObjectFree(value_obj);
		if( -1 == _oilsAuthStaffTimeout ) {
			osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for staff logins" );
			_oilsAuthStaffTimeout = 0;
		}

		value_obj = osrf_settings_host_value_object(
			"/apps/open-ils.auth/app_settings/default_timeout/temp" );
		_oilsAuthOverrideTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj ));
		jsonObjectFree(value_obj);
		if( -1 == _oilsAuthOverrideTimeout ) {
			osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for temp logins" );
			_oilsAuthOverrideTimeout = 0;
		}

		value_obj = osrf_settings_host_value_object(
			"/apps/open-ils.auth/app_settings/default_timeout/persist" );
		_oilsAuthPersistTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj ));
		jsonObjectFree(value_obj);
		if( -1 == _oilsAuthPersistTimeout ) {
			osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for persist logins" );
			_oilsAuthPersistTimeout = 0;
		}

		osrfLogInfo(OSRF_LOG_MARK, "Set default auth timeouts: "
			"opac => %ld : staff => %ld : temp => %ld : persist => %ld",
			_oilsAuthOPACTimeout, _oilsAuthStaffTimeout,
			_oilsAuthOverrideTimeout, _oilsAuthPersistTimeout );
	}

	int home_ou = (int) jsonObjectGetNumber( oilsFMGetObject( userObj, "home_ou" ));
	if(orgloc < 1)
		orgloc = home_ou;

	char* setting = NULL;
	long default_timeout = 0;

	if( !strcmp( type, OILS_AUTH_OPAC )) {
		setting = OILS_ORG_SETTING_OPAC_TIMEOUT;
		default_timeout = _oilsAuthOPACTimeout;
	} else if( !strcmp( type, OILS_AUTH_STAFF )) {
		setting = OILS_ORG_SETTING_STAFF_TIMEOUT;
		default_timeout = _oilsAuthStaffTimeout;
	} else if( !strcmp( type, OILS_AUTH_TEMP )) {
		setting = OILS_ORG_SETTING_TEMP_TIMEOUT;
		default_timeout = _oilsAuthOverrideTimeout;
	} else if( !strcmp( type, OILS_AUTH_PERSIST )) {
		setting = OILS_ORG_SETTING_PERSIST_TIMEOUT;
		default_timeout = _oilsAuthPersistTimeout;
	}

	// Get the org unit setting, if there is one.
	char* timeout = oilsUtilsFetchOrgSetting( orgloc, setting );
	if(!timeout) {
		if( orgloc != home_ou ) {
			osrfLogDebug(OSRF_LOG_MARK, "Auth timeout not defined for org %d, "
				"trying home_ou %d", orgloc, home_ou );
			timeout = oilsUtilsFetchOrgSetting( home_ou, setting );
		}
	}

	if(!timeout)
		return default_timeout;   // No override from org unit setting

	// Translate the org unit setting to a number
	long t;
	if( !*timeout ) {
		osrfLogWarning( OSRF_LOG_MARK,
			"Timeout org unit setting is an empty string for %s login; using default",
			timeout, type );
		t = default_timeout;
	} else {
		// Treat timeout string as an interval, and convert it to seconds
		t = oilsUtilsIntervalToSeconds( timeout );
		if( -1 == t ) {
			// Unable to convert; possibly an invalid interval string
			osrfLogError( OSRF_LOG_MARK,
				"Unable to convert timeout interval \"%s\" for %s login; using default",
				timeout, type );
			t = default_timeout;
		}
	}

	free(timeout);
	return t;
}