Ejemplo n.º 1
0
static int test_json_query( const char* json_query ) {

	jsonObject* hash = jsonParse( json_query );
	if( !hash ) {
		fprintf( stderr, "Invalid JSON\n" );
		return -1;
	}

	int flags = 0;

	if ( obj_is_true( jsonObjectGetKeyConst( hash, "distinct" )))
		flags |= SELECT_DISTINCT;

	if ( obj_is_true( jsonObjectGetKeyConst( hash, "no_i18n" )))
		flags |= DISABLE_I18N;

	char* sql_query = buildQuery( NULL, hash, flags );

	if ( !sql_query ) {
		fprintf( stderr, "Invalid query\n" );
		return -1;
	}
	else
		printf( "%s\n", sql_query );

	free( sql_query );
	jsonObjectFree( hash );
	return 0;
}
Ejemplo n.º 2
0
/**
 * Determines the correct recipient address based on the requested 
 * service or recipient address.  
 */
static int osrfHttpTranslatorSetTo(osrfHttpTranslator* trans) {
    int stat = 0;
    jsonObject* sessionCache = NULL;

    if(trans->service) {
        if(trans->recipient) {
            osrfLogError(OSRF_LOG_MARK, "Specifying both SERVICE and TO are not allowed");

        } else {
            // service is specified, build a recipient address 
            // from the router, domain, and service
            int size = snprintf(recipientBuf, 128, "%s@%s/%s", routerName,
                domainName, trans->service);
            recipientBuf[size] = '\0';
            osrfLogDebug(OSRF_LOG_MARK, "Set recipient to %s", recipientBuf);
            trans->recipient = recipientBuf;
            stat = 1;
        }

    } else {

        if(trans->recipient) {
            sessionCache = osrfCacheGetObject(trans->thread);

            if(sessionCache) {
                const char* ipAddr = jsonObjectGetString(
                    jsonObjectGetKeyConst( sessionCache, "ip" ));
                const char* recipient = jsonObjectGetString(
                    jsonObjectGetKeyConst( sessionCache, "jid" ));

                // choosing a specific recipient address requires that the recipient and 
                // thread be cached on the server (so drone processes cannot be hijacked)
                if(!strcmp(ipAddr, trans->remoteHost) && !strcmp(recipient, trans->recipient)) {
                    osrfLogDebug( OSRF_LOG_MARK,
                        "Found cached session from host %s and recipient %s",
                        trans->remoteHost, trans->recipient);
                    stat = 1;
                    trans->service = apr_pstrdup(
                        trans->apreq->pool, jsonObjectGetString(
                            jsonObjectGetKeyConst( sessionCache, "service" )));

                } else {
                    osrfLogError(OSRF_LOG_MARK, 
                        "Session cache for thread %s does not match request", trans->thread);
                }
            }  else {
                osrfLogError(OSRF_LOG_MARK, 
                    "attempt to send directly to %s without a session", trans->recipient);
            }
        } else {
            osrfLogError(OSRF_LOG_MARK, "No SERVICE or RECIPIENT defined");
        } 
    }

    jsonObjectFree(sessionCache);
    return stat;
}
Ejemplo n.º 3
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;
}
Ejemplo n.º 4
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( osrfMethodContext* ctx, jsonObject* userObj, const char* uname,
		const char* type, int orgloc, const char* workstation ) {

	oilsEvent* response = NULL;

    jsonObject* params = jsonNewObject(NULL);
    jsonObjectSetKey(params, "user_id", 
        jsonNewNumberObject(oilsFMGetObjectId(userObj)));
    jsonObjectSetKey(params,"org_unit", jsonNewNumberObject(orgloc));
    jsonObjectSetKey(params, "login_type", jsonNewObject(type));
    if (workstation) 
        jsonObjectSetKey(params, "workstation", jsonNewObject(workstation));

    jsonObject* authEvt = oilsUtilsQuickReqCtx(
        ctx,
        "open-ils.auth_internal",
        "open-ils.auth_internal.session.create", params);
    jsonObjectFree(params);

    if (authEvt) {

        response = oilsNewEvent2(
            OSRF_LOG_MARK, 
            jsonObjectGetString(jsonObjectGetKey(authEvt, "textcode")),
            jsonObjectGetKey(authEvt, "payload")   // cloned within Event
        );

        osrfLogActivity(OSRF_LOG_MARK,
            "successful login: username=%s, authtoken=%s, workstation=%s",
            uname,
            jsonObjectGetString(
                jsonObjectGetKeyConst(
                    jsonObjectGetKeyConst(authEvt, "payload"),
                    "authtoken"
                )
            ),
            workstation ? workstation : ""
        );

        jsonObjectFree(authEvt);

    } else {
        osrfLogError(OSRF_LOG_MARK, 
            "Error caching auth session in open-ils.auth_internal");
    }

    return response;
}
Ejemplo n.º 5
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;
}
Ejemplo n.º 6
0
int oilsAuthSessionRetrieve( osrfMethodContext* ctx ) {
	OSRF_METHOD_VERIFY_CONTEXT(ctx);
    bool returnFull = false;

	const char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0));

    if(ctx->params->size > 1) {
        // caller wants full cached object, with authtime, etc.
        const char* rt = jsonObjectGetString(jsonObjectGetIndex(ctx->params, 1));
        if(rt && strcmp(rt, "0") != 0) 
            returnFull = true;
    }

	jsonObject* cacheObj = NULL;
	oilsEvent* evt = NULL;

	if( authToken ){

		// Reset the timeout to keep the session alive
		evt = _oilsAuthResetTimeout(authToken, 0);

		if( evt && strcmp(evt->event, OILS_EVENT_SUCCESS) ) {
			osrfAppRespondComplete( ctx, oilsEventToJSON( evt ));    // can't reset timeout

		} else {

			// Retrieve the cached session object
			osrfLogDebug(OSRF_LOG_MARK, "Retrieving auth session: %s", authToken);
			char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken );
			cacheObj = osrfCacheGetObject( key );
			if(cacheObj) {
				// Return a copy of the cached user object
                if(returnFull)
				    osrfAppRespondComplete( ctx, cacheObj);
                else
				    osrfAppRespondComplete( ctx, jsonObjectGetKeyConst( cacheObj, "userobj"));
				jsonObjectFree(cacheObj);
			} else {
				// Auth token is invalid or expired
				oilsEvent* evt2 = oilsNewEvent(OSRF_LOG_MARK, OILS_EVENT_NO_SESSION);
				osrfAppRespondComplete( ctx, oilsEventToJSON(evt2) ); /* should be event.. */
				oilsEventFree(evt2);
			}
			free(key);
		}

	} else {

		// No session
		evt = oilsNewEvent(OSRF_LOG_MARK, OILS_EVENT_NO_SESSION);
		osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );
	}

	if(evt)
		oilsEventFree(evt);

	return 0;
}
Ejemplo n.º 7
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;
}
Ejemplo n.º 8
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;
}
Ejemplo n.º 9
0
/**
 * Returns true if the provided password is correct.
 * Turn the password into the nested md5 hash required of migrated
 * passwords, then check the password in the DB.
 */
