/** * @brief helper method to retrieve a user model of the currently authenticated user * * the function parses the authorization header of a request, * validates its format and ensures a proper session token is set. * it then takes the token and finds the user it belongs to. * * @param request a request * @return model of the currently authenticated user, taken from the storage * @throw kdbrest::exception::NoCurrentUserException * @throw kdbrest::exception::UserNotFoundException */ model::User AuthenticationApp::getCurrentUser (cppcms::http::request & request) { // authentication validation std::string headerAuthorization = request.http_authorization (); std::string token; if (headerAuthorization.empty ()) { // find token in get_parameters token = request.get (PARAM_TOKEN); if (token.empty ()) { throw exception::NoCurrentUserException (); } } else { if (!boost::starts_with (headerAuthorization, AUTH_HEADER_PREFIX)) { throw exception::NoCurrentUserException (); } token = headerAuthorization.substr (AUTH_HEADER_PREFIX.size ()); } /* request seems fine, lets build the jwt */ jwt_t * jwt; if (jwt_decode (&jwt, token.c_str (), reinterpret_cast<const unsigned char *> ( Config::instance ().getConfig ().get<std::string> ("jwt.encryption.secret").c_str ()), Config::instance ().getConfig ().get<std::string> ("jwt.encryption.secret").size ()) != 0) { throw exception::NoCurrentUserException (); } std::unique_ptr<jwt_t, void (*) (jwt_t *)> jwt_ptr (jwt, jwt_free); // check issuer and other grants if (std::string (jwt_get_grant (jwt_ptr.get (), "issuer")) != std::string (ELEKTRA_REST_AUTHENTICATION_JWT_ISSUER) || jwt_get_grant_int (jwt_ptr.get (), "expires") < std::time (NULL)) { throw exception::NoCurrentUserException (); } std::string username = std::string (jwt_get_grant (jwt_ptr.get (), "username")); if (username.empty ()) { throw exception::NoCurrentUserException (); } try { model::User user = service::StorageEngine::instance ().getUser (username); return user; } catch (exception::UserNotFoundException const & e) { throw exception::UserNotFoundException (); } }
END_TEST START_TEST(test_jwt_del_grant) { jwt_t *jwt = NULL; const char *val; const char testval[] = "testing"; int ret = 0; ret = jwt_new(&jwt); ck_assert_int_eq(ret, 0); ck_assert(jwt != NULL); ret = jwt_add_grant(jwt, "iss", testval); ck_assert_int_eq(ret, 0); ret = jwt_del_grant(jwt, "iss"); ck_assert_int_eq(ret, 0); val = jwt_get_grant(jwt, "iss"); ck_assert(val == NULL); /* Delete non existent. */ ret = jwt_del_grant(jwt, "iss"); ck_assert_int_eq(ret, 0); jwt_free(jwt); }
END_TEST START_TEST(test_jwt_grants_json) { const char *json = "{\"ref\":\"385d6518-fb73-45fc-b649-0527d8576130\"" ",\"id\":\"FVvGYTr3FhiURCFebsBOpBqTbzHdX/DvImiA2yheXr8=\"," "\"iss\":\"localhost\",\"scopes\":\"storage\",\"sub\":" "\"user0\"}"; jwt_t *jwt = NULL; const char *val; int ret = 0; ret = jwt_new(&jwt); ck_assert_int_eq(ret, 0); ck_assert(jwt != NULL); ret = jwt_add_grants_json(jwt, json); ck_assert_int_eq(ret, 0); val = jwt_get_grant(jwt, "ref"); ck_assert(val != NULL); ck_assert_str_eq(val, "385d6518-fb73-45fc-b649-0527d8576130"); jwt_free(jwt); }
/** * @brief validates the authorization header of a request() * * helper method that allows to validate whether a request contains a valid * session token or not. it checks for the authorization header, format and * validity of the token. * additionally the method can do checks on the authenticated users rank and * username (i.e. check for permissions or if is owner of something). * * @note if a rank and a username are given, it is sufficient that one of both matches the * requirement in order for the validation to succeed. * * @param request a request * @param response a response * @param rank optionally the rank of a user can be checked * @param orUser optionally the name of a user can be checked * @return true if an authentication is present and the authenticated user * matches all requirements, false otherwise */ bool AuthenticationApp::validateAuthentication (cppcms::http::request & request, cppcms::http::response & response, const int rank, const std::string orUser) { // authentication validation std::string headerAuthorization = request.http_authorization (); std::string token; if (headerAuthorization.empty ()) { // find token in get_parameters token = request.get (PARAM_TOKEN); if (token.empty ()) { RootApp::setUnauthorized (response, "Session token missing.", "NEED_AUTHENTICATION"); // send HTTP 401 return false; // not successful } } else { if (!boost::starts_with (headerAuthorization, AUTH_HEADER_PREFIX)) { RootApp::setUnauthorized (response, "Authentication header has wrong format.", "NEED_AUTHENTICATION"); return false; // not successful } token = headerAuthorization.substr (AUTH_HEADER_PREFIX.size ()); } /* request seems fine, so lets parse the token */ jwt_t * jwt; // decode it if (jwt_decode (&jwt, token.c_str (), reinterpret_cast<const unsigned char *> ( Config::instance ().getConfig ().get<std::string> ("jwt.encryption.secret").c_str ()), Config::instance ().getConfig ().get<std::string> ("jwt.encryption.secret").size ()) != 0) { RootApp::setUnauthorized (response, "Session token is invalid", "NEED_AUTHENTICATION"); // send HTTP 401 return false; } std::unique_ptr<jwt_t, void (*) (jwt_t *)> jwt_ptr (jwt, jwt_free); // check issuer and other grants if (std::string (jwt_get_grant (jwt_ptr.get (), "issuer")) != std::string (ELEKTRA_REST_AUTHENTICATION_JWT_ISSUER) || jwt_get_grant_int (jwt_ptr.get (), "expires") < std::time (NULL)) { RootApp::setUnauthorized (response, "Session token is invalid", "NEED_AUTHENTICATION"); // send HTTP 401 return false; } model::User currentUser = AuthenticationApp::getCurrentUser (request); if (currentUser.getRank () < rank && (orUser.empty () || orUser != currentUser.getUsername ())) { RootApp::setUnauthorized (response, "This action requires higher permissions.", "USER_INSUFFICIENT_PERMISSIONS"); return false; // not successful } return true; // authentication successful }
END_TEST START_TEST(test_jwt_grants_json) { const char *json = "{\"ref\":\"385d6518-fb73-45fc-b649-0527d8576130\"" ",\"id\":\"FVvGYTr3FhiURCFebsBOpBqTbzHdX/DvImiA2yheXr8=\"," "\"iss\":\"localhost\",\"scopes\":\"storage\",\"sub\":" "\"user0\"}"; const char *json2 = "{" "\"id\":\"FVvGYTr3FhiURCFebsBOpBqTbzHdX/DvImiA2yheXr8=\"," "\"iss\":\"localhost\",\"scopes\":\"storage\",\"sub\":" "\"user0\", \"nbf\":12345678, \"iat\":12346789}"; jwt_t *jwt = NULL; json_t *js_val; const char *val; int intval; int ret = 0; ret = jwt_new(&jwt); ck_assert_int_eq(ret, 0); ck_assert(jwt != NULL); ret = jwt_add_grants_json(jwt, json); ck_assert_int_eq(ret, 0); val = jwt_get_grant(jwt, "ref"); ck_assert(val != NULL); ck_assert_str_eq(val, "385d6518-fb73-45fc-b649-0527d8576130"); jwt_free(jwt); ret = jwt_new(&jwt); ck_assert_int_eq(ret, 0); ck_assert(jwt != NULL); ret = jwt_add_grants_json(jwt, json2); ck_assert_int_eq(ret, 0); js_val = jwt_get_grant_obj (jwt, "nbf"); ck_assert (js_val != NULL); ck_assert (json_is_integer (js_val)); ck_assert (json_integer_value (js_val) == 12345678); ret = jwt_get_grant_int_or_str (jwt, "nbf", &val, &intval); ck_assert_int_eq(ret, 0); ck_assert(val == NULL); ck_assert (intval == 12345678); }
END_TEST START_TEST(test_jwt_grant_invalid) { jwt_t *jwt = NULL; const char *val; int ret = 0; ret = jwt_new(&jwt); ck_assert_int_eq(ret, 0); ck_assert(jwt != NULL); ret = jwt_add_grant(jwt, "iss", NULL); ck_assert_int_eq(ret, EINVAL); ret = jwt_del_grant(jwt, ""); ck_assert_int_eq(ret, EINVAL); val = jwt_get_grant(jwt, NULL); ck_assert_int_eq(errno, EINVAL); ck_assert(val == NULL); jwt_free(jwt); }
int mosquitto_auth_unpwd_check(void *user_data, const char *username, const char *password) { jwt_t *jwt; #ifdef MQAP_DEBUG const char* val; int i_val; #endif time_t now; int iat; int exp; unsigned char key[32] = "012345678901234567890123456789AB"; if (username == NULL || password == NULL) { return MOSQ_ERR_AUTH; } #ifdef MQAP_DEBUG fprintf(stderr, "mosquitto_auth_unpwd_check: username=%s, password=%s\n", username, password); #endif if ( ! strcmp(username, "jwt") ) { time(&now); int status = jwt_decode(&jwt, password, key , sizeof(key)); if (( status == 0 ) && (jwt != NULL) ) { #ifdef MQAP_DEBUG fprintf(stderr, "mosquitto_auth_unpwd_check: password is a valid JWT token\n"); val = jwt_get_grant(jwt, "iss"); fprintf(stderr, "mosquitto_auth_unpwd_check: iss : %s\n", val); val = jwt_get_grant(jwt, "sub"); fprintf(stderr, "mosquitto_auth_unpwd_check: sub : %s\n", val); i_val = get_js_int(jwt->grants, "iat"); fprintf(stderr, "mosquitto_auth_unpwd_check: iat : %d\n", i_val); i_val = get_js_int(jwt->grants, "exp"); fprintf(stderr, "mosquitto_auth_unpwd_check: exp : %d\n", i_val); val = get_js_object(jwt->grants, "aud"); fprintf(stderr, "mosquitto_auth_unpwd_check: aud : %s\n", val); fprintf(stderr, "mosquitto_auth_unpwd_check: now : %d\n", (int)now); #endif iat = get_js_int(jwt->grants, "iat"); exp = get_js_int(jwt->grants, "exp"); if ( (now < iat) || (now > exp) ) { #ifdef MQAP_DEBUG fprintf(stderr, "mosquitto_auth_unpwd_check: token is expired\n"); #endif jwt_free(jwt); return MOSQ_ERR_AUTH; } // TODO add here some other controls about iss, sub, ... jwt_free(jwt); return MOSQ_ERR_SUCCESS; } else { #ifdef MQAP_DEBUG fprintf(stderr, "mosquitto_auth_unpwd_check: password is not a valid token %d\n", status); #endif } } return MOSQ_ERR_AUTH; }