. . "VAL_AuthenticateVspTutorial" . . . "2017-06-13T06:02:15Z" . . . . "VAL_AuthenticateVspTutorial" . . . . "2017-06-29T07:33:17Z" . . . . . . "2017-06-29T07:33:17.485276"^^ . "9a95db830d7cc3014901c9ebf1c39bcd" . . . "VAL_AuthenticateVspTutorial" . . . "2017-06-13T06:02:15.641255"^^ . . . . . . . . "%META:TOPICPARENT{name=\"ValQuickStartGuide\"}%\n\n---+ VAL VSP Authentication Tutorial\n\n%TOC%\n\n---++ Introduction\n\nVAL, the Virtuoso Authentication Layer, provides a means to easily add authentication and ACL support to existing or new VSP-based applications. This tutorial demonstrates\nthe main steps used to do so. We are using curi — the Compressed URI Service — as an example.\n\nFor curi we want to add login information, a means for the user to logout, and ACLs to protect the service.\n\n---++ Tutorial\n\n---+++ Step 1 - Check for existing authentication information\n\nThe first and simplest step is to check if the user has already provided authentication information as supported by VAL. This can \nbe achieved by simply calling \n[[http://docs.openlinksw.com/val/group__val__auth__module__main.html#gae4565eb3fc3b48886af76de88fb72dd2][VAL.DBA.authentication_details_for_connection()]] \nat the top of the VSP page:\n\n\nDECLARE val_serviceId, \n val_sid, \n val_realm, \n val_uname VARCHAR ;\nDECLARE val_isRealUser INTEGER ;\n\n-- Important since \"realm\" is an \"INOUT\" parameter!\nval_realm := NULL;\n\nVAL.DBA.authentication_details_for_connection \n (\n sid=>val_sid,\n serviceId=>val_serviceId,\n uname=>val_uname,\n isRealUser=>val_isRealUser,\n realm=>val_realm\n );\n\n\nNote: [[http://docs.openlinksw.com/val/group__val__auth__module__main.html#gae4565eb3fc3b48886af76de88fb72dd2][VAL.DBA.authentication_details_for_connection()]] \nwill honor a non-NULL realm parameter and only return authentication data for\nthe given realm. Additionally it will honor the app_realm setting in the virtual directory serving the page in question. Thus, there are\ntwo ways to define the realm for an application:\n * Set it in the virtual directory\n * Force it manually via the realm parameter\n\nAfter the call to [[http://docs.openlinksw.com/val/group__val__auth__module__main.html#gae4565eb3fc3b48886af76de88fb72dd2][VAL.DBA.authentication_details_for_connection()]], \nthe application can use the information. The most important one is\nthe value of val_serviceId which defines who is authenticated. If it is NULL, then the user has not authenticated yet.\n\n---+++ Step 2 - Add Log-in and Log-out Links\n\nVAL provides an authentication (log in) page and a logout page to support the most simple login and logout links possible. Given that the application\npage is stored in pageUrl, the following links can be used:\n\n * Authenticate (log in)\n\n\">Login\n\n * Log out\n\n\">Logout\n\n\nHowever, in our case a dedicated login page is more desirable, since it allows us to configure certain aspects of authenticate.vsp.\nThus, we create a new page login.vsp with the following content (or at least parts of it):\n\n\n\n\n\n\nThe settings should be obvious:\n| __val_auth_page__ | We tell authenticate.vsp to use login.vsp instead of its own URL for all login links. |\n| __val_req_res_label__ | A custom label for the login dialog to tell the user which service they log into. |\n| __val_oauth_scope__ | The optional OAuth scope to use (basic, profile, or dav). This is only of interest for applications that reuse the created OAuth sessions for additional API calls to the 3rd-party service. |\n\n\nSo we end up with code for creating a login/logout box like the following:\n\nIF (val_serviceId IS NOT NULL ) \n {\n http (sprintf ('Logged in as %s', val_serviceId));\n http (sprintf ('Logout', pageUrl));\n }\nELSE\n {\n http (sprintf ('Login', pageUrl));\n }\n\n\nWhen the user authenticates, they will be redirected to the pageUrl with a newly created sid cookie. The logout page will remove that\ncookie.\n\nTip: A slightly nicer logged in message with link can be created with code like the following which makes use of the two\nutility procedures [[http://docs.openlinksw.com/val/group__val__auth__module__tools.html#ga43385dfc887ccad03701cfc9f4a143c0][VAL.DBA.get_profile_url()]] and [[http://docs.openlinksw.com/val/group__val__auth__module__tools.html#gaca32625bec8fc5666c905918997e3c96][VAL.DBA.get_profile_name()]]:\n\nDECLARE x, n VARCHAR;\n\nx := VAL.DBA.get_profile_url (val_serviceId);\n\nn := COALESCE (VAL.DBA.get_profile_name (val_serviceId), val_serviceId);\n\nIF (NOT x IS NULL)\n http (sprintf ('%V', x, n));\nELSE\n http (n);\n\n\nWarning: Be aware that WebID logout is not always possible, as it requires a redirect to the unprotected (i.e., HTTP instead of HTTPS) application page.\n\n---+++ Step 3 - Set Up 40x Pages\n\nA typical situation for authentication-enabled applications is forcing the user to authenticate. Ideally this is done by combining 40x page options in the\nvirtual directory with VAL's authenticate.vsp page (which we used above for the login links). Here, we simply create a new file 40x.vsp\nwhich has the following content:\n\n\n\n\n\n\nauthenticate.vsp can be configured via a set of connection settings:\n| __val_req_res__ | The resource which is protected, i.e., which requires the login. This is only used to retrieve ownership information for the \"request access\" dialog that authenticate.vsp will show if access was denied. This will default to the returnto URL if not provided, and should that also be NULL (as is the case if authenticate.vsp is used as 40x_page) then the requested URL will be used. |\n| __val_req_acl_scope__ | The ACL scope in which the above resource is protected. This is only used to retrieve ownership information for the \"request access\" dialog that authenticate.vsp will show if access was denied. If not given, then no \"request access\" dialog is shown. |\n| __val_req_res_label__ | An optional label for the login dialog showing the user for which service they are authenticating. |\n| __val_auth_page__ | We tell authenticate.vsp to use our custom page login.vsp instead of its own URL for all login links. |\n| __val_err_msg__ | An error message indicating any kind of error. This should be set to http_param ('error.msg') for the simple reason that Virtuoso does clear the http params before processing the 40x page. |\n\nThis page will be used as 40x page in the virtual directory configuration:\n\n\nDB.DBA.VHOST_DEFINE (\n lpath=>'/c',\n ppath=>'/DAV/VAD/c_uri/',\n is_dav=>1,\n vsp_user=>'CURI',\n ses_vars=>0,\n opts=>vector (\n 'url_rewrite', 'c_uri_lst',\n '401_page', '40x.vsp',\n '403_page', '40x.vsp'),\n is_default_host=>0\n);\n\n\nThen the application can raise a permission denied error as shown in the following example:\n\n\nIF (val_serviceId IS NULL) \n {\n http_status_set(401);\n }\nELSE\n {\n connection_set ('__val_denied_service_id__', val_serviceId);\n http_status_set(403);\n }\nRETURN '';\n\n\nIf val_serviceId is NULL, then the user has not logged in, and the application simply requests that they do. Otherwise, 403 indicates that\npermission was denied to the authenticated user. The authenticated user is communicated to authenticate.vsp via the __val_denied_service_id__\nconnection setting.\n\n\n---+++ Step 4 - Use ACL Rules to Protect a Web Service\n\nAbove we saw how to use authenticate.vsp as a 40x_page. Now we will add ACL protection to the curi\nservice, and put the new 40x_page to use.\n\nWe want to be able to grant some people the right to create new compressed URIs, and others the right to read these. To that end, we define a new scope,\nurn:virtuoso:val:scopes:curi, which is only used for curi, and a virtual resource URI which is used to grant permissions, urn:virtuoso:access:curi.\nThese URIs are arbitrary; the scheme we use here is meant to be easily recognizable. In theory they could be any URI one wanted to use.\n\nVAL makes use of scope definitions to get default access modes for disabled scopes (the default). Thus we start by defining our new scope in the\ncorresponding VAL ACL schema graph (Hint: standard scopes for DAV, etc., are defined in the OpenLink ACL ontology, [[http://www.openlinksw.com/c/9BWWW4FP][http://www.openlinksw.com/ontology/acl#]]; example: [[http://www.openlinksw.com/c/9DGR44OK][oplacl:Dav]]):\n\n\nSPARQL\nPREFIX acl: \nPREFIX oplacl: \nINSERT\n INTO \n {\n a oplacl:Scope ;\n rdfs:label \"Compressed URIs\" ;\n rdfs:comment \"\"\"SQL ACL scope which contains all ACL rules granting permission to \n create and read compressed URIs. By default anyone can fully use \n the service.\"\"\" ;\n oplacl:hasApplicableAccess oplacl:Read , \n oplacl:Write ;\n oplacl:hasDefaultAccess oplacl:Read , \n oplacl:Write .\n };\n\n\nThe most important part is oplacl:hasDefaultAccess, which defines the access modes used when ACL evaluation has not been enabled for the curi scope. In\nsuch case, everyone is allowed to create and read compressed URIs.\n\nNow, at the top of the create.vsp page which allows to create new compressed URIs, we add the following ACL check (after the code from\nabove):\n\n\nIF ( NOT val_isRealUser \n OR NOT VAL.DBA.is_admin_user (val_uname)\n ) \n {\n IF ( NOT VAL.DBA.check_access_mode_for_resource \n (\n serviceId=>val_serviceId,\n resource=>'urn:virtuoso:access:curi',\n realm=>val_realm,\n scope=>'urn:virtuoso:val:scopes:curi',\n mode=>VAL.DBA.oplacl_iri ('Write'),\n webidGraph=>val_webidGraph,\n certificate=>val_cert,\n honorScopeState=>1\n )\n ) \n {\n connection_set ('__val_denied_service_id__', val_serviceId);\n connection_set ('__val_req_acl_mode__', VAL.DBA.oplacl_iri ('Write'));\n\n IF (val_serviceId is NULL)\n http_status_set(401);\n ELSE\n http_status_set(403);\n RETURN '';\n }\n }\n\n\nSome of this code we already know from before. But the big first part is new. First we check if we are logged in as an admin user. VAL provides\nus with the convenient procedure \n[[http://docs.openlinksw.com/val/group__val__auth__module__tools.html#ga6d55cc950a8e04c8e1372dad15521922][VAL.DBA.is_admin_user()]] \nfor that. Of course, only \"real\" (i.e., SQL) users can be administrators of the Virtuoso\ninstance. If no admin credentials were provided, we continue with the ACL check using \n[[http://docs.openlinksw.com/val/group__val__acl__module__internal__api.html#gacf6968026928ce87b9e96ac872247961][VAL.DBA.check_access_mode_for_resource()]], \nwhich allows us to check for exactly one mode on one resource for one service ID. Here we use all the details that were provided by \n[[http://docs.openlinksw.com/val/group__val__auth__module__main.html#gae4565eb3fc3b48886af76de88fb72dd2][VAL.DBA.authentication_details_for_connection()]], \nand combine them with the resource and scope URIs we defined above.\n\nSince we want to create a compressed URI, we use the oplacl:Write access mode. Should no ACL exist which grants access, we continue to raise a 40x\nerror. But before we do that, we set two more variables:\n| __val_denied_service_id__ | This is important, as it allows authenticate.vsp to know that access has been denied to a certain person, and the user should be asked to reauthenticate. Without this setting, having found authentication information, authenticate.vsp would simply return to the returnto URL. This would result in an endless loop. Should no authentication information exist yet, then authenticate.vsp will simply ask for it. |\n| __val_req_acl_mode__ | Like the resource and the scope settings above, the mode is only used for the \"request access\" dialog. It allows authenticate.vsp to create a more detailed access request message to the resource owner. |\n\nFinally we add the same code to the get.vsp page which handles the conversion of compressed to uncompressed URIs. The only difference is the access mode:\n\n\nIF ( NOT val_isRealUser \n OR NOT VAL.DBA.is_admin_user (val_uname) \n ) \n {\n IF ( NOT VAL.DBA.check_access_mode_for_resource \n (\n serviceId=>val_serviceId,\n resource=>'urn:virtuoso:access:curi',\n realm=>val_realm,\n scope=>'urn:virtuoso:val:scopes:curi',\n mode=>VAL.DBA.oplacl_iri ('Read'),\n webidGraph=>val_webidGraph,\n certificate=>val_cert,\n honorScopeState=>1\n )\n ) \n {\n connection_set ('__val_denied_service_id__', val_serviceId);\n connection_set ('__val_req_acl_mode__', VAL.DBA.oplacl_iri ('Read'));\n IF (val_serviceId IS NULL)\n http_status_set(401);\n ELSE\n http_status_set(403);\n RETURN '';\n }\n }\n\n\n\n---+++ Step 5 - The Access Request Dialog\n\nauthenticate.vsp provides a simple dialog through which users who have been denied access to a certain resource, may request such access from the resource owner. This dialog is shown\nby authenticate.vsp if the following conditions hold true:\n\n * The user has been denied access, i.e., __val_denied_service_id__ is non-NULL. (See above for details.)\n * The owner of the resource to which access has been denied can be determined. This means __val_req_res__ has to be non-NULL, and an owner has to be set.\n(DAV resources are handled as special cases; for every other resource, see \n[[http://docs.openlinksw.com/val/group__val__acl__module__utility__api.html#ga39324e0e0cf5fcaf259dac362357b92c][VAL.DBA.set_resource_ownership()]], \n[[http://docs.openlinksw.com/val/group__val__acl__module__utility__api.html#ga3eb4fcc5cff60079013beebdf58c9ae3][VAL.DBA.add_ownership_graph()]], \nand friends.)\n * VAL has a means to contact the owner. That means an email address has to be known (see \n[[http://docs.openlinksw.com/val/group__val__auth__module__tools.html#ga0e3510e9b6a77b33558bd6d78814afc3][VAL.DBA.email_address_for_service_id()]]), \nand the instance's sendmail configuration has to be valid (see [[http://docs.openlinksw.com/val/group__val__auth__module__tools.html#ga882a0ac491ca06f6514220b72f608bd2][VAL.DBA.smtp_server_available()]]).\n\nIf these conditions are fulfilled, the user is given the option to write a message to the owner of the resource, requesting that access be granted.\n\n\n---+++ Step 6 - Run an Application Under a Specific SQL User Account\n\nIn the case of curi, the VSP pages are not executed as the dba user, but using the dedicated account CURI which improves security and is generally recommended.\nHowever, since most of the internal VAL API procedures require special permissions, this user needs to be granted the VAL_AUTH and VAL_ACL roles to be\nable to execute:\n\n\nGRANT VAL_AUTH \n TO CURI;\nGRANT VAL_ACL \n TO CURI;\n\n\n(The [[http://docs.openlinksw.com/val/index.html][API documentation]] of various procedures includes hints about which role grants the right to execute that procedure.)" .