/*! \Copyright: Sertainty Corporation, 2020. All Rights Reserved. \File: sample_workflow_delegate.c \Brief: Demonstrates UXP authentication via Delegate Service. This sample exercises various Delegate Service operations using the UXP SDK. 1. Users, IDs, and Delegates are added to the Delegate Service database. 2. Users subscribe to the User Delegate ID. 3. UXP is created with the Delegate ID. 4. Upon access to a UXP by a User Subscriber of the Delegate ID, subscription metadata, like max access and access count, is observed. \Details: A UXP Identity database is a UXP Object that permits SQL access. The database is used to manage named user and Delegate definitions. A Delegate ID can be used to allow access to a UXP Object without having the named user's Identity embedded within the UXP Object. Access is still controlled by the data owner, but access is managed via the Delegate Sevice Database. This is Delegate ID containing named user and Delegate definitions needs to be exported as a *.iic file and stored in a location where the Delegate Service can access it. For an expanded explanation for Delegate Service, go to the Data Services Guide. \Author: Greg Smith 02/17/2020 \Edited: Melani Smith Weed 07/27/2020 \Note: Application expects to find the necessary source files in the current working folder. */ #include "uxpfile_c.h" #include "uxplicense_c.h" #include "uxpcredential_c.h" #include "uxlbytearray_c.h" #include "uxpid_c.h" #include "uxpdataservices_c.h" #include "uxpsys_c.h" #include #include #include /** * @brief Private external user validation routine. * @param username User attempting access. * @param data Optional user data. * @return True if valid. */ static int myValidate(const char *username, void *data) { const char *str = (const char *)data; return 1; } /** * @brief Callback function to allow host to evaluate a valid delegate user lookup. * @param username User attempting access. * @param app_data1 Optional application data. * @param app_data2 Optional application data. * @param owner Delegate owner. * @param delegate Delegate name. * @return */ static int myDelegateLookup(const char *username, const char *app_data1, const char *app_data2, const char *owner, const char *delegate) { printf("\n********************************************\n"); printf(" Delegate lookup callback:\n\n"); printf(" Username: %s\n", username); printf(" App data1: %s\n", app_data1); printf(" App data2: %s\n", app_data2); printf(" Delegate owner: %s\n", owner); printf(" Delegate name: %s\n", delegate); printf("********************************************\n\n"); return 1; } /** * @brief Checks for an error and logs message. * @param handle Handle to check. * @param msg Message to display. */ void checkError(void *handle, const char *msg) { if (uxpsys_hasError(handle)) { const char *errMsg = uxpsys_getErrorMessage(handle); printf("%s error: %s\n", msg, errMsg); exit(1); } } /** * Get challenge from the user. * @param ch Challenge handle. * @return True if accepted. False if cancelled. */ static int getResponse(uxpChallengeHandle ch); /** * @brief Main entry point. * @param argc Number of arguments. * @param argv Array of argument tokens. * @return 0 - success, 1 - error. */ int main(int argc, char *argv[]) { uxpFileHandle appHandle; int status; int done,i; uxlByteArray *buffer, *id, *id2, *id3; uxpCallStatusHandle callstatus = uxpsys_newCallStatusHandle(); uxpDataServiceHandle lib = uxpds_getService(); long long session; uxpListHandle tmplist; printf("\n\nSample C Delegate Application using ID\n\n"); /* Allocate a new buffer. Automatically zeros it. */ buffer = uxpba_newHandle(); id = uxpba_newHandle(); id2 = uxpba_newHandle(); id3 = uxpba_newHandle(); /* Set up log file and initialize library. Must do this before any active calls. */ uxpsys_initLibrary(buffer, argc, argv, "sertainty.lic", "SertintyOne", "SampleC", "Sample 2.0.0"); if (uxpba_getSize(buffer)) { printf("Error initializing environment: %s\n", uxpba_getData(buffer)); return 1; } printf("Setting up required IDs\n"); /* Create a new ID from the sampleid.xml ID definition. */ uxpsys_fileReadAll(callstatus, "sampleid.xml", buffer); checkError(callstatus, "Read sampleid.xml"); uxpid_publishToBuffer(callstatus, id, uxpba_getData(buffer)); checkError(callstatus, "Create sampleid.xml ID"); /* Create a new ID from the sampleid2.xml ID definition. */ uxpsys_fileReadAll(callstatus, "sampleid2.xml", buffer); checkError(callstatus, "Read sampleid2.xml"); uxpid_publishToBuffer(callstatus, id2, uxpba_getData(buffer)); checkError(callstatus, "Create sampleid2.xml ID"); /* Create new Delegate Service database from ID. */ printf("Opening new data service database\n\n"); uxpds_initializeDatabase(lib, "dataservices.db", id, "SampleUser@myemail.com","",""); checkError(lib, "Initialize data services database"); /* Open the database ... includes authentication. */ printf("Credentials necessary to access database:\n"); printf(" Username = SampleUser@myemail.com\n"); printf(" Challenge 1 = Response 1\n"); printf(" Challenge 2 = Response 2\n"); printf(" ... \n"); printf(" Challenge 10 = Response 10\n\n"); uxpds_setValidationCallback(lib, myValidate, ""); uxpds_setDelegateCallback(lib, myDelegateLookup); uxpds_openDatabase(lib, "dataservices.db", 0); checkError(lib, "Open data services database"); session = uxpds_openSession(lib, "SampleUser@myemail.com"); checkError(lib, "Data services session"); done = 0; while (!done) { status = uxpds_authenticate(lib, session); switch (status) { case StatusAuthorized: done = 1; break; case StatusNotAuthorized: printf("You are not authorized\n"); uxpds_closeSession(lib, session); uxpds_closeDatabase(lib); return 1; case StatusChallenged: for (i = 0; i < uxpds_getChallengeCount(lib, session); i++) { uxpChallengeHandle ch = uxpds_getChallenge(lib, session, i); getResponse(ch); uxpds_addResponse(lib, session, ch); uxpch_freeHandle(ch); } break; default: break; } } uxpds_setServer(lib, session, "file:///local"); printf("\nCreating 2nd user and delegate\n"); /* Create a 2nd user. One user already exists. This first user was created when the Delegate Service database was created. */ uxpds_newUser(lib, session, "SampleUser2@myemail.com", "User number 2","Joe User", "SampleUser2@myemail.com", PRIV_NORMAL,"Sample App Data","More App Data"); checkError(lib, "Create user SampleUser2@myemail.com"); uxpba_clearData(buffer); uxpds_newUserId(lib, session, "SampleUser2@myemail.com", "MyID", 12, "My main ID", id2, buffer); checkError(lib, "Create user ID for SampleUser2@myemail.com"); /* Create a Delegate ID. */ uxpds_newDelegate(lib, session, "SampleUser@myemail.com", "MyList", "This is a test delegate", 0, 1, 24, 0); checkError(lib, "Create delegate MyList"); /* Create a subscription for user number 2. This will permit user 2 to connect to UXP Objects that have the Delegate ID as a user. When the validation occurs, user 2 will be able to login using the Delegate ID that was registered when the user was added to the Delegate Server database. */ uxpds_subscribe(lib, session, "SampleUser@myemail.com", "MyList", "SampleUser2@myemail.com", 0, 1, 2, 10, 0); checkError(lib, "Subscribe to MyList"); printf("\nUsers\n"); printf("-----\n"); uxlByteArray *formalname = uxpba_newHandle(); uxlByteArray *description = uxpba_newHandle(); uxlByteArray *email = uxpba_newHandle(); uxlByteArray *app_data1 = uxpba_newHandle(); uxlByteArray *app_data2 = uxpba_newHandle(); tmplist = uxpds_getUsers(lib, session); checkError(lib, "Get users"); for (i = 0; i < uxplist_count(tmplist); i++) { uxlByteArray *tmp = uxplist_getByteArray(tmplist, i); int privs; if (i) printf("\n"); printf("Username: %s\n", uxpba_getData(tmp)); uxpds_getUser(lib, session, uxpba_getData(tmp), formalname, description, email, &privs, app_data1, app_data2); checkError(lib, "Get User"); printf("Formalname: %s\n", uxpba_getData(formalname)); printf("Description: %s\n", uxpba_getData(description)); printf("Email: %s\n", uxpba_getData(email)); printf("Privileges: %d\n", privs); printf("AppData1: %s\n", uxpba_getData(app_data1)); printf("AppData2: %s\n", uxpba_getData(app_data2)); uxpba_freeHandle(tmp); } uxplist_freeList(tmplist); uxpba_freeHandle(formalname); uxpba_freeHandle(description); uxpba_freeHandle(email); uxpba_freeHandle(app_data1); uxpba_freeHandle(app_data2); printf("\nDelegates for SampleUser@myemail.com\n"); printf("------------------------------------\n"); description = uxpba_newHandle(); int permit_offline, offline_duration, access_max, access_count; uxlByteArray *checksum = uxpba_newHandle(); time_t expiration; tmplist = uxpds_getDelegates(lib, session, "SampleUser@myemail.com"); checkError(lib, "Get delegates"); for (i = 0; i < uxplist_count(tmplist); i++) { uxlByteArray *tmp = uxplist_getByteArray(tmplist, i); if (i) printf("\n"); uxpds_getDelegate(lib, session, "SampleUser@myemail.com", uxpba_getData(tmp), description, &expiration, &permit_offline, &offline_duration, &access_max, checksum, id3); checkError(lib, "Get delegate"); printf("Name: %s\n", uxpba_getData(tmp)); printf("Description: %s\n", uxpba_getData(description)); printf("Expiration: %d\n", (int)expiration); printf("Permit Offline: %d\n", permit_offline); printf("Offline Duration: %d\n", offline_duration); printf("Access Max: %d\n", access_max); printf("Checksum: %s\n", uxpba_getData(checksum)); printf("Uxpid: ******\n"); uxpba_freeHandle(tmp); } uxpba_freeHandle(description); uxpba_freeHandle(checksum); uxplist_freeList(tmplist); tmplist = uxpds_getSubscribers(lib, session, "SampleUser@myemail.com", "MyList"); checkError(lib, "Get subscribers"); printf("\nSubscribers\n"); printf("-----------\n"); for (i = 0; i < uxplist_count(tmplist); i++) { uxlByteArray *tmp = uxplist_getByteArray(tmplist, i); uxpds_getSubscription(lib, session, "SampleUser@myemail.com", "MyList", uxpba_getData(tmp), &expiration, &permit_offline, &offline_duration, &access_max, &access_count); checkError(lib, "Get subscription"); printf("Delegate: MyList\n"); printf("Subscriber: %s\n", uxpba_getData(tmp)); printf("Expiration: %d\n", (int)expiration); printf("Permit Offline: %d\n", permit_offline); printf("Offline Duration: %d\n", offline_duration); printf("Access Max: %d\n", access_max); printf("Access Count: %d\n", access_count); uxpba_freeHandle(tmp); } uxplist_freeList(tmplist); printf("\nSubscriptions for SampleUser2@myemail.com\n"); printf("-----------------------------------------\n"); tmplist = uxpds_getSubscriptions(lib, session, "SampleUser2@myemail.com"); checkError(lib, "Get subscriptions"); for (i = 0; i < uxplist_count(tmplist); i++) { uxlByteArray *tmp = uxplist_getByteArray(tmplist, i); printf("Owner / Name: %s\n", uxpba_getData(tmp)); uxpba_freeHandle(tmp); } uxplist_freeList(tmplist); /* Create a new UXP Object with the Delegate ID. */ printf("\nCreating new UXP containing delegate: sample.uxp\n"); printf("Protecting document data.pdf\n"); /* The Delegate ID is used to protect data in this sample. Though easy to do, this isn't recommended because there is only limited or default policy control. It is recommended to create a new ID that includes a named user as well as the Delegate User from the Delegate ID. * From there, set the policies for the new ID that includes both the named user and the Delegate User and publish ID definition to an *.iic file. */ appHandle = uxpfile_newHandle(); uxpfile_openNewFile2(appHandle, "sample_delegate.uxp", uxpba_getData(id3), uxpba_getSize(id3), IdBuffer, ModifierReplace, 0); checkError(appHandle, "Create new UXP"); /* Create a new virtual file inside the data.*/ uxpfile_addVirtualFromFile(appHandle, "data.pdf", "data.pdf", -1, -1, ModifierCompress); checkError(appHandle, "Add data to UXP"); printf("Closing new UXP\n"); uxpfile_close(appHandle); printf("Opening new UXP\n\n"); printf("Credentials necessary to access UXP via Delegate Services:\n\n"); printf(" Username = SampleUser2@myemail.com\n"); printf(" Challenge 1 = MyResponse 1\n"); printf(" Challenge 2 = MyResponse 2\n"); printf(" ... \n"); printf(" Challenge 10 = MyResponse 10\n\n"); uxpfile_openFile(appHandle, "sample_delegate.uxp", ShareReadOnly); checkError(appHandle, "Opening UXP"); done = 0; while (!done) { status = uxpfile_authenticate(appHandle, 0); switch (status) { case StatusAuthorized: printf("\nYou're authorized via Delegate Services\n"); done = 1; break; case StatusNotAuthorized: printf("\nYou're not authorized\n"); return 1; case StatusChallenged: for (i = 0; i < uxpfile_getChallengeCount(appHandle); i++) { uxpChallengeHandle ch = uxpfile_getChallenge(appHandle, i); getResponse(ch); uxpfile_addResponse(appHandle, ch); uxpch_freeHandle(ch); } break; default: break; } } /* Read the virtual file and write it back out to disk. */ printf("\nExtracting data.pdf to copy2.pdf\n"); uxpfile_exportVirtualFile(appHandle, "data.pdf", "copy.pdf", ModifierReplace); checkError(appHandle, "Extracting data"); uxpfile_close(appHandle); tmplist = uxpds_getSubscribers(lib, session, "SampleUser@myemail.com", "MyList"); checkError(lib, "Get subscribers"); printf("\nSubscribers after Remote Authentication\n"); printf("---------------------------------------\n"); for (i = 0; i < uxplist_count(tmplist); i++) { uxlByteArray *tmp = uxplist_getByteArray(tmplist, i); uxpds_getSubscription(lib, session, "SampleUser@myemail.com", "MyList", uxpba_getData(tmp), &expiration, &permit_offline, &offline_duration, &access_max, &access_count); checkError(lib, "Get subscription"); printf("Delegate: MyList\n"); printf("Subscriber: %s\n", uxpba_getData(tmp)); printf("Expiration: %d\n", (int)expiration); printf("Permit Offline: %d\n", permit_offline); printf("Offline Duration: %d\n", offline_duration); printf("Access Max: %d\n", access_max); printf("Access Count: %d\n", access_count); uxpba_freeHandle(tmp); } uxplist_freeList(tmplist); /* Clean up. */ uxpds_closeDatabase(lib); uxpba_freeHandle(id); uxpba_freeHandle(id2); uxpba_freeHandle(id3); uxpba_freeHandle(buffer); printf("\nSample finished running\n"); return 0; } /** * Get challenge from the user. * @param ch Challenge handle. * @return True if accepted. False if cancelled. */ static int getResponse(uxpChallengeHandle ch) { char value[1000]; char c, *ptr; uxlByteArray *prompt = uxpba_newHandle(); uxpch_getPrompt(ch, prompt); uxpch_startTimer(ch); for (;;) { printf("%s> ", uxpba_getData(prompt)); ptr = value; for (;;) { c = getchar(); if (c == '\n' || c == '\r') break; *ptr++ = c; } *ptr = '\0'; if (!strcmp(value, "Q") || !strcmp(value, "q")) return 0; if (strlen(value) > 0) break; } uxpch_endTimer(ch); uxpch_setValueString(ch, value); uxpba_freeHandle(prompt); return 1; }