Color scheme: Light
You are here: start » en » Documentation » Data integration and data access » WKL Database Gateway » Overview of WKL Database Gateway Manager and the VBA class » Reference » WklGatewayClient: VBA class for the data service

WklGatewayClient: VBA class for the data service

WklGatewayClient lets you call the commands of the data service's (Wkl.Data.Gateway) administration interface from VBA: issue keys, set up the second factor, test connections, and more. It also serves as a template for any VBA application that talks to the service over HTTP.

An Access application reads and edits lists (data sources, rules, people, keys) as linked tables through the service's ODBC driver. Anything that returns a secret or affects more than a single row is a command, and that's what this class is for. The entity sets and commands themselves are described in HTTP: administration (reference) (data service documentation).

Supported environment

Microsoft Access from Microsoft 365, 32-bit and 64-bit, in an ACCDB, an ACCDE, and the Access Runtime. The administration app calls its commands through this class.

Prerequisites

PrerequisiteUsed for
Microsoft Access from Microsoft 365, 32-bit or 64-bitsee Supported environment
A reference to WKL Office JSON 1.1 (Wkl.Office.JSON.dll registered for your Office bitness; up to release 1.0.3, WKL Access JSON 1.0 with Wkl.Access.JSON.dll)parsing responses as JDoc and building requests
WinHTTP (WinHttp.WinHttpRequest.5.1, part of Windows)HTTP; late-bound, so no reference is needed
A service with an administration store, and a person with the admin permissionthe commands

Add the class to a project

  1. In the VBA editor, choose File > Import File and import WklGatewayClient.cls (a class module, found in the repository under source/classes/) and, if you want the examples, WklGatewayClientExamples.bas (under client/).
  2. Choose Tools > References and select “WKL Office JSON 1.1” (“WKL Access JSON 1.0” up to release 1.0.3).
  3. Compile the project (Debug > Compile).

The files use the format the VBA editor exports and imports. Access's LoadFromText expects a different format for a class module (without VERSION 1.0 CLASS … END and without VB_Name); given the export format, it creates a standard module instead. client\tests\Run-AccessTests.ps1 shows how to convert between the two.

Usage

Use one instance for the application's entire session, because the instance holds the second-factor session.

Private m_Client As WklGatewayClient
 
Public Function Gateway() As WklGatewayClient
    If m_Client Is Nothing Then
        Set m_Client = New WklGatewayClient
        m_Client.BaseUrl = "https://data.example.com"
        m_Client.SignIn = gwPersonalKey          ' Key from Windows Credential Manager
    End If
    Set Gateway = m_Client
End Function
 
Sub Example()
    Dim answer As JDoc
    On Error GoTo ErrorHandler
 
    Set answer = Gateway().IssueKey("Jane Doe", Label:="Laptop", Delivery:="link")
    Debug.Print answer.GetString("$.link")
 
FinalHandler:
    Exit Sub
 
ErrorHandler:
    MsgBox Err.Description, vbExclamation       ' Error numbers: see section "Errors"
    Resume FinalHandler
End Sub

WklGatewayClientExamples.bas contains every command once, with error handling by error number.

Sign-in

SignInFirst factorTaken from
gwPersonalKey (default)personal key (wkl_…) in X-Api-Keythe Key property; otherwise Windows Credential Manager (StoreKey, ForgetKey, HasStoredKey)
gwWindowsthe process's Windows account (Negotiate)—
gwSqlLoginSQL login (HTTP Basic)UserName, Password
gwServiceKeyservice keythe environment variable named in KeyVariable

Every sign-in (personal key, service key, password, and Windows account) is sent only over https or to the local computer; the class refuses an http address of another computer with gwErrConfiguration before sending anything. StoreKey saves the key under the same target the ODBC driver uses for an administration connection to the same address (Wkl.Data.OData.Odbc:(persönlicher Schlüssel)@<BaseUrl>/admin/v1/odata/), so one saved key serves both the linked tables and the class. The key is never stored in the database file.

Second factor

If the service requires a second factor (a 401 response with Wkl-Totp), the class asks for the code, exchanges it for a session, and repeats the request. It gets the code from these sources, in this order:

  1. The TotpCode property (used once), for code that prompts for the code itself.
  2. The CodeRequired(Message, Code, Cancel) event of an instance declared WithEvents.
  3. An InputBox (which also works in the Runtime); turn it off with AskWithInputBox = False.

