Exemple #1
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;
}
Exemple #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;
}
Exemple #3
0
jsonObject* oilsUtilsFetchWorkstationByName( const char* name ) {
	jsonObject* p = jsonParseFmt("{\"name\":\"%s\"}", name);
	jsonObject* r = oilsUtilsCStoreReq(
		"open-ils.cstore.direct.actor.workstation.search", p);
	jsonObjectFree(p);
	return r;
}
Exemple #4
0
// free() response
static char* oilsAuthGetSalt(int user_id) {
    char* salt_str = NULL;

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

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

    jsonObjectFree(params);

    if (salt_obj) {

        if (salt_obj->type != JSON_NULL) {

            const char* salt_val = jsonObjectGetString(
                jsonObjectGetKeyConst(salt_obj, "actor.get_salt"));

            // caller expects a free-able string, could be NULL.
            if (salt_val) { salt_str = strdup(salt_val); } 
        }

        jsonObjectFree(salt_obj);
    }

    return salt_str;
}
Exemple #5
0
int osrfMathRun( osrfMethodContext* ctx ) {
	if( osrfMethodVerifyContext( ctx ) ) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	/* collect the request params */
	const jsonObject* x = jsonObjectGetIndex(ctx->params, 0);
	const jsonObject* y = jsonObjectGetIndex(ctx->params, 1);

	if( x && y ) {

		/* pull out the params as strings since they may be either
			strings or numbers depending on the client */
		char* a = jsonObjectToSimpleString(x);
		char* b = jsonObjectToSimpleString(y);

		if( a && b ) {

			osrfLogActivity( OSRF_LOG_MARK, "Running opensrf.math %s [ %s : %s ]",
					ctx->method->name, a, b );

			/* construct a new params object to send to dbmath */
			jsonObject* newParams = jsonParseFmt( "[ %s, %s ]", a, b );
			free(a); free(b);

			/* connect to db math */
			osrfAppSession* ses = osrfAppSessionClientInit("opensrf.dbmath");

			/* forcing an explicit connect allows us to talk to one worker backend
			 * regardless of "stateful" config settings for the server
			 * This buys us nothing here since we're only sending one request...
			 * */
			/*osrfAppSessionConnect(ses);*/

			/* dbmath uses the same method names that math does */
			int req_id = osrfAppSessionSendRequest( ses, newParams, ctx->method->name, 1 );
			osrfMessage* omsg = osrfAppSessionRequestRecv( ses, req_id, 60 );
			jsonObjectFree(newParams);

			if(omsg) {
				/* return dbmath's response to the user */
				osrfAppRespondComplete( ctx, osrfMessageGetResult(omsg) );
				osrfMessageFree(omsg);
				osrfAppSessionFree(ses);
				return 0;
			}

			osrfAppSessionFree(ses);
		}
		else {
			if(a)
				free(a);
			if(b)
				free(b);
		}
	}

	return -1;
}
Exemple #6
0
jsonObject* oilsUtilsFetchWorkstation( long id ) {
	jsonObject* p = jsonParseFmt("[%ld]", id);
	jsonObject* r = oilsUtilsQuickReq(
		"open-ils.storage",
		"open-ils.storage.direct.actor.workstation.retrieve", p );
	jsonObjectFree(p);
	return r;
}
Exemple #7
0
/**
	@brief Given a username, fetch the corresponding row from the actor.usr table, if any.
	@param name The username 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.

	The calling code is responsible for freeing the returned object by calling jsonObjectFree().
*/
jsonObject* oilsUtilsFetchUserByUsername( const char* name ) {
	if(!name) return NULL;
	jsonObject* params = jsonParseFmt("{\"usrname\":\"%s\"}", name);
	jsonObject* user = oilsUtilsQuickReq(
		"open-ils.cstore", "open-ils.cstore.direct.actor.user.search", params );

	jsonObjectFree(params);
	long id = oilsFMGetObjectId(user);
	osrfLogDebug(OSRF_LOG_MARK, "Fetched user %s:%ld", name, id);
	return user;
}
Exemple #8
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;
}
Exemple #9
0
char* oilsUtilsLogin( const char* uname, const char* passwd, const char* type, int orgId ) {
	if(!(uname && passwd)) return NULL;

	osrfLogDebug(OSRF_LOG_MARK, "Logging in with username %s", uname );
	char* token = NULL;

	jsonObject* params = jsonParseFmt("[\"%s\"]", uname);

	jsonObject* o = oilsUtilsQuickReq(
		"open-ils.auth", "open-ils.auth.authenticate.init", params );

	const char* seed = jsonObjectGetString(o);
	char* passhash = md5sum(passwd);
	char buf[256];
	snprintf(buf, sizeof(buf), "%s%s", seed, passhash);
	char* fullhash = md5sum(buf);

	jsonObjectFree(o);
	jsonObjectFree(params);
	free(passhash);

	params = jsonParseFmt( "[\"%s\", \"%s\", \"%s\", \"%d\"]", uname, fullhash, type, orgId );
	o = oilsUtilsQuickReq( "open-ils.auth",
		"open-ils.auth.authenticate.complete", params );

	if(o) {
		const char* tok = jsonObjectGetString(
			jsonObjectGetKeyConst( jsonObjectGetKey( o,"payload" ), "authtoken" ));
		if( tok )
			token = strdup( tok );
	}

	free(fullhash);
	jsonObjectFree(params);
	jsonObjectFree(o);

	return token;
}
Exemple #10
0
char* oilsUtilsFetchOrgSetting( int orgid, const char* setting ) {
	if(!setting) return NULL;

	jsonObject* params = jsonParseFmt("[%d, \"%s\"]", orgid, setting );

	jsonObject* set = oilsUtilsQuickReq(
		"open-ils.actor",
		"open-ils.actor.ou_setting.ancestor_default", params);

	char* value = jsonObjectToSimpleString( jsonObjectGetKeyConst( set, "value" ));
	jsonObjectFree(params);
	jsonObjectFree(set);
	osrfLogDebug(OSRF_LOG_MARK, "Fetched org [%d] setting: %s => %s", orgid, setting, value);
	return value;
}
Exemple #11
0
// ident is either a username or barcode
// Returns the init seed -> requires free();
static char* oilsAuthBuildInitCache(
    int user_id, const char* ident, const char* ident_type, const char* nonce) {

    char* cache_key  = va_list_to_string(
        "%s%s%s", OILS_AUTH_CACHE_PRFX, ident, nonce);

    char* count_key = va_list_to_string(
        "%s%s%s", OILS_AUTH_CACHE_PRFX, ident, OILS_AUTH_COUNT_SFFX);

    char* auth_seed;
    if (user_id == -1) {
        // user does not exist.  Use a dummy seed
        auth_seed = strdup("x");
    } else {
        auth_seed = oilsAuthGetSalt(user_id);
    }

    jsonObject* seed_object = jsonParseFmt(
        "{\"%s\":\"%s\",\"user_id\":%d,\"seed\":\"%s\"}",
        ident_type, ident, user_id, auth_seed);

    jsonObject* count_object = osrfCacheGetObject(count_key);
    if(!count_object) {
        count_object = jsonNewNumberObject((double) 0);
    }

    osrfCachePutObject(cache_key, seed_object, _oilsAuthSeedTimeout);

    if (user_id != -1) {
        // Only track login counts for existing users, since a 
        // login for a nonexistent user will never succeed anyway.
        osrfCachePutObject(count_key, count_object, _oilsAuthBlockTimeout);
    }

    osrfLogDebug(OSRF_LOG_MARK, 
        "oilsAuthInit(): has seed %s and key %s", auth_seed, cache_key);

    free(cache_key);
    free(count_key);
    jsonObjectFree(count_object);
    jsonObjectFree(seed_object);

    return auth_seed;
}
Exemple #12
0
oilsEvent* oilsUtilsCheckPerms( int userid, int orgid, char* permissions[], int size ) {
    if (!permissions) return NULL;
    int i;
    oilsEvent* evt = NULL;

    // Find the root org unit, i.e. the one with no parent.
    // Assumption: there is only one org unit with no parent.
    if (orgid == -1) {
        jsonObject* where_clause = jsonParse( "{\"parent_ou\":null}" );
        jsonObject* org = oilsUtilsQuickReq(
                              "open-ils.cstore",
                              "open-ils.cstore.direct.actor.org_unit.search",
                              where_clause
                          );
        jsonObjectFree( where_clause );

        orgid = (int)jsonObjectGetNumber( oilsFMGetObject( org, "id" ) );

        jsonObjectFree(org);
    }

    for( i = 0; i < size && permissions[i]; i++ ) {

        char* perm = permissions[i];
        jsonObject* params = jsonParseFmt("[%d, \"%s\", %d]", userid, perm, orgid);
        jsonObject* o = oilsUtilsQuickReq( "open-ils.storage",
                                           "open-ils.storage.permission.user_has_perm", params );

        char* r = jsonObjectToSimpleString(o);

        if(r && !strcmp(r, "0"))
            evt = oilsNewEvent3( OSRF_LOG_MARK, OILS_EVENT_PERM_FAILURE, perm, orgid );

        jsonObjectFree(params);
        jsonObjectFree(o);
        free(r);

        if(evt)
            break;
    }

    return evt;
}
Exemple #13
0
int oilsUtilsTrackUserActivity(long usr, const char* ewho, const char* ewhat, const char* ehow) {
    if (!usr && !(ewho || ewhat || ehow)) return 0;
    int rowcount = 0;

    jsonObject* params = jsonParseFmt(
        "{\"from\":[\"actor.insert_usr_activity\", %ld, \"%s\", \"%s\", \"%s\"]}",
        usr, 
        (NULL == ewho)  ? "" : ewho, 
        (NULL == ewhat) ? "" : ewhat, 
        (NULL == ehow)  ? "" : ehow
    );

	osrfAppSession* session = osrfAppSessionClientInit("open-ils.cstore");
    osrfAppSessionConnect(session);
    int reqid = osrfAppSessionSendRequest(session, NULL, "open-ils.cstore.transaction.begin", 1);
	osrfMessage* omsg = osrfAppSessionRequestRecv(session, reqid, 60);

    if(omsg) {
        osrfMessageFree(omsg);
        reqid = osrfAppSessionSendRequest(session, params, "open-ils.cstore.json_query", 1);
	    omsg = osrfAppSessionRequestRecv(session, reqid, 60);

        if(omsg) {
            const jsonObject* rows = osrfMessageGetResult(omsg);
            if (rows) rowcount = rows->size;
            osrfMessageFree(omsg); // frees rows
            if (rowcount) {
                reqid = osrfAppSessionSendRequest(session, NULL, "open-ils.cstore.transaction.commit", 1);
	            omsg = osrfAppSessionRequestRecv(session, reqid, 60);
                osrfMessageFree(omsg);
            } else {
                reqid = osrfAppSessionSendRequest(session, NULL, "open-ils.cstore.transaction.rollback", 1);
	            omsg = osrfAppSessionRequestRecv(session, reqid, 60);
                osrfMessageFree(omsg);
            }
        }
    }

    osrfAppSessionFree(session); // calls disconnect internally
    jsonObjectFree(params);
    return rowcount;
}
Exemple #14
0
oilsEvent* oilsUtilsCheckPerms( int userid, int orgid, char* permissions[], int size ) {
    if (!permissions) return NULL;
    int i;

    // Check perms against the root org unit if no org unit is provided.
    if (orgid == -1)
        orgid = oilsUtilsGetRootOrgId();

    for( i = 0; i < size && permissions[i]; i++ ) {
        oilsEvent* evt = NULL;
        char* perm = permissions[i];

        jsonObject* params = jsonParseFmt(
            "{\"from\":[\"permission.usr_has_perm\",\"%d\",\"%s\",\"%d\"]}",
            userid, perm, orgid
        );

        // Execute the query
        jsonObject* result = oilsUtilsCStoreReq(
            "open-ils.cstore.json_query", params);

        const jsonObject* hasPermStr = 
            jsonObjectGetKeyConst(result, "permission.usr_has_perm");

        if (!oilsUtilsIsDBTrue(jsonObjectGetString(hasPermStr))) {
            evt = oilsNewEvent3(
                OSRF_LOG_MARK, OILS_EVENT_PERM_FAILURE, perm, orgid);
        }

        jsonObjectFree(params);
        jsonObjectFree(result);

        // return first failed permission check.
        if (evt) return evt;
    }

    return NULL; // all perm checks succeeded
}
Exemple #15
0
/**
	@brief Convert a string to a number representing a time interval in seconds.
	@param interval Pointer to string, e.g. "420" or "2 weeks".
	@return If successful, the number of seconds that the string represents; otherwise -1.

	If the string is all digits (apart from any leading or trailing white space), convert
	it directly.  Otherwise pass it to PostgreSQL for translation.

	The result is the same as if we were to pass every string to PostgreSQL, except that,
	depending on the value of LONG_MAX, we return values for some strings that represent
	intervals too long for PostgreSQL to represent (i.e. more than 2147483647 seconds).

	WARNING: a valid interval of -1 second will be indistinguishable from an error.  If
	such an interval is a plausible possibility, don't use this function.
*/
long oilsUtilsIntervalToSeconds( const char* s ) {

	if( !s ) {
		osrfLogWarning( OSRF_LOG_MARK, "String to be converted is NULL" );
		return -1;
	}

	// Skip leading white space
	while( isspace( (unsigned char) *s ))
		++s;

	if( '\0' == *s ) {
		osrfLogWarning( OSRF_LOG_MARK, "String to be converted is empty or all white space" );
		return -1;
	}

	// See if the string is a raw number, i.e. all digits
	// (apart from any leading or trailing white space)

	const char* p = s;   // For traversing and examining the remaining string
	if( isdigit( (unsigned char) *p )) {
		// Looks like a number so far...skip over the digits
		do {
			++p;
		} while( isdigit( (unsigned char) *p ));
		// Skip over any following white space
		while( isspace( (unsigned char) *p ))
			++p;
		if( '\0' == *p ) {
			// This string is a raw number.  Convert it directly.
			long n = strtol( s, NULL, 10 );
			if( LONG_MAX == n ) {
				// numeric overflow
				osrfLogWarning( OSRF_LOG_MARK,
					"String \"%s\"represents a number too big for a long", s );
				return -1;
			} else
				return n;
		}
	}

	// If we get to this point, the string is not all digits.  Pass it to PostgreSQL.

	// Build the query
	jsonObject* query_obj = jsonParseFmt(
		"{\"from\":[\"config.interval_to_seconds\",\"%s\"]}", s );

	// Execute the query
	jsonObject* result = oilsUtilsCStoreReq(
		"open-ils.cstore.json_query", query_obj );
	jsonObjectFree( query_obj );

	// Get the results
	const jsonObject* seconds_obj = jsonObjectGetKeyConst( result, "config.interval_to_seconds" );
	long seconds = -1;
	if( seconds_obj && JSON_NUMBER == seconds_obj->type )
		seconds = (long) jsonObjectGetNumber( seconds_obj );
	else
		osrfLogError( OSRF_LOG_MARK,
			"Error calling json_query to convert \"%s\" to seconds", s );

	jsonObjectFree( result );
	return seconds;
}
int oilsAuthInternalValidate(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* barcode     = jsonObjectGetString(jsonObjectGetKeyConst(args, "barcode"));
    const char* login_type  = jsonObjectGetString(jsonObjectGetKeyConst(args, "login_type"));
    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;
    jsonObject *userObj = NULL, *params = NULL;
    char* tmp_str = NULL;
    int user_exists = 0, user_active = 0, 
        user_barred = 0, user_deleted = 0;

    // Confirm user exists, active=true, barred=false, deleted=false
    params = jsonNewNumberStringObject(user_id);
    userObj = oilsUtilsCStoreReqCtx(
        ctx, "open-ils.cstore.direct.actor.user.retrieve", params);
    jsonObjectFree(params);

    if (userObj && userObj->type != JSON_NULL) {
        user_exists = 1;

        tmp_str = oilsFMGetString(userObj, "active");
        user_active = oilsUtilsIsDBTrue(tmp_str);
        free(tmp_str);

        tmp_str = oilsFMGetString(userObj, "barred");
        user_barred = oilsUtilsIsDBTrue(tmp_str);
        free(tmp_str);

        tmp_str = oilsFMGetString(userObj, "deleted");
        user_deleted = oilsUtilsIsDBTrue(tmp_str);
        free(tmp_str);
    }

    if (!user_exists || user_barred || user_deleted) {
        response = oilsNewEvent(OILS_LOG_MARK_SAFE, OILS_EVENT_AUTH_FAILED);
    }

    if (!response && !user_active) {
        // In some cases, it's useful for the caller to know if the
        // patron was unable to login becuase the account is inactive.
        // Return a specific event for this.
        response = oilsNewEvent(OILS_LOG_MARK_SAFE, "PATRON_INACTIVE");
    }

    if (!response && barcode) {
        // Caller provided a barcode.  Ensure it exists and is active.

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

        if (card && card->type != JSON_NULL) {
            tmp_str = oilsFMGetString(card, "active");
            card_ok = oilsUtilsIsDBTrue(tmp_str);
            free(tmp_str);
        }

        jsonObjectFree(card); // card=NULL OK here.

        if (!card_ok) {
            response = oilsNewEvent(
                OILS_LOG_MARK_SAFE, "PATRON_CARD_INACTIVE");
        }
    }

    // XXX: login permission checks are always global (see 
    // oilsAuthCheckLoginPerm()).  No need to extract the 
    // workstation org unit here.

    if (!response) { // Still OK
        // Confirm user has permission to login w/ the requested type.
        response = oilsAuthCheckLoginPerm(
            ctx, atoi(user_id), org_unit, login_type);
    }


    if (!response) {
        // No tests failed.  Return SUCCESS.
        response = oilsNewEvent(OSRF_LOG_MARK, OILS_EVENT_SUCCESS);
    }


    jsonObjectFree(userObj); // userObj=NULL OK here.
    osrfAppRespondComplete(ctx, oilsEventToJSON(response));
    oilsEventFree(response);

    return 0;
}
Exemple #17
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;
}
/**
    @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;
}