/*! \Copyright: Sertainty Corporation, 2020. All Rights Reserved. \File: sample_workflow_delegate.cpp \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.h" #include "uxplicense.h" #include "uxpcredential.h" #include "uxlbytearray.h" #include "uxpid.h" #include "uxpidmanager.h" #include "uxpdataservices.h" #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 = reinterpret_cast(data); return true; } /** * @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) { std::cout << std::endl << "********************************************" << std::endl; std::cout << "Delegate lookup callback:" << std::endl << std::endl; std::cout << " Username: " << username << std::endl; std::cout << " App data1: " << app_data1 << std::endl; std::cout << " App data2: " << app_data2 << std::endl; std::cout << " Delegate owner: " << owner << std::endl; std::cout << " Delegate name: " << delegate << std::endl; std::cout << "********************************************" << std::endl << std::endl; return true; } /** * Response from the user. * @param ch Challenge Object * @return True if accepted. False if cancelled. */ static int getResponse(uxp::challenge &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[]) { bool licensed; std::string outfile("sample_delegate.uxp"); std::string dbspec("dataservices.db"); uxl::bytearray idbuf, outbuf, id, id2; int status; bool done; uxp_int64 session; std::vector challenges; uxp::delegate_t delegate; uxp::user_t user; uxp::userid_t userid; uxp::dataServices &lib = *uxp::dataServices::getService(); uxp::subscription_t subscription; char identstring[1000]; std::cout << std::endl << std::endl << "Sample C++ Delegate Application using ID" << std::endl << std::endl; /* Set up log file and initialize library. Must do this before any active calls. */ try { uxp::sys::setLogFile("SampleDelegateCPP", "Sample Delegate C++ 1.0.0"); /* Attempt to find the license file. */ try { uxp::sys::initializeLibrary(argc, argv, "sertainty.lic", "SertintyOne"); licensed = true; } catch (uxl::exception &) { licensed = false; } uxp::sys::setLogging(uxpLogInfo); if (!licensed) { std::cout << "UXP-432, A valid license was not found." << std::endl; return 1; } strcpy(identstring, "In validation"); lib.setValidationCallback(myValidate, identstring); lib.setDelegateCallback(myDelegateLookup); /* Create a new ID from the sampleid.xml ID definition. */ std::cout << "Setting up required IDs" << std::endl; uxp::sys::fileReadAll("sampleid.xml", idbuf); uxp::idManager::publishToBuffer(id, idbuf); uxp::sys::fileReadAll("sampleid2.xml", idbuf); uxp::idManager::publishToBuffer(id2, idbuf); /* Create new Delegate Service database from ID */ std::cout << "Opening new data services database" << std::endl << std::endl; lib.initializeDatabase(dbspec, id, "SampleUser@myemail.com"); /* Open the database ... includes authentication. */ std::cout << "Credentials necessary to access database:" << std::endl << std::endl; std::cout << " Username = SampleUser@myemail.com" << std::endl; std::cout << " Challenge 1 = Response 1" << std::endl; std::cout << " Challenge 2 = Response 2" << std::endl; std::cout << " ... " << std::endl; std::cout << " Challenge 10 = Response 10" << std::endl << std::endl; session = lib.openSession("SampleUser@myemail.com"); done = false; while (!done) { status = lib.authenticate(session); switch (status) { case StatusAuthorized: done = true; break; case StatusNotAuthorized: std::cout << "You are not authorized" << std::endl; lib.closeSession(session); lib.closeDatabase(); return 1; case StatusChallenged: for (auto t_ch: lib.getChallenges(session)) { status = getResponse(t_ch); if (!status) { std::cout << "Canceled" << std::endl; lib.closeSession(session); lib.closeDatabase(); return 1; } lib.addResponse(session, t_ch); } break; default: break; } } lib.setServer(session, "file:///local"); std::cout << std::endl << "Creating 2nd user and delegate" << std::endl; /* Create a 2nd user. One user already exists. This first user was created when the Delegate Service database was created. */ lib.newUser(session, "SampleUser2@myemail.com", "User number 2","Joe User", "SampleUser2@myemail.com", PRIV_NORMAL,"Sample App Data","More App Data"); lib.newUserId(session, "SampleUser2@myemail.com", "MyID", 12, "My main ID", id2, uxl::bytearray()); /* Create a Delegate ID. */ lib.newDelegate(session, "SampleUser@myemail.com", "MyList", "This is a test delegate", uxl::datetime(), 1, 24, 0); /* 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. */ lib.subscribe(session, "SampleUser@myemail.com", "MyList", "SampleUser2@myemail.com", uxl::datetime(), 1, 2, 10, 0); std::cout << std::endl << "Users" << std::endl; std::cout << "-----" << std::endl; for (auto const &p: lib.getUsers(session)) { std::cout << std::endl << "Username: " << p << std::endl; lib.getUser(session, p, user.formalname, user.description, user.email, user.privileges, user.app_data1, user.app_data2); std::cout << "Formalname: " << user.formalname << std::endl; std::cout << "Description: " << user.description << std::endl; std::cout << "Email: " << user.email << std::endl; std::cout << "Privileges: " << user.privileges << std::endl; std::cout << "AppData1: " << user.app_data1 << std::endl; std::cout << "AppData2: " << user.app_data2 << std::endl; } std::cout << std::endl << "Delegates for SampleUser@myemail.com" << std::endl; std::cout << "------------------------------------" << std::endl; for (auto const &p: lib.getDelegates(session, "SampleUser@myemail.com")) { std::cout << std::endl; lib.getDelegate(session, "SampleUser@myemail.com", p, delegate.description, delegate.expiration, delegate.permit_offline, delegate.offline_duration, delegate.access_max, delegate.checksum, delegate.uxpid); std::cout << "Name: " << p << std::endl; std::cout << "Description: " << delegate.description << std::endl; std::cout << "Expiration: " << delegate.expiration.toString() << std::endl; std::cout << "Permit Offline: " << delegate.permit_offline << std::endl; std::cout << "Offline Duration: " << delegate.offline_duration << std::endl; std::cout << "Access Max: " << delegate.access_max << std::endl; std::cout << "Checksum: " << delegate.checksum << std::endl; std::cout << "Uxpid: " << "******" << std::endl; } std::cout << std::endl << "Subscribers" << std::endl; std::cout << "-----------" << std::endl; for (auto const &p: lib.getSubscribers(session, "SampleUser@myemail.com", "MyList")) { lib.getSubscription(session, "SampleUser@myemail.com", "MyList", p, subscription.expiration, subscription.permit_offline, subscription.offline_duration, subscription.access_max, subscription.access_count); std::cout << "Delegate: MyList" << std::endl; std::cout << "Subscriber: " << p << std::endl; std::cout << "Expiration: " << subscription.expiration.toString() << std::endl; std::cout << "Permit Offline: " < ", prompt.data()); value.clear(); for (;;) { c = static_cast(getchar()); if (c == '\n' || c == '\r') break; value.push_back(c); } if (value == "q" || value == "Q") return 0; if (!value.empty()) break; } ch.endTimer(); ch.setValue(value); return 1; }