static int oilsAuthLoginCheckPassword(int user_id, const char* password) {

    growing_buffer* gb = buffer_init(33); // free me 1
    char* salt = oilsAuthGetSalt(user_id); // free me 2
    char* passhash = md5sum(password); // free me 3

    buffer_add(gb, salt); // gb strdup's internally
    buffer_add(gb, passhash);

    free(salt); // free 2
    free(passhash); // free 3

    // salt + md5(password)
    passhash = buffer_release(gb); // free 1 ; free me 4
    char* finalpass = md5sum(passhash); // free me 5

    free(passhash); // free 4

    jsonObject *arr = jsonNewObjectType(JSON_ARRAY);
    jsonObjectPush(arr, jsonNewObject("actor.verify_passwd"));
    jsonObjectPush(arr, jsonNewNumberObject((long) user_id));
    jsonObjectPush(arr, jsonNewObject("main"));
    jsonObjectPush(arr, jsonNewObject(finalpass));
    jsonObject *params = jsonNewObjectType(JSON_HASH); // free me 6
    jsonObjectSetKey(params, "from", arr);

    free(finalpass); // free 5

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

    jsonObjectFree(params); // free 6

    if (!verify_obj) return 0; // error

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

    jsonObjectFree(verify_obj);

    return verified;
}
Ejemplo n.º 10
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;
}
Ejemplo n.º 11
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
}
Ejemplo n.º 12
0
/**
 * Parse opensrf request and relay the request to the opensrf network.
 */