If the code is wrong, the class asks again, showing the service's message, up to three times. OpenSession starts the session explicitly, and ForgetSession discards it. The ODBC driver keeps its own session. To make one code work for both, the application passes SessionToken to the driver with the SESSION= keyword of a connection it opens itself (this is what the administration app does). Without that handover, the driver and the class each ask for a code once.

Interrupted connection

If the connection drops without a response, the class retries the request after 1 second and again after 3 seconds. Every write request carries an Idempotency-Key, and the service answers a retry from memory instead of writing a second time. The service remembers the key for 10 minutes and only in memory, so if it restarts between the dropped connection and the retry, a request can arrive twice (service behavior as described in HTTP: administration (reference), section “Many operations at once” (data service documentation)). A request that times out (TimeoutSeconds, 60 by default) isn't retried: the service might still carry it out, and the class has no way of knowing. In that case, read the data back to check whether the change went through.

What the class protects, and what it doesn't

  • It sends keys, passwords, and the Windows sign-in only over HTTPS or to the local computer; otherwise, it refuses with gwErrConfiguration. WinHTTP validates the server certificate against the Windows certificate store, and an invalid certificate makes the request fail; the class never turns that check off.
  • It keeps Key and Password only in its instance's memory. A key is saved only when you call StoreKey, and then only in the user's Windows Credential Manager.
  • A service key (gwServiceKey) is never stored in code or in the database. The class reads it at run time from the environment variable named in KeyVariable. Anyone who can read that variable (any process running as that user) has the key.

Reference

