From db6848e8ae1ea0153a6003aadab494ae9f2a5ba8 Mon Sep 17 00:00:00 2001 From: Sachin Agrawal Date: Mon, 14 Sep 2015 16:55:15 -0700 Subject: [PATCH] Updated ACL & DOXM entity handler to process GET query Updated ACL resource entity handler to process GET query and DOXM resource entity handler to process GET query for device ID. Change-Id: Ia293fd633e0ce4f70202a6f80a04bd0f283510fa Signed-off-by: Shilpa Sodani Signed-off-by: Sachin Agrawal Reviewed-on: https://gerrit.iotivity.org/gerrit/2411 Tested-by: jenkins-iotivity --- resource/csdk/security/src/aclresource.c | 180 ++++++++++++++++----- resource/csdk/security/src/doxmresource.c | 39 ++++- .../csdk/security/unittest/aclresourcetest.cpp | 103 ++++++++---- resource/csdk/security/unittest/doxmresource.cpp | 12 ++ 4 files changed, 260 insertions(+), 74 deletions(-) diff --git a/resource/csdk/security/src/aclresource.c b/resource/csdk/security/src/aclresource.c index 1b858f8..e0f0937 100644 --- a/resource/csdk/security/src/aclresource.c +++ b/resource/csdk/security/src/aclresource.c @@ -437,7 +437,7 @@ static OCStackResult RemoveACE(const OicUuid_t * subject, } //If resource is NULL then delete all the ACE for the subject. - if(NULL == resource) + if(NULL == resource || resource[0] == '\0') { LL_FOREACH_SAFE(gAcl, acl, tempAcl) { @@ -505,16 +505,139 @@ static OCStackResult RemoveACE(const OicUuid_t * subject, return ret; } +/* + * This method parses the query string received for REST requests and + * retrieves the 'subject' field. + * + * @param query querystring passed in REST request + * @param subject subject UUID parsed from query string + * + * @return true if query parsed successfully and found 'subject', else false. + */ +static bool GetSubjectFromQueryString(const char *query, OicUuid_t *subject) +{ + OicParseQueryIter_t parseIter = {.attrPos=NULL}; + + ParseQueryIterInit((unsigned char *)query, &parseIter); + + while(GetNextQuery(&parseIter)) + { + if(strncasecmp((char *)parseIter.attrPos, OIC_JSON_SUBJECT_NAME, parseIter.attrLen) == 0) + { + VERIFY_SUCCESS(TAG, 0 != parseIter.valLen, ERROR); + unsigned char base64Buff[sizeof(((OicUuid_t*)0)->id)] = {}; + uint32_t outLen = 0; + B64Result b64Ret = B64_OK; + b64Ret = b64Decode((char *)parseIter.valPos, parseIter.valLen, base64Buff, + sizeof(base64Buff), &outLen); + VERIFY_SUCCESS(TAG, (B64_OK == b64Ret && outLen <= sizeof(subject->id)), ERROR); + memcpy(subject->id, base64Buff, outLen); + + return true; + } + } + +exit: + return false; +} + +/* + * This method parses the query string received for REST requests and + * retrieves the 'resource' field. + * + * @param query querystring passed in REST request + * @param resource resource parsed from query string + * @param resourceSize size of the memory pointed to resource + * + * @return true if query parsed successfully and found 'resource', else false. + */ +static bool GetResourceFromQueryString(const char *query, char *resource, size_t resourceSize) +{ + OicParseQueryIter_t parseIter = {.attrPos=NULL}; + + ParseQueryIterInit((unsigned char *)query, &parseIter); + + while(GetNextQuery(&parseIter)) + { + if(strncasecmp((char *)parseIter.attrPos, OIC_JSON_RESOURCES_NAME, parseIter.attrLen) == 0) + { + VERIFY_SUCCESS(TAG, 0 != parseIter.valLen, ERROR); + OICStrcpy(resource, resourceSize, (char *)parseIter.valPos); + + return true; + } + } + +exit: + return false; +} + + + static OCEntityHandlerResult HandleACLGetRequest (const OCEntityHandlerRequest * ehRequest) { - // Convert ACL data into JSON for transmission - char* jsonStr = BinToAclJSON(gAcl); + OCEntityHandlerResult ehRet = OC_EH_ERROR; + char* jsonStr = NULL; - /* - * A device should 'always' have a default ACL. Therefore, - * jsonStr should never be NULL. - */ - OCEntityHandlerResult ehRet = (jsonStr ? OC_EH_OK : OC_EH_ERROR); + // Process the REST querystring parameters + if(ehRequest->query) + { + OC_LOG (INFO, TAG, PCF("HandleACLGetRequest processing query")); + + OicUuid_t subject = {.id={0}}; + char resource[MAX_URI_LENGTH] = {0}; + + OicSecAcl_t *savePtr = NULL; + const OicSecAcl_t *currentAce = NULL; + + // 'Subject' field is MUST for processing a querystring in REST request. + VERIFY_SUCCESS(TAG, + true == GetSubjectFromQueryString(ehRequest->query, &subject), + ERROR); + + GetResourceFromQueryString(ehRequest->query, resource, sizeof(resource)); + + /* + * TODO : Currently, this code only provides one ACE for a Subject. + * Below code needs to be updated for scenarios when Subject have + * multiple ACE's in ACL resource. + */ + while((currentAce = GetACLResourceData(&subject, &savePtr))) + { + /* + * If REST querystring contains a specific resource, we need + * to search for that resource in ACE. + */ + if (resource[0] != '\0') + { + for(size_t n = 0; n < currentAce->resourcesLen; n++) + { + if((currentAce->resources[n]) && + (0 == strcmp(resource, currentAce->resources[n]) || + 0 == strcmp(WILDCARD_RESOURCE_URI, currentAce->resources[n]))) + { + // Convert ACL data into JSON for transmission + jsonStr = BinToAclJSON(currentAce); + goto exit; + } + } + } + else + { + // Convert ACL data into JSON for transmission + jsonStr = BinToAclJSON(currentAce); + goto exit; + } + } + } + else + { + // Convert ACL data into JSON for transmission + jsonStr = BinToAclJSON(gAcl); + } + +exit: + ehRet = (jsonStr ? OC_EH_OK : OC_EH_ERROR); // Send response payload to request originator SendSRMResponse(ehRequest, ehRet, jsonStr); @@ -554,50 +677,27 @@ static OCEntityHandlerResult HandleACLDeleteRequest(const OCEntityHandlerRequest { OC_LOG (INFO, TAG, PCF("Processing ACLDeleteRequest")); OCEntityHandlerResult ehRet = OC_EH_ERROR; - - if(NULL == ehRequest->query) - { - return ehRet; - } - OicParseQueryIter_t parseIter = {.attrPos=NULL}; OicUuid_t subject = {.id={0}}; - char * resource = NULL; + char resource[MAX_URI_LENGTH] = {0}; - //Parsing REST query to get subject & resource - ParseQueryIterInit((unsigned char *)ehRequest->query, &parseIter); + VERIFY_NON_NULL(TAG, ehRequest->query, ERROR); - while(GetNextQuery(&parseIter)) - { - if(strncasecmp((char *)parseIter.attrPos, OIC_JSON_SUBJECT_NAME, parseIter.attrLen) == 0) - { - unsigned char base64Buff[sizeof(((OicUuid_t*)0)->id)] = {}; - uint32_t outLen = 0; - B64Result b64Ret = B64_OK; + // 'Subject' field is MUST for processing a querystring in REST request. + VERIFY_SUCCESS(TAG, + true == GetSubjectFromQueryString(ehRequest->query, &subject), + ERROR); - b64Ret = b64Decode((char *)parseIter.valPos, parseIter.valLen, base64Buff, - sizeof(base64Buff), &outLen); - - VERIFY_SUCCESS(TAG, (b64Ret == B64_OK && outLen <= sizeof(subject.id)), ERROR); - memcpy(subject.id, base64Buff, outLen); - } - if(strncasecmp((char *)parseIter.attrPos, OIC_JSON_RESOURCES_NAME, parseIter.attrLen) == 0) - { - resource = (char *)OICMalloc(parseIter.valLen); - VERIFY_NON_NULL(TAG, resource, ERROR); - OICStrcpy(resource, sizeof(resource), (char *)parseIter.valPos); - } - } + GetResourceFromQueryString(ehRequest->query, resource, sizeof(resource)); if(OC_STACK_RESOURCE_DELETED == RemoveACE(&subject, resource)) { ehRet = OC_EH_RESOURCE_DELETED; } - OICFree(resource); +exit: // Send payload to request originator SendSRMResponse(ehRequest, ehRet, NULL); -exit: return ehRet; } @@ -620,7 +720,7 @@ OCEntityHandlerResult ACLEntityHandler (OCEntityHandlerFlag flag, if (flag & OC_REQUEST_FLAG) { - // TODO : Handle PUT and DEL methods + // TODO : Handle PUT method OC_LOG (INFO, TAG, PCF("Flag includes OC_REQUEST_FLAG")); switch (ehRequest->method) { diff --git a/resource/csdk/security/src/doxmresource.c b/resource/csdk/security/src/doxmresource.c index 78d91ba..6725151 100644 --- a/resource/csdk/security/src/doxmresource.c +++ b/resource/csdk/security/src/doxmresource.c @@ -340,7 +340,7 @@ static bool UpdatePersistentStorage(OicSecDoxm_t * doxm) return bRet; } -static bool ValidateQuery(unsigned char * query) +static bool ValidateQuery(const char * query) { // Send doxm resource data if the state of doxm resource // matches with the query parameters. @@ -356,27 +356,54 @@ static bool ValidateQuery(unsigned char * query) return false; } + bool bOwnedQry = false; // does querystring contains 'owned' query ? + bool bOwnedMatch = false; // does 'owned' query value matches with doxm.owned status? + bool bDeviceIDQry = false; // does querystring contains 'deviceid' query ? + bool bDeviceIDMatch = false; // does 'deviceid' query matches with doxm.deviceid ? + OicParseQueryIter_t parseIter = {.attrPos = NULL}; - ParseQueryIterInit(query, &parseIter); + ParseQueryIterInit((unsigned char*)query, &parseIter); while(GetNextQuery(&parseIter)) { if(strncasecmp((char *)parseIter.attrPos, OIC_JSON_OWNED_NAME, parseIter.attrLen) == 0) { + bOwnedQry = true; if((strncasecmp((char *)parseIter.valPos, OIC_SEC_TRUE, parseIter.valLen) == 0) && (gDoxm->owned)) { - return true; + bOwnedMatch = true; } else if((strncasecmp((char *)parseIter.valPos, OIC_SEC_FALSE, parseIter.valLen) == 0) && (!gDoxm->owned)) { - return true; + bOwnedMatch = true; + } + } + + if(strncasecmp((char *)parseIter.attrPos, OIC_JSON_DEVICE_ID_NAME, parseIter.attrLen) == 0) + { + bDeviceIDQry = true; + OicUuid_t subject = {.id={0}}; + unsigned char base64Buff[sizeof(((OicUuid_t*)0)->id)] = {}; + uint32_t outLen = 0; + B64Result b64Ret = B64_OK; + + b64Ret = b64Decode((char *)parseIter.valPos, parseIter.valLen, base64Buff, + sizeof(base64Buff), &outLen); + + VERIFY_SUCCESS(TAG, (B64_OK == b64Ret && outLen <= sizeof(subject.id)), ERROR); + memcpy(subject.id, base64Buff, outLen); + if(0 == memcmp(&gDoxm->deviceID.id, &subject.id, sizeof(gDoxm->deviceID.id))) + { + bDeviceIDMatch = true; } } } - return false; + +exit: + return ((bOwnedQry ? bOwnedMatch : true) && (bDeviceIDQry ? bDeviceIDMatch : true)); } static OCEntityHandlerResult HandleDoxmGetRequest (const OCEntityHandlerRequest * ehRequest) @@ -390,7 +417,7 @@ static OCEntityHandlerResult HandleDoxmGetRequest (const OCEntityHandlerRequest if(ehRequest->query) { OC_LOG (INFO, TAG, PCF("HandleDoxmGetRequest processing query")); - if(!ValidateQuery((unsigned char *)ehRequest->query)) + if(!ValidateQuery(ehRequest->query)) { ehRet = OC_EH_ERROR; } diff --git a/resource/csdk/security/unittest/aclresourcetest.cpp b/resource/csdk/security/unittest/aclresourcetest.cpp index 9bbca6c..6ab674c 100644 --- a/resource/csdk/security/unittest/aclresourcetest.cpp +++ b/resource/csdk/security/unittest/aclresourcetest.cpp @@ -205,6 +205,35 @@ TEST(ACLResourceTest, GetACLResourceTests) OICFree(jsonStr); } } + +static OCStackResult populateAcl(OicSecAcl_t *acl, int numRsrc) +{ + OCStackResult ret = OC_STACK_ERROR; + memcpy(acl->subject.id, "2222222222222222", sizeof(acl->subject.id)); + acl->resourcesLen = numRsrc; + acl->resources = (char**)OICCalloc(acl->resourcesLen, sizeof(char*)); + VERIFY_NON_NULL(TAG, acl->resources, ERROR); + acl->resources[0] = (char*)OICMalloc(strlen("/a/led")+1); + VERIFY_NON_NULL(TAG, acl->resources[0], ERROR); + OICStrcpy(acl->resources[0], sizeof(acl->resources[0]), "/a/led"); + if(numRsrc == 2) + { + acl->resources[1] = (char*)OICMalloc(strlen("/a/fan")+1); + VERIFY_NON_NULL(TAG, acl->resources[1], ERROR); + OICStrcpy(acl->resources[1], sizeof(acl->resources[1]), "/a/fan"); + } + acl->permission = 6; + acl->ownersLen = 1; + acl->owners = (OicUuid_t*)OICCalloc(acl->ownersLen, sizeof(OicUuid_t)); + VERIFY_NON_NULL(TAG, acl->owners, ERROR); + memcpy(acl->owners->id, "1111111111111111", sizeof(acl->owners->id)); + + ret = OC_STACK_OK; +exit: + return ret; + +} + //'DELETE' ACL test TEST(ACLResourceTest, ACLDeleteWithSingleResourceTest) { @@ -220,19 +249,8 @@ TEST(ACLResourceTest, ACLDeleteWithSingleResourceTest) SetPersistentHandler(&ps, true); - //ACE to POST - memcpy(acl.subject.id, "2222222222222222", sizeof(acl.subject.id)); - acl.resourcesLen = 1; - acl.resources = (char**)OICCalloc(acl.resourcesLen, sizeof(char*)); - VERIFY_NON_NULL(TAG, acl.resources, ERROR); - acl.resources[0] = (char*)OICMalloc(strlen("/a/led")+1); - VERIFY_NON_NULL(TAG, acl.resources[0], ERROR); - OICStrcpy(acl.resources[0], sizeof(acl.resources[0]), "/a/led"); - acl.permission = 6; - acl.ownersLen = 1; - acl.owners = (OicUuid_t*)OICCalloc(acl.ownersLen, sizeof(OicUuid_t)); - VERIFY_NON_NULL(TAG, acl.owners, ERROR); - memcpy(acl.owners->id, "1111111111111111", sizeof(acl.owners->id)); + //Populate ACL + VERIFY_SUCCESS(TAG, (OC_STACK_OK == populateAcl(&acl, 1)), ERROR); //GET json POST payload jsonStr = BinToAclJSON(&acl); @@ -288,22 +306,10 @@ TEST(ACLResourceTest, ACLDeleteWithMultiResourceTest) SetPersistentHandler(&ps, true); - memcpy(acl.subject.id, "2222222222222222", sizeof(acl.subject.id)); - acl.resourcesLen = 2; - acl.resources = (char**)OICCalloc(acl.resourcesLen, sizeof(char*)); - VERIFY_NON_NULL(TAG, acl.resources, ERROR); - acl.resources[0] = (char*)OICMalloc(strlen("/a/led")+1); - VERIFY_NON_NULL(TAG, acl.resources[0], ERROR); - OICStrcpy(acl.resources[0], sizeof(acl.resources[0]), "/a/led"); - acl.resources[1] = (char*)OICMalloc(strlen("/a/fan")+1); - VERIFY_NON_NULL(TAG, acl.resources[1], ERROR); - OICStrcpy(acl.resources[1], sizeof(acl.resources[1]), "/a/fan"); - acl.permission = 6; - acl.ownersLen = 1; - acl.owners = (OicUuid_t*)OICCalloc(acl.ownersLen, sizeof(OicUuid_t)); - VERIFY_NON_NULL(TAG, acl.owners, ERROR); - memcpy(acl.owners->id, "1111111111111111", sizeof(acl.owners->id)); + //Populate ACL + VERIFY_SUCCESS(TAG, (OC_STACK_OK == populateAcl(&acl, 2)), ERROR); + //GET json POST payload jsonStr = BinToAclJSON(&acl); VERIFY_NON_NULL(TAG, jsonStr, ERROR); @@ -345,3 +351,44 @@ exit: OICFree(jsonStr); } +//'GET' with query ACL test + +TEST(ACLResourceTest, ACLGetWithQueryTest) +{ + OCEntityHandlerRequest ehReq = OCEntityHandlerRequest(); + static OCPersistentStorage ps = OCPersistentStorage(); + OicSecAcl_t acl = OicSecAcl_t(); + char *jsonStr = NULL; + OCEntityHandlerResult ehRet = OC_EH_ERROR; + char query[] = "sub=MjIyMjIyMjIyMjIyMjIyMg==;rsrc=/a/led"; + + SetPersistentHandler(&ps, true); + + //Populate ACL + VERIFY_SUCCESS(TAG, (OC_STACK_OK == populateAcl(&acl, 1)), ERROR); + + //GET json POST payload + jsonStr = BinToAclJSON(&acl); + VERIFY_NON_NULL(TAG, jsonStr, ERROR); + + //Create Entity Handler POST request payload + ehReq.method = OC_REST_POST; + ehReq.payload = (OCPayload*)OCSecurityPayloadCreate(jsonStr); + ehRet = ACLEntityHandler(OC_REQUEST_FLAG, &ehReq); + EXPECT_TRUE(OC_EH_ERROR == ehRet); + + //Create Entity Handler GET request wit query + ehReq.method = OC_REST_GET; + ehReq.query = (char*)OICMalloc(strlen(query)+1); + VERIFY_NON_NULL(TAG, ehReq.query, ERROR); + OICStrcpy(ehReq.query, strlen(query)+1, query); + + ehRet = ACLEntityHandler(OC_REQUEST_FLAG, &ehReq); + EXPECT_TRUE(OC_EH_OK == ehRet); + +exit: + // Perform cleanup + OCPayloadDestroy(ehReq.payload); + OICFree(ehReq.query); + OICFree(jsonStr); +} diff --git a/resource/csdk/security/unittest/doxmresource.cpp b/resource/csdk/security/unittest/doxmresource.cpp index cfee71a..29ca779 100644 --- a/resource/csdk/security/unittest/doxmresource.cpp +++ b/resource/csdk/security/unittest/doxmresource.cpp @@ -140,6 +140,18 @@ TEST(DoxmEntityHandlerTest, DoxmEntityHandlerValidRequest) OICFree(req.query); } +TEST(DoxmEntityHandlerTest, DoxmEntityHandlerDeviceIdQuery) +{ + EXPECT_EQ(OC_STACK_INVALID_PARAM, InitDoxmResource()); + char query[] = "deviceid=MjIyMjIyMjIyMjIyMjIyMg=="; + OCEntityHandlerRequest req = OCEntityHandlerRequest(); + req.method = OC_REST_GET; + req.query = OICStrdup(query); + EXPECT_EQ(OC_EH_ERROR, DoxmEntityHandler(OCEntityHandlerFlag::OC_REQUEST_FLAG, &req)); + + OICFree(req.query); +} + //BinToDoxmJSON Tests TEST(BinToDoxmJSONTest, BinToDoxmJSONNullDoxm) { -- 2.7.4