static size_t on_message_handler_body(void *data,
                const WebSocketServer *server, const int type, 
                unsigned char *buffer, const size_t buffer_size) {

    request_rec *r = server->request(server);

    jsonObject *msg_wrapper = NULL; // free me
    const jsonObject *tmp_obj = NULL;
    const jsonObject *osrf_msg = NULL;
    const char *service = NULL;
    const char *thread = NULL;
    const char *log_xid = NULL;
    char *msg_body = NULL;
    char *recipient = NULL;
    int i;

    if (buffer_size <= 0) return OK;

    // generate a new log trace for this request. it 
    // may be replaced by a client-provided trace below.
    osrfLogMkXid();

    osrfLogDebug(OSRF_LOG_MARK, "WS received message size=%d", buffer_size);

    // buffer may not be \0-terminated, which jsonParse requires
    char buf[buffer_size + 1];
    memcpy(buf, buffer, buffer_size);
    buf[buffer_size] = '\0';

    osrfLogInternal(OSRF_LOG_MARK, "WS received inbound message: %s", buf);

    msg_wrapper = jsonParse(buf);

    if (msg_wrapper == NULL) {
        osrfLogWarning(OSRF_LOG_MARK, "WS Invalid JSON: %s", buf);
        return HTTP_BAD_REQUEST;
    }

    osrf_msg = jsonObjectGetKeyConst(msg_wrapper, "osrf_msg");

    if (tmp_obj = jsonObjectGetKeyConst(msg_wrapper, "service")) 
        service = jsonObjectGetString(tmp_obj);

    if (tmp_obj = jsonObjectGetKeyConst(msg_wrapper, "thread")) 
        thread = jsonObjectGetString(tmp_obj);

    if (tmp_obj = jsonObjectGetKeyConst(msg_wrapper, "log_xid")) 
        log_xid = jsonObjectGetString(tmp_obj);

    if (log_xid) {

        // use the caller-provide log trace id
        if (strlen(log_xid) > MAX_THREAD_SIZE) {
            osrfLogWarning(OSRF_LOG_MARK, "WS log_xid exceeds max length");
            return HTTP_BAD_REQUEST;
        }

        osrfLogForceXid(log_xid);
    }

    if (thread) {

        if (strlen(thread) > MAX_THREAD_SIZE) {
            osrfLogWarning(OSRF_LOG_MARK, "WS thread exceeds max length");
            return HTTP_BAD_REQUEST;
        }

        // since clients can provide their own threads at session start time,
        // the presence of a thread does not guarantee a cached recipient
        recipient = (char*) apr_hash_get(
            trans->stateful_session_cache, thread, APR_HASH_KEY_STRING);

        if (recipient) {
            osrfLogDebug(OSRF_LOG_MARK, "WS found cached recipient %s", recipient);
        }
    }

    if (!recipient) {

        if (service) {
            int size = snprintf(recipient_buf, RECIP_BUF_SIZE - 1,
                "%s@%s/%s", trans->osrf_router, trans->osrf_domain, service);                                    
            recipient_buf[size] = '\0';                                          
            recipient = recipient_buf;

        } else {
            osrfLogWarning(OSRF_LOG_MARK, "WS Unable to determine recipient");
            return HTTP_BAD_REQUEST;
        }
    }

    osrfLogDebug(OSRF_LOG_MARK, 
        "WS relaying message to opensrf thread=%s, recipient=%s", 
            thread, recipient);

    msg_body = extract_inbound_messages(
        r, service, thread, recipient, osrf_msg);

    osrfLogInternal(OSRF_LOG_MARK, 
        "WS relaying inbound message: %s", msg_body);

    transport_message *tmsg = message_init(
        msg_body, NULL, thread, recipient, NULL);

    message_set_osrf_xid(tmsg, osrfLogGetXid());
    client_send_message(osrf_handle, tmsg);


    osrfLogClearXid();
    message_free(tmsg);                                                         
    jsonObjectFree(msg_wrapper);
    free(msg_body);

    last_activity_time = time(NULL);
    return OK;
}
Ejemplo n.º 13
0
/**
	@brief Parse a hash (JSON object), and create a JSON_HASH for it; decode class hints.
	@param parser Pointer to a Parser.
	@return Pointer to a newly created jsonObject, or NULL upon error.

	This function is similar to get_hash(), @em except:

	If the hash includes a member with a key equal to JSON_CLASS_KEY ("__c" by default),
	then look for a member whose key is JSON_DATA_KEY ("__p" by default).  If you find one,
	return the data associated with it; otherwise return a jsonObject of type JSON_NULL.

	If there is no member with a key equal to JSON_CLASS_KEY, then return the same sort of
	jsonObject as get_hash() would return (except of course that lower levels may be
	decoded as described above).
*/
static jsonObject* get_decoded_hash( Parser* parser ) {
	jsonObject* hash = jsonNewObjectType( JSON_HASH );

	char c = skip_white_space( parser );
	if( '}' == c )
		return hash;           // Empty hash

	char* class_name = NULL;

	for( ;; ) {

		// Get the key string
		if( '"' != c ) {
			report_error( parser, c,
					"Expected quotation mark to begin hash key; didn't find it\n" );
			jsonObjectFree( hash );
			return NULL;
		}

		const char* key = get_string( parser );
		if( ! key ) {
			jsonObjectFree( hash );
			return NULL;
		}
		char* key_copy = strdup( key );

		if( jsonObjectGetKeyConst( hash, key_copy ) ) {
			report_error( parser, '"', "Duplicate key in JSON object" );
			jsonObjectFree( hash );
			return NULL;
		}

		// Get the colon
		c = skip_white_space( parser );
		if( c != ':' ) {
			report_error( parser, c,
					"Expected colon after hash key; didn't find it\n" );
			free( key_copy );
			jsonObjectFree( hash );
			return NULL;
		}

		// Get the associated value
		jsonObject* obj = get_json_node( parser, skip_white_space( parser ) );
		if( !obj ) {
			free( key_copy );
			jsonObjectFree( hash );
			return NULL;
		}

		// Add a new entry to the hash
		jsonObjectSetKey( hash, key_copy, obj );

		// Save info for class hint, if present
		if( !strcmp( key_copy, JSON_CLASS_KEY ) )
			class_name = jsonObjectToSimpleString( obj );

		free( key_copy );

		// Look for comma or right brace
		c = skip_white_space( parser );
		if( '}' == c )
			break;
		else if( c != ',' ) {
			report_error( parser, c,
					"Expected comma or brace in hash, didn't find it" );
			jsonObjectFree( hash );
			return NULL;
		}
		c = skip_white_space( parser );
	}

	if( class_name ) {
		// We found a class hint.  Extract the data node and return it.
		jsonObject* class_data = osrfHashExtract( hash->value.h, JSON_DATA_KEY );
		if( class_data ) {
			class_data->parent = NULL;
			jsonObjectFree( hash );
			hash = class_data;
			hash->parent = NULL;
			hash->classname = class_name;
		} else {
			// Huh?  We have a class name but no data for it.
			// Throw away what we have and return a JSON_NULL.
			jsonObjectFree( hash );
			hash = jsonNewObjectType( JSON_NULL );
		}

	} else {
		if( class_name )
			free( class_name );
	}

	return hash;
}
Ejemplo n.º 14
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;
}
Ejemplo n.º 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;
}
Ejemplo n.º 16
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;
}
Ejemplo n.º 17
0
int oilsAuthLogin(osrfMethodContext* ctx) {
    OSRF_METHOD_VERIFY_CONTEXT(ctx);

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

    const char* username    = jsonObjectGetString(jsonObjectGetKeyConst(args, "username"));
    const char* identifier  = jsonObjectGetString(jsonObjectGetKeyConst(args, "identifier"));
    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* ewho        = jsonObjectGetString(jsonObjectGetKeyConst(args, "agent"));

    const char* ws = (workstation) ? workstation : "";
    if (!type) type = OILS_AUTH_STAFF;

    jsonObject* userObj = NULL; // free me
    oilsEvent* response = NULL; // free me

    /* Use __FILE__, harmless_line_number for creating
     * OILS_EVENT_AUTH_FAILED events (instead of OSRF_LOG_MARK) to avoid
     * giving away information about why an authentication attempt failed.
     */
    int harmless_line_number = __LINE__;

    // translate a generic identifier into a username or barcode if necessary.
    if (identifier && !username && !barcode) {
        if (oilsAuthIdentIsBarcode(identifier, orgloc)) {
            barcode = identifier;
        } else {
            username = identifier;
        }
    }

    if (username) {
        barcode = NULL; // avoid superfluous identifiers
        userObj = oilsUtilsFetchUserByUsername(ctx, username);

    } else if (barcode) {
        userObj = oilsUtilsFetchUserByBarcode(ctx, barcode);

    } else {
        // not enough params
        return osrfAppRequestRespondException(ctx->session, ctx->request,
            "username/barcode and password required for method: %s", 
            ctx->method->name);
    }

    if (!userObj) { // user not found.  
        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);
        osrfAppRespondComplete(ctx, oilsEventToJSON(response));
        oilsEventFree(response); // frees event JSON
        return 0;
    }

    long user_id = oilsFMGetObjectId(userObj);

    // username is freed when userObj is freed.
    // From here we can use the username as the generic identifier
    // since it's guaranteed to have a value.
    if (!username) username = oilsFMGetStringConst(userObj, "usrname");

    // See if the user is allowed to login.
    jsonObject* params = jsonNewObject(NULL);
    jsonObjectSetKey(params, "user_id", jsonNewNumberObject(user_id));
    jsonObjectSetKey(params,"org_unit", jsonNewNumberObject(orgloc));
    jsonObjectSetKey(params, "login_type", jsonNewObject(type));
    if (barcode) jsonObjectSetKey(params, "barcode", jsonNewObject(barcode));

    jsonObject* authEvt = oilsUtilsQuickReqCtx( // freed after password test
        ctx,
        "open-ils.auth_internal",
        "open-ils.auth_internal.user.validate", params);
    jsonObjectFree(params);

    if (!authEvt) { // unknown error
        jsonObjectFree(userObj);
        return -1;
    }

    const char* authEvtCode = 
        jsonObjectGetString(jsonObjectGetKey(authEvt, "textcode"));

    if (!strcmp(authEvtCode, OILS_EVENT_AUTH_FAILED)) {
        // Received the generic login failure event.

        osrfLogInfo(OSRF_LOG_MARK,  
            "failed login: username=%s, barcode=%s, workstation=%s",
            username, (barcode ? barcode : "(none)"), ws);

        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);
    }

    if (!response && // user exists and is not barred, etc.
        !oilsAuthLoginVerifyPassword(ctx, user_id, username, password)) {
        // User provided the wrong password or is blocked from too 
        // many previous login failures.

        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);

        osrfLogInfo(OSRF_LOG_MARK,  
            "failed login: username=%s, barcode=%s, workstation=%s",
                username, (barcode ? barcode : "(none)"), ws );
    }

    // Below here, we know the password check succeeded if no response
    // object is present.

    if (!response && (
        !strcmp(authEvtCode, "PATRON_INACTIVE") ||
        !strcmp(authEvtCode, "PATRON_CARD_INACTIVE"))) {
        // Patron and/or card is inactive but the correct password 
        // was provided.  Alert the caller to the inactive-ness.
        response = oilsNewEvent2(
            OSRF_LOG_MARK, authEvtCode,
            jsonObjectGetKey(authEvt, "payload")   // cloned within Event
        );
    }

    if (!response && strcmp(authEvtCode, OILS_EVENT_SUCCESS)) {
        // Validate API returned an unexpected non-success event.
        // To be safe, treat this as a generic login failure.

        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);
    }

    if (!response) {
        // password OK and no other events have prevented login completion.

        char* ewhat = "login";

        if (0 == strcmp(ctx->method->name, "open-ils.auth.authenticate.verify")) {
            response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_SUCCESS );
            ewhat = "verify";

        } else {
            response = oilsAuthHandleLoginOK(
                ctx, userObj, username, type, orgloc, workstation);
        }

        oilsUtilsTrackUserActivity(
            ctx,
            oilsFMGetObjectId(userObj), 
            ewho, ewhat, 
            osrfAppSessionGetIngress()
        );
    }

    // reply
    osrfAppRespondComplete(ctx, oilsEventToJSON(response));

    // clean up
    oilsEventFree(response);
    jsonObjectFree(userObj);
    jsonObjectFree(authEvt);

	return 0;
}
Ejemplo n.º 18
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"
		- "agent" (what software/interface/3rd-party is making the request)
		- "nonce" optional login seed to differentiate logins using the same username.

	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* identifier  = jsonObjectGetString(jsonObjectGetKeyConst(args, "identifier"));
    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* ewho        = jsonObjectGetString(jsonObjectGetKeyConst(args, "agent"));
    const char* nonce       = jsonObjectGetString(jsonObjectGetKeyConst(args, "nonce"));

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

    // we no longer care how the identifier reaches us, 
    // as long as we have one.
    if (!identifier) {
        if (uname) {
            identifier = uname;
        } else if (barcode) {
            identifier = barcode;
        }
    }

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

    osrfLogInfo(OSRF_LOG_MARK, 
        "Patron completing authentication with identifer %s", identifier);

    /* Use __FILE__, harmless_line_number for creating
     * OILS_EVENT_AUTH_FAILED events (instead of OSRF_LOG_MARK) to avoid
     * giving away information about why an authentication attempt failed.
     */
    int harmless_line_number = __LINE__;

    if( !type )
         type = OILS_AUTH_STAFF;

    oilsEvent* response = NULL; // free
    jsonObject* userObj = NULL; // free

    char* cache_key = va_list_to_string(
        "%s%s%s", OILS_AUTH_CACHE_PRFX, identifier, nonce);
    jsonObject* cacheObj = osrfCacheGetObject(cache_key); // free

    if (!cacheObj) {
        return osrfAppRequestRespondException(ctx->session,
            ctx->request, "No authentication seed found. "
            "open-ils.auth.authenticate.init must be called first "
            " (check that memcached is running and can be connected to) "
        );
    }

    int user_id = jsonObjectGetNumber(
        jsonObjectGetKeyConst(cacheObj, "user_id"));

    if (user_id == -1) {
        // User was not found during init.  Clean up and exit early.
        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);
        osrfAppRespondComplete(ctx, oilsEventToJSON(response));
        oilsEventFree(response); // frees event JSON
        osrfCacheRemove(cache_key);
        jsonObjectFree(cacheObj);
        return 0;
    }

    jsonObject* param = jsonNewNumberObject(user_id); // free
    userObj = oilsUtilsCStoreReqCtx(
        ctx, "open-ils.cstore.direct.actor.user.retrieve", param);
    jsonObjectFree(param);

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

    // See if the user is allowed to login.

    jsonObject* params = jsonNewObject(NULL);
    jsonObjectSetKey(params, "user_id", 
        jsonNewNumberObject(oilsFMGetObjectId(userObj)));
    jsonObjectSetKey(params,"org_unit", jsonNewNumberObject(orgloc));
    jsonObjectSetKey(params, "login_type", jsonNewObject(type));
    if (barcode) jsonObjectSetKey(params, "barcode", jsonNewObject(barcode));

    jsonObject* authEvt = oilsUtilsQuickReqCtx( // freed after password test
        ctx,
        "open-ils.auth_internal",
        "open-ils.auth_internal.user.validate", params);
    jsonObjectFree(params);

    if (!authEvt) {
        // Something went seriously wrong.  Get outta here before 
        // we start segfaulting.
        jsonObjectFree(userObj);
        if(freeable_uname) free(freeable_uname);
        return -1;
    }

    const char* authEvtCode = 
        jsonObjectGetString(jsonObjectGetKey(authEvt, "textcode"));

    if (!strcmp(authEvtCode, OILS_EVENT_AUTH_FAILED)) {
        // Received the generic login failure event.

        osrfLogInfo(OSRF_LOG_MARK,  
            "failed login: username=%s, barcode=%s, workstation=%s",
            uname, (barcode ? barcode : "(none)"), ws);

        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);
    }

    int passOK = 0;
    
    if (!response) {
        // User exists and is not barred, etc.  Test the password.

        passOK = oilsAuthVerifyPassword(
            ctx, user_id, identifier, password, nonce);

        if (!passOK) {
            // Password check failed. Return generic login failure.

            response = oilsNewEvent(
                __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);

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


    // Below here, we know the password check succeeded if no response
    // object is present.

    if (!response && (
        !strcmp(authEvtCode, "PATRON_INACTIVE") ||
        !strcmp(authEvtCode, "PATRON_CARD_INACTIVE"))) {
        // Patron and/or card is inactive but the correct password 
        // was provided.  Alert the caller to the inactive-ness.
        response = oilsNewEvent2(
            OSRF_LOG_MARK, authEvtCode,
            jsonObjectGetKey(authEvt, "payload")   // cloned within Event
        );
    }

    if (!response && strcmp(authEvtCode, OILS_EVENT_SUCCESS)) {
        // Validate API returned an unexpected non-success event.
        // To be safe, treat this as a generic login failure.

        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);
    }

    if (!response) {
        // password OK and no other events have prevented login completion.

        char* ewhat = "login";

        if (0 == strcmp(ctx->method->name, "open-ils.auth.authenticate.verify")) {
            response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_SUCCESS );
            ewhat = "verify";

        } else {
            response = oilsAuthHandleLoginOK(
                ctx, userObj, uname, type, orgloc, workstation);
        }

        oilsUtilsTrackUserActivity(
            ctx,
            oilsFMGetObjectId(userObj), 
            ewho, ewhat, 
            osrfAppSessionGetIngress()
        );
    }

    // reply
    osrfAppRespondComplete(ctx, oilsEventToJSON(response));

    // clean up
    oilsEventFree(response);
    jsonObjectFree(userObj);
    jsonObjectFree(authEvt);
    jsonObjectFree(cacheObj);
    if(freeable_uname)
        free(freeable_uname);

    return 0;
}
Ejemplo n.º 19
0
/**
	@brief Parse a hash (JSON object), and create a JSON_HASH for it.
	@param parser Pointer to a Parser.
	@return Pointer to a newly created jsonObject of type JSON_HASH, or NULL upon error.

	Look for a series of name/value pairs, separated by commas and terminated by a right
	curly brace.  Each name/value pair consists of a quoted string, followed by a colon,
	followed a JSON node of any sort.  Parse the value recursively.

	Collect the name/value pairs into a newly created jsonObject of type JSON_ARRAY, and
	return a pointer to it.

	Upon error, log an error message and return NULL.
*/
static jsonObject* get_hash( Parser* parser ) {
	jsonObject* hash = jsonNewObjectType( JSON_HASH );

	char c = skip_white_space( parser );
	if( '}' == c )
		return hash;           // Empty hash

	for( ;; ) {

		// Get the key string
		if( '"' != c ) {
			report_error( parser, c,
						  "Expected quotation mark to begin hash key; didn't find it\n" );
			jsonObjectFree( hash );
			return NULL;
		}

		const char* key = get_string( parser );
		if( ! key ) {
			jsonObjectFree( hash );
			return NULL;
		}
		char* key_copy = strdup( key );

		if( jsonObjectGetKeyConst( hash, key_copy ) ) {
			report_error( parser, '"', "Duplicate key in JSON object" );
			jsonObjectFree( hash );
			return NULL;
		}

		// Get the colon
		c = skip_white_space( parser );
		if( c != ':' ) {
			report_error( parser, c,
						  "Expected colon after hash key; didn't find it\n" );
			free( key_copy );
			jsonObjectFree( hash );
			return NULL;
		}

		// Get the associated value
		jsonObject* obj = get_json_node( parser, skip_white_space( parser ) );
		if( !obj ) {
			free( key_copy );
			jsonObjectFree( hash );
			return NULL;
		}

		// Add a new entry to the hash
		jsonObjectSetKey( hash, key_copy, obj );
		free( key_copy );

		// Look for comma or right brace
		c = skip_white_space( parser );
		if( '}' == c )
			break;
		else if( c != ',' ) {
			report_error( parser, c,
						  "Expected comma or brace in hash, didn't find it" );
			jsonObjectFree( hash );
			return NULL;
		}
		c = skip_white_space( parser );
	}

	return hash;
}
Ejemplo n.º 20
0
/**
	@brief Translate a jsonObject into a single osrfMessage.
	@param obj Pointer to the jsonObject to be translated.
	@return Pointer to a newly created osrfMessage.

	It is assumed that @a obj is non-NULL and points to a valid representation of a message.
	For a description of the expected structure of this representations, see osrfMessageToJSON().

	The calling code is responsible for freeing the osrfMessage by calling osrfMessageFree().
*/
static osrfMessage* deserialize_one_message( const jsonObject* obj ) {

	// Get the message type.  If it isn't present, default to CONNECT.
	const jsonObject* tmp = jsonObjectGetKeyConst( obj, "type" );

	enum M_TYPE type = CONNECT;
	const char* t = jsonObjectGetString( tmp );
	if( t ) {

		if(      !strcmp( t, "CONNECT"    ))   type = CONNECT;
		else if( !strcmp( t, "DISCONNECT" ))   type = DISCONNECT;
		else if( !strcmp( t, "STATUS"     ))   type = STATUS;
		else if( !strcmp( t, "REQUEST"    ))   type = REQUEST;
		else if( !strcmp( t, "RESULT"     ))   type = RESULT;
	}

	// Get the thread trace, defaulting to zero.
	int trace = 0;
	tmp = jsonObjectGetKeyConst( obj, "threadTrace" );
	if( tmp ) {
		const char* tt = jsonObjectGetString( tmp );
		if( tt ) {
			trace = atoi( tt );
		}
	}

	// Get the protocol, defaulting to zero.
	int protocol = 0;
	tmp = jsonObjectGetKeyConst( obj, "protocol" );
	if(tmp) {
		const char* proto = jsonObjectGetString(tmp);
		if( proto ) {
			protocol = atoi( proto );
		}
	}

	// Now that we have the essentials, create an osrfMessage
	osrfMessage* msg = osrf_message_init( type, trace, protocol );

	// Update current_locale with the locale of the message
	// (or set it to NULL if not specified)
	tmp = jsonObjectGetKeyConst( obj, "locale" );
	if(tmp && ( msg->sender_locale = jsonObjectToSimpleString(tmp))) {
		if ( current_locale ) {
			if( strcmp( current_locale, msg->sender_locale ) ) {
				free( current_locale );
				current_locale = strdup( msg->sender_locale );
			} // else they're the same already, so don't replace one with the other
		} else
			current_locale = strdup( msg->sender_locale );
	} else {
		if ( current_locale ) {
			free( current_locale );
			current_locale = NULL;
		}
	}

	tmp = jsonObjectGetKeyConst( obj, "payload" );
	if(tmp) {
		// Get method name and parameters for a REQUEST
		const jsonObject* tmp0 = jsonObjectGetKeyConst(tmp,"method");
		const char* tmp_str = jsonObjectGetString(tmp0);
		if(tmp_str)
			msg->method_name = strdup(tmp_str);

		tmp0 = jsonObjectGetKeyConst(tmp,"params");
		if(tmp0) {
			// Note that we use jsonObjectDecodeClass() instead of
			// jsonObjectClone().  The classnames are already decoded,
			// but jsonObjectDecodeClass removes the decoded classnames.
			msg->_params = jsonObjectDecodeClass( tmp0 );
			if(msg->_params && msg->_params->type == JSON_NULL)
				msg->_params->type = JSON_ARRAY;
		}

		// Get status fields for a RESULT or STATUS
		if(tmp->classname)
			msg->status_name = strdup(tmp->classname);

		tmp0 = jsonObjectGetKeyConst(tmp,"status");
		tmp_str = jsonObjectGetString(tmp0);
		if(tmp_str)
			msg->status_text = strdup(tmp_str);

		tmp0 = jsonObjectGetKeyConst(tmp,"statusCode");
		if(tmp0) {
			tmp_str = jsonObjectGetString(tmp0);
			if(tmp_str)
				msg->status_code = atoi(tmp_str);
			if(tmp0->type == JSON_NUMBER)
				msg->status_code = (int) jsonObjectGetNumber(tmp0);
		}

		// Get the content for a RESULT
		tmp0 = jsonObjectGetKeyConst(tmp,"content");
		if(tmp0) {
			// Note that we use jsonObjectDecodeClass() instead of
			// jsonObjectClone().  The classnames are already decoded,
			// but jsonObjectDecodeClass removes the decoded classnames.
			msg->_result_content = jsonObjectDecodeClass( tmp0 );
		}

	}

	return msg;
}
Ejemplo n.º 21
0
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;
}