MemberDescription
BaseUrlthe service's address: scheme, host, and port, without a path
SignIn, Key, KeyVariable, UserName, Passwordsign-in; see above
TotpCode, AskWithInputBox, CodeRequiredsecond factor
TimeoutSecondstime limit per request, in seconds
LastStatus, LastMessageHTTP status and message of the last response
HasSession, SessionExpiresAt, OpenSession, ForgetSessionthe second-factor session
SessionTokenthe session itself, for handing over to the ODBC driver in the same process (SESSION=); keep it in memory only
CredentialTarget, StoreKey, ForgetKey, HasStoredKeythe key in Windows Credential Manager. ForgetKey and HasStoredKey return False only when no key is stored; if Credential Manager itself fails, both raise gwErrConfiguration
IssueKey(Person, [Label], [ExpiresAt], [ReadOnly], [Databases], [FromNetworks], [ExceptNetworks], [TotpExempt], [Delivery], [OnDemand])issues a key; Delivery is link (default) or key; OnDemand = dormant until activated. With key, the response contains the key in plain text, so it ends up in your VBA code: never display, store, or log it. That's why the administration app only ever uses link
ActivateKey(KeyId, Hours) / DeactivateKey(KeyId)activates an on-demand key for Hours hours, or makes it dormant again immediately; see HTTP: sign-in (reference), section “On-demand keys” (data service documentation)
ExplainSignIn(Person, OriginNames, OriginAddresses, [Scope], [OriginCountries], [OriginOperators], [OriginClients], [OriginTimes])what the sign-in policy says for the person, by sign-in method and origin, with the rules responsible (rules), the rules on trial (tried), and those an exception lifts (spared); an empty address means an unknown origin. The per-origin fields for country (TH), network operator (AS3320), program, and time (UTC) ask “what if …”; left empty, the service determines them itself
SignInProfile(Person, [Days])what is usual for the person at sign-in over the last Days days (1 to 3,650, default 90): total, activeDays, and, under dimensions, the values per country, network, network operator, program, method, and hour, with count and first and last day. Successful sign-ins only; requires the sign-in-events right (otherwise 403); an unknown person returns 404
SaveSignInRule(Id, Scope, Method, Effect, Label, [FromNetworks], [ExceptNetworks], [Persons], [ExceptPersons], [WindowsGroups], [ExceptWindowsGroups], [Countries], [ExceptCountries], [Operators], [ExceptOperators], [Clients], [ExceptClients], [Hours], [ExceptHours], [Exceptions], [Mode])creates a sign-in rule with all its conditions and exceptions in a single change (Id 0), or replaces it as a whole, so any list you leave out ends up empty, and without Mode the rule is enforced. Lists for and against: networks, people (by name), Windows groups, countries, network operators, programs, time windows; an empty list of people means “every caller.” Exceptions: an array of Scripting.Dictionary with the lists persons, windowsGroups, countries, from, operators, clients, hours, the end until (a Date in UTC), and label. Mode: Enforce or Report (try only). Response: id
EnrollTotp(Person, [Delivery]), ResetTotp(Person)sets up or removes the second factor
OwnKeys([DataSource]), IssueOwnKey([Label], [ExpiresAt], [ReadOnly], [Databases], [DataSource]), RevokeOwnKey(Id, [DataSource])lists, issues (response key, shown once), and revokes your own keys; see HTTP: sign-in (reference), section “Self-service” (data service documentation). Requires signing in with gwWindows or gwSqlLogin; for a SQL login, DataSource names the data source the login is checked against
TestConnection(DataSource), ReloadCatalog([DataSource])tests a data source (answer reachable; when it fails, also message, number, and failure, the kind of error independent of the server's language); discards the cached catalog
ServiceAccount()the account the service signs in with at databases when it uses Windows sign-in: account (as a SQL Server on the same machine sees it), networkAccount (on another machine), kind, domain; the name the database administrator creates the login for
SetGeoIpSource(Source, UpdateDays, [Account], [LicenseKey], [RemoveLicenseKey])chooses the source of the geolocation database: none, dbip-lite, or maxmind (with account ID and license key); UpdateDays 0 to 90, where 0 means on command only. Without LicenseKey, the stored key is kept; the service never returns it
UpdateGeoIp()loads both geolocation files now and puts them into use without a restart; response files with kind, buildDate, and changed per file
UploadGeoIp(FileName, [Source], [Force])hands a geolocation file to the service (.mmdb, .mmdb.gz, or MaxMind's .tar.gz) for a server without internet access; Source sets the attribution, Force also accepts an older file
ExportLoad([Days])the load log of the last Days days (default 30) in the wkl-load-export/1 format, without persons, keys, addresses, or values; save it with JDoc.Save
CheckStore(), BackupStore(), ResealStore()checks the store, backs it up (to the sicherungen folder next to the store; response file, schemaVersion, bytes), and reseals it
ReadSet(SetName, [Filter], [OrderBy], [Top])reads an entity set; rows under $.value
CallProcedure(DataSource, Name, Name1, Value1, Name2, Value2, …)calls a stored procedure, with arguments in pairs and names without @; an omitted argument takes the procedure's default value. Values: text, numbers (Currency and Decimal exact), Boolean, Date (sent as ISO 8601 text, which SQL Server converts to the parameter type), and Null. Response: the first result set under $.data ($.data[0].<column>) or affectedRowCount. If the procedure itself raises a message (THROW, or RAISERROR with text), it comes back as gwErrRejected with that text. Contract: HTTP: native API (reference), section “Calling procedures” (data service documentation); requires a rule with procedures and POST
Execute(Method, Path, [Body])any other request; the response comes back as a JDoc

Errors

Every error is raised with a fixed number (WklGatewayError) and a German description, which ends with the service's message when there is one.

ConstantNumberMeaning
gwErrUnreachablevbObjectError + 2001no connection, even after retrying
gwErrTimeout+ 2002no response within TimeoutSeconds
gwErrUnauthenticated+ 2003401: key, account, or password rejected, or no key available
gwErrSecondFactor+ 2004a second factor is required but wasn't provided, or it was rejected
gwErrForbidden+ 2005403
gwErrNotFound+ 2006404
gwErrBadRequest+ 2007400
gwErrConflict+ 2008409, 412: changed in the meantime
gwErrRejected+ 2009422: rejected by the service; for a message the application raises in the database itself, the description ends with that message
gwErrTooMany+ 2010429
gwErrService+ 20115xx
gwErrAnswer+ 2012the response isn't valid JSON
gwErrConfiguration+ 2013the class's settings contradict each other

Limitations

  • The driver and the class keep separate second-factor sessions unless the application hands the session over with SESSION=.
  • Each response is read in full within the time limit. For large amounts of data, use linked tables (through the ODBC driver), not this class.
en/wiki/data-conectivity/database-gateway/admin/reference/vba-class.txt · Last modified: by Wolfgang van der Stille