Documentation
Download Latest Version Index TroubleShooting History Templates Classes Adding Secwin
CapeSoft Logo  

CapeSoft Secwin
Documentation

Installed Version Latest Version

Introduction

Secwin aims to provide program-level-security to your application. It allows your customer to control who can use your program, and what aspects of the program they can use. We call this Program Access Control. The key goals here are flexibility (every end user has different policies) and usability (customers need to be able to manage their own security and not rely on the developer to do it for them.)

In addition large customers have different needs to small customers (such as Active Directory integration) and so Secwin is designed to be flexible, allowing customers to tailor the security to their needs. A range of features, including password resets, self-sign-on, guest accounts and more make Secwin the last security tool you will need.

It also allows you to control which features in your program the customer has access to. We call this Licensing. The primary goal of licensing is to ensure that you get paid. Licensing needs to be flexible to match your needs, easy for both you and your customer, and online so that you can easily update your clients license as their needs (or lack of payment) change. Designed for both single-sale software and subscription services. It includes a web server app that can be used as a licensing server, or you can make use of our SecwinOnlineServer to host your licenses for you.

Secwin is designed to work on desktop, and web applications, and supports single-tenant or multi-tenant setups.

Secwin does not provide data-level-security. That is provided by MyTable and Cryptonite.
Secwin protects its own data from external viewing using MyTable and Cryptonite so these accessories are required in order for Secwin to compile.

In today's world no program, or user, stands alone. Secwin is designed to integrate to a SendEmail or SendSMS function (or both). These allow features like second factor authentication, password resets, self-sign-up accounts and so on. For this reason NetTalk (Desktop level or higher) is recommended even for Desktop programs. NetTalk also allows for online licensing features and Active Directory integration. NetTalk is officially an "optional" requirement, however many features require it, so it is highly recommended.

If unauthorized uses have access to the database, and they can tamper with fields (for example deleting them, or causing them to become invalid) or delete records, then certain Secwin features may no longer be effective. For example if someone can TopScan the DSSW7 file, and remove a user record, then that user will no longer be able to log in.

For this reason physical access to the database should be restricted using whatever mechanism is appropriate for the database. For TPS this means adding an owner to the table. For SQL it means limiting access to the security database and so on.

If an unauthorized person does get access to the database, data inside it will not be exposed. Any data they damage can be restored from backups, and the data itself will not be compromised.

History

Secwin was one of CapeSoft's first accessories for Clarion for Windows, and shipped in the mid 90's. It has proved to be a work-horse providing access control, and licensing for a generation of Clarion programmers.

Fast-forward to the mid-teens and cwin's age is starting to show. Designed originally for desktop use, it's not ideal on the web. Plus it uses a DLL, which is now something we prefer not to do. And best-practices with regard to passwords have changed enormously over the last twenty five years.

Enter Secwin 7. A complete re-design, and a brand new implementation of Secwin. This is a complete, from the ground up, rebuild of Secwin and uses next to none of the existing code or table structures. It takes the current best practices and provides them in a modern way, aimed not just at desktop users but web users as well.

None of this would be useful though if your existing security settings, or customer setups, are lost. Secwin 7 is therefore a drop-in replacement for Secwin 6 (and earlier) and converting an application from Secwin 6 to Secwin 7 takes a minimal amount of time and fuss. Equally, a simple utility for converting existing Secwin data to Secwin 7 is provided.

What to read next

Program security is a vast topic, and chances are you don't spend a lot of time thinking about it. So there's probably a lot of helpful background information in this document that will help you understand how all the parts fir together.

Much of this document has been written not just to explain what a template setting or class method does, but more holistically to explain concepts and the ways in which those concepts apply to your program. Topics such as Multi-Tenant support, Chained logins, Active Directory integration and so on may all be new to you and you may not even know if you want (or need) them in your program.

For this reason it is recommended that a quick, complete, read of the document is done (up to the Reference sections.) This will better prepare you for implementing Secwin in an application, or upgrading from an earlier Secwin version. Ultimately this will make the implementation smoother, and it will go faster.

Features

Logins

The user logs in using a user name and password. However individual customers have individual requirements that can vary enormously from one customer to another. In addition the platform being used (desktop or web) may have special requirements that need to be supported.

To make logins as powerful, safe, and as feature-rich as possible - while at the same time making them easy enough to setup and use by mere humans has been a challenge. The result though is the most comprehensive login system available for Clarion. All facets of the login system can be controlled at runtime so each customer can configure it to their own needs and policies. These features include;
Note [1]
Password-Less Active Directory Logins are currently only available in Desktop apps.

Security Tables

Required

Desktop Apps

For ABC apps (including AnyScreen Apps, ABC apps running under TS Plus etc), Secwin requires a number of other CapeSoft building blocks;

NetTalk Web Apps

Possible Updates

IF you have the following items in your app, then you may need to update
Product Minimum Version
iDash 1.15
MultiProj TBA
NetTalk 11 11.41
Recent Lookups 1.34
Runscreen 1.26
SendTo 3.34

Coexisting

Installing Secwin 7 will overwrite the Secwin 6 template file. So Secwin 6 and Secwin 7 cannot co-exist in the same Clarion install.
You can however clone your Clarion environment using the Versions system, and have one environment with Secwin 6, and another with Secwin 7. For more information on doing this see https://clarionhub.com/t/multiple-active-clarion-systems-macs/1138

If you are using Secwin in a NetTalk Web Server app, then Secwin 7 MUST be paired with NetTalk 12 (or later). You cannot use Secwin 6 with NetTalk 12, and you cannot use Secwin 7 with NetTalk 11 or earlier.

Clarion / Legacy Apps

Applications using the Clarion template chain (aka Legacy Apps) are supported by Secwin 7. However these apps must have the switch "Enable the user of ABC Classes" turned on [1].

To turn this on, go to the global properties for the app, to the Classes tab, and tick on the checkbox there. If you have a multi-app system then do this for al the apps in your system.

Note 1
Turning this option on does not include any ABC classes in your application. However it sets some conditional compile stitches so that ABC Classes can be used in the program. This is necessary because some of the Secwin Classes make use of CriticalSections, and these in turn use those ABC conditional compiles.

Webinars

Number Description
#563 Secwin 7 release part 1. Focuses on Access Control in Desktop programs.
#564 Secwin 7 release part 2. Focuses on Licensing
#569 Secwin 7 release part 3. Focuses on Access Control in (NetTalk 12) Web programs.

Beta Program Upgrading

From 7.00
  1. Reimport dictionary DCTX. (Changes to SecwinGlobals, not tables).
  2. Reimport SecwinLicense procedure. (Delete NetTalk extension from it if you are not using NetTalk)
  3. In Frame, Security tab, Registration button, update the actions to SecwinLicense so it has a blank string parameter.
  4. Optionally Add Background Logo Procedure, and Add call to it from Frame Extension.

From 7.00 or  7.01

  1. Reimport dictionary DCTX. New Table, SecwinRights (only used by web apps). If you get an error in import (likely) then import just SecwinRights.Dctx. If you don't have a MEMORY driver installed see Adding Secwin to your Dictionary.
    If you are not building NetTalk Web Server apps (with Secwin) then you can skip this step.
  2. You'll need to be on StringTheory 3.21 or later
  3. IF you are using NetTalk 11, then you need to be on build 11.41 or later. (If you are on NetTalk 10 or earlier then you will still need Cryptonite.)

 

Support

Your questions, comments and suggestions are welcome. See our web page (www.capesoft.com) for new versions. You can also contact us in one of the following ways:
CapeSoft Support
Email
Telephone +27 (0)87 828 0123

Installation

To download the latest installation please visit the CapeSoft Downloads page.

To install extract the Installation Program from the SAF file using the free CapeSoft Safe Reader (download for free from https://www.capesoft.com/utilities/Safe/safereader.htm).

Run the Installation Program for your version of Clarion.

Distribution

The following files should be distributed with your application, in the exe folder; If you are using NetTalk then be sure to check out the requirements for Deploying a TLS Client or Server. This will be in play because Secwin will make use of the encryption offered by NetTalk.

License & Copyright

This template is copyright © 2024 by CapeSoft Software. None of the included files may be distributed. Your programs which use Secwin can be distributed without any Secwin royalties.

This product is provided as-is. CapeSoft Software and CapeSoft Electronics (collectively trading as CapeSoft), their employees and dealers explicitly accept no liability for any loss or damages which may occur from using this package. Use of this package constitutes agreement with this license. This package is used entirely at your own risk.

Use of this product implies your acceptance of this, along with the recognition of the copyright stated above. In no way will CapeSoft , their employees or affiliates be liable in any way for any damages or business losses you may incur as a direct or indirect result of using this product.

For the full EULA see https://capesoft.com/eula.html

Background

It's likely that some, or many, of the concepts in Secwin may be new to you. This section provides a helpful primer to some of the terminology used, so that the subsequent documentation makes more sense.

Secrets

By necessity security involves the use of secrets. Without secrets stolen databases would be easy to read, or alter.

Secwin makes use of multiple secrets, set on the global extension template, to offer maximum flexibility and security. If multiple apps (think multi-app systems, or desktop app and web app) are using the same security tables, then they will all need to use the same secrets.

If your suite consists of multiple app files, compiling as DLL's and Exe's, then these apps will need to be set to use the same secrets. In this situation the use of a global, unthreaded, variable likely offers the most flexibility. These global variable need to be primed before the application starts accessing the tables.

The Program Secret is a secret that the developer of the program (you) sets. This secret will be used in all the Secwin data tables. If this secret is changed, or forgotten, then the data in the Secwin tables will be lost. This secret is usually compiled into the program and is therefore something the program "knows". It does not need to be stored externally.

Fundamentally the goal of the Program Secret is to protect the protected data from being read or altered by other programs. It should not be shared outside of the program developers, and is not needed by customers or support staff.

The Table Secret is a secret that the developer of the program (you) sets. This secret will be used in all the Secwin data tables. If this secret is changed, or forgotten, then the data in the Secwin tables will be lost. This secret is usually compiled into the program and is therefore something the program "knows". It does not need to be stored externally.

The Table Secret is also used as the Owner (or Password) for ISAM (TPS) file. This secret needs to be known for read access to the TPS file via TopScan etc. This secret should ideally not be shared, but can be shared with appropriate customers or support staff who need to use TopScan to verify table information. Note that even with this secret data should not be edited via TopScan as without the program secret that data will be considered invalid.

The table secret should not be the same as the program secret.

The Customer Secret is the other end of the equation. This secret is optional. The customer secret locks the protected data from access by other customers of the program (including the developers). To be completely secure the customer secret should not be stored in a place where anyone else can access it. The most secure form is a value that the user has to enter when the program starts.

Of course for most users the use of a custom Customer Secret is unnecessary - the program being protected simply does not warrant the extra inconvenience offered by the extra security. Nevertheless a custom Customer Secret, stored in a separate location to the data tables, is a useful security step.

IMPORTANT: Changing Secrets is very difficult and time consuming. So selecting a unique Program secret and unique Table secret early is very important. Customer Secrets, if used, need to be set when the program is first installed for a customer.

Note that in this context these secrets apply to the Secwin Tables, not to generic data in the application. For more information on securing data in the application itself use MyTable.

The secrets can be set on the Secrets tab of the Global Extension Template.

Hint: Desktop programs expose the actual EXE file to possible attackers, Setting the program secret to an expression, rather than a fixed string, makes it harder to find in the Exe file. So something like
'sec' & 10 * 4 & 'ret'
is more secure than just
'secret'
This is less of an issue in Web programs, or pseudo web applications like H5, AnyScreen or TS Plus.

Tables

It's important to understand the editing the Secwin data tables through a program that does not know, or use, the program secret is not possible. The use of tools like TopScan, or SQL Enterprise Manager therefor have limited use when it comes to these tables. Even data which is stored in plain text (like the user login or user name) is protected via a hash. Changing these values will prevent these records from being used in your program.

Encrypted fields are stored as base-64 encoded, so they can more easily be exported to text etc.

Binding specific security tables to an install is a nuanced topic, and discussed in the Security Table Distribution section. When the time comes to start distribution this is a section you will want to read through carefully.

Guids

Secwin tables do not make use of auto-incremented numbers. Rather each row is identified by a Guid field [1].
This is nothing more, or less, than a 16-character random string which is easily generated (internally) by the StringTheory MakeGuid method.

Many of the various class methods use Guid (String 16) fields to identify rows in the tables. So each User has a Guid, each Group has a Guid, and so on. There's nothing magical about this - it is just less complicated, and less prone to problems, than auto-numbering. As a bonus it makes data importing, exporting, and merging much easier.

Note 1
Do not confuse this with the MsSQL GUID data type - which is also a string(16) but which is often displayed as a 37 character hex-encoded string (with 3 hyphens).

Examples

In all the examples the user password is set to the username followed by a 1.
So Demo / Demo1
and so on.
A SysAdmin User, Demo, Demo1 has been added so use that for your first login.

Access Control, Licensing or Both?

One question that comes up a lot is whether you can implement just access control, or just licensing, or do you have to do both?
The answer is that you don't have to do both, you can do the two separately - however to do them separately you end up doing both anyway.

In other words, consider the case where access control is not required. You could implement "just licensing", but since access control is settable at runtime, it makes sense to implement at least the rudiments of access control, and then allow the user to simply not make use of it.

Equally with licensing - if you simply don't set any licensing requirements in your app, and don't populate the SecwinLicense window for the user to see, then you have "not implemented licensing". That said, even if you don't actual enforce any license restrictions, it's not a bad thing to gather customer information for informational and marketing reasons. So adding licensing, even in free applications, makes a lot of sense.

Program Access Control

User Groups

User Groups are a convenient way to assign standard access rights to users. A group is given a set of "rights" and anyone who is assigned to the group inherits those rights.

If a user is in multiple groups then they accumulate the rights for all those groups - as long as any one group has a right, then the user also has that right.

It is possible to override the group rights at the user level, denying a right to a specific user, even if they are in a group that does have that right.

Doors

A number of systems refer to an approach known as "Doors". In this approach a "collection" of procedures and controls is brought together under a single name. Users can then be assigned rights based on this name.

For example;
A door called "Salaries" is defined. Under this single umbrella access to the Salaries field on the Employee Form, the Salary column on several reports, and the UpdateSalaries procedure are granted.

Functionally Doors, and Groups are the same thing. Users who are assigned to a group get the rights for that group. By creating a group called Salaries, rights can then be assigned to that group. Employees who need access to those rights can be assigned that group.

Perhaps the most striking difference between Doors and Groups is that Doors are intrinsically defined by the programmer - they collect all the procedures and controls together under one name - which is then "locked in" at compile time. By contrast Groups allow rights to be more fluid, and can be created and maintained at runtime.

While moving the maintenance to runtime increases the flexibility of the system, it can also impose a higher load on the customer. Since they don't (yet) know all the places the Salaries occur in the system, they would find it burdensome to create and maintain these kinds of groups.

To overcome this limitation, programmers can pre-create groups, with specific rights. These groups can then be maintained by the programmer, and distributed with the program. These can then exist as a starting point for customers, and also be updated as the programmer adds more controls to the program.

Program Defined Groups

Coming Soon

Attributes

One of the goals with Secwin 7 is to make the user table completely extensible. Different programmers have different information that needs to be stored in the table. Having the table in the dictionary helps in this regard, but adding fields there (and matching hash fields to prevent tampering), and converting the old tables to new tables can be a lot of work.

To reduce this workload the Attributes BLOB has been added to the table. This blob contains a structured JSON document, and is freely extensible with virtually no effort. Indeed to add an attribute to a user simply;
  1. Go to the SecwinUpdateGroups procedure
  2. Add a field into the Local Data, inside the Attributes Group
  3. Add this field on to the form.
That's it. The rest of the form is done for you.

Later in the code, you can access the attribute via the SecwinGroupsCache object.

  whatever = SecwinGroupsCache.GetAttribute('whatever')

You are free to store anything you like as a group attribute using this technique. 

Users

A user, is, as the name suggests, a person who uses the program.  A user has a Login and a Password. In addition Email and Phone fields allow for second factor and password reset communications.

A user can belong to one or more groups, and can also be assigned their own rights that can override their group rights.

User Levels

There are six possible levels for a user.
  • No Level (sw:NoLevel)
  • Guest (sw:Guest)
    Same as an operator but logs in without a password.
  • Operator (sw:Operator)
    Can be assigned to groups. Has limited access as set by a Supervisor or Administrator.
  • Supervisor (sw:Supervisor)
    Has unlimited access to the program.
    Can assign rights to Operators and Groups.
    Can not create, delete or edit, users or groups.
  • Administrator (sw:Administrator)
    Has complete access to the system, and can add, edit and remove groups and users from the system. They also control the runtime Secwin settings for the company they are in.
  • SysAdmin (sw:SysAdmin)
    Same as an Administrator, except has control over all the companies in the system. The first user in the database has to be a SysAdmin level user, and this user cannot be deleted if other users exist.
Aside: The levels are equates, with more powerful levels having a higher numeric value in the equate.

Hint: Use SecwinCurrentUser.GetLevel() to get the current user's level.

Default Rights

Users of type SysAdmin, Administrator and Supervisor have access to all the program functions of their type. Users of Type No Access cannot access the application at all. For these users the Default Rights setting does not apply.

For Operator and Guest users you can set their default access rights. This is the right that will be assumed, if the right is not explicitly set on the group, or user level.

The possible options are All, Group or None.

If set to All then the user will have access to the whole program except for places where they have personally (or all the groups they are in have specifically) been set to No Access. This is the most permissive option and should only be used for users who have "almost Supervisor" levels of access.

If set to Group then the user will inherit their access from their Group(s), including the default access given to the group(s).

If set to None then only access rights explicitly assigned to the user, or to one of the groups the user is in, will be available to the user.

Unlocking a User

If a user gets their password wrong too often they may be temporarily  or permanently "locked" in the system. In the temporary case they will be automatically unlocked after some period of time.

In the permanent case (or indeed in the temporary case) a use can be unlocked by a administrator by editing the user record, and clicking on the Unlock User button.

Deleting Users

Because users appear in logs and so on, user records are not deleted. If you "delete" a user then they appear in the users list as deleted, and they can no longer log in.

A user can be undeleted by an administrator by editing the user record, and clicking on the Undelete User button.

User Self Signup

In most desktop applications a user has to be added to the program, before they can log in. However in many web applications it is desirable for users to be able to add themselves to the system - usually with some limited set of functionality being offered. As products like H5, AnyScreen and TS Plus blur the lines between desktop and web, this functionality needs to be available for desktop programs as well.

As with most Secwin features, this functionality is available in both Desktop and Web applications.

This feature is only available when using Program Login or Windows Login types. It is not available for programs using Active Directory authentication.

This feature is only available if either SMS or Email functionality is available in the program. (The initial password is sent to them using one of these options).

When a user clicks on the Register button on the login window, then they will be asked for a Login, their name, an Email address and Phone number. Once they have completed the form a new password is emailed (or sms'd to them) so they can login.

User Login Type

Using the Security Policies, it's possible to set the login type that the program will use. There are a number of options, including Program logins, and Active Directory logins.

In Active Directory systems it may be desirable to add additional users - ones not in the Active Directory database, but which need access to the program. It may also be desirable to have a SysAdmin user, who can log in without using Active Directory to avoid The Login Paradox.

Users can be set to have a specific login type (ie Program Login) even if the program is set to use a different login type. This is done on the Update User form, on the Other tab. [1]

Note 1: If upgrading from a version of Secwin 7, before 7.16, your Other tab may not have a Login Type setting. If that is the case then you can either re-import the procedure from one of the examples, or add the option manually. To add manually;

Desktop

  1. Add a variable called LoginType of type LONG to the Attributes GROUP in the Local Data Pad of the SecwinUpdateUsers procedure.
  2. Add a radio option to the Other Tab for the new LoginType variable. The Option should have settings for Default(0) and Program Login (1).
    the code will look like this;

       OPTION('Login Type'),AT(10,52,321,51),USE(LoginType),BOXED
         RADIO('Default'),AT(20,69),USE(?LOGINTYPE:Default),VALUE('0')
         RADIO('Program Login'),AT(20,83),USE(?Login:Program),VALUE('1')
       END

Web

  1. Add a variable called LoginType of type LONG to the Attributes GROUP in the Local Data Pad of the SecwinUpdateUsersWeb procedure.
  2. Add a RADIO option to the form, on the Other tab, using this variable.
  3. Add two radio options, 'Default' (sw:DefaultSetting) and 'Program Login' (sw:ProgramLogin) to the field.

Attributes

One of the goals with Secwin 7 is to make the user table completely extensible. Different programmers have different information that needs to be stored in the table. Having the table in the dictionary helps in this regard, but adding fields there (and matching hash fields to prevent tampering), and converting the old tables to new tables can be a lot of work.

To reduce this workload the Attributes BLOB has been added to the table. This blob contains a structured JSON document, and is freely extensible with virtually no effort. Indeed to add an attribute to a user simply;
  1. Go to the SecwinUpdateUsers or SecwinUpdateUsersWeb procedure
  2. Add a field into the Local Data, inside the Attributes Group
  3. Add this field on to the form.
That's it. The rest of the form is done for you. You can then access the attribute for the currently logged in user using the SecwinCurrentUser object, using the GetAttribute method. For example;

whatever = SecwinCurrentUser.GetAttribute('whatever')

If you wish to set an attribute at runtime, then you will need a SecwinUserRecordClass object. Once that is created, and initialized, then you can call the SetAttribute method. For example;

secUser  SecwinUserRecordClass
  code
  secUser.Init(SecwinUsers,'programsecret','tablesecret','customersecret')
  If secUser.LoadUserByGuid(SecwinCurrentUser.GetGuid()) = sw:ok   
     secUser.SetAttribute('whatever',somevalue)
     secUser.SaveUser()
  End


Incidentally the user will inherit attributes from the System Settings as well, so you can access these via the same GetAttribute method. For more on extending SystemSetting attributes see Extending the Settings.

For more information on updating an attribute after a successful login see Updating an Attribute after a Successful Login.

Workgroups

Workgroups are a feature from Secwin 1 (through Secwin 6). It is a LONG value that can be assigned to a user. For backward compatibility reasons workgroups are preserved in Secwin 7, although new application may want to take a different approach.

Workgroups can be used in a browse

Runtime Settings

Security is always a balance between security and convenience. The higher the security level, the more inconvenient it is. Each customer will have different needs and will find this balance in a different place.

A key aspect is the ability to configure Secwin at runtime so that each of your customers can apply the features and policies that they prefer, and thus balance their security needs against the inconvenience they are prepared to tolerate.

To configure these settings a user has to be User Level of Administrator, or SysAdmin.

General

SMS Available: Tick this option on if your program has the ability to send SMS's. If this option is on then SMS can be used as a second factor option, or as a way for user's to reset their own passwords.

Email Available: : Tick this option on if the program has the ability to send Emails. If this option is on then Email can be used as a second factor option, or as a way for user's to reset their own passwords.

Login Policies

Login Type: Select the Login method that the program should use.
  • No Login
    If this is chosen then no user login screen will be presented to the user. The user will automatically be logged in as an SysAdmin.

  • Program Login
    The user will log in with a username and password. These will be stored locally in a database.
  • Active Directory Login (Get username and password)
    The user will enter a login and password, this will then be validated against an Active Directory server. Once inside the program groups and rights are assigned as normal, and this information is stored in a local database.

    Generally speaking, the existence of at least two SysAdmin users is a good idea. If the ability for one of them to login is lost, then the other can be used to restore the first one to working order. When using Active Directory authentication the use of one local user (See User Login Type)  is recommended in case the Active Directory credentials are changed unexpectedly.
  • Active Directory Login (User Name from Windows)
    The Windows User Name will be checked against the Active Directory server. No password to the program is required.
    Once inside the program groups and rights are assigned as normal, and this information is stored in a local database.
  • Windows Login
    The Windows User Name will be used, no password is required. Once inside the program groups and rights are assigned as normal, and this information is stored in a local database.

Default User Name From Windows: Prime the User Name field with the Windows user name.

Allow Remember Login: Allow the program to remember the user who is logging in, and store that for future logins.

Tokens Expire After (days): The number of days that auto-login-tokens are good for. If set to 0 then the tokens do not expire.

Allow Guest Login: Allow the user to login using a Guest account.

Lockout Policies

 This policy can automatically lock a user account if brute-force logins are detected.

You can set how long the lockout is for, and how many incorrect passwords will trigger it. When a valid login occurs then the lockout counter is reset to 0.

If  a user is locked, then they can be unlocked by an administrator in the program. The administrator does this on the update user screen in the program, by clicking on the Unlock User button.

Lockout Type:
  • None: Users are not locked out, regardless of password fails.
  • Temporary: Lockouts are temporary and expire after a certain amount of time.
  • Permanent: Lockouts are permanent. A user must be unlocked by an Administrator before they can log in again.

If Incorrect Password: The number of incorrect passwords allowed (in the time specified) before the user will be locked out.

Times in: The time period being monitored. If, for example, this is set to 12 hours then the failed counter will reset after 12 hours. If this is left blank then there is no limit to the time between fails.

Password Policies

Passwords are always Case Sensitive.

Password policies are often set by the customer against their specific set of rules. Interestingly these policies often weaken passwords rather than strengthen them. Nevertheless they may be required for various reasons.

Minimum Password Length: : Longer passwords are better.  However requiring passwords that are too long can hinder rather than help security. [1] There is no limit to the password length in Secwin (although allowing this to be set to greater than 256 characters will require the entry field on the Login Window to be made bigger.)

Password Expiry Days: Force the user to change their password after some fixed number of days. Use of this option will weaken security. [2]

Do not allow common passwords: Tick this on to prevent users from using a password that is commonly used. A list of the hundred thousand (100 000) most common passwords is shipped with Secwin as CommonPasswords.Txt. This file should be included in the same folder as the EXE. This is a very good option to have on.

Password must contain one or more digits
Password must contain one or more uppercase characters
Password must contain one or more lowercase characters
Password must contain one or more special characters


All of the above are self-explanatory, but do nothing to improve security [1,2].
Use them only if local (bad) policies require them.

Do not allow users to recycle passwords: If this is on then users will not be able to reuse a recently used password. In places where mandatory password expiry is set this prevents a small number of passwords from simply being rotated. (This is a good policy if you are expiring passwords.)

User Password Self-Reset Method: If Email or SMS are available then you can allow users to reset their own passwords (without knowing the current password.)

Setting this option to Not Allowed means that an Administrator will be required to reset the password for the user, on the Update User screen.

Be aware that both SMS and Email can be compromised though so allowing users to reset their own passwords (without knowing the existing password) can be a security risk. Requiring an Administrator to reset the password is more secure, but brings another human into the loop and communication of the new password to the user still has to happen securely. Either way it is recommended that the user change their password, after logging in, the first time the password is used.

Note 1
 https://www.enzoic.com/surprising-new-password-guidelines-nist/
 https://pages.nist.gov/800-63-3/
 https://www.microsoft.com/en-us/research/wp-content/uploads/2016/06/Microsoft_Password_Guidance-1.pdf

Note 2
 https://www.microsoft.com/en-us/research/publication/do-strong-web-passwords-accomplish-anything/

Second Factor Policies

Access to any system can be achieved using one, or more, of three different items.
  • Something you know: This is the most common. The password is a "secret" in your head. It's effective as long as this secret remains secret. This approach works best when the user is making use of a password manager (which assists with passwords never being reused for two different programs or services.)
  • Something you are: This is biometrically linked to you, for example a fingerprint, voice print or face recognition. The quality of these systems vary somewhat but generally they provide good security.
  • Something you have: : This makes use of a phone, or email, or other device which you control. This approach remains effective as long as you have control of the device.
Secwin supports logins that make use of two-factor authentication, in other words it allows you to login with a password, and/or email/sms. This approach though can be tedious to use every time so several policies control how this behaves.

Note that use of this feature implies that your program supports the sending of Emails or SMSs. If you do not have this facility in your program, then you cannot make use of Second Factor Authentication.

Second Factor Authentication
  • Not Available: This is set if you don't want to allow second factor authentication, or Email and SMS are not available in your program
  • User Optional: the user can determine if they want to use Second Factor or not
  • Required (Either SMS or Email): Second Factor logins (at least some of the time) are required to use this program. Either SMS or Email could be used for this.
  • Required (Both SMS and Email): Second Factor logins (at least some of the time) are required to use this program. Both SMS and Email will be used for this, the user must have access to both for the login to work.
Trigger Second Factor When
  • Always: The second factor is triggered on every login.
  • On New Machine: The second factor is only triggered when the user logs in from a new machine.
  • On Password Change: The second factor is triggered when the user uses a new password for the first time.
  • Every n Days: The second factor is triggered on a regular basis, like once a week and so on.

All the above options can be used independently. In other words the customer may require second factors to be used on a new machine, and also when passwords change.

Stale Time: : Second factor authentication works by sending the user a one-time code to their email, or sms device. This code is generated, and sent, while they are logging in. (This can delay the login process until the code is received.) However the code itself should become "stale" at some point - in other words they need to use it within a time (typically a few minutes) of generation. A good option here would be 15 minutes or so.

Active Directory

These options are only visible on the settings form, if the Login Type is set to one of the Active Directory options.

In this context the word Directory has nothing to do with Folders on the disk. Rather it refers to a central user directory which allows administrators to control computer use from a remote location. Active Directory uses a technology called LDAP and Secwin (via NetTalk) supports LDAP. The most common Active Directory in use is the Windows Active Directory Directory Services.

There are two approaches to using Active Directory;

1. The user enters a login and password as usual. Instead of being validated against a local database though they are passed to the Active Directory Server and validated there. In addition to this the user has to be placed in an Active Directory Group in order to gain access to the program. This allows a remote administrator to control access to the program - for example terminated employees can be removed from the group centrally, instead of having to visit all the different programs individually.

2. The program passes the Windows User Name to the Active Directory Server. The server then checks the group to see if that user has access to the program.  The user does not (and can not) enter the user name, and there is no password required. This approach assumes that logging into the machine itself, as a valid user, is sufficient authentication. This is also sometimes known as single-sign-on. This approach can be used in conjunction with Second Factor Policies to improve security. This approach is only supported in Desktop programs, not Web programs.

In order to make use of an Active Directory server, some settings are required. You will need to liaise with the customer's IT department to get these settings.

Active Directory Server: These details allow the program to connect to the server itself.
  • Host: (required) This is the address of the server. This could be an IP address, or a DNS (or WINS) name that resolves to an IP address.
  • Port: (required) This is the port the server is running on. The default port for Active Directory connections is 389 (for insecure) or 636 (for secure).
  • TLS: Tick this on if the connection to the server should occur over TLS.
  • Domain: (required) This is the domain, on the server, in which all the users, and groups exist.

    NOTE: If one of the required fields above are not set, then the program will be unable to validate against the Active Directory server. In this situation a user (if they have a level of Administrator or higher) will be logged in (without checking) primarily so that the Administrator can correct the settings.
Active Directory Admin User / Password: These are the details for an Admin user - one that allows you to make requests to the server to check if a user is in a group.

NOTE: If these are not set then the user logging in (if they have a level of Administrator or higher) will NOT be checked against a Group, even if the Active Directory Group name is set.

NOTE: If these fields are set, but either the user name, or password, is incorrect, then the user logging in (if they have a level of Administrator or higher) will NOT be checked against a Group, even if the Active Directory Group name is set.

Active Directory Rights: This is the name of the Active Directory Group, that the user must be a part of, in order to gain access to the program.

To learn more about LDAP in general, and Active Directory Directory Services in particular, see https://www.capesoft.com/docs/NetTalk12/NetTalkLDAP.Htm.  Of special interest is the number of user logins that are possible for an Active Directory user - https://www.capesoft.com/docs/NetTalk12/NetTalkLDAP.Htm#UserLogin

Aside: If you need to brush up on Active Directory Directory Services because your client wants you to support it, but you know nothing about it then I recommend this YouTube playlist. Start with item 5 on that list. It's an excellent introduction to Active Directory. 

User Self Signup

In most desktop applications a user has to be added to the program, before they can log in. However in many web applications it is desirable for users to be able to add themselves to the system - usually with some limited set of functionality being offered. As products like H5, AnyScreen and TS Plus blur the lines between desktop and web, this functionality needs to be available for desktop programs as well.

As with most Secwin features, this functionality is available in both Desktop and Web applications.

This feature is only available when using Program Login or Windows Login types. It is not available for programs using Active Directory authentication.
Allow Self-Signup of new users: If this option is on then a REGISTER button, or link, will appear on the Login screen.

Group for Self-signed-up users: Select a group here that the new user will automatically be placed into. the new user will have the level of Operator. By controlling the access rights of this group you effectively control the access rights that self-signed-in users will have. Once signed up an Administrator can assign the user into other groups, change their rights, and so on.

Multi Tenant

Default Record: This is ticked on if this is the default settings record. In a single tenant situation there should only be a single record in the settings table, and this field should be checked.

Company: The name of the company that uses these settings. The value in this field needs to be unique. In multi-tenant situations the company name is used to distinguish between tenants. When users log in they may need to identify the company they are logging into.
 
PIN: A PIN code identifier for the company.  The value in this field needs to be unique. The use of PIN codes allows users, in a multi-tenant setup, to easily identify the company they are logging into (without needing the whole company name). It is most useful in situation when displaying a list of companies, and asking the user to select the correct one, is not desirable. 

See Also : Multi-Tenant, Making a Multi-Tenant Web Server Program, with different data folders / databases

Extending the Settings

This bit is technical, feel free to skip it.

The settings are stored in a table typically called SecwinSettings. More accurately the settings themselves are stored in a BLOB in that table called Settings. The blob is encrypted and hashed to protect the settings while they are on the disk.

On the SecwinSettings Window they exist as fields in a group called SettingsGroup. Unfortunately you cannot manually add items to this group, because the template will drop all your changes on the next generate of the procedure.

For this reason an Attributes blob also exists in the table. You can create a group, and add any fields you like to the group. These fields can be populated on the Settings window. The Control template for the window allows you to assign the group to the Attributes blob in the table.

This means you are not limited to the features that Secwin provides - you can easily extend the system to include features specific to your requirements.

The example app contains a group on the window called AttributesGroup, with a simple string field called Comments. This comments field is on the window on the Other tab. The control template has been set to use this group for the Attributes blob. In this example you can add whatever fields you like to this group. Secwin itself does not use the comments field for anything, and so it can be removed if you wish to do so.

Logins

One key feature of Secwin is that it allows you to create (or more likely import) a login window for your application. While at first glance this should be a trivial feature, there are a lot of login flavors, and this can make the procedure quite complicated. Secwin is designed to support a whole range of optional login features, and at the same time allow your customers to choose which of those features they would like to make use of.

Login Procedure

The start of each application is the login window (the SecwinLogin procedure). While the cosmetics of the window can be altered, functional changes to the window should be applied with care.

The flow of the login window is governed by the Runtime Settings. If the login does not work as you wish it to then it is likely the result of a setting that can be altered. Because many of the settings are now set at runtime the options in the UserLogin template have been vastly reduced.

Change Login

A ChangeLogin Code template exists to call the SecwinLogin procedure. This code template can be added to menu items, or buttons to change the user that is currently logged in. If this window is called, and the login fails, or is cancelled, then the current user remains logged in.

This code template has a template setting for options to pass to the procedure. It is likely (recommended?) that the option sw:noToken be used here. This means that any "saved login" will not be used for this login. (Any existing token will still be used for the next program login.)

Logout

A Logout Code template exists to call the SecwinLogout procedure. This is typically called from a button or menu item on the Frame procedure.

Important: The Logout function deletes the Remember Login Token. So if a user (who is remembered) wants to not-be-remembered-anymore then they can do so by clicking the Logout button.

Tip: If this button is added to the frame, then it's recommended that on the frame, the User Screen Security, Access Control Tab, No ACCESS Column at Runtime checkbox is turned ON. Failure to do this means the program will close immediately when the logout button is pressed. (This makes sense - if the user is logged out, and the Frame has the Access Column for runtime settings, then the current user (which is "no one") suddenly does not have access to the frame, and the frame therefore closes.

User Changing Their Password

When a user is logged in they are able to change their password by going to the SecwinChangePassword procedure. This feature is only applicable if the Login Type is set to Program Login (as set on the Runtime Settings window) . If one of the other types are used then the procedure posts an error message, and closes.

The user will need to enter the old password, and a new password.

The new password will need to conform to the Password Policies (as set on the Runtime Settings window) in order to be saved.

Current User

A global object called SecwinCurrentUser (SecwinCurrentUserClass) is created by the global template. This object is updated when the logged-in user changes.
You can access the properties, and methods, of this object in your code if you need details about the user.

See Also SecwinCurrentUser

Remembering

One of the settings that the end-user can choose to activate is allowing a user login to be remembered.
This makes sense when the user is accessing the program from their personal computer or device, and where the penalty for someone else logging in as themself is limited. Of course if a different person gets access to the device then access to the program is compromised.

In order to remember a user a Token is generated by Secwin, and this token is stored in the database in the SecwinTokens table. This token is also stored locally on the device[1]. When the program is next started, the token is loaded from the device and tested. If the token is valid then it is automatically used, and the user is logged in.

In desktop apps the token includes machine information, which makes the token unportable from one machine to another. On the web (in a browser) it is not (legally / ethically / transparently) possible to bind the token to a specific machine. Technically is is possible to bind the token to an IP address, but since device-IP-Addresses change so frequently, this is likely not useful (and is not done by default.)

In both cases the contents of the token are not a secret. The token itself does not contain the user login, or password[2]. However the token itself is a valid login, so it should be treated as such.

Note 1
By default, on the desktop, the token is simply stored in the INI file [3]. The token is bound to the machine (and the program) so "stealing" the token has no value. There can only be one token stored per machine per user.

On the web the token is stored as a cookie. It can be erased by the user simply by clearing their cookies (or by clicking on Logout). The browser usually protects cookies, as long as the connection to the server is secure (ie using TLS). These tokens are not bound to the device though, so should be used with care.

In both desktop and web cases, if the user expressly logs out (SecwinLogout procedure) then the token is deleted on the server, and the client,  and thus any (client side) copies of the token become useless.

Tokens have an expiry date - by default 30 days. This is one of the settings set by the SysAdmin when setting the Runtime Settings. Setting the expiry days to 0 means that the tokens do not expire.

Note 2
The user login and password are NOT stored in the token. So if a user changes his password, or has his user login changed, this does not invalidate the token, and remembered logins will still work.

Note 3
The name and location of the INI file is specified on the Secwin Global extension, Options tab.

If you don't want to use an INI file then you can override the storage method in Desktop Apps by overriding the LoadTokenFromStore, and SaveTokenToStore methods in the SecwinLogin and SecwinLogout procedures.

Note 4
Remembering is only in play for the Program Login, and Active Directory with User and Password login types. Logging in using the Windows login name bypasses the need for a user and password, and hence bypasses the need for a remember token.

Chaining

Secwin allows a user logged into one Exe to be automatically logged into another exe if that second EXE is RUN from the first exe. This is known as chaining.

Support for chaining has to be turned on. It is off by default. Historically this was done on the UserLoginHere template (and this setting is still supported if it is on). However the setting should be set as a parameter when calling the SecwinLogin procedure.

Chaining is accomplished by passing a token on the command line, with the syntax login=sometoken. This should be invisible to your program (unless it it is paying particular attention to the command line).

Chaining consists of two parts - the caller, and the callee. The caller creates a token, and passes it to the callee. The callee then uses the token to log the user in.

Caller

The token is created with a SecwinTokenRecordClass. Add this to the procedure that will make the call, using the SecwinTokenClass extension template. This will create the object (secToken), and take care of initializing the object.

Once the object exists, and has been initialized, it can be used in one of two ways;
  1. Generate a command line parameter, and then use this as part of your normal RUN command. For example;
    parameter = SecToken.GetChainToken('callee.exe',SecwinCurrentUser))
    RUN('demo.exe' & clip(parameter))
  2. OR call the RUN method of the object. For example;
    SecToken.Run(SecwinCurrentUser,'callee.exe')

    If you have your own parameters then pass them as an extra parameter
    SecToken.run(SecwinCurrentUser,'
    callee.exe','yourparameters')

    You can also pass a waitflag if you want to pause program execution
    SecToken.run(SecwinCurrentUser,'callee.exe','yourparameters',true)
In both the above cases, the currently-logged-in-user information is gotten from the SecwinCurrentUser object. This global object exists in your program, so it can be used here as-is.

Note: The Caller APP must include a declaration of the SecwinToken table.

Callee

Historically this option was set on the User Login Here extension template. This template was added as an extension to the procedure that called the Login procedure (like the Main, or Frame procedure.) The setting was called Allow Automatic Login from other Exes. For backward compatibility, if this option is set in your program, then it will be respected, however once this option is off it will disappear from these options.

The option can (and should) now be set in the login procedure itself.
Go to the SecwinLogin procedure, to the FOR SecwinLogin extension and turn on the option there.

Note: The Callee program should have Secwin installed just as you would for any Exe, and it's capable of being run as an Exe by itself. The chaining facility does not alter the Secwin functionality in any way, it simply allows the program to automatically log a user in using a chained token.

The Login Paradox

The system is setup by Administrator, or SysAdmin users.
In order to login, a user has to meet all the requirements of the system.
If the system is set such that it cannot validate any Administrator or sysAdmin user, then no-one can login to fix the system setup.

When the program is first installed, no users exist, so the user is logged in as a sysAdmin. However if users already exist, and the Admin user can no longer login, then  how can the settings be corrected?

For example, say the program is set up to use Active Directory authentication. This requires that the program be set up to communicate with the Active Directory server (server name, port number etc). What happens if those settings are, or become, invalid. How then to log in to correct the settings. A solution to this is to create a SysAdmin user who uses local credentials, not Active Directory credentials. See User Login Type for more on this.

Generally speaking, the existence of at least two SysAdmin users is a good idea. If the ability for one of them to login is lost, then the other can be used to restore the first one to working order. When using Active Directory authentication the use of one local user (See User Login Type)  is recommended in case the Active Directory credentials are changed unexpectedly.

Embedding

This section is technical, feel free to skip it.

Embedding in the login window can be difficult to do correctly, because of the many moving parts and options that go into a login. However this flow-chart serves as a general guide to the path the login may take, and hence the best place to embed your own code;

Login Flow Chart

Setting Security Rights

Logging into an application is and important thing to do, but within the application the customer may want to limit functionality for users, based on what they are allowed to do. One of the goals of Secwin is to allow end-users to do this in an intuitive way, but a way that allows them to understand what a user's rights are, and how to set them.

A user has to be the level of Supervisor, Administrator or SysAdmin in order to assign rights to individuals or groups.

Groups

Secwin allows User Groups to be created. A user has to be at least at the Administrator Level in order to create groups.

Using Groups is useful because rights can be assigned to groups. Then when a new user is added to the system it is not necessary to set all their rights individually, you can simply assign the user one or more groups.

If a user is a member of more than one group then they get the access rights of all the groups. So as long as they have access to something through at least one of their groups, they will have access to that something. In technical terms access rights are OR not AND.

When a group is created you can set its access rights to default to either ALL or NONE. If set to ALL then the group has access to everything (by default) and it can be edited to restrict specific access. If set to NONE then specific access has to be granted to the group.

Users

A user has to be at least at the Administrator Level in order to add users. (With the exception of systems which allow users to create themselves, more on that later.)

In addition to being in a group a user can have specific rights which override these group rights. These allows the administrator to not just to allow specific users to do specific things outside of their group, but it also allows the administrator to prevent a user doing something, even if their groups are allowed to do it.

Users who are not part of any group can also have their rights set on an individual basis.

When a user is created you can set their access rights to default to either ALL, GROUP or NONE. If set to ALL then the user has access to everything (by default.) This can then be edited to restrict specific access. If set to GROUP then the default rights of their group(s) are used. If any are set to ALL then the user's default is ALL, if all are set to NONE then the user's default is NONE.

Compile Time

The program developer determines which windows, and which controls, in the application need to be protected. This is done with the User Screen Security template.

Procedures can be protected based on the User Level (the user has to be at or higher than a specific level in order to view the procedure). This is not changeable at runtime.

The procedure can be set so that access, to the screen as a whole, can be set (or not set) at runtime.

Controls, or in the case of List Controls, Columns, can be set as restrictable-at-runtime. These controls can be grouped together, or have their rights assigned separately.

New controls can inherit their access from an existing control. So for example if your program has a Delete button, and you have set rights to that button (at runtime) and then later on you add a Purge button then you can "default" the purge button to be the same as the Delete button. (This is just a default, the Purge button can be set specifically just like any other protected control.)

Run Time

There are two approaches to controlling the access rights of users at run time.
    • Go to the window that needs protecting
    • Press the system-wide Hot Key (Ctrl-F8 by default)
    • Edit the rights for users and groups for that screen
    This approach is ideally suited to the less-technical supervisor. They do not need to understand the bigger application, they simply go to what they want to protect, and protect it. It takes slightly longer to do it this way, but can be done with minimal user training.
  1. Make use of the Global "Set Access Rights" window. This window presents a lit of all the procedures which can be protected, and allows the supervisor to set the rights for all the procedures, and controls in one place. This approach is faster, but the user needs an understanding of how the application is arranged, and what procedures refer to what functions.

Bubbling

Menu Items and Buttons are often set to "Call a Procedure". Having the button present, but going to an Access Denied message is not very friendly.
The solution for this in Secwin 6 and earlier was to protect both the button in the calling procedure, and the destination procedure as well. this can duplicate work when setting up the system.

Secwin 7 introduces the concept of Bubbling - if a user does not have access to a procedure then buttons and menu items that call that procedure (using the template Actions setting) are automatically hidden.

Additional Program Restrictions

Disabling, and Hiding of controls for access control purposes does not happen in isolation. There may be other considerations which also come into play.

One Time

For example an Invoice marked as Paid or Unpaid may affect other actions the user can perform on a form. The code belongs in the procedure in the SecAccess.SetWindow method, after the Parent call. For example

If INV:paid = False
   Disable(?Export)
End


You should only further disable items in this code, not enable items. You can Disable or Hide controls here.

Update Dynamically

Elsewhere in your code you may need to enable, or disable controls based on some changing value. For example, in a Browse, say you wanted to enable, or disable and update button based on the selected row. So something like "Unpaid invoices can be altered, Paid invoices cannot be altered."

In this situation you need to combine your test with the data test to decide whether the control should be allowed or not. This is done using the GetControlAccess method. For example;

If inv:paid = false and secAccess.GetControlAccess(?Change) = sw:Ok
  Enable(?Change)
Else
  Disable(?Change)
End


The above code could be placed on the NewSelection event for the browse, or anywhere else where the inv:paid condition may change.

Multi-Tenant

Multi-Tenant support is optional. It is more likely to occur in a web context than a desktop context. You can design a system to be single-tenant, and expand it to be multi-tenant later if you so wish [1].

See also : Runtime Settings - Multi-Tenant, Making a Multi-Tenant Web Server Program, with different data folders / databases .

Introduction

In a Multi-Tenant situation multiple distinct groups of users exist. These groups are known as Companies. Before the user can login they (may) have to first identify which company they are logging in to [2]. (To avoid confusion, a Tenant is a Company, and a Company is a Tenant. The terms can be used interchangeably. They both refer to a record in the SecwinSettings table.)

Each company will have a record in the SecwinSettings table. In a single-tenant case a single record will be created in the table. One of the records (or the only record) in the table will be a designated "Default Record" and this in turn will be the default values for any other companies that are added to the list.

Users and User Groups belong to a specific company. If a user wants to access multiple different companies they will need to be added to multiple times (and will then exist as distinct users.) It is possible for these uses to have the same login, however they are fundamentally different records in the user's table. Thus they can be assigned to different groups, and have different rights, depending on which "user" is actually logging in. It is especially worth noting they that can (and likely will) have different passwords.

Note 1
If you are using SQL, then keeping your Secwin database  separate to your actual database may be something to consider at this point. When you do implement multi-tenant then you will want Secwin to be in a different database.

Note 2
See below for a unique-user approach.

If Company-and-user approach is used, then the company needs to be identified. This may be done specifically by the user, or it may be done using some other automated means. There are various techniques to do this, especially on the web.
  • The user could use a URL that identifies the company - for example somecustomer.capesoft.com or someoneelse.capesoft.com.
  • The user could pass a parameter to the login form. For example www.capesoft.com?company=somecompany
  • The user could select from a list (perhaps saving that selection in a cookie for future use.)
  • The user could enter a PIN value, matching the company PIN value to identify the company they are logging into.

Company-And-User versus Unique-User

There are two ways to identify a user.

Company-And-User

In this approach the user name and company together are unique. This approach allows for multiple different users to exist, with the same user login - as long as they are for different companies.  So two customers could each have a user login (called say Humperdink), but since the company is also captured during login it is clear who is logging in.

This approach is more flexible, but does REQUIRE that the user identifies which company they belong to. (This may be active, as in selecting from a list, entering a PIN code or passive - like by using a specific URL.)

If the system is single-tenant then that company is assumed, and the user does not need to identify the company.

Unique-User

In this approach the user name must be unique across ALL users in the Secwin database, regardless of which data set they will be accessing.

If Humperdink already exists for one tenant, then another tenant may not also create a user called Humperdink. This is not a huge issue for people related login names, (people can be very creative with their login names) but it can become an issue when more generic names would be used. For example Administrator or Sales or other generic titles like that.

Login names in this situation are unique, so there is no need to select the company during the login process - since the user is unique, and the user is assigned to a company, the company is known when the user name is entered.

In Self-sign-up  situations it would still be necessary to determine which tenant the user was signing up for. Again this might be via selection from a list, PIN code, or URL.

Note: In self-sign-up situations, where a new Settings record is created (ie a new Company) then a new user MUST be created at the same time to login to this company. If no user exists for a company, then it will be necessary for the sysAdmin user to log in and create a user.

Selecting the Approach

This is one decision that the developer will need to make.

It only needs to be made once provision for the second tenant is required. Single tenant systems (or systems that are currently single tenant) do not need to choose.

If the Unique-User approach is chosen, then it is possible to switch to a Company-And-User approach with very little effort later on. If the Company-and-User approach is chosen then conversion to a Unique-User approach is likely to be more difficult [1]. For this reason the Secwin tables are set up by default to assume Unique-User.

To Switch from Unique-User to Company-And-User the UserLoginKey in the SecwinUsers Table, in the dictionary must be changed from UNIQUE to NOT UNIQUE. Your Secwin database (DSSW7.TPS or whatever) also needs to be converted using your favorite table-converting technique.

Note 1
If all the user logins are unique at the time of conversion then it will be simple to switch. If not then some users for some tenants will need to have their login changed - which is likely not ideal.

Note 2
Using a unique-user approach, make some of the other features more complex. For example Default User Name from Windows would require a lot of effort, across all tenants, to be useful.

Login Procedure

If multiple tenants exist, and the Company-And-User approach to login names is being used, then the user will need to identify their company in the SecwinLogin procedure.

This is done via a Browse Control on the Company tab.  The goal of this control is to set the LoginDesktop.SettingsGuid field to the value that the user selects. The user may then click on the ?LoginButton control (which will read as Select at this point.)

This approach is easy for the user, but does show the user a list of tenants. In some cases this may not be desirable. If this is a problem then entering a unique PIN code for the company can be used instead. This PIN code is set by the SysAdmin user when creating the company. A PIN field exists on the tab, and can be unhidden when the Browse control is deleted.

If neither of these approaches is suitable then feel free to add any UI to the Company Tab that you like. The only outcome that is required is that the LoginDesktop.SettingsGuid field is set to the SecSet:Guid field of the appropriate company record.

Multi Tenant <> Distinct Data Set

While it is probable that each tenant will have a distinct data set (ie in a different database, or different TPS folder) the two concepts should not be conflated.

Having multiple tenants does not imply multiple data sets. And multiple data sets does not imply multiple tenants. It is possible to have multiple tenants sharing the same database, and it's possible for a single tenant to have multiple data sets.

Managing multiple data sets is a completely different task to program access control, and so it falls outside the scope of what Secwin provides. It should in turn be compatible with any dataset-manager that you use.

Note that Secwin data should be stored in it's own database (or in the case of TPS it's own TPS file location). In other words Secwin data should not be inside any one data set. This will make creating distinct data sets easier to do.

SysAdmin User

If a user has the user access level of SysAdmin, then they are able to manage the multi-tenant setup. They are able to create new tenants - create users, and groups, for tenants, and generally maintain the access rights - across all the tenants.

A SysAdmin user is assigned to a specific set of settings and policies, (such as password requirements) and these will apply to her.

A SysAdmin has access to the SecwinBrowseTenants procedure, and from here can create new tenants, as well as users and groups for those tenants. In most cases when creating a new tenant, users will need to be created as well (at the very least an Administrator user for that tenant so they can add more users and groups)

On the SecwinBrowseTenants a COPY button exists to allow you to copy settings from an existing tenant when making a new tenant. This will copy the Settings, and Groups (including Group Access Rights).

When accessing the GlobalSetAccess screen, a SysAdmin will see the settings for all the groups, and all the operators/guests for all the tenants.

Licensing

Introduction

The goal of adding licensing to a program is to increase the revenue received from the program. There are a number of common approaches to increasing revenue, and Secwin attempts to allow you to use whichever one(s) you prefer.

Piracy

It should be noted that the goal of licensing is not "copy protection". While this may seem to be the same as increasing revenue the two concepts are largely orthogonal, and in some cases contradictory.

For example the perfect way to prevent piracy is simply not to release your program. However that generally leads to low revenue. By contrast high levels of piracy, for a program that is well licensed, can turn pirate users into paying users, which has the effect of increasing revenue.

Market Segmentation

Market Segmentation is the idea that all customers are not equal. Managing the customer requirements, and budget, allow you to price your product in many different ways according to what the customer needs, and can afford.

Reoccurring Revenue

Relying on new sales as the primary point of revenue is problematic when markets become mature, and saturated. When the cost of the existing customer base exceeds the revenue received from new customers, the business is in trouble. Some programs need little or no after-sales support, and the cost of the existing base is minimal. For other programs the need for existing customers to adequately pay for after-sales costs has lead to the reoccurring revenue model becoming both very necessary and very popular.

Customer Branding

Being able to brand the program to a specific customer encourages customers to move from being unpaid customers to paying customers. While copying a program (and license) is easy, it is less attractive if customer-specific branding appears on reports etc.

Customer Details

The collection, and proper storage of customer details is imperative to maintaining an ongoing relationship with the customer.

Program Levels

Levels allow you to create variations in your product offering - and thus presumably offer different pricing, and feature sets to customers.
Levels are always accumulative. So Level 3 always includes all of Levels 2 and 1.
You can have as many levels in your program as you like.
Programs have a level number, and a Level name.
Level Numbers do not need to be sequential - you can leaves gaps for future possible interim levels.

Optional Modules

Optional modules serve the same purpose as levels - allowing for product, and price, differentiation.
Unlike levels, modules are not additive. Access to a specific module does not imply access to any other module.
Any number of modules can be created. Each one has a number and a name.

Network Copies

The number of copies of the program, that can be run, at the same time, on a LAN.
This feature requires NetTalk Desktop.

Expiry Date

The date on which the license will expire.
Online license servers make it easy to implement re-occurring revenue situations because license expiry dates, and license renewals, can be done automatically.

Attributes

Attributes are any other details that may affect your program license, but which are unrelated to the levels or modules that a user has purchased.
Each attribute has a name, and a value. There is no limit on the number of attributes that you may create.

For example an attribute might be something like Free Delivery, or Customer Service Level, or Account In Arrears, or anything of that nature that you may find useful to add to the license.

In some cases you may find it useful to use attributes set, or gleaned, from the client system. This is discussed in detail in the section Using License Attributes for Client Generated Information.

Tables

You can limit the number of records that are allowed in specific tables. So you may sell a system that is limited to say 100 employee records, or 10 external devices, or whatever else makes sense for your program.

Counters

Counters allow you to license your program for specific amounts of usage. For example programs may see on a per-report-generated basis, or page-view-displayed basis or other items like that.
The use of counters requires that the program have access to the internet while running the program, and also that an online registration server is being used.

License Manager

Introduction

Adding licensing to a program you create is a two part process. The program itself has to receive the license, and manage the program in accordance with what the license states, but equally the programmer needs to be able to create, and manage, customer licenses. The License Manager utility assists with this task.

This utility is designed to be run by the developer, not by the end user.

It makes sense to do most license management online. Customers can access their licenses (or more accurately, programs installed at customer sites can access their license) online when the developer is not awake. Developers can update licenses on the fly, and the new license will be retried by the program.

In some cases customers may not be able to access the online license server as their machines do not have access to the internet. In cases like this the license file will need to be transferred to the customer via some other means (Email etc.) Note that a small number of license features require the use of an internet connection, so for these users those features are not available.

The License Manager is provided as both a web application (based on NetTalk 11 Server) and a desktop application (based on ABC). Both applications have a similar feature set, and use a common dictionary. Both are supplied as compiled EXE programs, and as source APP files. Since they are meant to be working programs both make use of other CapeSoft Accessories and are compiled in Clarion 11. You are free to compile the Exe's yourself if you wish to do so. Non-critical accessories (that you don't have) can be removed or replaced.

If you wish the benefits of an online server but don't have an online server of your own, then you can make use of the CapeSoft Secwin Online Server hosted service for a nominal annual fee.

Install

The License Manager programs (LicenseManagerDesktop.Exe and LicenseManagerWeb.Exe) are installed into your \Clarion\Accessory\SecwinLicenseManager folder.

It is NOT recommended that you run the programs directly from this folder. Rather move them from this folder to a production folder. Items in this \Clarion\Accessory\SecwinLicenseManager folder may be updated by subsequent Secwin installs, so customizations in this folder (or sub folders) could be lost.

The program and DLL's it uses, as installed in the above folder, is (likely) compiled with a different version of Clarion to the one you have. Therefore DLL's in this folder should be considered to be "tied" to these Exes, and should not be copied to folders with other Clarion programs.

Source

Source code for the programs are in the \Clarion\Accessory\SecwinLicenseManager\source folder. The source apps, and dictionary are written in Clarion 11. TXA's and a DCTX file are provided if you wish to back-port the source to an earlier Clarion version.

As these are primarily designed to be working programs "as is" they include a number of accessories. If you do not have all the accessories then these will be dropped from the app when you open it. In some cases this will reduce functionality, but not inhibit the core purpose of the program in generating licenses.

Desktop
Required Optional
jFiles Draw
MyTable File Manager 3
NetTalk (Desktop) Hyperactive
RecentLookups OfficeInside
Secwin Premiere
StringTheory Resize and Split

RunScreen

SendTo

xFiles

WinEvent

Web
Required Optional
jFiles File Manager 3
MyTable SelfService
NetTalk (Server) WinEvent
RecentLookups
Secwin
StringTheory
xFiles
As at build 7.03 the Desktop program is "complete" and  the Web program API is "complete", but the Web interface needs work.

Products

Not surprisingly, the license process starts with the product you have created. Most companies may end up with only one product in the list - some others may end up with more.

Most of the product information concepts have been discussed above, but the following items are of interest here;
Product License Name This is the name as entered on the Secwin Global Extension, License Tab
Product Version This is the (major) version number as entered on the Secwin Global Extension, License Tab
Default License Period The default amount of time (in days) to allow when temporary default licenses are issued.
License Secret This is the secret as entered in the program, on the Secwin Global Extension, Secrets tab.
External ID An ID field provided for your use for linking this table to other tables in other programs in your business.

Customers

Company information is pretty straight-forward. The only item of interest here is the External ID which can be used to link this record to other records in other programs. You can put whatever you like in this field.

Licenses

When the product is running at a customer site, the program will periodically connect to the licensing server. In this way the program can provide updated customer information to you, and you can push updated license information to the customer.

In cases where the customer is not online (or the developer is not making use of an online server) then the license can be generated by the License Manager and transferred to the program via another means. A typical license looks like this;

--- LICENSE START ---
R8TzjTglS9K5loTTLwFaLvHsz69sY72/TUOLbAwUOglv4p5AzYgVJWR9cYN0wYImCsnTXhigcxC6
jeN20krlYXDw+JL6/G/JH9Ukjzc8BZFQzIyM+QhOGyZoRPG/itUsUu1YbU5PuHz77SnpG3UHXTMm
9qOvtInz21TE73mH/bkdb3Pubf+0G1MN9CcrI/gt7l283wo0f2CXDagIyx0Uk0JT2b80Uun5aa58
d+hZw/AK5c4qhTC2z9ruMBe4GIR/EzN6LEovF3/jeoYYds2HUmHj4L3SsaSj6HlCIPFYd5AftkWg
sW0/AibpQp8GCFYzfzzVk8HOo+NqKOjswzS7ipJS4vTYDoZLWKGwn6JNp4KiUMSvBK3MDDYu32zy
/ZXa6/BYoHx7lCpLr/dzFd6eLemsyQWwdS957a7npr0g2c1FCTYzfEtkVHbflZsPjODBup2cDA72
bvGJWAMWdCjPfeawfdPkFES5vMHrxhK4/Xo=
--- LICENSE END ---


the license includes all the information, including the customer information. So in an offline situation the customer does not need to match their customer details to your customer details - you can simply capture their details and send them the license.

The License Manager has the options to create an email, or copy the license to the clipboard for you.

In addition to the customer, and product details there are a number of other fields you can capture on the Update License window. Most of these fields don't intrinsically mean anything, so you are free to use them for any purpose.
Dealer The name, or ID of a dealer that delivered the sale. The program doesn't do anything with this value, but allows you to sort the browse by dealer.
Serial Number  The program doesn't do anything with this value, but allows you to sort the browse by serial number.
Type The form offers two radio options, Temporary and Permanent. However you can add additional options here if you like. Again this option does nothing by default, it is mostly for information for the customer. Regardless of the type the Expiry Date still applies.

Secwin Online Server

Secwin Online Server is a hosted version of the License Manager Web program. This means you don't need to host your own servr, but rather make use of the one provided by CapeSoft.

As a hosted service there is a small annunal fee asociated with using this server (currently $69 per year). This fee is fixed regardless of how many client programs you sell that make use of the service. To order the service go to ClarionShop

Once your order has been processed you will be issued with a Dataset name, Login and Password so that that you can access the site (https://api7.secwinonline.com/. Using this interface you can add products and customers, as well as issue, or modify client licenses.

Online Setup

The minimum you will need to do on the server is
  1. Using your browser log onto https://api7.secwinonline.com/
  2. Go to the Browse / Products menu item
  3. Add your product to the list. the most important settings are on the first tab. The Product License Name, Product Version and Product License Secret must all match the settings

For more product details you can set see the section on Products above.

Customer information can mostly be entered by the customer themselves on the License window in your application.

You can control the license issued to the customer via the Licenses menu option.

Program Changes

Adding support for the Secwin Online Server to your program is straight-forward.
  1. Go to the SecwinLicense procedure, to the FOR the SecwinLicense Procedure Extension template.
  2. Set the Online Server URL to 'https://api7.secwinonline.com'
  3. Set the Online Server Dataset to the name issued to you by ClarionShop.

Global Objects

To improve performance for some common tasks, and to reduce access to the database, some global objects are generated by the templates, and used in various places in the code. Primary it is used as the user navigates around the program to determine the access rights to that procedure. However they may also be useful to you for quick access to information.

SecwinGroupsCache Global Object

This is a global, unthreaded, object which is internally thread safe. Do not access the properties of this object directly - use the supplied methods. This object is available in both desktop and web applications.

This object contains a cache of all the groups, and related group information. the cache is loaded by a call to LoadCache, and if the group information has been altered it can be refreshed with a call to LoadCache(sw:force).

The GetNames method takes in one, or more guids (as a pipe separated list, or json document) and returns one, or more, group names, as a comma separated list.

In a multi-tenant program this object contains all the groups, for all the tenants. To get a list of groups (as a pipe-separated list) that belong to a specific tenant use the GetGuids method.

The internal list of group guids and names can be copied out of the object, into a local queue, using the CopyQueue method. This is used internally in the SetAccessRights and GlobalSetAccessRights screens.

For a complete class reference see SecwinGroupsCacheClass.

SecwinCurrentUser Global Object

After a user has logged in, a global object called SecwinCurrentUser is populated with the details of the user. This class is designed to quickly access details about the current user without needing to re-read the database. Internally it is used to determine the user's access as she navigates around the program.

The SecwinCurrentUser object can also be used in your own code to get information about the current user. This information is exposed via a number of methods provided by the object.

For a complete class reference see SecwinCurrentUserClass

SecwinProcedureList Global Object

This object is populated globally when the program starts. It is used to translate procedure names (such as you would call when using say a button or menu item) to a procedure ID (which is used for setting access rights).

This object is unthreaded, so is made threadsafe via a critical section. You will not typically make se of this object from your own hand-code.

For a complete class reference see SecwinProcedureListClass.

Background Logo Procedure

The general idea of the Background Logo procedure is to provide a background to your application frame which is more than just a graphic or color. The window is an actual window in your application, and as such can contain anything you want.  This window never gets focus, however it can contain buttons, so can make for a background-menu of sorts.

Use of this feature is important for Branding, and can also provide a useful place to display information to the user.

Support for this falls into two places;

MDI Logo Procedure

An extension to a normal Clarion window. This window MUST have the MDI attribute ON.
For best results, set the FrameType (ie the border) of the window to NONE, the Caption to nothing, the Position to CENTER.
TURN OFF the "Save and Restore window Position" on this window.

The examples include a window that behaves like this. It is the Background procedure. Not that this example procedure also includes embed code, showing how to create custom strings to display on the window.

This window will automatically be centered in the frame, and remain centered in the frame. If you wish to trigger a refresh on this window (because some item you have been displaying is updated elsewhere, then you can post an event to the window, and it will refresh.

Frame Procedure

An extension needs to be added to the application frame procedure. This is the Run Logo Screen extension template.

The only setting on this template is the name of the MDI Logo Procedure, as defined above.

Adding Secwin

I understand you are in a hurry to get going, but choices you make now will become important later on. It is strongly recommended that you read through the  Program Access Control section before following these steps. At a bare minimum please read through the section on Multi-Tenant support.

Checklist

Done it many times before? Here's the checklist;
  1. Import Secwin7.dctx into dictionary (\clarion\accessory\libsrc\win\secwin7.dctx)
  2. Import NetTalkEmail.dctx into dictionary (\clarion\accessory\libsrc\win\NetTalkEmail.dctx)
  3. Optionally import NetTalkSMS.dctx into dictionary  (\clarion\accessory\libsrc\win\NetTalkSms.dctx)
  4. Add Global Extensions (StringTheory, Cryptonite or NetTalk, MyTable, jFiles, Secwin, [desktop] Secwin Auto Populate Screen Security)
  5. If Multi-DLL then set Multi-DLL tab for all the above (excluding NetTalk) as appropriate.
  6. Import NetTalk procedures (NetTalk Global Extension, Procedures tab). Email, SMS and Active Directory
  7. [Desktop Apps] Disable User Access Control on SendEmail and ActiveDirectory procedures.
  8. [Desktop Apps] Import Secwin Desktop Procedures (Secwin Extension, Procedures Tab)
  9. [Desktop Apps] Set NetTalk procedures in Secwin Extension, Procedures tab.
  10. [Web Apps] Import Secwin Web Procedures (Secwin Extension, Procedures Tab)
  11. [Web Apps] Set optional Netalk procedures (Secwin Extension, Procedures Tab, NetTalk Tab)
  12. Set Secrets (Secwin Extension, Secrets tab)
  13. SendEmail procedure, update use of GLO:UserEmail and GLO:UserName [1]
  14. [Desktop Apps] Buttons / Menu items to Frame (Logout, Login, Users, Settings, GlobalSetAccess, Email Settings)
  15. [Desktop Apps] Add UserLogin Extension to Frame.
  16. [Desktop Apps] Always Allow Access to Frame. Set Min Level of Frame to All.
  17. [Desktop Apps] On Frame, display LoggedIn Name, Level etc in Status bar
  18. [Desktop Apps] Set options on Procedures, User Screen Security, as desired.
  19. [Web Apps] Add Secwin Extension (FOR WebServer procedure) to WebServer procedure
  20. [Web Apps] Add Secwin Extension (FOR WebHandler procedure) to WebHandler procedure
  21. [Web Apps] Goto NetTalk Global Extension (Activate NetTalk Web Server), WebApps tab, Tick on Use Secwin.
  22. [Web Apps] [Optionally] add <div class="nt-current-user"><!-- Net:s:CurrentUser --></div> to xHTML on Header or Footer.
  23. [Web Apps] Add menu items to Login, Logout, Users, Settings, GlobalSetAccess

Multi DLL

If you have a multi-dll system (ie multiple apps) then the instructions for adding Secwin are extended somewhat.
  1. Import the dictionary changes to the apps as normal
  2. The Global Extension is added to all the apps in the system.
    For all the global extensions (ie for Secwin, StringTheory etc) the Multi-DLL tab must be set correctly in all apps. This means setting both checkboxes to ON in the data dll, and the first checkbox to on in all the other apps. (ESPECIALLY the EXE apps)
  3. For all the apps, the Secrets on the Secwin global extension, Secrets Tab, must be set to the same values.
  4. For all the apps the License details on the Secwin global extension, License tab , must be set to the same values.  (Hint: For MultiDLL apps you may want to use global variables here, which can then be set, and updated, in one place. (For ABC, That's in the Data DLL, Program Setup embed point, Priority 10; for Legacy that's in the Exe App,

Secwin Procedures

The Secwin procedures are flexible, so you have some choices when it comes to their location.

The obvious choice is to simply import them into your data DLL, exporting them from there.  Then they are normal External Procedures in the other places where you need to call them.

However the procedures don't have to be in the Data DLL - they can be in a "higher" DLL, with the understanding though that if they are in a "higher" DLL, then procedures in "lower" DLL's cannot be protected.

Secwin Global Set Access Rights Procedure

One way of setting the access rights to specific users is to use the SecwinGlobalSetAccessRights procedure. In a Multi-DLL situation this procedure has to be aware of other apps in the system that contain the Secwin extension. To do this;
  1. Go to the app that contains this procedure (probably the Data DLL)
  2. Go to the SecwinGlobalSetAccessRights procedure, to the template extensions
  3. Go to the FOR the SecwinGlobalSetAccessRights Procedure extension template
  4. Go to the Apps tab
  5. Click on the Insert button and add the app name of the other apps in the system. Just the app name will do, no extension or quoes are needed.

Appname.Secwin.Inc Files

In a multi-DLL context the template will generate appname.secwin.inc files for each app. These files are then included by the Global Set Access Rights procedure, and this is used to build the tree-list of all the protected procedures and controls in the application.

Therefore, if you add procedures to an app, or add protected controls to a window, you will need to compile that app and then re-compile the app containing the Global Set Access Rights procedure in order for those new controls and windows to appear in that procedure.

Adding Secwin to Your Dictionary

Secwin makes use of a number of tables to store data in. These tables need to be added to your dictionary, and possibly modified to match your requirements.
  1. Open your Dictionary
  2. Click on the dropdown next to the  Import/Export button.

  3. From the drop down select Import Dictionary from Text
  4. Navigate to your \clarion\accessory\libsrc\win folder and select Secwin7.dctx

You can leave the tables as-is, but it's possible you will want to adjust the tables to match your requirements. The most likely change at this point is to change the File Driver from TOPSPEED to whatever database you are using. This would necessitate changing the Full Path Name and Owner Name properties of the tables.

The SecwinRights table (which is only used by the NetTalk Web Server Apps) is set to make use of the MEMORY driver. If you do not have the MEMORY driver installed then change this table to use the TOPSPEED (or any other) driver. (The Owner and Pathname properties for the table are set so that changing the driver should be straight-forward).

This may be a good time to consider whether the tables should be part of your actual database, or kept in a separate database. For a full discussion on that see Multi-Tenant. Related to this is the choice between Company-And-User versus Unique-User.

For a more indepth look at the various tables, see the Dictionary Reference section of this document.

Blobs

Secwin makes extensive use of Blobs to store various information in the tables. Information in these blobs are encrypted, and then base64 encoded. As such they only contain ASCII data, and don't need to be set as Binary.

For the TopSpeed driver Blobs have always been well supported, and as long as the field is marked as a BLOB in the dictionary there is nothing else to do.

For SQL systems, blob support has been a bit patchy, and there are some "ideals" to strive for.  Since the fields are always base64 encoded, they can be treated as text. This means the BINARY switch in the database can be OFF, and the backend can use a VARCHAR as the datatype. If you use a VARCHARBINARY then the BINARY switch in the Dictionary MUST be turned on.

Note that this applies to the SecwinConverter program as well. Currently the Dictionary used by the converter is set for the blobs to be not-binary, if you are going to use VARCHARBINARY on the backend then you need to change the converter dictionary and re-compile the converter program (currently requires Clarion 11 or later.)

NetTalk

If you have NetTalk Desktop then you will definitely want to make use of the Send Email features. You may also want to make use of the Send SMS and Active Directory features.

If you have not already done so, this is a good time to import the suggested NetTalk tables in NettalkEmail.dctx and NetTalkSMS.dctx.

Adding Global Extensions

The following Global Extensions are required. Add them to your app. If you have multiple apps then they will be need to be added to all the apps.
  • StringTheory
  • Cryptonite (or NetTalk 11 Desktop or later)
  • MyTable
  • jFiles
  • NetTalk (Desktop or later) - this one is optional but recommended.
  • Secwin 7
    • Auto Populate User Screen Security (desktop apps only, not web apps)
After adding the extensions, and BEFORE moving on, set the following on the Secwin Global Extension;
  1. Secrets Tab: Program Secret
  2. Secrets Tab: Table Secret
If this is a Multi-App system, then all the APPs will need to have Secrets set the same.  Secrets can be changed, but only at the cost of throwing away existing Secwin Tables, so you will want to settle on suitable values here BEFORE distributing your application. You can use Global Variables in these template options, but then you will need to populate the global variables in your code, very early in the program startup.

NetTalk Support (Desktop and Web)

Whether you are making a desktop app, or a web app, Secwin can make use of a number of NetTalk features to make the security system in your application better. If you are making a web application these steps should be considered as "required". For a desktop program these steps are optional, but recommended.

NetTalk Procedures

Optional NetTalk Procedures  
These procedures are provided by NetTalk, and can be imported via the NetTalk Global Extension (Build 11.36 or later) on the Procedures Tab.
SendEmail Allows the program to send emails. This is useful not just for Secwin but for other features in your application.
SendSMS  
ActiveDirectory_UserInGroup checks that the user is in an Active Directory group on the Active Directory server. This allows an Active Directory Administrator to control overall user access to the program (for ALL user levels.) Once inside the program users can have rights, and be in groups, just like any other Login Type.
ActiveDirectory_ValidateUser Allows the user to login via an Active Directory Server.
UpdateEmailSettings (Desktop only) Allows the program user to configure the email settings.
BrowseEmailLog (Desktop only)  Allows you to browse the Email Send Log to see what was sent, and to resend if necessary. (Although resending Secwin Emails is meaningless, as they time-expire anyway)

Adding Email Support (optional)

Support for Email implies that you have a tool suitable for sending emails in your Clarion toolbox. These instructions apply to using NetTalk Desktop for emailing, however any email tool can be used as long as you provide a procedure which takes a parameter list declared as

(EmailParametersGroup pEmail)

This group structure is declared by Secwin for you if you don't have NetTalk in the application.
Adding NetTalk Email
  1. If you don't already have them then import the EmailSettings and EmailLog tables into your dictionary.
  2. Make sure the Global Extension, Activate NetTalk, has been added to your app.
  3. If you don't already have one, then import a SendEmail procedure on the Procedures tab of the NetTalk Global Extension.

    This procedure can be tailored to your requirements, but should have the prototype;
    SendEmail(EmailParametersGroup pEmail),Long,Proc
  4. Inside the SendEmail procedure replace;
    GLO:UserEmail with SecwinCurrentUser.GetEmail() and
    GLO:UserName with SecwinCurrentUser.GetName()

  5. Disable the User Screen Security in the SendEmail procedure.

Adding SMS Support (optional)

Unlike Email, SMS costs money so your SMS procedure will depend a lot on the SMS provider that you use. As with Email you can use any tool you like to construct this procedure.
Adding NetTalk SMS
  1. Make sure the Global Extension, Activate NetTalk, has been added to your app.
  2. ImImport a SendSMS procedure from either one of your other applications, or from the NetTalk Twilio Demo app(Clarion11\Examples\NetTalk\WebClient\Twilio) . This procedure can be tailored to your requirements, but should have the prototype;
    SendSms(SMSParametersGroup pSms),Long,Proc
  3. Go to the Global Secwin Extension in the application, to the Procedures tab, and select the name of the SendSMS procedure.
  4. Disable the User Screen Security in the SendSMS procedure.

Adding Active Directory Support (optional)

To make use of the Active Directory features in Secwin your application will need to communicate with the Active Directory Server. This functionality is usually provided by NetTalk Desktop. As with Email, and SMS, you can use another tool to create the AD functions in your application if you wish.

The required procedures are;
ActiveDirectory_ValidateUser(LDAPParametersGroupType pParms),Long
and
ActiveDirectory_UserInGroup(LDAPParametersGroupType pParms),Long

Adding NetTalk Active Directory Support
  1. Make sure the Global Extension, Activate NetTalk, has been added to your app.
  2. Import the ActiveDirectory_ValidateUser and ActiveDirectory_UserInGroup procedures from the NetTalk Example \Clarion11\Examples\NetTalk\NetLDAP\ABC\LdapDemo.app or
    \Clarion11\Examples\NetTalk\NetLDAP\Legacy\LdapDemo.app
  3. Go to the Global Secwin Extension in the application, to the Procedures tab, and select the names of the two Active Directory procedures.
  4. Disable the User Screen Security Extension Template in the two Active Directory procedures.

Adding Secwin to Your Desktop Program

Access Control

Adding Secwin procedures (required)

Secwin uses a number of procedures to create a user interface to the user. In order to give you complete control over the UI of the procedures, so that they seamlessly fit into the UI of the rest of your program, these are implemented as procedures in your application.

These procedures can either be imported into the program directly  using one, or more, of the Global Extension, Procedures Tab, Import Procedures buttons, or they can be imported from one of the example applications.

The following procedures may be added to the application. If this is a multi-app system then do this in the Data DLL.
Access Control Procedures Description
SecwinBrowseGroups A browse of all the User Groups.
SecwinBrowseTenants A browse of all the Tenants.
SecwinBrowseUsers A browse of all the Users.
SecwinGlobalSetAccessRights A central screen which lets an Administrator set the Access Rights for all the users, and all the groups, for all the procedures in the application.
SecwinLogin The login procedure.
SecwinLogout The logout procedure.
SecwinSetAccessRights A local screen for setting the access rights for all groups and users, for a single procedure in the application.
SecwinUpdateGroups A form procedure for updating the details for a group.
SecwinUpdatePassword A form procedure which allows a user to change their password.
SecwinUpdateSettings A from procedure for updating the policies and settings for a tenant.
SecwinUpdateUsers A form procedure for updating a user.

Calling Procedures From your Frame

TIP: A Control template exists for adding these procedure calls to a toolbar on the frame.

You will need to add Menu items, or Toolbar Buttons for the following procedures;
Access Control
Required Calls To
Description
SecwinUpdateSettings This allows the end user to set policies for the program. Until these policies are set, and the Users added, there is no login required to access the program. This procedure takes a single string parameter, so from the Frame the parameter list should be set to ''.
SecwinBrowseUsers In order to add Users this menu item is required.
Access Control
Optional Calls To
 
SecwinLogout Allows the User to Logout. This also clears the "remember me" token if the user wants to be forgotten on the machine.
SecwinLogin Allows another user to login. This does not clear the "remember me" token, unless the new user selects the "remember me" checkbox.
SecwinUpdatePassword Allows the user to set a new password for themself.
SecwinGlobalSetAccessRights Allows an Administrator to set the access rights for all users, and groups, globally.
SecwinBrowseTenants Only necessary in situations where Multi-Tenant support is desired.
Licensing
Required Calls To
 
SecwinLicense You'll need this so the user can register the application.

Calling the SecwinLogin procedure

In order to access most of your programs features, your user will need to login. This can be done by the  User_Login extension template
(typically to the Main / Frame procedure), or using the Call SecWin's Login function code template (also known as the Call_ChangeLogin code template.)

The UserLoginHere template generates code into the window startup code.
The Call_ChangeLogin code template allows the user to login, or for a new user to login.

Adding Screen Security

The Access Rights to each screen are controlled by the User Screen Security template. This can be manually added to some subset of windows in the application, or it can be added to all procedures globally. Adding globally is a lot less work. To add globally
  1. Go to the Global Extensions for the APP.
  2. Highlight the Activate CapeSoft Secwin item in the extensions list
  3. Click the Insert button
  4. Select AutoPopulateUserScreenSecurity extension from the list (in the Class Secwin10 section).

Adding Licensing

Adding Secwin Procedures (required)

Secwin uses a number of procedures to create a user interface to the user. In order to give you complete control over the UI of the procedures, so that they seamlessly fit into the UI of the rest of your program, these are implemented as procedures in your application.

These procedures can be imported into the program directly  by going to the Global Extension, Procedures Tab, Import Secwin Licensing Procedures button, or they can be imported from one of the example applications.

The following procedures may be added to the application. If this is a multi-app system then do this in the Data DLL. 
Licensing Procedure  
SecwinLicense A registration window where the user can enter their license details, and get activation codes from the web.

Checking program license on startup (or anywhere)

If you are using an online license server (Secwin Online Server, or your own) then any procedure can be automatically set to check the license when the procedure is called. Setting this on the main application frame for example would mean this test is done when the program starts up.

TIP : The test is only performed once per day - if you want to repeat the test (while debugging or testing for example) then remove the [license] checkdate setting from the secwin.ini file.
To set a procedure to do the test;
  1. Go to the procedure in the application tree
  2. Go to the User Screen Security extension.
  3. Go to the Licensing / Expiry tab
  4. Check on the option Check for new license from web here.
Note that the check is performed asynchronously on a separate thread. The current window will progress with the current license - if there is a new license it will be downloaded and silently applied. However it will only become applicable the following time the program is run.
Note also that this is a HTTPS connection to a remote server. So this makes your program a TLS Client. See https://www.capesoft.com/docs/NetTalk12/NetTalk.htm#DeployingAtlsClientOrServer if you are unfamiliar with shipping a TLS client program.

Priming License Values

The SecwinLicence procedure is where the user can alter their details, fetch a new license and so on. Note that most of the fields on ths window are not set by the user, they are set as part of the license itself. However fields in the CustomerGroup (local data) can be primed in your code. Typically you would only prime these details if they were blank.
Priming is done inside the INIT method, after the Window is Opened, and after the calls to GetLicense. these calls load the groups, especially, in this case, the CustomerGroup. You can then prime the values. For example;

If cg:CompanyName = '' then cg:CompanyName = 'Wacky Wingnuts'.

Adding Number-of-Copies Test (optional)

One of the licensing features allows you to limit the number of copies running on a LAN. This is done using NetTalk, so this feature requires NetTalk Desktop level (or higher.)

NOTE: The copies check takes a few seconds to complete as the program starts. This is known as the startup window. Multiple instances of the program starting inside this window can all fail the test if they exceed the allowed copies. In other words starting 2 instances right after each other can result in both being disqualified, even if one would typically be allowed. The default startup window time is 5 seconds, although this can be adjusted if necessary.

Unlike most of the other licensing features the Limit Copies feature takes some extra steps in the application to activate.

  1. Only one of the procedures in the application does this test. This procedure is most commonly the Frame procedure. Whichever procedure is used it has to be the top-most window of a thread. In other words, like the frame, it should remain as the top-most window in the thread it is on.
  2. Add a NetTalk Object to the Extension list of this procedure. The default name for this object is LimitCopies. The base class must either be NetClient (NOT NetWebClient) or some class that is derived from NetClient.

  3. On the Settings tab for this object set the service name to something (typically something similar to the License Name is useful), and make sure the Private Service option is OFF.

  4. In the same procedure, on the Secwin User Screen Security extension, Licensing / Copies tab, tick on the option to Test to License Copies here.
  5. Set the NetClient Object name to the same you specified in step 2 above. This is typically LimitCopies.
  6. A second option here is Action. The default action is "Default" which is described below. And alternative is "Message" which simply displays a message to the user and closes the procedure.

    The default behavior though is somewhat more complex (under the hood). It uses the NetTalk object (LimitCopies) to query the other instances, and get their current user information. This is presented to the user as a list of logged-in user names, and also the machine names they are running on. This can help to identify which users are using the program, and if necessary ask them to exit.

Adding a Background Logo Procedure (optional)

Adding License Branding (optional)

It will encourage pirate users to convert to paying users if you display program license information to users of the program, and also on reports that may be distributed to external people (customers, suppliers and so on). A simple mechanism is provided to access any of the license or company information that has been recorded.

Adding Secwin to a NetTalk Web App

This section assumes you have completed the steps in the general Adding Secwin section, and are now implementing the functionality specific to a web application.

Access Control

Adding Secwin Extension Templates (required)

The WebServer and WebHandler procedure both have custom Procedure Extension templates which add the Secwin support to those procedures.
  1. Add the FOR WebServer Procedure extension template to the WebServer procedure.
  2. Add the FOR WebHandler Procedure extension template to the WebHandler procedure.

Adding NetTalk Scripts

The Secwin windows make use of the new TREE form-field control. So make sure the Tree script is activated by going to the Web Server procedure, NetTalk Extension, Settings/Scripts tab, and tick on the TREE checkbox.

Adding Secwin procedures (required)

Secwin uses a number of procedures to create a user interface to the user. In order to give you complete control over the UI of the procedures, so that they seamlessly fit into the UI of the rest of your program, these are implemented as procedures in your web application.

These procedures can either be imported into the program directly  using one, or more, of the Global Extension, Procedures Tab, Import Procedures buttons, or they can be imported from one of the example applications.

The following procedures may be added to the application. If this is a multi-app system then do this in the WebHandler DLL.
Access Control Procedures Description
SecwinBrowseGroupsContainer A single container showing the users and groups at the same time.
SecwinBrowseGroupsWeb A browse of all the User Groups.
SecwinBrowseTenantsWeb A browse of all the Tenants.
SecwinBrowseUsersWeb A browse of all the Users.
SecwinBrowseUsersForGroup A variation on the Users list, used by SecwinBrowseGroupsContainer.
SecwinGlobalSetAccessRightsWeb A central screen which lets an Administrator set the Access Rights for all the users, and all the groups, for all the procedures in the application.
SecwinLoginWeb The login procedure.
SecwinLogoutWeb The logout procedure.
SecwinSetAccessRights A local screen for setting the access rights for all groups and users, for a single procedure in the application.
SecwinUpdateGroupsWeb A form procedure for updating the details for a group.
SecwinUpdatePassword A form procedure which allows a user to change their password.
SecwinUpdateSettingsWeb A from procedure for updating the policies and settings for a tenant.
SecwinUpdateUsersWeb A form procedure for updating a user.

Calling Procedures From your Menu

You will need to add Menu items for the following procedures;
Access Control
Required Calls To
Description
SecwinLoginWeb Allows the user to login.
SecwinLogoutWeb Allows the User to Logout.
SecwinUpdateSettingsWeb This allows the end user to set policies for the program. Until these policies are set, and the Users added, there is no login required to access the program.
SecwinBrowseUsersWeb In order to add Users this item is required.
SecwinBrowseGroupsContainer In order to add Groups this item is required.
Access Control
Optional Calls To
 
SecwinUpdatePasswordWeb Allows the user to set a new password for themself.
SecwinGlobalSetAccessRightsWeb Allows an Administrator to set the access rights for all users, and groups, globally.
SecwinBrowseTenantsWeb Only necessary in situations where Multi-Tenant support is desired.

Calling the SecwinLogin procedure

Unlike Desktop apps, Web apps can be a mix of Login Required procedures, and Login-Not-Required procedures. As such it is not necessary for the Login procedure to appear when the user first accesses the site. Usually the Login option is offered as a link, or menu item.

If the user has logged in, with remember-me ticked on, then they have a login Token. This token is passed to the server via a Cookie called BearerToken. If this cookie is passed to the server then the user will automatically be logged in. There is then no need for them to go to the login window.

Client programs (ie web service clients) can also login using the Authorization: header.  The header should be set to bearer followed by the token. For example;

authorization: bearer xxx

Note that if the user logs in via the Cookie, or Authorization header then all the other login checks are bypassed. The token itself is considered to be sufficient. Therefore if authentication is primarily done via Active Directory, then the lifespan of the token should be limited to a suitable value.

Note that the token does not contain any user login, or password information. It is simply a random value, which identifies a record in the SecwinTokens table. Deleting the record from the SecwinTokens table renders the token invalid. the token is not linked to the user password - changing the user's password will not invalidate the token.

Adding Global Screen Security

One of the imported procedures is called SecwinGlobalSetAccessRightsWeb. This procedure displays a list of groups, and users, on the left side of the screen, and a list of possible access rights on the right. You can change the rights by clicking on the right, for a procedure (or control) and cycling through the options.

To allow an Administrator, or SysAdmin, to set user, and group rights here, make sure this option is accessible via the menu. (Alternatively these users can use a direct URL to access this page).

Note that this procedure makes use of the TREE form control, so make sure the app has the Tree script included. (WebServer procedure, NetTalk extension, Settings, Scripts, Tree.)

Adding Screen Security

The Access Rights to each screen are controlled by the NetTalk procedure template, on the Security (or Secure) tab. Access rights to individual controls on the screen are also built-into the NetTalk procedure templates.

Key Procedure Access Control Decisions

Procedures can be set to require a specific user level to access them. The Secwin windows (like Policies, Users and so on) have this set appropriately by default. However there are other (more generic) procedures in your application which can be used by the security system, and these procedures need to be carefully considered and then set appropriately.
Procedure Discussion Recommended Setting
SecwinLicense If you are using Access Control AND Licensing features, then you may wish to limit access rights to this window. Requires Operator or higher
SendEmail, SendSMS, ActiveDirectory_ValidateUser, ActiveDirectory_UserInGroup These procedures need to be used before a user has logged in. Therefore User Access Control on these procedures needs to be disabled. Disabled
UpdateEmailSettings This procedure can be used to change the Email Server settings, and thus intercept outgoing emails containing password resets. Requires Administrator
UpdateSMSSettings This procedure can be used to change the SMS Service settings, and thus intercept outgoing SMS's containing password resets. Requires Administrator
Main (Frame) If the user is required to be logged in to this procedure, then a Logout will automatically terminate this procedure (and likely end the application). Always Allow Window Access

Security Table Distribution

The Security Tables are ultimately stored in either a file on the disk (TPS) or inside a database (SQL). Security of this data does not just extend to reading, and altering the information in these tables, but also deleting records in the security database. The scope of this problem depends a lot on your context.

The first important factor is the data storage. Disk files created by ISAM drivers (TPS, DAT etc), which are visible to end users, are more-or-less indefensible. SQL tables are much easier to protect using the security features of your Server. Web applications may store data in ISAM files but the disk is not visible to users, so this is not a concern.

The second important factor are the two kinds of information which need special protection; In conjunction with these two features, the behavior of Secwin when either these tables, or specific records in the tables are missing, becomes very important.

If your tables are secure (ie SQL, or WEB) then the program is free to create users, and settings (tenants) in an unrestricted way. The database administrator can empty the tables at any point, and they can be re-created as needed. Obviously if they do drop the table data then that data (and related data) is lost, so it is not recommended that they drop data, and it is recommended that these tables be suitably protected from malicious access.

If the database administrator does empty the SecwinSettings table then all the license details for the application will be lost (requiring the administrator to re-license the program.) All the other Secwin tables will also then contain orphaned, useless, data. Without the original SecwinSettings record this data will become useless. For this reason implementing licensing on programs using Topspeed storage is recommended.

If the database administrator leaves the settings record alone, but instead deletes all the users, then the program behavior comes into play, and you have choices.

As always, the records in all these tables are protected by the Program Secret. So the database administrator does not have the ability to create new records (or alter existing records) outside your actual program.

In short there are two "threats" you need to consider here. The degree to which these threats matter to you determines the program behavior that you choose.

The Database Administrator

If the tables are limited to only the database administrator, and the database administrator can be considered to be "non malicious" to the interests of the customer, then the areas of concern are greatly reduced.

The End User

If access to the tables themselves are impossible to the end user, then this is not a concern. However in a traditional desktop program it should be assumed that the user can delete (or otherwise remove) the DSSW7.TPS file.
 
Threat Mitigation
Desktop Program.
ISAM file on disk.
  • The TOPSPEED driver should be used, and all Secwin tables should be inside the same SuperFile
  • The tables should have an OWNER string (same for all the tables)
  • The program should not be able to create a new DSSW7.TPS file (remove the CREATE attribute from these tables in the dictionary)
  • The DSSW7.TPS file should be supplied by the developer, and be installed as part of the program install.
  • It should contain a preexisting Settings record in the SecwinSettings table
Web Program.
ISAM file on Web server disk.
  • The TOPSPEED driver should be used, and all Secwin tables should be inside the same SuperFile
  • The tables should have an OWNER string (same for all the tables)
  • Access to the server computer should be limited.
  •  Access to the DSSW7 file on the disk should be limited.
SecwinSettings table is stored in a SQL database.
  • Access to the tables should be limited so that only the program (and SysAdmin User) have access to the tables.
  • The SecwinSettings, SecwinUsers and SecwinGroups tables should not permit record deletions (at all).
 

Runtime

Checklist

Things to do when the program runs for the first time.
  1. Go to Security, Groups and add 1 or more groups.
  2. Go to Security, Users and add 1 or more users. Make the first user the SysAdmin.
  3. Go to the Secwin Settings window and select desired settings. Login Type certainly.

What to Expect: Web

When the program runs for the first time, this is what you can expect;
  1. You can login by clicking the login button. Since no users exist you will automatically be logged in as a System Administrator.
  2. You should now be able to see the Security menu. Go to the Security menu, Groups option
  3. Go to the Security / Users menu and add the first user. Set this to be the SysAdmin.
    then add some other uses if you like.
  4. Go to the Security menu, Policies item, and set the security policies for the application.

Case Studies

This section contains a walk-though of some specific implementations

Updating an Attribute after a Successful Login

If you have added a custom attribute to the user record, then one place where you may want to update that attribute is when the user successfully logs in. This can be done by embedding in the LoginComplete method, before the call to the parent method. For example;

LoginDesktop.LoginComplete PROCEDURE ()
ReturnValue Long
CODE
  self.SetAttribute('MyOwnAttribute','Last Login Time: ' & format(clock(),@t4))
  Parent.LoginComplete()

Editing the SecwinUser table in code

Secwin 6 offered a number of functions for editing user records. These functions were the only way to access the Users table, since the table was not in the dictionary. In Secwin 7 though the users are stored in the SecwinUsers table, and this is a table in your dictionary. In concept you can access this table just like you would any table in your dictionary, you can add records, delete records, edit records and so on. With three catches (There are always catches, right?)

Aside: Both techniques used by this table are provided by MyTable, and you can easily do the same in your own tables.

Catch 1: The row is encrypted.

Some of the fields in the row are encrypted, some of them are hashed, meaning that the record cannot be tampered with using TopScan or SQL Enterprise Manager or an equivalent outside program. Thus, after a record is read from the database it must be decrypted. Before writing it to the database it has to be encrypted again. (And if you continue to use the record, it should be decrypted again after writing.)

Conceptually this looks something like this;

SecUse:UserLogin = 'Englebert'
If Access:SecwinUsers.Fetch(SecUse:LoginKey) = Level:Benign
  ! decrypt record here
  ! do any adjustments to fields here
  ! encrypt record here
  Access:SecwinUsers.Update()   ! possible decrypt record again here
End


Fortunately a class (SecwinUserRecordClass) has been provided to do all of the hard work for you, reducing the encrypt and decrypt to single lines of code. You can see an example of this class in action in the SecwinUpdateUsers form procedure.

You can declare an instance of the class like this;

secUser   SecwinUserRecordClass

The object has to be initialized before it can be used. This is done with the Init method in a simple line of code.

secUser.Init(SecwinUsers,'KSLICO','CIRUXZ','')

The first parameter is the name of the table in the dictionary. The next are the program, table and customer secrets (as set on your Secwin global extension.) They will be different in your program to the example used above. If you are going to be using the secrets like this in hand-code it might be a good idea to make them Equates somewhere, then you can use them here, and also on the global extension.

Once the object has been initialized like this then you can call

secuser.AfterLoad()

after any read from the table and

secuser.BeforeSave()

before any write to the table. If you want to continue to use the record after it is written, then use AfterLoad again to decrypt it.

So conceptually the code now looks like;

secUser   SecwinUserRecordClass
  code  
  secUser.Init(SecwinUsers,'KSLICO','CIRUXZ','')  
  SecUse:UserLogin = 'Englebert'
  If Access:SecwinUsers.Fetch(SecUse:LoginKey) = Level:Benign
    secuser.AfterLoad()
    ! do any adjustments to fields here            
    secuser.BeforeSave()   
    Access:SecwinUsers.Update()  
  End

Catch 2: Some of the data is stored, as a JSON structure, in BLOB fields.

Some of the fields in the table are blobs. these blobs allow unstructured data to be stored about a user in a safe way. Inside the blobs the data is stored in a JSON document, which in turn makes it easier to access the individual fields. It is probably no surprise though that the SecwinUserRecord class makes interacting with this blob data easy.
Attributes Blob.
This blob is designed to hold all the attributes for the user. These will vary from program to program, but basically allow you to store any data about any user. The SecwinUpdateUsers window can trivially be extended to support more attributes. And in the program attributes for the current user can be read using the SecwinCurrentUser.GetAttribute method.

So, editing attributes in code (using the SecwinUserRecordClass,) only makes sense if you are wanting to write to the current user, or write to some other user. Editing the attributes can be done in one of two ways;

1. You can move the attributes into a Clarion GROUP structure. What this GROUP consists of is up to you. The SecwinUpdateUsers window contains a locally declared AttributesGroup (and you can add more fields to this if you like.) The SecwinUserRecordClass offers two methods, LoadAttributes and SaveAttributes which can move all the attributes into and out of the group together. This move is additive, in other words the group does not overwrite the blob, rather the contents of the group are merged with the contents of the blob.

2. You can read, and write attributes individually using the GetAttribute, DeleteAttribute and SetAttribute methods. If you are doing simple manipulations (like setting a single attribute) then this approach removes the need to declare a local group.

Catch 3: Passwords are not stored

Passwords are not stored in the table, rather a hash of the password is stored in the PasswordHash field. Typically this is not an issue since you would seldom interact with the password in code. However if you are wanting to specifically set a password then you would need to do a lot of work to get it properly hashed. To avoid this the table contains a "dummy" field called NewPassword. If this field contains something then the call to BeforeSave will automatically hash the contents of the NewPassword field, and put the result in the PasswordHash field.

Other Things to Know

  1. The GUID field contains a random string of 16 characters. If you are adding users you can use the StringTheory MakeGuid method to populate this field. For example;
    secuse:guid = secglo:st.MakeGuid()
  2. The SettingsGuid field is a relation to the SecwinSettings table, Guid field. If you are adding a new user you can use the SettingsGuid of the current user as a possible value. For example;
    secuse:SettingsGuid = SecwinCurrentUser.GetSettingsGuid()
  3. The timestamps are a REAL value, representing thousandths of a second since Jan 1, 1970. You should leave these as 0 (or as they are.) If they are needed they will be set by triggers elsewhere in the program.
  4. The EncryptionVersion field should not be touched. Before decryption it will have a value > 0, after decryption it will be equal to 0. You can read this field if you like, but do not write to it.
  5. The RowHash (and all the other hash fields) should not be touched.
  6. The UserLevel field should contain one of the following equates; sw:sysadmin, sw:administrator, sw:operator, sw:guest, sw:nolevel
  7. The UserWorkGroup field is the same as the WorkGroup field in Secwin 6.
  8. The Biometrics and Questions blobs are not used yet.

Making a Multi-Tenant Web Server Program, (maybe with different data folders / databases)

This case study matches the example \examples\nettalk\web server\SecwinMultiTenant (88) .
The example is set up to use the Topspeed database, but a SQL database would be set up in a similar way. The obvious difference is in the way Full PathName and Owner mean somewhat different things in ISAM and SQL. So bear that in mind when reading the instructions.

Tip: Setting up a multi-tenant program, where user share the same database is covered by this as well. Just use the same connection string for all the tenants.

In a multi-tenant situation, there are three categories of tables;
  1. Secwin data tables - these are shared across all the tenants.
  2. System configuration tables - these are shared across all the tenants - in this example the emailSettings, emailLog, smsSettings and smsLog tables fall into this category.
  3. Tenant tables - these are the tables containing the data unique to the tenant. In this example this is Customers, Products, Invoices and so on. In most cases each tenant will have their own database, but they could also share a database.
The first task is to set the dictionary to cover the above distinctions.
  1. For SQL you'll have a shared database for the shared tables (Secwin tables, System tables and so on). So this will have one owner string (called say SecGlo:SharedOwner).

    Shared Topspeed tables will be placed in a directory common to all tenants. This can be done using variables in the dictionary, or by simply setting the table names in the dictionary. ISAM SecwinTables are also encrypted with a password, in this example that is set to !SecGlo:TableSecret
  2. In the dictionary set the Tenant tables to use global THREADED variables for the Full PathName and/or Owner Name. For ISAM the usual approach is to set the full path name and possibly the owner, in SQL it's usual to set only the Owner.
  3. Add variables used in step 1 to Dictionary Globals list. Make sure the Tenant variables are THREADed.

Next are changes to the Application.

  1. If all the tenants will be sharing the same security file, then on the Secwin Global Extension, make sure the Customer Secret is a fixed value. Although the tenants may use different databases, and hence have different Customer Secrets, those secrets will apply to the customer tables NOT the Secwin tables.
  2. Create a SetFileNames source procedure to set tablename variables, and tableowner variable(s) based on sessionvalues. This procedure will take one (optional) parameter (<NetWebServerWorker p_web>).
    Set this procedure to be Declared Globally.
  3. In the procedure set the variables in the case where;
    a) p_Web.GetSessionLoggedIn() = False
    b) p_Web.GetSessionLoggedIn() = True
    Use session values where necessary, in the example SecwinDataSecret and SecwinUserPath are used. See Step 6 for where these SessionValues are set.

    For example;

    folder  cstring(255)
      code
      if omitted(p_web) or p_web.GetSessionLoggedIn() = False
        SecGlo:CustomerSecret = 'loggedout'
        folder =  clip(p_web.site.DataPath) & 'loggedout'
      else
        SecGlo:CustomerSecret =  p_web.GetSessionValue('SecwinDataSecret')
        folder =  clip(p_web.site.DataPath) & p_web.GetSessionValue('SecwinUserPath')   
      end 
      p_web.webserver.CreateFolder(folder)
     
      Glo:ProductsFileName = folder & '\Product.Tps'
      ! set all the rest of the filename variables here...
  4. In the WebHandler procedure, Secwin FOR_WebHandler Extension, Options tab, select the SetFileNames procedure.
  5. In the WebHandler, SetSessionLoggedIn Method, after the parent call (and before the call to SetFileNames) set the session values used in step 4.

    For example;

      If p_Value
        self.SetSessionValue('SecwinDataSecret',SecwinCurrentUser.GetAttribute('DataSecret'))
        self.SetSessionValue('SecwinUserPath',SecwinCurrentUser.GetAttribute('DataPath'))
      Else
        self.SetSessionValue('SecwinDataSecret','loggedout')
        self.SetSessionValue('SecwinUserPath','loggedout') 
      End
  6. The system will need a SecwinBrowseTenantsWeb procedure if you don't already have one. This is a NetWebBrowse on the SecwinSettings tab. This procedure should be limited to access only by the sw:SysAdmin users on the Secure tab. Link to the browse from the Security menu.

    The form for this browse is the SecwinUpdateSettingsWeb procedure (which you should already have, and should already be limited to the level sw:Administrator . If you have a menu item for Policies - which calls SecwinUpdateSettingsWeb then keep that - this call to SecwinBrowseTenants web should be in addition to that.
  7. The SecwinUpdateSettingsProcedure needs two more attributes added. These attributes are used to set the Session values in step 6 above. To create these;
    a) Add them into the Local Data Pad, into the the Attributes Group. In the example they are called DataPath and DataSecret. Make them both STRING(255)
    b) Add them to the form as Entry fields, likely on the Multi-Tenant tab.

    These settings are then moved into the session queue in step 6, and from there into the File variables in step 4 above.
  8. The SecwinBrowseUsersWeb browse needs a new column on the SecSet:Company field. This means relating SecwinSettings to SecwinUsers (in the Data pad) using the Custom Join SecSet:Guid = SecUse:SettingsGuid . The new column in the browse should be set to require security level sw:SysAdmin. I recommend making this the first column in the browse. See the example app for an example of this column.
  9. The SecwinUpdateUsersWeb form needs a new field added to the form (on the general tab, typically above the User Login field.) This is the SecUse:SettingsGuid field. Set the prompt to 'Tenant:' . On the String tab turn on Lookup Button. On the Lookup Settings tab set the Procedure to SecwinBrowseTenantsWeb, From Table to SecwinSettings, OrderBy and Description fields to SecSet:Company, and Value Field to SecSet:Guid. Turn on Display Description instead of Value. Set the Security so the user must be logged in, and have a level >= sw:SysAdmin. See the example app for an example of this field.
  10. Repeat steps 11 and 12 for the SecwinBrowseGroupsWeb and SecwinUpdateGroupsWeb procedures.

Running the App for the First Time

  1. Click the Login button. There are no users at this point and no policies, so you will be logged in as a SysAdmin.
  2. Go to the Security Menu, Browse Users, and add a User. This is the SysAdmin who will have complete control over everything. (Incidentally if you stop here ANY login to the system will work, because the policies are still "no login required".)
  3. Go to the Security menu, Browse Tenants, and you should see the Default tenant there. Edit this policy, at the very least set the login type to Program Login. On the Multi-Tenant tab set the data path, and data secret for this tenant.

    In the example the data is stored in ISAM files, so the data path becomes the folder name for the data set. And the Data Secret is the TPS Encryption password for the TPS files. (The example doesn't use this, but it could.)

From this point on a valid login will be required to get into the program.

Adding a Tenant

  1. Login as the SysAdmin User. Go to the Security menu, to the Browse Tenants option. Click the Insert button to add a tenant.
  2. On the Multi-Tenant tab set the Company name for the new tenant. A unique PIN should be generated for you, but you can make the PIN more descriptive (to the company name or whatever) if you like. The PIN is not (much of) a secret. Knowing the PIN for a different company does not compromise security.
  3. Set the Data Path and Data Secret for this tenant.
  4. On the Login Policies tab Set the login type to (at least) Program Login.
  5. On the Users tab add (at least) an Administrator user for the new tenant.

Using License Attributes for Client-Generated Information

It is not uncommon for developers to want to supplement the license information with additional data, retrieved from the client site. For example, you may choose to gather information about the client hardware, and make use of that in the license. This section walks through the process of both gathering, and then checking, that information.

Note: It's worth pointing out that binding licenses to hardware will likely result in licenses failing if hardware changes. This can add substantial load on your support department. Thus the use of this technique should be used cautiously. It should also be noted that some "hardware" may only exist virtually, and making use of hardware (in this era of Virtual Machines, and Containers) to identify computers is not generally reliable in all cases.

Create an Attribute

Whether you are setting the attribute yourself, or setting it via code in the program, the first step is to define the attribute. You do this in the LicenseManager (Desktop or Web) program. go to the Products Browse, and edit your product there. On the Attributes tab add an attribute name. Note that the name of the attribute is visible to the end user, so you may want to use something vague rather than a specific hardware name.

Set the Value of the Attribute in the Program when Getting a New License

There are two ways you (typically) get a license. One is from the Desktop License Manager program, the other is from the Web License Manager.

Desktop License Manager

In the desktop program the client information (Company name, Phone Number etc)  is captured manually from the client and entered into the desktop program. If you wish to capture additional information, then you should make it available to the user - ideally on the SecwinLicense screen, or elsewhere in the program where it is appropriate to display it. Note that you don't alter the Attributes Queue (in the SecwinLicense procedure) since that information is part of the license itself.

You can then capture this information into the license (as an attribute). When the license is generates this value will be included with the license, and will automatically populate the attribute on the client side.

Web License Manager

When making use of the Web License Manager, information can (and is) sent from the client program to the server. This allows the client to keep their data updated, and also to receive licenses automatically.

If you are using this approach then your SecwinLicense procedure will contain a NetWebClient (NetTalk) object, which communicates to the License Server. A number of values are sent with the request - these are generated for you by the template. You may choose to send the information automatically, or you may not - that is up to you.

It's worth remembering that the goal of something like this is to detect a change, and allow (presumably) a human in your support department to evaluate the change, and if necessary, update the license. If you update the information automatically then you negate the point of the item in the first place. Nevertheless you may choose to send this information to the Web License Manager.

If you inspect the code, you will for example see,

net.SetValue('machineid',secLicense.GetMachineId())

In the embed immediately before this code you can generate additional values for the server;

net.SetValue('somethingNew',someValue)

This value could be something the user enters, or it could be something you compute.

For example, say you have a function to get the hard drive serial number. Then you might have a line like this;

net.SetValue('HardDrive',GetHardDriveSerialNumber())

This value will be added to the POST request that comes into the Web License Manager, and you can use it there. You might use it to flag a problem to support staff, or you may use it to update the attribute for the license. What you do with it on the server side is up to you.

Checking the License and Hardware still Match


Of course there's no point collecting the data if you do not make use of it to validate the license in some way. You can do this by embedding code into the secAccess.CheckLicense method in a procedure. Add your attribute code AFTER the parent call. if you are satisfied with the attribute value, set the return value to sw:ok, otherwise set it to sw:notok.

For example;

ReturnValue = PARENT.CheckLicense (pPostEvent)
if ReturnValue = sw:ok
   ! do additional checks here
  if SecwinCurrentUser.GetAttribute('HardDrive') <> GetHardDriveSerialNumber()
    ReturnValue = sw:notok
  end
end


(Hint: The CurrentUser object inherits Attributes from the User, from the Security Policies, as well as the License.)


Trouble Shooting

Sometimes steps get missed, or common errors occur. This section helps to clear those up.

Unknown Identifier GLO:UserEmail / Unknown Identifier GLO:UserName

You've imported the NetTalk SendEmail procedure (good) but you need to tweak it to make use of the SecwinCurrentUser.

Error Number [-290] Unable to Encrypt or Decrypt Data

One of the first things the program needs to do when logging in, or checking a license, is read the Settings table and get the current Secwin login settings, and license settings, from the Table. If the program is unable to decrypt the information in this record, then it will generate an error -290. Since this error is usually fatal, it will usually result in the message being displayed, and the program terminated.



The cause of the error depends on which encryption library your program is using.

NetTalk

If NetTalk 11 (or later) is in your application then this is the engine that will be used. This means your program needs to include the OpenSSL libraries as described at https://www.capesoft.com/docs/NetTalk12/NetTalk.htm#DeployingAtlsClientOrServer. (You can adjust the URL to match your version of NetTalk). A good test to see if everything is installed correctly is to run OpenSSL.Exe from your application folder. If everything is installed correctly then you should see a command prompt. If it is not installed correctly you will see a windows error.

Cryptonite

If your program is not using NetTalk then it is likely using the Cryptonite engine. This makes use of the Windows CryptoAPI library (which is installed as part of Windows.) While you don't have any install requirements here, it is possible that the CryptoAPI functions are being blocked by an Anti-Virus or anti-Malware package, as some malware makes use of these API's. For more information on this see the Cryptonite docs at https://www.capesoft.com/docs/CryptoNite/CryptoNite.htm#FAQ.

Upgrading From Secwin 6

Upgrading Check List

This section acts as a checklist for upgrading apps using Secwin 6 or earlier. Skip it if you are new to Secwin.
  1. Read the docs. Sure you've used Secwin forever, but this is all-new code and functionality. You will save a LOT of time if you run through the docs first. Seriously.
  2. If you are upgrading an existing app then Add Secwin to your program FIRST.
    1. Dictionary changes
    2. Global Extensions, Including Global Secrets
    3. Import New Secwin procedures
  3. Once this is done remove the excess bits of old Secwin next.

Errors You May See

Unknown Procedure Label
This will appear for any calls to ds_ functions in embed code. This code will need replacing (or removing).

If you have not yet Imported the new Secwin Procedures, then you will also get this error with various Secwinxxx functions. Proceed to the sections on Adding Secwin to your Dictionary and Adding Secwin to your Desktop Program.
Old Procedure Call Replace With
ds_ExportTables Remove
ds_ImportTables Remove
ds_CurrentLogin(appnum) SecwinCurrentUser.GetLogin()
Company = ds_CurrentLicence()  Company = SecwinCurrentUser.GetLicense('customer','companyname')
Serial = ds_CurrentSerialNumber() Serial = SecwinCurrentUser.GetLicense('license','serialnumber')
Copies = ds_CurrentCopies() Copies = SecwinCurrentUser.GetLicense('license','copies')
Counter = ds_CurrentCounter()
Level = ds_CurrentAppLevel() Level = SecwinCurrentUser.GetLicense('license','level') OR
Level = SecwinCurrentUser.GetLicense('license','levelname')
Expiry = ds_CurrentExpiryDate() Expiry = SecwinCurrentUser.GetLicense('license','expirydate')

Global Extensions

Make sure the app has the following Global Extensions added
  • MyTable
  • Cryptonite
  • StringTheory
  • jFiles
  • NetTalk (optional)

Removing old Custom Secwin Windows

Secwin 6 (and earlier) allowed you to create custom windows in the app to replace the windows built-in to the DLL. These windows are now broken and need to be removed. (They'll be replaced by all-new Secwin 7 windows.) These windows were identified on the global extension, on the Options tab. The original default names for the windows were;
  • SecwinBrowseOperators
  • SecwinBrowseUserGroups
  • SecwinChangeLogin
  • SecwinChangePassword
  • SecwinFirstUser
  • SecwinFunctions
  • SecwinLoginScreen
  • SecwinOperatorForm
  • SecwinSetAccess
  • SecwinSetAllAccess
  • SecwinSplashLogin
  • SecwinUserGroupForm
If the procedures remain as TODO's then find where they are called from, and remove those calls (replacing with the new ones is applicable.) Once all the procedures, and all the TODO's are gone then you can also do an Application Menu / Delete Empty modules.

TODO's sometimes go away after a compile, or after a change to a parent procedure (like the Frame) is made.

Converting DSSW5 to DSSW7

In this context DSSW5 refers to the data file used by Secwin 6 (or earlier). DSSW7 refers to the data tables used by Secwin 7. Note that since Secwin supports SQL the exact name, and location, of your tables may vary - however that is not material to understanding the conversion.

Clearly the old data needs to be converted to the new data. However this has to be done in a way that does not generically undermine the security offered by earlier versions of Secwin. For this reason a compiled utility is provided which will do the conversion. The utility can be manually run in "human" mode, or it can be driven by command-line parameters and so executed by an install script etc.

The source code for the utility is included (so you can customize the interface, brand it etc), but not the source code to the DsswConverter*.DLL (which it uses.) The compiled DLL is compiled in Clarion 11, and should work in Clarion 10 or Clarion 11 programs.

Several versions of the converter exe, and DLL's exist, to match the various Drivers that Secwin 6 supports. Use the converter that matches your old Secwin 6 data set.

All the users, groups, access rights, and license details are transferred from the old tables to the new tables.

Settings

Setting Description
Old
DSSW5.TPS Path. (TPS version only). This is the location of the file, and does not include the file name.
Database Connection String (SQL Only) The complete connection string to connect to the database containing the Secwin 6 tables.
App Name
Secwin 6 Global Extension, Rqd Options Tab, Unique Application Name setting.
Encryption Key
Secwin 6 Global Extension, Rqd Options Tab, Encryption Key setting.
If the setting is a variable, then you will need the actual contents of the variable.
If this setting is a fixed string (in quotes) then do not include the quotes in the utility.
Area Name Secwin 6 Global Extension, Login Options Tab, Default Login Area setting.
(Can't find it, search the CLWs for a call to ds_Register, the Area is the bit AFTER the pipe symbol |)
Schema
Secwin 6 Global Extension, Files Tab, Schema setting.  (eg dbo.) (including the trailing . )
New
New DB Connection String (SQL Only)
The complete connection string to connect to the database to contain the Secwin 7 tables.This does not need to be the same database as the Secwin 6 tables.
Program Secret
Secwin 7 Global Extension, Secrets Tab, Program Secret setting.
If the setting is a variable, then you will need the actual contents of the variable.
If this setting is a fixed string (in quotes) then do not include the quotes in the utility.
Table Secret
Secwin 7 Global Extension, Secrets Tab, Table Secret setting.
If the setting is a variable, then you will need the actual contents of the variable.
If this setting is a fixed string (in quotes) then do not include the quotes in the utility.
Customer Secret
Secwin 7 Global Extension, Secrets Tab, Customer Secret setting.
If the setting is a variable, then you will need the actual contents of the variable.
If this setting is a fixed string (in quotes) then do not include the quotes in the utility.
Settings Name (optional) Enter the Tenant name here.
This would only be used if you are merging data sets, and using the Multi-Tenant support in Secwin. In most cases you can leave this option blank.
Schema New Schema. Database table prefix (SQL only) - for example dbo. (including the trailing . )

Command Line parameters

Parameter Setting
tp= DSSW5.TPS Path. (TPS version only).
ocs= Database Connection String (SQL Only)
app=
App Name
key=
Encryption Key
area=
Area Name
os= Old Schema. Database table prefix (SQL only) - for example dbo. (including the trailing . )


ncs=
New DB Connection String (SQL Only)
ps=
Program Secret
ts=
Table Secret
cs=
Customer Secret
sn= Settings Name (optional)
ns= New Schema. Database table prefix (SQL only) - for example dbo. (including the trailing . )
/auto Runs the conversion when the program is run, does not wait for any user input
/hide Runs the converter silently

Example:

SecwinConverterTPS.exe tp=c:\temp "app=Demo App" key=1234 area=Main ps=4567 ts=0987 cs=1122 /auto /hide

Technical Notes

  1. The conversion DLL, and example conversion EXE, are installed into the \clarion\accessory\SecwinConverter folder. the folders, and sub-folders, contain readme.txt files for more information.
  2. Copies of the Secwin6 SLA*.DLL's exist in this folder. These DLL's are compiled with Secwin 6.50 (or later). These DLL's need to be shipped with the Converter.EXE program (or your program if you are calling the DsswConverter.DLL directly.
  3. Versions of the converter exist for TPS, MsSQL and Firebird. Additional versions can be created, and included, on request (just email us).
  4. Source to the conversion DLL (DsswConverter.DLL) is not provided, as this would expose the security in older programs.
  5. This conversion DLL can be called from a utility (like SecwinConverter.Exe), or, if you prefer, you can add it into your own application.
  6. The following attributes (unused by Secwin 7) are written into the Groups if they exists for the Secwin 6 Group;
    GroupNumber
  7. The following attributes (unused by Secwin 7)  are written in the User table if they exist for the Secwin 6 user;
    OperatorNumber, SiteLimiter, SiteHi, SiteLow, SuperUser, ExtraLong, ExtraString

Licensing Changes

If you are familiar with the Licensing features in Secwin 6 and earlier, here are some key concept-changes to understand as you migrate to using Secwin 7.
 
Concept Change
Program Levels In Secwin 6 there were 5 pre-defined program levels. Demo(1), Lite(2), Standard(3), Professional(4) and Enterprise(5).
In Secwin 7 there are no "pre defined" levels, and no restriction on the number of levels. In the License Manager you can create levels (and name them) for your application. In the program you should either create equates for the levels, or just use the level number.
Optional Modules As with levels, there is no no restriction on the number, or naming, of optional modules. The modules are simply declared in the LicenseManager and used (by number) in the program.
Activation Code In Secwin 6 the license information was set to be the same values in the program, and on the license server. the activation code served as a "CRC" of the two data sets to make sure they were the same. This was necessary to keep the activation code short (so it could be typed in).
In Secwin 7 there is no need to limit the length of the code in this way. The code can be fetched over the internet from a license server, or transferred via a file, or email, or whatever. So now the license physically contains all the license information, and the user does not need to set anything (except for their own details.)
XML Files The old Secwin Online Server could generate XML files with license information, and an activation code. In Secwin 7 this is no longer an XML file but is formatted as something like;

--- LICENSE START ---
h1/KSF3ZPgj5o/Cb+jbVRFYZk5oIYNEv1xz8icfNjB3AnO8qvB7cYOMR50Gtjo5B6GnlnRGBkLsL
UG8BkC4zHvhbr03SCoHdEX2LvicimKVFX/OSYHa/SYjrf/OaKwiTAjwgHbHn7Q5jZ7SNWpLiowSP
--- LICENSE END ---
License Manager In Secwin 6 you had the choice of using a desktop license manager program (register.app), or either a self-deployed Secwin Online Server (available as a separate purchase) or as a hosted service (SecwinOnlineServer.com).
The hosted service is still available, but Secwin now ships with both a desktop LicenseManager app and a NetTalk web-based Licensed Manager app.

User Login Here template

The following settings were on the Secwin6 (and earlier) Login User Here template. This table explains the status of these settings.
Old Setting Current Status
Unique Area Name No longer used. The concept of "areas" falls away.
Allow Case Insensitive User Password No longer allowed. Not a good practice.
Force Password Change every 30 days Set at runtime
Force Long Password Set at runtime
Use User Account Authentication (aka windows User Name) Set at runtime
Make Login Optional to End User set at runtime
Allow only 3 tries No longer allowed. If the login fails the login window closes.
Allow automatic login from other Exe's Still supported here, but setting in SecwinLogin procedure now preferred.
Allow default Login values Set at runtime via Guest accounts.
Don't show screen if default fails No longer used.
Display Login screen after window displays Still here, but you need to have (and set here) a Button or menu item control that exists to do a login.
Level Required for this window The level number for this window. Old Secwin used level names here (demo, standard and so on) but new Secwin allows for any number of levels, so an integer number should be used here.

Template Compatibility Quicklist

Template Type Featureset Status Notes
Call_Change_Login Code Access Control Compatible The code generated by the template has changed. However the functionality of the template is preserved and new (compatible) code will be generated.
Call_ChangePassword Code Access Control Compatible This is now just a simple call to the SecwinChangePassword function.
Call_Current Code Access Control Compatible The code generated by the template has changed. However the functionality of the template is preserved and new (compatible) code will be generated.
Call_CurrentlyLoggedIn Code Licensing    
Call_LockScreen Code Access Control Removed this functionality is not included in Secwin 7. If you are using it please contact CapeSoft.
Call_OperatorBrowse Code Access Control Compatible This is now just a simple call to the SecwinBrowseUsers function.
Call_RunAnotherExe Code Access Control Compatible The code generated by the template has changed. However the functionality of the template is preserved and new (compatible) code will be generated.
Call_SetLanguage Code   Obsolete Any calls to this template can be removed. Since all the Secwin windows are now procedures in the app tree, this functionality is taken over by the app's translation system.
Call_SetPin Code      
         
Call_SetDefaultFont Extension   Obsolete This template is obsolete and can be removed.
Create_Secwin_Menu Extension   Removed This template made a simple task difficult as has been removed.
Add menu items as desired in the normal way.
MakeLogoScreen Extension   Compatible The code generated by the template has changed. However the functionality of the template is preserved and new (compatible) code will be generated.
RunLogoScreen Extension   Compatible The code generated by the template has changed. However the functionality of the template is preserved and new (compatible) code will be generated.
UserLogin     Compatible Template has been simplified as most features have been moved to Runtime. See UserLoginHere Template
UserScreenSecurity     Compatible  
User_WorkGroup_Filtering Extension   Compatible  Allows the current user's workgroup (SecwinCurrentUser.GetWorkgroup() ) to be easily used in a browse filter as a simple variable (CWG). This variable is BINDed for you.

Dictionary Reference

All the tables used by Secwin are declared in your dictionary. Secwin is designed so that you can extend these tables without inhibiting the work that Secwin does. They are also designed to allow you to store extra information in their fields. This section in the documentation explores the technical workings of these tables.

Note that while fields can be added to the record structures, removal of existing fields is not allowed. Existing fields should not be renamed (but can have different External names if desired.) Existing field types should be changed only with caution and understanding of the implications.

New fields, and new keys on these fields may be added, however additional keys should not have the UNIQUE attribute.

Database

Secwin supports multi-tenant systems. This means that data can be segmented into different databases. When a user logs in they are then directed to a specific database. If you make use of this feature it is probably desirable to store your Secwin tables in a different database to your actual data tables. Take this into account when planning the Owner Name and Full Path Name properties

If the Secwin tables are stored in a separate database then this would allow a user to login, and only once they have logged in, for other table connection parameters to be set.

If the SecwinSettings table is deleted then the data in the SecwinUsers and SecwinGroups tables become lost. In other words users, and groups, are related to the parent Settings record.

For various reasons (auditing, data-distribution etc) Users and Group records are not "deleted" when the delete button is used. Instead the DeletedTimeStamp field is set. For this reason an Administrator or SysAdmin user can undelete records by going to the respective forms, and clicking on the Undelete button.

File Drivers

The tables are designed to use a minimal amount of field types, and should work with any File Driver that supports these types. The types used are STRING, REAL [2], LONG, DATE [1] and BLOB [3].

The timestamp fields have external names. These may need to be tweaked. [4]

When changing the file driver be sure to adjust the Owner Name and Full Path Name properties in the table properties to a value suitable for the driver.

Note 1
The DATE type can be replaced by a LONG if your chosen backend does not support DATE.

Note 2
If using PostgreSQL then use BIGINT on the backend where there is a REAL declared in the dictionary.
Blobs should be TEXT on the backend (because the blobs are all ASCII not BINARY).

Note 3
If Using MsSQL then use VARCHAR(MAX) for the BLOB fields (not IMAGE) if the BINARY switch in the dictionary is OFF. If the Binary switch is on (it should not need to be on) then use VARBINARY(MAX).  If using FM3 to create the table make use of the ForceSQLDataType option on the blob fields (https://www.capesoft.com/docs/fm3/fm3.htm#duoForceSQLDataType)

Note 4
If using Firebird then External Name fields must be in Uppercase. ie TS, STS and DTS.

Unique Row Identifiers

All Secwin tables use a client-side string field called GUID as the unique row identifier. This value is used when encrypting values in the row, so data cannot be copied (externally) from one row to another. Changing the field value is not allowed - doing so will render the row invalid.

Record Timestamps

Three timestamp fields are in the tables. TimeStamp, ServerTimeStamp and DeletedTimeStamp allow support for undelete and distributed data should that become necessary. These fields are not necessarily populated at this time. Use of these fields by your own code is not recommended, however filtering data based on DeletedTimeStamp > 0 is recommended.

Encryption

Because most of the data in most of the tables is encrypted, most interactions with these tables will be done via the supplied classes, and not directly through the Clarion drivers. That said you can still use templates, and simple code, on the table records, and on the unencrypted fields. For example it's still possible to list all the Secwin Groups simply by browsing the SecwinGroups table.

Fields can be written to, and (if encrypted) read from only by using the supplied class. Writing to fields, without using the supplied class will likely result in the row becoming unusable.

Each table contains a field called EncryptionVersion. This field is for internal use only. Do not change the contents of this field. If you do so the data in the record will not decrypt correct, and may lead to the data being lost.

The rowhash field contains an encrypted hash of the plain-text fields. This prevents tampering of these fields outside the program.

The encryption algorithm used by the tables is AES256. Hashes are SHA256. These values may be updated from time to time - conversion to new values will be automatic, and invisible.

The underlying field encryption is provided by MyTable. If you wish to understand the encryption level in more detail, refer to the MyTable documentation.

SecwinSettings Table

The SecwinSettings table is the heart of the system, and the other tables are related to this. This table can contain multiple records, thus acting as the root table for multi-tenant systems.

Records, and values in this table can be accessed using an object of type  SecwinSettingsRecordClass.

In addition to the primary key field (Guid) another field, Company, is also a unique value in the table. This field serves to identify each record to humans. The table should contain at one record, with the DefaultRecord option set. This row should not be renamed.

Field Encrypted Hashed Description
Default Record No RowHash True(1) or false(0). Set to true if this is the default record. One (and only one) row in the table should have this set. In situations where the company is unknown, but settings are required by the code, this record is used.
Company No RowHash Contains a unique human-readable name for the record. This field is only used in multi-tenant situations.
PIN No RowHash A short code, that a user can use to identify the company being logged into. Used in situations where it's not desired to display a simple list of companies. This code must be unique.
Settings Yes SettingsHash This blob, and associated hash field, contain the Secwin settings for this company. Editing these values is usually done via the SecwinSettings procedure (desktop programs) or the SecwinSettingsWeb procedure (web programs).
License Yes LicenseHash This blob, and associated hash field, contain the Secwin license for this company. Editing these values is usually done via the SecwinLicense procedure (desktop programs) or the SecwinLicenseWeb procedure (web programs).
Attributes Yes AttributesHash This blob, and associated hash field, contain the programmer-defined settings for this company. This allows the programmer to add any additional (secure) information that is needed to this table. To read, or set, attributes see the Attribute Methods.

SecwinGroups Table

The SecwinGroups table contains a list of user groups for the system.

Records, and values in this table can be accessed using an object of type  SecwinGroupRecordClass.

Field Encrypted Hashed Description
SettingsGuid No RowHash Links this group to a specific SecwinSettings record. This allows group lists to be filtered based on the company settings being used.
Name No RowHash The name of the group
Rights Yes RightsHash Contains a list of the user rights for this group. To determine if a user has a specific right use the GetRight method.
Attributes Yes AttributesHash A place for any additional attributes of the group to be placed.  To read, or set, attributes see the Attribute Methods.

SecwinUsers Table

The SecwinUsers table contains a list of users for the system.

Records, and values in this table can be accessed using an object of type  SecwinUserRecordClass.
Field Encrypted Hashed Description
SettingsGuid No RowHash Links this user to a specific SecwinSettings record. This will control items such as login approach, password policies, and so on.
UserLogin No RowHash An identification field for the user, used as part of the login process. This field is unique.
UserName No RowHash A human-readable name associated with the login. This field is not unique. It is used for display purposes only.
UserLevel No RowHash The user's Level.
UserWorkgroup No RowHash The user's Workgroup.
PasswordHash No   A salted-hash of the user's current password.
NewPassword No   This field is used as a place-holder. It will not be written to the database.
Email Yes   The user's email address. Used for Second Factor, Password Resets and so on.
EmailValid No   Set to a value if the email address has been validated. 0 otherwise.
Phone Yes   The users mobile phone number. Used for Second Factor, Password Resets and so on.
PhoneValid No   Set to a value if the phone number has been validated. 0 otherwise.
Rights Yes RightsHash Contains a list of the user rights for this user. To determine if a user has a specific right use the GetRight method.
Groups Yes GroupsHash Contains a list of the groups that the user is in. To determine if the user is in a group use the InGroup method.
Biometrics Yes BiometricsHash Not currently used.
Questions Yes QuestionsHash Not currently used.
Attributes Yes AttributesHash A place for any additional attributes of the user to be placed.  To read, or set, attributes see the Attribute Methods.

SecwinTokens Table

When the login Remember Me feature is on, and the user chooses to remember the login on a specific machine, then an Access Token is issued to the user, and stored in this table.  For desktop programs, tokens are bound to a specific computer.

Adding fields to this table is not necessary, nor recommended. If you wish to delete a token then either set the DeletedTimeStamp field, or set the TokenExpiryDate to earlier-than-today. Deleting the actual record is not recommended (for distributed-data reasons.)

Records, and values in this table can be accessed using an object of type  SecwinTokenRecordClass.
Field Encrypted Hashed Description
UserGuid no RowHash The guid of the user this token belongs to.
SettingsGuid no RowHash The guid for the settings this token belongs to.
TokenVersion no RowHash Used internally to describe the algorithm used to generate this token.
TokenExpiryDate no RowHash The date when the token will expire. Changing this value will invalidate the token.
MachineId no RowHash The ID of the machine where the token can be used. Changing this value will invalidate the token.
Token yes   The login token.

SecwinLoginFactor Table

This table is used internally when doing Second Factor authentication. The records in it are short-lived .

Adding fields to this table is not necessary, nor recommended.

Records, and values in this table can be accessed using an object of type  Secwin2FactorRecordClass.

Template Reference

Global Activation Template

General Tab

Disable All Secwin Features
If this is on then no Secwin Template code will be generated in the application. Any hand-coded calls will usually result in compile errors.
Disable Access Control Features
If this is on then no Secwin Template code related to Access Control features (Logins, User Screen Security etc) will be generated in the application. Any hand-coded calls will usually result in compile errors.
Disable Licensing Features
If this is on then no Secwin Template code related to Licensing features (Program Expiry Dates etc) will be generated in the application. Any hand-coded calls will usually result in compile errors.

Options Tab

Set Access Rights Key
This is the keyboard key that triggers the Local Set Access window in any procedure with the User Screen Security template. It defaults to Ctrl-F8. Only users with the level of Supervisor (or higher) can use this key.
Application Description
This is the description, that appears in the Global Set Access window, for this APP. Procedures in this app will fall under this description in the program tree (as presented on that window.)
Bubbling Action
This is the default action to take when a menu item or button is suppressed because of Bubbling. The default is Hide and Disable which is the safest.
INI File Name
This is the location, and name, of the INI file that will store the Remember-Me tokens.

Procedures Tab

These are the procedures in your app which do the task listed. When in doubt use the default procedure name.
The optional procedures should be set if you have NetTalk, and so are making use of those features.
Login
Name of the login procedure. Default is SecwinLogin.
Logout
Name of the logout procedure. Default is SecwinLogout.
UpdatePassword
This is the window that allows the user to change their password, once they have logged in.
UpdateSettings
This is the window that allows the end user to set the runtmie security policies for the program.
SetAccessRights
Name of the procedure that sets the Access Rights locally (ie Ctrl F8). Default is SecwinSetAccessRights.
GlobalSetAccessRights
Name of the procedure that allows the Access Rights to be set globally. Default is SecwinGlobalSetAccessRights.
BrowseUsers
A list of Users.
BrowseTenanlts
A list of Tenants.
License
the Secwin Licensing procedure.
SendEmail
Name of the Email procedure. Default (in NetTalk) is SendEmail. If you are not using NetTalk, but you have an alternate Email procedure for sending emails, then you can use that here. The procedure MUST have a prototype of (EmailParametersGroup pEmail),Long
SendSMS
Name of the SMS procedure. Default (in NetTalk) is SendSMS. If you are not using NetTalk, but you have an alternate Email procedure for sending emails, then you can use that here. The procedure MUST have a prototype of (SMSParametersGroup pSMS),Long
ActiveDirectory UserInGroup
Name of the ActiveDirectory procedure that tests if the user is in the group.
ActiveDirectory Validate User
Name of the ActiveDirectory procedure that tests the user credentials against the AD server.

License Tab

Product License Name
The name of the program, as set by the license server.
Product License Version
The license version of this program (as set in the license). If you change the version number here then the customer will need to re-license the program (and a new license will need to be issued to them by the server.)

This version number is not necessarily the version number of the program. Changing this number invalidates existing licenses, so you should only change this number when you want customers to need to re-register.

This value must be an integer.
Default Action to Take if License Check Fails
Select the default option which will be haken when a license check in a procedure fails. This setting can be overriden in the local template (and there are a couple extra procedure-specific options there as well.)

Secrets Tab

For more on the concepts of the secrets, and how they are used, see the section on Secrets. All the apps in a system need to share the same values for the secrets. This can include multiple DLL's and multiple EXE's.
Program Secret
Known only to the developers.
Table Secret
Known to developers, and support staff.
Customer Secret
Known only to the customer
License Secret
Known only to the developer, and the Licensing server.

Multi DLL Tab

This is part of a Multi-DLL Program
Tick this on for all modules.
Export Secwin classes from this DLL
Tick this on if this is the DLL that will export the Classes to the rest of the program. Usually the Data DLL but could be a classes DLL.

Classes Tab

The names and classes used by various Global Objects generated by the templates.

Global Auto-Populate User Screen Security Template

This template adds the Local User Screen Security extension to all procedures where it is applicable. It has no settings of it's own.

Call User Login Extension Template

Login Options Tab

Disable Login Here
If this is set then this extension is disabled on this window.
Display Login Screen After Window Displays
If this is set then the login screen will not apear before the window (typically frame) of the application is displayed. Rather a POST Event:Accepted is sent to the Login Control (ie the Login button or menu item) when the window opens. So the window itself is visible, but an immediate login is required.
Login Control
The control to trigger if the previous checkbox is set.
Show Current
A convenient way to show the current user information in the status bar.

Licensing Tab

User Screen Security Extension Template

Make Logo Screen Extension Template

This template is added to an MDI child window to turn it into a Background Logo procedure.
Do not call Secwin Current Functions
If this is on then the local variables in this procedure, ds_CurrentLicence, ds_CurrentExpiryDate, ds_CurrentAppLevel and ds_CurrentlyLoggedIn are not automatically populated from the SecwinCurrentUser object.

Run Logo Screen Extension Template

This template is added to the Frame procedure. It calls the the MDI Background window, as created using the Make Logo Screen Extension template. This template only has one setting, the name of the Logo Screen procedure.
Logo Procedure
The name of the background logo procedure, which contains the Make Logo Screen Extension template.


Class Reference

Class Hierarchy

Classes

SecwinBaseClass

This class contains generic debugging, translation and error handling methods. All the other classes include this functionality.

Derivation

Properties

None

Methods

SecwinBaseClass

InterpretError

InterpretError()

Description

Returns error text based on the error code stored in the error property.

Return Value

String

SecwinBaseClass

InterpretUserLevel

InterpretUserLevel(Long pLevel)

Description

Returns error text based on the error code stored in the error property.

Parameters

Parameter Description
pLevel The level to get the name of. Usually one of sw:sysadmin, sw:administrator, sw:supervisor, sw:operator, sw:guest, sw:noaccess

Return Value

String

Classes

SecwinRecordBaseClass

This class contains generic debugging, translation and error handling methods. All the other classes include this functionality. It is similar to the SecwinBaseClass but is derived from the SecureMyTableClass. It is used for classes that read and write table records.

Derivation

Properties

None

Methods

SecwinRecordBaseClass

AfterLoad

AfterLoad()

Description

Called after a record is loaded from the database. This decrypts encrypted fields, and checks the hashes for hashed fields.

Return Value

sw:ok if successful, sw:notok if not.

Example

Get(SecwinGroups)
secGroups.AfterLoad()


See Also

BeforeSave

SecwinRecordBaseClass

BeforeSave

BeforeSave()

Description

Called before a record is saved to the database. This encrypts encrypted fields, and creates the hashes for hashed fields.

Return Value

sw:ok if successful, sw:notok if not.

Example

Get(SecwinGroups)
secGroups.AfterLoad()
secGro:Name = upper(secGro:Name)
secGroups.BeforeSave()
Put(SecwinGroups)


See Also

AfterLoad

SecwinRecordBaseClass

InterpretError

InterpretError()

See InterpretError

SecwinRecordBaseClass

InterpretUserLevel

InterpretUserLevel(Long pLevel)

Description

Given a User Level (number) returns a text name of that level (as a string).

Parameters

Parameter Description
pLevel The level (number).
Return Value

STRING

Example

level = secUser.InterpretUserLevel(secuse:UserLevel)

Classes

SecwinAttributesRecordClass

The SecwinSettings, SecwinGroups and SecwinUsers table have a BLOB field call Attributes. The data in this blob is stored as a JSON formatted text field. This class provides the methods for easily reading and writing these attributes into and out of the JSON blob.

Derivation

Properties

Property Type Description
AttributesJson &JsonClass The JSON object that holds all the attributes.

Methods

SecwinAttributesRecordClass

DeleteAttribute

DeleteAttribute(String pAttributeName)

Description

Deletes an attribute, by name from the JSON property.

Parameters

Parameter Description
pAttributeName The name of the attribute to delete.

Return Value

sw:ok if successful, sw:notok if not successful.

Example

secUser.DeleteAttribute('weight')

See Also

SetAttribute

SecwinAttributesRecordClass

LoadAttributes

LoadAttributes(*Group pGroup)

Description

Loads all the attributes (by name) into a Clarion GROUP structure. This is commonly used on Forms to load a number of attributes into a local group, so they can be edited on the screen by the user. Be sure to call SaveAttributes before saving the record back to the disk.

Parameters
Parameter Description
pGroup The name of the local group. The group does not need to include all the attributes in the JSON (and hence in the BLOB) - only the subset you wish to edit or display need to be included here. 

Return Value

sw:ok

Example

secUser.LoadAttributes(AttributesGroup)

See Also


SaveAttributes

SecwinAttributesRecordClass

GetAttribute

GetAttribute(String pAttributeName)

Description

Gets the current value of an attribute in the JSON property.

Parameters

Parameter Description
pAttributeName The name of the attribute to get.

Return Value

String. The value in the attribute. If the attribute is not found then a blank string is returned.

Example

weight = secUser.GetAttribute('weight')

See Also

SetAttribute , DeleteAttribute , Extending the Settings.

SecwinAttributesRecordClass

SaveAttributes

SaveAttributes(*Group pGroup)

Description

Saves the contents of a group into the JSON property. (This does not update the record, or write the values to the disk, it just updates the property).

Parameters

Parameter Description
pAttributeName The name of the local group. The group does not need to include all the attributes in the JSON (and hence in the BLOB) - only the subset you wish to edit or display need to be included here. 

Return Value

String

Example

secUser.SaveAttributes(AttributesGroup)

See Also

LoadAttributes

SecwinAttributesRecordClass

SetAttribute

SetAttribute(String pAttributeName,String pValue)
SetAttribute(String pAttributeName,StringTheory pValue)


Description

Sets the value of an attribute in the JSON property.

Parameters

Parameter Description
pAttributeName The name of the attribute to delete.
pValue The value of the attribute to write.

Return Value

sw:ok

Example

secUser.SetAttribute('weight','123')

See Also

GetAttribute, DeleteAttribute

Classes

SecwinGroupsCacheClass

A global object, called SecwinGroupsCache, of this class is created by the template. It is unlikely that you will need to instantiate any more objects of this class, however using the generated object is very useful.

Groups are identified by a Guid

This object must be initialized before it is used. The cache needs to be loaded before the other methods are useful.

Derivation

Properties

Property Type Description
Loaded Byte Set to true if the cache has been loaded.

Methods

SecwinGroupsCacheClass

CopyQueue

CopyQueue(*Queue pGroupsQueue,String pSettingsGuid)

Description

Copies information out the group cache queue into your own queue. Your queue should have one, or more, of the following field names;
groupname or name : for the name of the group
guid : for the guid of the group
settingsguid : for the settingsguid of this group
defaultrightaccess: for the defaultrightaccess for this group
you are free to have as many other fields as you like in the queue, and the order of the fields in the queue does not matter.

Parameters

Parameter Description
pGroupsQueue Your queue to hold the result.
pSettingsGuid If this is set then only the groups for that company is returned. If itis blank then all the groups are returned.
Return Value

None. The passed in queue is populated with the cache.

Example

list   queue
name  string(255)
guid  string(16)
settingsguid  string(16)
  end
  code
  SecwinGroupsCache(list,'')


See Also

Guids

SecwinGroupsCacheClass

Free

Free()

Description

Empties the internal queue.

Return Value

None. the loaded property is set to false.

Example

SecwinGroupsCache.Free()

See Also

LoadCache

SecwinGroupsCacheClass

Init

Init(*File pTable, String pProgramSecret, String pTableSecret, String pCustomerSecret)

Description

Initializes the GroupCache object. Typically called in the SecwinLogin procedure.

Parameters

Parameter Description
pTable SecwinGroups table.
pProgramSecret As per your program.
pTableSecret As per your program.
pCustomerSecret As per your customer.
Return Value

sw:ok if successful, sw:notOk if not successful.

Example

SecwinGroupsCache(SecwinGroups,'programSecret','tableSecret','customerSecret')

See Also

SecwinGroupsCacheClass

LoadCache

LoadCache(Long pForce)

Description

Loads the data from the table into a memory cache.

Parameters

Parameter Description
pForce If this is sw:notforce, and the cache has already been loaded, then it is not reloaded. If this is sw:force then the cache will be thrown away, and reloaded from the disk.
Return Value

None

Example

SecwinGroupsCache.LoadCache(sw:force)

See Also

Free

SecwinGroupsCacheClass

GetGroupDefaultRights

GetGroupDefaultRights(String pGuid)

Description

Gets the Default Right Access for the group.

Parameters

Parameter Description
pGuid The guid of the group to query
Return Value

A LONG. One of sw:defaultgroupyesaccess (for ALL), sw:defaultgroupnoaccess (for NONE)
If the group is not found then sw:defaultgroupnoaccess is returned.

Example

r = SecwinGroupsCache.GetGroupDefaultRights('VCEO8ITLVFS67MLQ')

See Also

Guids

SecwinGroupsCacheClass

GetRights

GetRights(String pGuid, StringTheory pRightsStr)

Description

Sets

Parameters

Parameter Description
pGuid
pRightsStr  
Return Value

None

Example

See Also

Guids

SecwinGroupsCacheClass

GetNames

GetNames(Blob pGroupsList)
GetNames(String pGroupsList)
GetNames(StringTheory pGroupsList)


Description

Gets a list of group names, as a comma separated list. If only one guid is passed in then only one name is returned.

Parameters

Parameter Description
pGroupsList A list of guids in either JSON or pipe-separated format.
for example;
[{"guid":"VCEO8ITLVFS67MLQ"},{"guid":"N6R33JR1R5NZ8KK2"}]
or
|VCEO8ITLVFS67MLQ|N6R33JR1R5NZ8KK2|
Return Value

A string containing a comma separated list of names.

Example

names = SecwinGroupsCache(CurrentSecwinUser.GetInGroups())

See Also

GetGuids, CurrentSecwinUser.GetInGroups, Guids

SecwinGroupsCacheClass

GetGuids

GetGuids(String pSettingsGuid)

Description

Gets a pipe-separated list of groups that belong to a specific tenant.

Parameters

Parameter Description
pSettingsGuid The guid of the settings (tenant) record.
Return Value

A STRING

Example

See Also

GetNames, Guids

Classes

SecwinChangeGroupListClass

A small utility class to set

Derivation

Properties

None

Methods

SecwinSettingsRecordClass

SetInOutGroup

SetInOutGroup(String pGroupList,String pGroupGuid,Long pInGroup)

Description

Takes in a list of groups, a specific group id, and a specific group status. It adjusts the group list to either include, or remove, that guid.

Parameters

Parameter Description
pGroupsList A list of guids in JSON format. For example;
[{"guid":"VCEO8ITLVFS67MLQ"},{"guid":"N6R33JR1R5NZ8KK2"}]
pGroupGuid
The specific guid to add (or remove) from the list
pInGroup
Set to true if the group should be in the list, false if the group should not be in the list.
Return Value

String. A list of Guids in JSON format.

See Also

.

Classes

SecwinSettingsRecordClass

This class provides the basic functionality for doing a login. It includes no UI. The UI interactions are provided by the SecwinLoginDesktopClass and SecwinLoginWebClass, which are derived from this class.

Derivation

Properties

Property Type Description
Company String(120) The name of the company (tenant) for this record
Guid String(16) The unique identifier for this record.
LicenseJson &JsonClass The License for this tenant.
Loaded Byte Set to true if LoadRecord method has been called.
SecwinSettingsTable &File The SecwinSettings table as declared in the dictionary.
SettingsJson &JsonClass The Settings for this tenant.
TableOpened Byte Set to true if the table was opened by the object.

Methods

SecwinSettingsRecordClass

AfterLoad

AfterLoad()

Description

Decrypts the record after it has been loaded. Called automatically for the Load methods in this class. Should be called manually after a direct table read (ie GET, NEXT etc.)

Return Value

Long. One of sw:ok or sw:notok If notok, then the Errorcode parameter holds the error reason.

See Also

BeforeSave, LoadRecord, LoadByName, LoadByGuid.

SecwinSettingsRecordClass

BeforeSave

BeforeSave()

Description

Encrypts the record before it is saved to the disk. Called automatically by the SaveRecord method. Should be called manually before a direct table write, like ADD, PUT etc.

Return Value

Long. One of sw:ok or sw:notok If notok, then the Errorcode parameter holds the error reason. 


See Also

AfterLoad , SaveRecord.

SecwinSettingsRecordClass

GetDefaultGuid

GetDefaultGuid()

Description

Gets the guid for the default settings record. If one does not exist then a record will be created (and the guid returned.)
This method does not load the record, however the contents of the existing record are not necessarily preserved.

Return Value

String. The guid of the default tenant record.

See Also

.

SecwinSettingsRecordClass

Init

Init(*File pSettingsTable,String pGuid,String pProgramSecret,<String pTableSecret>,String <pCustomerSecret>)

Description

Initializes the object. This needs to be done before any of the other methods are called.

Parameters

Parameter Description
pSettingsTable The SecwinSettings table, as declared in the dictionary.
pGuid A value to prime the Guid property. If this field is blank then the Guid property will be primed with the guid of the default record (and the default record will be loaded).
pProgramSecret The program secret for this program, as set on the Secwin Global extension.
pTableSecret The table secret for this table, as set in the dictionary.
pCustomerSecret The customer secret for this program, as set on the Secwin Global extension.

Return Value

Long. sw:ok if successful, sw:notok if failed.

See Also

.

SecwinSettingsRecordClass

LoadByGuid

LoadByGuid(String pGuid)

Description

Loads the settings record, based on the guid passed in. Does an AfterLoad. If the table is not opened then it will be opened. The Guid property of the object is set to this guid.

Return Value

Long. One of sw:ok or sw:notok . If notok, then the Error parameter holds the error reason. If the error is a file error, then the errorcode parameter holds the file error.

See Also

SaveRecord, LoadByName, LoadByGuid, AfterLoad, BeforeSave.

SecwinSettingsRecordClass

LoadByName

LoadByName(String pCompany)

Description

Gets the guid of the record, based on the Company field matching the passed in parameter. If no record with this name exists then it will be created. If the record is found then it will be loaded.

Return Value

String. the Guid of the loaded record. If the load fails then a blank string is returned and the Error property holds the error reason. If the error is a file error, then the errorcode parameter holds the file error.

See Also

SaveRecord, LoadByName, LoadByGuid, AfterLoad, BeforeSave.

SecwinSettingsRecordClass

LoadRecord

LoadRecord()

Description

Loads the settings record, based on the guid in the self.guid property. Does an AfterLoad. If the table is not opened then it will be opened.

Return Value

Long. One of sw:ok or sw:notok . If notok, then the Error parameter holds the error reason. If the error is a file error, then the errorcode parameter holds the file error.

See Also

SaveRecord, LoadByName, LoadByGuid, AfterLoad, BeforeSave.

SecwinSettingsRecordClass

SaveRecord

SaveRecord()

Description

Encrypts the user record, does a PUT, and an AfterLoad. Note that this method may bypass ABC triggers (like the setting of timestamps.)

Return Value

Long. One of sw:ok or sw:notok . If notok, then the Error parameter holds the error reason. If the error is a file error, then the errorcode parameter holds the file error.

See Also

LoadRecord, LoadByName, LoadByGuid, AfterLoad, BeforeSave.

Classes

SecwinUserRecordClass

This class provides the basic functionality for doing a login. It includes no UI. The UI interactions are provided by the SecwinLoginDesktopClass and SecwinLoginWebClass, which are derived from this class.

Derivation

Properties

Property Type Description
BiometricsJson &JsonClass A jFiles object containing a tree of the items in the Biometrics blob. Not currently used.
GroupsJson &JsonClass A jFiles object containing a tree of the items in the Groups blob.
PasswordsKept Long The number of previous passwords to keep for this employee. Prevents password reuse.
SecwinUserTable &File A pointer to the SecwinUsers table
SettingsGuid String A relation field, relating this user to a tenant in the SecwinSettings table.
QuestionsJson &JsonClass A jFiles object containing a tree of the items in the Questions blob. Not currently used.
RightsJson &JsonClass A jFiles object containing a tree of the items in the Rights blob.
TableOpened Byte Used internally to determine if the object opened the SecwinUsers table, or not.
UserGuid String The GUID of the user record
UserLoaded Byte Set to true if the user record has been loaded.
UserLogin String The user login for the user.

Methods

SecwinUserRecordClass

AfterLoad

AfterLoad()

Description

Decrypts the record after it has been loaded. Called automatically for the Load methods in this class. Should be called manually after a direct table read (ie GET, NEXT etc.)

Return Value

Long. One of sw:ok or sw:notok If notok, then the Errorcode parameter holds the error reason.

See Also

BeforeSave   .

SecwinUserRecordClass

BeforeSave

BeforeSave()

Description

Encrypts the record before it is saved to the disk. Called automatically by the SaveUser method. Should be called manually before a direct table write, like ADD, PUT etc.

Return Value

Long. One of sw:ok or sw:notok If notok, then the Errorcode parameter holds the error reason. 


See Also

AfterLoad .

SecwinUserRecordClass

CheckPasswordHistory

CheckPasswordHistory(String pPassword)

Description

Checks to see if the user has recently used this value for a password. Can be used to prevent the reuse of old passwords when the user is entering a new password. This method assumes the user record to check has already been loaded.

Parameters

Parameter Description
pPassword The prospective new password to check against the history.

Return Value

Long. One of sw:ok or sw:notok If notok, then the Errorcode parameter holds the error reason.  If the value in Errorcode is 0, then the password has been used before.

See Also

 .

SecwinUserRecordClass

CreateUser

CreateUser(String pSettingsGuid, String pUserLogin, String pUserName, String pEmail, String pPhone, String pGroup, Long pPasswordExpiryDays, Long pUserLevel)

Description

Creates a new record in the SecwinUsers table. Designed to be used when user are self-registering. Triggers an automatic password reset, in other words a random password is generated for the user and sent to them via either Email or SMS (depending on how the system is configured for password resets.) 

Parameters

Parameter Description
pSettingsGuid The Guid for the matching record in the SecwinSettings table - ie the Tenant that this user will belong to.
pUserLogin The Login string that will be used by the user.
pUserName The name of the user.
pEmail The email address of the user.
pPhone The phone number for the user.
pGroup The group for the user. Will be passed to the InOutGroup method.
pPasswordExpiryDays The number of days that the password is valid before before the user will need to reset it themselves. This applies to just this first password. Typically this is a very low value (like 1) because the password will travel over an insecure path (email or sms.)
pUserLevel The user level of the user to be added. For example sw:operator .

Return Value

Long. One of sw:ok or sw:notok If notok, then the Errorcode parameter holds the error reason. 

See Also

InOutGroup , RegisterNewUser .

SecwinUserRecordClass

GetRight

GetRight(String pProcedureName, String pRestrictID)

Description

Determine whether a user has a right, or not.

Parameters

Parameter Description
pProcedureName The name of the procedure to check rights for.
pRestrictID The name of the restriction (aka the Restrict ID) inside the nominated procedure.

Return Value

A string containing the text yes, or no

See Also

SetRight  .

SecwinUserRecordClass

InGroup

InGroup(String pGroupGuid)

Description

Determines if the user is a member of a specific group.

Parameters

Parameter Description
pGroupGuid The guid of the group to check.

Return Value

Long. One of sw:ok or sw:notok .

See Also

GetRight  .

SecwinUserRecordClass

Init

Init(*File pUserTable,String pProgramSecret,<String pTableSecret>,<String pCustomerSecret>)

Description

Initializes the object. This needs to be done before any of the other methods are called.

Parameters

Parameter Description
pUserTable The SecwinUsers table, as declared in the dictionary.
pProgramSecret The program secret for this program, as set on the Secwin Global extension.
pTableSecret The table secret for this table, as set in the dictionary.
pCustomerSecret The customer secret for this program, as set on the Secwin Global extension.

Return Value

sw:ok

See Also

 .

SecwinUserRecordClass

LoadBlobs

LoadBlobs()

Description

Loads the blobs into their associated JSON objects. Called automatically by AfterLoad. 

Return Value

sw:ok

See Also

AfterLoad .

SecwinUserRecordClass

LoadUserByGuid

LoadUserByGuid(String pUserGuid)

Description

Loads a user record, based on a passed in guid. Users that are marked as deleted (ie DeletedtimeStamp > 0) are not loaded, an error is returned. 

Parameters

Parameter Description
pUserGuid The GUID of the user record.

Return Value

Long. One of sw:ok or sw:notok If notok, then the Errorcode parameter holds the error reason.

See Also

LoadUserByGuid, LoadUserByLogin, LoadUserBySettingsLogin .

SecwinUserRecordClass

LoadUserByLogin

LoadUserByLogin(String pUserLogin, Long pTry=False)

Description

Loads a user based on their login value. Used where the login value is set to be unique in the dictionary. Users that are marked as deleted (ie DeletedtimeStamp > 0) are not loaded, an error is returned.

Parameters

Parameter Description
pUserLogin The user login value.
pTry If false, and the user does not exist, then ErrorTrap is called. If set to true then errortrap is not called.

Return Value

Long. One of sw:ok or sw:notok If notok, then the Errorcode parameter holds the error reason.

See Also

LoadUserByGuid, LoadUserByLogin, LoadUserBySettingsLogin .

SecwinUserRecordClass

LoadUserBySettingsLogin

LoadUserBySettingsLogin(String pSettingsGuid, String pUserLogin, Long pTry=False)

Description

Used when the login value is not unique, but is unique for the tenant. In other words the tenany ID and login ID are needed to get a unique user record. Users that are marked as deleted (ie DeletedtimeStamp > 0) are not loaded, an error is returned.

Parameters

Parameter Description
pSettingsGuid The user SettingsGuid value.
pUserLogin The user login value.
pTry If false, and the user does not exist, then ErrorTrap is called. If set to true then errortrap is not called.

Return Value

Long. One of sw:ok or sw:notok If notok, then the Errorcode parameter holds the error reason. 

See Also

LoadUserByGuid, LoadUserByLogin, LoadUserBySettingsLogin .  

SecwinUserRecordClass

LoadUserRecordByKey

LoadUserRecordByKey(Key ptrKey, String pUserLogin, Long pTry=False)

Description

Designed to be used internally. Does a GET on the SecwinUsers table, based on the passed in Key. Users that are marked as deleted (ie DeletedtimeStamp > 0) are not loaded, an error is returned.

Parameters

Parameter Description
ptrKey A key, the components of which have already been primed.
pUserLogin The login of the user, passed to errortrap if there is an issue for a more informative message. Not used as part of the load.
pTry If false, and the user does not exist, then ErrorTrap is called. If set to true then errortrap is not called.

Return Value

Long. One of sw:ok or sw:notok . If notok, then the Error parameter holds the error reason. If the error is a file error, then the errorcode parameter holds the file error.

See Also

LoadUserByGuid, LoadUserByLogin, LoadUserBySettingsLogin .

SecwinUserRecordClass

SaveBlobs

SaveBlobs()

Description

Copies the blobs from their respective JSON objects back into the blob fields. Called automatically from BeforeSave.

Return Value

sw:ok

See Also

LoadBlobs , BeforeSave  .

SecwinUserRecordClass

SaveGroups

SaveGroups(*Queue pQueue)

Description

The first field in the queue is saved to the GroupsJson object. The queue can have additional fields, only the first field (the unique identifier) guid, field is saved. The queue should be complete, this method is not additive.

Parameters

Parameter Description
pQueue A queue of Group GUID values.

Return Value

sw:ok

See Also

  .

SecwinUserRecordClass

SaveUser

SaveUser()

Description

Encrypts the user record, does a PUT, and an AfterLoad. Note that this method may bypass ABC triggers (like the setting of timestamps.)

Return Value

Long. One of sw:ok or sw:notok . If notok, then the Error parameter holds the error reason. If the error is a file error, then the errorcode parameter holds the file error.

See Also

... .

SecwinUserRecordClass

SetInOutGroup

SetInOutGroup(String pGroupGuid,Long pInGroup)

Description

Sets the user in to, or out of, a group. 

Parameters

Parameter Description
pGroupGuid The guid of a SecwinGroups record.
pInGroup If true, put the user into the group, if false take the user out the group.

Return Value

Long. One of sw:ok or sw:notok . If notok, then the Error parameter holds the error reason.

See Also

SetRight .

SecwinUserRecordClass

SetLoginFails

SetLoginFails(real pUtc, real pPasswordFailsWindow)

Description

Adds a timestamp to the passwordfails attribute. Called after a password attempt has failed. 

Parameters

Parameter Description
pUtc The UTC time of the fail, as measured in thousandths of a second since Jan 1 1970. If this value is 0, then the current time is used.
pPasswordFailsWindow The time window for the maximum amount of password attempts allowed.

Return Value

Long. One of sw:ok or sw:notok . If notok, then the Error parameter holds the error reason.

See Also

... .

SecwinUserRecordClass

SetNewPasswordField

SetNewPasswordField(String pNewPassword)

Description

Sets the value of the newPassword field to the passed in parameter. This value will be used by BeforeSave to create the new password hash (and this field will be cleared.)

Parameters

Parameter Description
pNewPassword The value to put into the field.

Return Value

Long. One of sw:ok or sw:notok . If notok, then the Error parameter holds the error reason.

See Also

... .

SecwinUserRecordClass

SetPasswordHash

SetPasswordHash(StringTheory pHash)

Description

Designed to be called internally, not externally. Sets the passwordHash field to a value, and stores the value in the password history as well.

Parameters

Parameter Description
pHash The generated hash to be stored.

Return Value

The value returned by SetPasswordHistory. 

See Also

SetPasswordHistory.  .

SecwinUserRecordClass

SetPasswordHistory

SetPasswordHistory(StringTheory pHash)

Description

Designed to be called internally, not externally. Adds a password hash to the passwordhistory attribute fot the user.

Parameters

Parameter Description
pHash The generated hash to be stored.

Return Value

Long. One of sw:ok or sw:notok . If notok, then the Error parameter holds the error reason. 

See Also

SetPasswordHash  .

SecwinUserRecordClass

SetRight

SetRight(String pProcedureName, String pRestrictID, String pRight)

Description

Sets the right for the user, for a specific Procedure and Restriction.  

Parameters

Parameter Description
pProcedureName The procedure name.
pRestrictId The restrict ID inside the procedure.
pRight A string containing one of '0', 'no' for no right, or one of '1' or 'yes' for right granted. And other value sets the right to no.

Return Value

A string containing the text yes, or no.

See Also

GetRight .

Classes

SecwinLoginClass

This class provides the basic functionality for doing a login. It includes no UI. The UI interactions are provided by the SecwinLoginDesktopClass and SecwinLoginWebClass, which are derived from this class.

Derivation

Properties

Property Type Description
ADAdminPassword String The ActiveDirectory Admin Password. Loaded from Settings.
ADAdminUser String The ActiveDirectory Admin User. Loaded from Settings.
ADDomain String The ActiveDirectory Domain. Loaded from Settings.
ADGroup String The ActiveDirectory Group that the user must be in. Loaded from Settings.
ADHost String The ActiveDirectory server host name. Loaded from Settings.
ADPort Long The ActiveDirectory server port number. Loaded from Settings.
ADTLS Byte The ActiveDirectory TLS Setting. True if the connection to the AD server is over TLS. Loaded from Settings.
AttributesStr StringTheory Loaded with the user's attributes after a successful login by LoginComplete method. This is a flattened form of the JSON, so can be interrogated as a string. This object is also already SPLIT.
AllowGuestLogin Byte Loaded from Settings.
AllowPasswordReset Byte Loaded from Settings.
AllowRememberLogin Byte Loaded from Settings.
AllowUserSignup Byte Loaded from Settings.
DefaultRights Long Loaded with the user's default access rights after a successful login by LoginComplete method.
EmailAvailable Byte Loaded from Settings.
GroupsStr StringTheory Loaded with the user's groups after a successful login. This is a flattened form of the JSON, so can be interrogated as a string.
IniFileName String The file name of the INI file where the remember-me-tokens are stored on the machine. Used by the LoadTokenFromStore and SaveTokenToStore methods. Needs to be set during object startup.
LicenseStr StringTheory Loaded with the License associated with the (settings used by the) User. This is a flattened form of the JSON, so can be interrogated as a string.
Level Long Loaded with the user's level after a successful login by LoginComplete method.
LoadedByToken Byte Set to true if the user was logged in via a remember-me-token.
Lockout Byte Loaded from Settings.
LockedOut Byte If the CheckIfLockedOut method fails, then this property is set to true.
LockoutTime Long Loaded from Settings.
LoginType Byte Loaded from Settings.
NewPasswordRequired Byte Set during login if the user password has expired.
PasswordExpiryDays Long Loaded from Settings.
PasswordFailsWindow Long Loaded from Settings.
PasswordsLimitInWindow Long Loaded from Settings.
Options Long Bit mask.
sw:noToken : remember-me-tokens won't be used in this login.
sw:AllowChainTokens : chain-tokens may be used in this login.
sw:LoginSilent : guest login will occur (silently) if allowed, and a guest user exists.
ResetPassword SecwinUserResetPasswordClass Used when a password reset is necessary. The password is reset, and the new password emailed, or SMS'd to the user.
RightsStr StringTheory Loaded with the user's rights after a successful login by LoginComplete method. This is a flattened form of the JSON, so can be interrogated as a string. This object is also already SPLIT.
SecondFactor Secwin2FactorRecordClass Used when a Second Factor is required for the person to log in.
SecondFactorRequired Byte Loaded from Settings.
Settings SecwinSettingsRecordClass An object, of the type SecwinSettingsRecordClass which is passed into this object, via the INIT method.
SettingsLoaded Byte Set to true once the corrects settings record for this user has been loaded.
ShowError Byte Defaults to true. If the login fails a reason will be displayed to the user.
SMSAvailable Byte Loaded from Settings.
Tokens SecwinTokenRecordClass Used to determine if the token is valid or not. And if valid, to which user it belongs.
TokensExpire Long Loaded from Settings.
TryEmail String The email address as entered by the user.
TryPassword String The password as entered by the user.
TrySMS String The SMS phone number as entered by the user.
TryUserLogin String The login as entered by the user.
UniqueUserMultiTenant Byte Set to true if there are multiple tenants in the system, and the UserLogin field in the SecwinUsersTable is unique.
UserLoginUnique Byte Set to true if the UserLogin field in the SecwinUsersTable is unique.
UserSignupGroup String(16) The guid of the group, that self-signed-up users will be assigned to.
Workgroup Long Loaded with the user's workgroup after a successful login by LoginComplete method.

Methods

SecwinLoginClass

ActiveDirectoryAuthenticate

ActiveDirectoryAuthenticate(*LDAPParametersGroupType pParms)

Description

A pure virtual method designed to be populated in the calling Login procedure. Calls the ActiveDirectoryAuthenticate procedure in the application.

Parameters

Parameter Description
pParms All the parameters needed to connect to the server , and the details of the user to authenticate.

Return Value

sw:ok if successful, sw:notok if not successful.


See Also

ActiveDirectoryInGroup

SecwinLoginClass

ActiveDirectoryInGroup

ActiveDirectoryInGroup(*LDAPParametersGroupType pParms)

Description

A pure virtual method designed to be populated in the calling Login procedure. Calls the ActiveDirectoryInGroup procedure in the application.

Parameters
Parameter Description
pParms All the parameters needed to connect to the server , and the details of the user, and group, to authenticate.

Return Value

sw:ok if successful, sw:notok if not successful.


See Also

ActiveDirectoryAuthenticate

SecwinLoginClass

Authenticate

Authenticate()

Description

Takes all the required information, and determines if the login is successful or not.

Return Value

sw:ok if successful, sw:notok if not successful.

Example

secLogin.Authenticate()

See Also

CheckFactor, CheckIfLockedOut, CheckPassword, CallActiveDirectoryInGroup, CallActiveDirectoryAuthenticate

SecwinLoginClass

CallActiveDirectoryAuthenticate

CallActiveDirectoryAuthenticate()

Description

Prepares the LDAPParametersGroupType structure and calls the ActiveDirectoryAuthenticate method.

Return Value

returns sw:ok if the ActiveDirectoryAuthenticate method returned sw:ok. sw:notok otherwise.


See Also

ActiveDirectoryInGroup, ActiveDirectoryAuthenticate, CallActiveDirectoryInGroup

SecwinLoginClass

CallActiveDirectoryInGroup

CallActiveDirectoryInGroup()

Description

Prepares the LDAPParametersGroupType structure and calls the ActiveDirectoryInGroup method.

Parameters
Parameter Description
pParms All the parameters needed to connect to the server , and the details of the user, and group, to authenticate.

Return Value

returns sw:ok if the ActiveDirectorynGroup method returned sw:ok. sw:notok otherwise.

See Also

ActiveDirectoryAuthenticate, ActiveDirectoryInGroup, CallActiveDirectoryAuthenticate

SecwinLoginClass

CheckExpiry

CheckExpiry()

Description

Checks to see if the user's password has expired. This test only applies to the Program Login type.

Return Value

sw:ok if successful, sw:notok if not successful.

Example

secUser.DeleteAttribute('weight')

See Also

CheckFactor, CheckIfLockedOut, CheckPassword

SecwinLoginClass

CheckFactor

CheckFactor()

Description

Checks the second factor entered by the user to see if it is the correct one. Calls the SecondFactor property, Validate method.


Return Value

sw:ok if successful, sw:notok if not successful.

Example

secUser.DeleteAttribute('weight')

See Also

CheckExpiry, CheckIfLockedOut, CheckPassword

SecwinLoginClass

CheckIfLockedOut

CheckIfLockedOut()

Description

This only applies to Program Login and AuthLogin types. If the person has entered the password incorrectly too many times, then they are locked out of the system (in accordance with the runtime rules.) This method determines if that limit has been reached.


Return Value

sw:ok if successful, sw:notok if not successful.

Example

result = secLogin.CheckIfLockedOut()

See Also

CheckFactor, CheckExpiry, CheckPassword

SecwinLoginClass

CheckPassword

CheckPassword()

Description

Checks to see if the password entered by the user is correct.


Return Value

sw:ok if successful, sw:notok if not successful.

Example

result  = secLogin.checkPassword()

See Also

CheckFactor, CheckIfLockedOut, CheckExpiry

SecwinLoginClass

DeleteTokenFromStore

DeleteTokenFromStore()

Description

Deletes the remember-me token from the disk. Same as calling SaveTokenToStore with a blank token.

Return Value

sw:ok if successful, sw:notok if not successful.

Example

result  = secLogin.DeleteTokenFromStore()

See Also

SaveTokenToStore, LoadTokenFromStore

SecwinLoginClass

Init

Init(*SecwinSettingsRecordClass pSettings, *SecwinTokenRecordClass pTokens, *File pUserTable, String pProgramSecret,<String pTableSecret>,<String pCustomerSecret>)

Description

Initializes the object with all the things it needs it order to work. Skipping this call could lead to a program GPF.

Parameters

Parameter Description
pSettings A SecwinSettingsRecordClass object.
pTokens A SecwinTokenRecordClass object
pUserTable The label of the SecwinUsers table. (ie SecwinUsers)
pProgramSecret The program secret.
pTableSecret The table secret for the SecwinUsers table.
pCustomerSecret The customer secret.

Return Value

sw:ok if successful, sw:notok if not successful.

Example

secSettings  SecwinSettingsRecordClass
secTokens    SecwinSettingsTokenClass
  code
  secLogin
.Init(secSettings, secTokens, SecwinUsers, 'ABC','DEF',GHI')

See Also

SecwinSettingsRecordClass, SecwinSettingsTokenClass

SecwinLoginClass

LoadSettings

LoadSettings(*SecwinSettingsRecordClass pSettings)

Description

Loads all the settings from the passed in settings object. The settings record must already be loaded. this method just copies the settings from that record into this object. In addition the internal SecondFactor object also loads any settings it needs from the passed in object.

Parameters
Parameter Description
pSettings A SecwinSettingsRecordClass object.

Return Value

sw:ok 

Example

secSettings  SecwinSettingsRecordClass
secTokens    SecwinSettingsTokenClass
  code
 
! ...
  secLogin
.LoadSettings(secSettings)

See Also


LoadSettingsByToken

SecwinLoginClass

LoadSettingsByToken

LoadSettingsByToken()
Checks to see if there is a valid remember-me token. Loads teh token from the store, and then verifies that the token is correct, and valid. If it is then the user belonging to the token is loaded, and the settings belonging to the user are loaded.

Return Value

String. The SettingsGuid for the user if the token is valid. sw:notok if there is no token, the token is invalid, or the user or settings record do not exist.

Example

settingsGuid = secLogin.LoadSettingsByToken()

See Also


LoadSettings

SecwinLoginClass

LoadTokenFromStore

LoadTokenFromStore(StringTheory pToken)

Description

Loads a token (if one exists), decrypts it, and places it in the pToken parameter.

Parameters
Parameter Description
pToken An empty StringTheory object to receive the token (if it exists).

Return Value

sw:ok 

Example

token  StringTheory
  code
  secLogin
.LoadTokenFromStore(token)

See Also


LoadSettingsByToken, SaveTokenToStore

SecwinLoginClass

LoadUserByToken

LoadUserByToken(<String pGuid>)

Description

Given the Guid of a token, it loads that token (from SecwinTokens) and if that token is valid then loads the user record associated with the token.

Parameters
Parameter Description
pGuid The guid of a record in the SecwinTokens table. If this parameter is omitted then the token is first loaded from the store, and then used.

Return Value

sw:ok  if the token was valid, and the user loaded ok. Otherwise sw:notOk. Possible errors in the Error property include sw:TokenNotAvailable, sw:TokenDeleted, sw:TokenExpired, sw:TokenOnWrongMachine, sw:UserNotFound.
The Errorcode property contains the Clarion errorcode() when the error occured.

Example

secLogin.LoadUserByToken()

See Also


LoadSettingsByToken, SaveTokenToStore, LoadTokenFromStore

SecwinLoginClass

LoginAsGuest

LoginAsGuest()

Description

Logs the user in with the guest account.

Return Value

sw:ok  if successful, otherwise sw:notok. Possible error properties include;
sw:GuestLoginNotAllowed, sw:UnexpectedErrorOnNext, sw:NoGuestUser
If successful then LoginComplete is called, if unsuccessful then LoginFailed is called.

Example

token  StringTheory
  code
  secLogin
.LoadTokenFromStore(token)

See Also


LoadSettingsByToken, SaveTokenToStore, LoginComplete, LoginFailed

SecwinLoginClass

LoginComplete

LoginComplete()

Description

A login has completed successfully. The user record (if the Login type is not set to No Logins) is loaded. This method is useful for embedding into, after the Parent call. The SecwinLogin procedure in the app makes use of this embed point.

Return Value

sw:ok 

Example

token  StringTheory
  code
  secLogin
.LoginComplete()

See Also


LoginFailed

SecwinLoginClass

LoginFailed

LoginFailed()

Description

Called if the login attempt was unsuccessful.

Return Value

sw:ok 

Example

secLogin.LoginFailed()

See Also


LoginComplete

SecwinLoginClass

NoLoginRequired

NoLoginRequired()

Description

Pure virtual called when login type is set to NoLogin. If it returns sw:ok then LoginComplete is called. Included as a way to add additional checks before the NoLogin is accepted.

Return Value

sw:ok if no logins are allowed, sw:notok otherwise.

Example

if secLogin.NoLoginRequired() = sw:ok
  secLogin.LoginComplete()
end


See Also


LoginComplete

SecwinLoginClass

RegisterNewUser

RegisterNewUser(string pUserLogin, String pUserName, String pEmail, string pPhone, Long pUserLevel)

Description

Called when a new user has registered themself. The user is added to the database, and their new password is sent to them.

Parameters
Parameter Description
pUserLogin The users login
pUserName The users name
pEmail The users email address
pPhone The users phone number
pUserLevel The user's level. For example sw:operator .


Return Value

sw:ok  if successful. sw:notOk if the user could not be added, or the new password could not be sent. Possible values in the error property are;
sw:UserLoginBlank, sw:UserNameBlank, sw:UserLoginAlreadyExists, sw:CantAddUserRecord, sw:PasswordResetNotAllowed, sw:UserEmailBlank, sw:UserPhoneBlank

Example

secLogin.RegisterNewUser('bob','Robert','robert@gmail.com','011 11 1111',sw:operator)

See Also


CreateUser, ResetPassword

SecwinLoginClass

SaveLogin

SaveLogin()

Description

If the AllowRememberLogin property is true, then a token for the login is created and stored in the SecwinTokens table, and also on the local machine using SaveTokenToStore.

Return Value

sw:ok 

Example

secLogin.LoginFailed()

See Also


SaveTokenToStore

SecwinLoginClass

SaveTokenToStore

SaveTokenToStore(StringTheory pToken)

Description

Saves a token on the local machine.

Parameters
Parameter Description
pToken The token to save

Return Value

sw:ok 

Example

str  StringTheory
  code 
  secLogin
.SaveTokenToStore(str)

See Also


LoadTokenFromStore

SecwinLoginClass

TryLoadSettingsRecord

TryLoadSettingsRecord()

Description

Attempts to load the user via the login value stored in TryUserLogin. If the user loads ok, then the settings record for that user is loaded. This does not do a login, merely loads the settings appropriate to the user who is attempting to log in.

Return Value

sw:ok  if a settings record was loaded. sw:notok if not.

Example

secLogin.TryUserLogin = 'demo'
secLogin
.TryLoadSettingsRecord()

See Also


LoginComplete

Classes

SecwinUserRightsClass

Derivation

Properties

No Public Properties

Methods

SecwinCurrentUserClass

SetCache

SetCache(SecwinGroupsCacheClass pGroupsCache)

Description

Attaches a SecwinGroupsCacheClass object to this object. The template generates a SecwinGroupsCacheClass (global, unthreaded) object, and this is the cache typically passed into this method.

Parameters

Parameter Description
pGroupsCache An object of the SecwinGroupsCacheClass which the program has instantiated elsewhere.
Return Value

Nothing

Example

SecwinGroupsCache SecwinGroupsCacheClass
  code
  SecwinCurrentUser.SetCache(cache)


SecwinCurrentUserClass

GetRight

GetRight(String pProcedureName, Long pUserDefaultAccess, String pRestrictID, String pRestrictDefault,StringTheory pRights,StringTheory pInGroups, StringTheory pAnalysis, String pRestrictText, String pControlText)

Description

Determines if a user has a right to the procedure, and to a control on the procedure.

Parameters

Parameter Description
pProcedureName The ID of the procedure. This is typically the same as the name, but is fixed, allowing the rights to be preserved even if the procedrue name is changed.
pUserDefaultAccess The default access rights for the user. One of sw:yesaccess, sw:defaultyesaccess, sw:noaccess, sw:defaultnoaccess or something else. If something else then the user has default rights set to Group.
pRestrictId The ID of the control being restricted.
pRestrictDefault A "parent/default" ID that the Restrict ID is derived from.
pRights A JSON list of rights that the user has.
pInGroups A list of groups that the user is in. This object must have already been SPLIT.
pAnalysis A StringTheory object which gets text explaining why the right was allowed, or disallowed. Useful for understanding the logics that were applied by this method.
pRestrictText The friendly text name of the control, which is used when describing it in the analysis.
pControlText The text visible to the user on the screen for the control, which is then used when describing it in the analysis.
Return Value

A Long. One of sw:yesaccess or sw:noaccess

Example



Classes

SecwinCurrentUserClass

A global object, called SecwinCurrentUser, of this class is created by the template. It is unlikely that you will need to instantiate any more objects of this class, however using the generated object is very useful.

Users are identified by a Guid

Derivation

Properties

All properties in the class are private and should be considered as read-only. Changing the value of the properties does not change the value for the user as stored in the database. The value in each property should be accessed via the appropriate GET method.

Methods

SecwinCurrentUserClass

GetAttribute

GetAttribute(String pAttribute, Long pOptions=sw:UserAttribute + sw:SettingAttribute)

Description

Returns an attribute for the user. The attribute can be in the SecwinUsers Attributes blob, or the SecwinSettings Attributes blob (which the CurrentUser will inherit.)

Parameters

Parameter Description
pAttribute The name of the attribute to get
pOptions Determine which attributes to check. If the options parameter includes sw:UserAttribute then the user attributes are checked (first). If the attribute is not found (or this option does not include sw:UserAttribute) and the options parameter contains sw:SettingsAttribute then the attributes for the SytemSettings for the user are checked. If this parameter is omitted then the default if for both options to be on.
Return Value

The value of the attribute if it exists, a blank string if it does not exist.

Example

ExpiryDate = SecwinCurrentUser.GetAttribute('PasswordExpiryDate')

SecwinCurrentUserClass

GetCompany

GetCompany()

Description

Gets the Company field, of the Tenant that this user belongs to.

Return Value

A STRING.

Example

name = SecwinCurrentUser.GetCompany()

See Also

GetLogin, GetName, GetPhone

SecwinCurrentUserClass

GetDefaultRightAccess

GetDefaultRightAccess()

Description

Gets the value in the DefaultRightAccess property.

Return Value

A LONG. The current value in the DefaultRightAccess property. One of sw:unknownaccess, sw:yesaccess,

Example

default = SecwinCurrentUser.GetDefaultRightAccess()

SecwinCurrentUserClass

GetEmail

GetEmail()

Description

Gets the Email of the user who is currently logged in.

Return Value

A STRING.

Example

name = SecwinCurrentUser.GetEmail()

See Also

GetLogin, GetName, GetPhone

SecwinCurrentUserClass

GetGuid

GetGuid()

Description

Gets the GUID of the user who is currently logged in.

Return Value

A STRING. The current value in the GUID property.

Example

guid = SecwinCurrentUser.GetGuid()

See Also

GetSettingsGuid, Guids

SecwinCurrentUserClass

GetInGroup

GetInGroup(String pGroupGuid)

Description

Gets the GUID of the user who is currently logged in.

Parameters

Parameter Description
pGroupGuid The GUID of a group.

Return Value

If the current user is in the group then sw:ok is returned. sw:notok if not.

Example

if SecwinCurrentUser.GetInGroup(groupGuid) = sw:ok
  ! yay, user is in the group
end

See Also

GetInGroups, Guids

SecwinCurrentUserClass

GetInGroups

GetInGroups()

Description

Returns a pipe-separated list of the guid's that the user is in. The string starts and ends with a pipe.

Return Value

A STRING.

Example

groups = SecwinCurrentUser.GetInGroups()
! |VCEO8ITLVFS67MLQ|N6R33JR1R5NZ8KK2|
groupnames = SecwinGroupsCache.GetNames(groups)
! sales, support

See Also

GetInGroup, SecwinGroupsCache.GetNames, Guids

SecwinCurrentUserClass

GetLevel

GetLevel()

Description

Gets the value in the Level property.

Return Value

A LONG. The current value in the level property. One of sw:sysAdmin, sw:Administrator, sw:Supervisor, sw:operator, sw:Guest, sw:NoAccess.

Example

level = SecwinCurrentUser.GetLevel()

See Also

GetWorkgroup, GetLogin, GetName, GetLevelName

SecwinCurrentUserClass

GetLicense

GetLicense(String pSection, String pItem)

Description

Returns some property of the license.

Parameters
Parameter Description
pSection The section name of the license. Valid values include;
license, customer, modules, attributes, tables, counters
pItem The specific item of the section to retrieve. For a list of possible items see below. If you have added any items to the license structures then these would be retrieved here as well.
license
licenseid, productlicensename, productlicenseversion, licensetype, copies, level, levelname, serialnumber, dealer, expiry date, datecreated, timecreated, dateissued, timeissued
customer
companyname, companyphone, country, contactname, contactphonenumber, contactmobilenumber, contactemail
modules
A module number
attributes
Any attribute name. This field is not case sensitive
tables
A table label. This field is not case sensitive.
counters
The name of a counter. This field is not case sensitive.

Return Value

String.

Example

licensedTo = SecwinCurrentUser.GetLicense('customer','companyname')

See Also

SetLicense

SecwinCurrentUserClass

GetLevelName

GetLevelName()

Description

Returns the level name, based on the Level property. The text is passed through the Translate method before returning.

Return Value

String. Assuming no translation is made, returns one of SysAdmin, Administrator, Supervisor, Operator, Guest, Not Logged In.

Example

level = SecwinCurrentUser.GetLevelName()

See Also

GetWorkgroup, GetLogin, GetName, GetLevel

SecwinCurrentUserClass

GetLogin

GetLogin()

Description

Gets the UserLogin of the user who is currently logged in.

Return Value

A STRING.

Example

user = SecwinCurrentUser.GetLogin()

See Also

GetName, GetEmail, GetPhone

SecwinCurrentUserClass

GetName

GetName()

Description

Gets the UserName of the user who is currently logged in.

Return Value

A STRING.

Example

name = SecwinCurrentUser.GetName()

See Also

GetLogin, GetEmail, GetPhone

SecwinCurrentUserClass

GetPhone

GetPhone()

Description

Gets the Phone number of the user who is currently logged in.

Return Value

A STRING.

Example

phone = SecwinCurrentUser.GetPhone()

See Also

GetLogin, GetEmail, GetName

SecwinCurrentUserClass

GetSettingsGuid

GetSettingsGuid()

Description

Gets the SettingsGuid property for the current user. This allows the settings record to be loaded if needed.

Return Value

A STRING.

Example

settingsguid = SecwinCurrentUser.GetSettingsGuid()

See Also

GetGuid, Guids

SecwinCurrentUserClass

GetWorkgroup

GetWorkgroup()

Description

Gets the value in the workgroup property.

Return Value

A LONG. The current value in the workgroup property.

Example

workgroup = SecwinCurrentUser.GetWorkgroup()

SecwinCurrentUserClass

Refresh

Refresh()

Description

Reapplies Access rights and License rights to open windows. Called when a new user logs in, or the license for the program changes. This method sends an Event:SecwinRefresh to all (1 to 100) threads. This will cause those windows to reapply user and license restrictions. Windows that are not topmost in their thread will reapply those restrictions when they next become the topmost window in their thread.

Return Value

Nothing

Example

SecwinCurrentUser.Refresh()

See Also

RefreshUser .
SecwinCurrentUserClass

RefreshUser

RefreshUser(SecwinSettingsRecordClass pSettings, SecwinUserRecordClass pUser)

Description

Reloads the current user details off the disk.

The SecwinCurrentUser object exists globally and contains a cache of the user details, for the user who is currently logged in. This includes the user information, and also the current tenant information. It is possible that during the program run, the user and/or tenant information can change. If it does change then you can call this method to update the SecwinCurrentUser object.

Note that this is called for you automatically in the SecwinUpdateSettings and SecwinUpdateUsers procedures, for the currently logged in user.

This method is somewhat unusual in that it requires two other objects (which have been Initialized) to be passed into the method.

Parameters

Parameter Description
pSettings An SecwinSettingsRecordClass object, which has been initialized with the table and secret parameters.
pUser An SecwinUserRecordClass object, which has been initialized with the table and secret parameters.

Example

secSettings SecwinSettingsRecordClass
secUser     SecwinUserRecordClass
  code
  secSettings.Init(SecwinSettings,'program secret','table secret', 'customer secret')
  secUser.Init(SecwinUsers,'program secret','table secret', 'customer secret')
  If SecwinCurrentUser.RefreshUser(secSettings,secUser) = sw:ok
    ! current user refreshed
  End


Return Value

sw:ok if successful, sw:notok if failed. If it failed then the error and errorcode property are primed with information about the cause of the failure.

Example

SecwinCurrentUser.Refresh()

See Also

RefreshUser .
SecwinCurrentUserClass

SetLicense

SetLicense(StringTheory pLicense)

Description

This is called whenever a new license is loaded for the program.

Parameters
Parameter Description
pLicense A flattened for of the JSON license structure.

Return Value

Nothing

Example

workgroup = SecwinCurrentUser.GetWorkgroup()

SecwinCurrentUserClass

UserLogin

UserLogin(String pGuid, String pSettingsGuid, String pCompany, String pLogin, String pName, Long pLevel, String pEmail, String pPhone, Long pDefaultRightAccess, StringTheory pAttributesStr, StringTheory pRightsStr, StringTheory pInGroupsStr, Long pWorkgroup)

Description

Called after a user completes a login.

Parameters

Parameter Description
pGuid  The guid field from the User table for this user.
pSettingsGuid  The guid field from the Settings table for this user.
pCompany The company name from the Settings table for this user.
pLogin  The user login the user used to login.
pName The name of the current user
pLevel  The level of the current user
pEmail  The email of the current user
pPhone  The phone of the current user
pDefaultRightAccess  
pAttributesStr  
pRightsStr  
pInGroupsStr  
pWorkgroup  
Return Value

None

Example

See Also

UserLogout

SecwinCurrentUserClass

UserLogout

UserLogout()

Description

Called when the user logs out.

Return Value

None

Example

SecwinCurrentUser.Logout()

See Also

UserLogin

Classes

SecwinApplyAccessLimitsClass

This class applies the user access rights, and program license restrictions, to a single desktop window.

Only users with the level Guest or Operator can have restricted access rights. Program license restrictions apply to all users.

It can also call the SecwinSetAccess procedure on the appropriate keystroke, when activated by a user with a sufficient level.

Derivation

Properties

Property Type Description
AccessRightsKeycode Long The keycode to trigger the SetAccessrights window. Default is CtrlF8.
AlwaysAllowWindowAccess Byte If this is set to true then access to the window is always allowed. Access to controls on the window can still be set.
LevelRequired Long The minimum User Level required to access the window. Defaults to sw:Guest
LicenseExpiryDate Long The current expiry date of the license.
LicenseName String The current product license name.
LicenseVersion Long The current product license version number.
ProcedureID String The ID of the procedure. This should not be changed for a procedure or all the existing settings for the procedure will be lost. If multiple procedures are to have the exact same settings then they can share the same ProcedureID
ProcedureDesc String A descriptive name for the procedure. This is the name that appears in the Global Set Access screen.

Methods

SecwinApplyAccessLimitsClass

AccessDenied

AccessDenied()

Description

Called when access to the whole window has been denied. By default displays a MESSAGE to the user.

Return Value


Nothing

Example

Not usually called directly. Is called from inside the class when access to the window is denied.

See Also

SetWindow

SecwinApplyAccessLimitsClass

AllowColumn

AllowColumn(Long pFieldEquate, Long pColumn, Long pWidth=100)

Description

Called when a column in a list control is allowed. This may be overridden in the window by other code, like RunScreen etc.

Parameters

Parameter Description
pFieldEquate The FieldEquate of the List control.
pColumn The column number.
pWidth The width to set the column to, default is 100.
Return Value

None

Example

secAccess.AllowColumn(?list1,2,200)

See Also

DenyColumn

SecwinApplyAccessLimitsClass

BindControl  (1)

BindControl(Long pFieldEquate, String pProcedureID, Long pLevelRequired, Long pFailAction)

Description

Called for all controls (like menu items, and buttons) that call a procedure. This enables the class to dig into the call tree to see if the user has access to the destination procedure. If they don't have access then the control can be automatically disabled or hidden.

Parameters

Parameter Description
pFieldEquate The field equate number of the field
pProcedure The ID of the procedure that the field calls
pLevelRequired The user level required by that procedure.
pFailAction The action to take if the access fails. One of
sw:FailDisable, sw:FailHide or sw:FailDisable + sw:FailHide
Return Value

None

Example

 secAccess.BindControl(?BrowseInvoice, |
                      SecwinProcedureList.GetId('BrowseInvoice'), |
                      SecwinProcedureList.GetLevelRequired('BrowseInvoice'), |
                      sw:FailDisable + sw:FailHide)


See Also

SecwinProcedureListClass, BindControl (2), Init, SetWindow

SecwinApplyAccessLimitsClass

BindControl (2)

BindControl(Long pOrder, String pRestrictID, String pRestrictText, String pRestrictDefault, Long pLevelRequired, Long pFieldEquate, Long pFailAction, Long pColumn=0)

Description

Sets the requirements for controls on the window. This method is called just after the window opens once for each restricted control on the window.

Parameters

Parameter Description
pOrder  
pRestrictID  
pRestrictText  
pRestrictDefault  
pLevelRequired  
pFieldEquate  
pFailAction  
pColumn  
Return Value

None

Example

See Also

BindControl(1), Init, SetWindow

SecwinApplyAccessLimitsClass

DenyColumn

DenyColumn(Long pFieldEquate, Long pColumn)

Description

Called when a column in a list control is not allowed. This may be overridden in the window by other code, like RunScreen etc.

Parameters

Parameter Description
pFieldEquate The FieldEquate of the List control
pColumn The column number.
Return Value

None

Example

secAccess.DenyColumn(?list1,2)

See Also

AllowColumn

SecwinApplyAccessLimitsClass

GetControlAccess

GetControlAccess(Long pControl)

Description

Determine if the user has access to a specific control, or not.

Parameters

Parameter Description
pControl The Use Equate (FEQ) of the control to be checked.
Return Value

sw:ok if the user has access to the control, sw:notok if the user does not have access.

Example

If inv:paid = false and secAccess.GetControlAccess(?change) = sw:Ok
   Enable(?
change)
Else
   Disable(?
change)
End


See Also

SetWindow, Additional Program Restrictions

SecwinApplyAccessLimitsClass

Init

Init(String pProcedureID, String pProcedureDesc, Long pLevelRequired, *SecwinCurrentUserClass pCurrentUser, Long pAccessRightsKeycode)

Description

Sets the basic parameters for the object.

Parameters

Parameter Description
pProcedureID The ID of this procedure.
pProcedureDesc The description of this procedure/
pLevelRequired The minimum UserLevel required to access this procedure.
pCurrentUser The SecwinCurrentUser object
pAccessRightsKeycode The alert key to trigger the SecwinAccessRights window.
Return Value

LONG. sw:ok

Example

secAccess.Init('ListInvoices','BrowseInvoice',sw:operator,SecwinCurrentUser,CtrlF8)

See Also

SetWindow

SecwinApplyAccessLimitsClass

SetControls

SetControls()

Description

Loops through all the controls on the window (that have been added via a call to BindControl) and explicitly makes them hidden/visible or enabled/disabled (depending on their declared fail action). For browse columns the AllowColumn and DenyColumn methods are called.

Return Value

None

Example

secAccess.SetControls()

See Also

BindControl, DenyColumn, AllowColumn

SecwinApplyAccessLimitsClass

SetAccessRights

SetAccessRights(String pProcedureNameX,StringTheory pColumnsX)

Description

This is a pure virtual method for calling the SecwinSetAccess procedure. The code is usually populated in the window procedure by the template.

Parameters

Parameter Description
pProcedureNameX The first parameter to pass to SecwinSetAccess.
pColumnsX The second parameter to pass to SecwinSetAccess.
Return Value

LONG. sw:ok

Example

secAccess.SetAccessRights PROCEDURE (String pProcedureNameX,StringTheory pColumnsX)
ReturnValue Long
CODE
  SecwinSetAccessRights(pProcedureNameX,pColumnsX)


See Also

SecwinApplyAccessLimitsClass

SetWindow

SetWindow(Long pPostEvent=true)

Description

Applies all the user restrictions to the window, calling AccessDenied, GrantAccess and RestrictAccessin the process.

Parameters

Parameter Description
pPostEvent If this parameter is true, and the user does not have rights to the window, then the window is hidden, and automatically closed after the call to AccessDenied.
It is typically only set to false if called before the ACCEPT loop has started.
Return Value

None

Example

secAccess.SetWindow()

See Also

GrantAccess, RestrictAccess, AccessDenied

Classes

SecwinProcedureListClass

This class maps procedure names to procedure ID's. It also contains the user level required to run the procedure. An unthreaded global object called SecwinProcedureList is created to hold a system-wide list of names and ID's. This list is primarily used for bubbling support.

Derivation

Properties

Property   Description
cs CriticalSection A critical section for the object.
Procedures SecwinProceduresQueueType A list of procedures in the application, with their matching ID's. Do not access this queue directly, use the supplied methods. If you must access the queue directly be sure to make use of the critical section.

Methods

SecwinProcedureListClass

GetId

GetId(String pProcedureName)

Description

Given a procedure name, return the ProcedureID

Parameters

Parameter Description
pProcedureName The name of the procedure to get.
Return Value

String. The ID of the procedure.

Example

id = Secwin.GetLevelID('BrowseCustomers')

See Also

SetID, GetLevelRequired

SecwinProcedureListClass

GetLicenseLevelRequired

GetLicenseLevelRequired(String pProcedureName)

Description

Given a procedure name, return the license level required.

Parameters

Parameter Description
pProcedureName The Name of the procedure to get.
Return Value

LONG.

Example

licenselevel = Secwin.GetLicenseLevelRequired('BrowseCustomers')

See Also

GetId, SetID

SecwinProcedureListClass

GetLicenseModulesRequired

GetLicenseModulesRequired(String pProcedureName)

Description

Given a procedure name, return the optional modules required by this procedure. This is in the form of a pip-separated list.

Parameters

Parameter Description
pProcedureName The Name of the procedure to get.
Return Value

String. A Pipe separated list of numbers.

Example

modules = Secwin.GetLicensseModulesRequired('BrowseCustomers')  ! |1|3|5|

See Also

GetId, SetID

SecwinProcedureListClass

GetLevelRequired

GetLevelRequired(String pProcedureName)

Description

Given a procedure name, return the level required.

Parameters

Parameter Description
pProcedureName The Name of the procedure to get.
Return Value

LONG. Usually one of sw:sysadmin, sw:administrator, sw:supervisor, sw:operator, sw:guest, sw:noaccess

Example

level = Secwin.GetLevelRequired('BrowseCustomers')

See Also

GetId, SetID

SecwinProcedureListClass

SetId

SetId(Long pProcedureControlType, String pProcedureName, String pProcedureID, Long pLevelRequired, Long pLicenseLevelRequired=0, <String pLicenseModulesRequired>)

Description

Set the procedureID and level required for a procedure.

Parameters

Parameter Description
pProcedureControlType One of sw:accesscontrol, sw:licensecontrol, sw:accesscontrol + sw:licensecontrol
pProcedureName The name of the procedure
pProcedureID The ID of the procedure
pLevelRequired The user level required for the procedure. Usually one of sw:sysadmin, sw:administrator, sw:supervisor, sw:operator, sw:guest, sw:noaccess
pLicenseLevelRequired The license level number required by this procedure.
pLicenseLevelModulesRequired The license modules required by this procedure. This is a pipe separated list of numbers.
Return Value

None

Example

SecwinProcedureList.SetId('BrowseCustomers','BrowseCustomers',sw:administrator)

See Also

GetId, GetLevelRequired

    Version History

ToDo List

  • Support for Tables and Counters for licensing
Version 7.57 (10 September 2024) 
  • Fix: GlobalSetAccess window did not separate procedures by App Description.
Version 7.56 (26 August 2024) 
  • Add: Embed point after settings loaded in Desktop Login window
  • Fix: Web Global Set Access Rights screen could GPF
  • Fix: Desktop: Update Users screen, Sec:User Settings guid was not being primed
  • Fix: Desktop: Update Users screen, If Company of user was changed, Groups browse did not refresh automatically.
Version 7.55 (25 July 2024) 
  • Fix: Web Global Set Access Rights could exclude some control restrictions.
Version 7.54 (13 June 2024) 
  • Add: Tolerance for %SecwinAccessCtrls
  • Group setting with, or without quotes.
  • Change: If AD used, but AD Group not set, then Group Check will "pass" and login will proceed
Version 7.53 (16 January 2024) 
  • Add: Desktop: If user is logged in as a sw:SysAdmin, then the Users, and Groups, browse and form show, and can set, the Company the user or group belongs to. To make use of this reimport the Desktop Access Control Procedures (Global Extension, Procedures Tab, Desktop Access Control Procedures, Replace All).
Version 7.52 (16 January 2024) 
  • Fix: ABC Browse, Change and Delete buttons could be enabled event if records=0.
  • Internal: TreeQueueType renamed to SecwinTreequeueType to avoid TabTree clash.
  • Internal: Update to Capexx.tpw version 4.30
Version 7.51 (4 December 2023) 
  • Add: Docs: Updating an Attribute after a Successful Login
  • Fix: Docs: Incorrectly suggested calling BeforeSave before SaveUser.
  • Fix: Calling BeforeSave before SaveUser could result in blobs being stored unencrypted. (Excess call is now handled gracefully.)
Version 7.50 (29 November 2023) 
  • Add: License attributes now "visible" as attributes in the SecwinCurrentUser object.
  • Fix: In SecwinLoginWeb procedure (in NetTalk app), do not log user out if event has been set.
  • Add: Internal attribute stringtheory objects no longer "private". Read-only access recommended though.
  • Change: If secwin encoding is ANSI, then MakeLicense converts to UTF-8 so that the json is always utf-8
Version 7.49 (21 September 2023) 
  • New Level: sw:apiAccess
  • Change: InterpretUserLevel returns level number if level number not recognized
  • Fix: WebLogin: If Factors were entered incorrectly, there was no way to try again.
Version 7.48 (7 July 2023) 
  • Change: UserLevel parameter added to CreateUser, RegisterNewUser methods.
  • Add: SmsCode, EmailCode as properties to SecwinUserResetPasswordClass
  • Change: SecwinGlobalSetUserAccessClass.AddProcedure, Item not added if already exists.
Version 7.47 (18 March 2023) 
  • Add: SecwinCurrentUserClass.RefreshUser method.
  • Add: RefreshUser method called from SecwinUpdateSettings, SecwinUpdateUsers if the current user or tenant is being edited.
  • Internal: Optimization to GetControlRecordA
  • ccess
Version 7.46 (13 March 2023) 
  • Change: If Active Directory setup (server, port, domain) is invalid or incomplete then only users with level Administrator (or higher) will be allowed to login (without login checks).
  • Change: If Active Directory admin user / password set incorrectly, then Must Be In Group test will be skipped for users with Administrator level (or higher). (requires NetTalk 12.56 or later, and updated ActiveDirectory_UserInGroup procedure.)
  • Change: Update Settings screen (aka Security Policies screen) - if AD option is chosen then AD Server, Port and Domain are Required fields.
Version 7.45 (2 February 2023) 
  • Change: SecwinRights table did not support Multi-Tenant if the users editing were from different tenants.
Version 7.44 (1 February 2023) 
  • Change: Various SecwinTables added to SecwinGlobalSetAccessRightsWeb.
Version 7.43 (25 January 2023) 
  • Fix: BindControl was adding controls as order 0, not st:bubble.
Version 7.42 (10 January 2023) 
Version 7.41 (20 December 2022) 
  • Fix: SecwinLicense Procedure, in Legacy apps, would generate SaveCustomer field in the wrong place.
Version 7.40 (1 December 2022) 
  • Add: SecwinSettingsRecordClass.LoadByGuid method.
  • Add: SecwinUserRecordClass.OverrideLoginType method.
  • Add: SecwinLoginClass.Tries & MaxTries properties added. Template setting in Secwin_Login_procedure, Allow (this many) tries.
  • Add: SecwinLoginDesktopClass.DisplayWindow method
  • Add: SecwinGlobalSetUserAccessClass.DontShowOperatorsWithDefaultGroupRights property.
  • Add: SecwinSetUserAccess.SetUserAccess method.
  • Add: Column Number parameter to BindControl method.
  • Add: SecwinapplyAccessLimitsClass.CheckDisabled method.
  • Add: SecwinLicenseClass Encoding, CodePage properties and SetEncoding method.
  • Add: sw:DisabledStyle equate
  • Add: Global Template: None as possible global Bubbling Action
  • Add: Global Template: Reset Update Buttons option. (On by default.)
  • Add: Global Template: Global option to suppress License Warning.  %LocalNoWarn, not set in right place
  • Add: UserScreenSecurity Template: Add "Enable all except" option to possible License Fail actions.
  • Add: GlobalSetUserAccess: Only Show Operators not in Groups
  • Add: SecwinLicense: Add Encoding tab.
  • Add: SecwinLicense: Add Embeds.
  • Add: Update Users Template: Default New Operators Rights To...
  • Add: Support for extended chars in Company and Contact Name when setting a license.
  • Change: SecwinUserRecordClass.LoadUserByLogin opens table if necessary
  • Fix: Better template defaults for %LevelControls and %LevelRqd for Secwin 6 users.
Version 7.39 (7 September 2022)     
  • Fix: In some cases when a license failed, on the Frame, controls were not disabled because the frame wasn't open yet.
Version 7.38 (7 September 2022) 
  • Change: If multiple access rights (direct and bubbling) are applied to the same control (like Browse Change buttons) then ALL access has to be ALLOWED or the access to the control is denied.
Version 7.37 (5 September 2022) 
  • Change: If Login Type is not Program Login, then suppress Reset password option.
Version 7.36 (24 August 2022) 
  • Add: If ActiveDirectory server settings not set, then information sent to DebugView to highlight this.
  • Add: RememberMe : Feature enabled for ActiveDirectory UserName/Password logins.
  • Fix: Web Logins, Active Directory UserName and Password did not work.
  • Fix: Logout on web did not delete client-side BearerToken cookie. (It did delete on server side, so feature did work.)
Version 7.35 (11 August 2022) 
  • Update to MyTable 1.23 or later required
  • Change: SecwinUpdateSettings window primes lockout parameters if Temporary or Permanent lockout options are chosen.
  • Change: Lockout options, if set to zero, remain at default values (15 minutes, and 5 tries.)
  • Change: If User is locked out, then Login window is NOT closed after User name is entered.
  • Change: Users with Level Administrator or SysAdmin cannot be locked out permanently. They are automatically locked out for 24 hours. This prevents "all users being permanently locked out".
  • Change: If a user is locked out then Local time, not UTC time is displayed in the message.
Version 7.34 (10 August 2022) 
  • Add: LicenseCheckFailed, LicenseMaxCopies, UserCounter,  property to SecwinApplyAccessLimitsClass
  • Add: If license fails copies test then a list of current users is shown to the user.
  • Change: License Limit Copies check Window reduced from 13 seconds to 5 seconds (by default). Now a property (CopiesTestDelay) that can be set.
  • Fix: If 2FA set to always, then Remember Me switch blocked user from logging in.
Version 7.33 (7 July 2022)         
  • Update to NetTalk 12.38 or later required to compile LicenseManagerWeb .
  • Hint: If you get compile errors on building, try Global Extension, Refresh Classes
  • Fix: TryLoadSettingsRecord will open the SecwinUserTable if it's not open.
  • Workaround: Running password fields under Anyscreen makes the fields untypeable if AppsKey is ALERTed.
Version 7.32 (9 May 2022) 
  • Change: The Default Rights attribute for a user was case sensitive. Since users can be added in hand-code, and that hand-code might use the wrong case, the class now treats this value as case insensitive.
  • Fix: LicenseManagerDesktop program did not set filenames.
Version 7.31 (5 May 2022) 
  • Fix: New ClearTable code missing from the build.
Version 7.30 (4 May 2022) 
  • Add: ClearTable methods in RecordsClasses now clear out JsonClass properties as well.
  • Fix: Converter: Wasn't clearing JsonClass properties between updates to groups and users.
Version 7.29 (28 April 2022) 
  • Add: Encryption / Decryption check (error -290) to Login and License check. See Trouble Shooting.
  • Add: Ability to ClearCheckDate method in License check.
  • Add: Manage trailing / on License site URL
  • Add: SetLoginFails method, if pUtc=0 then use current time.
  • Add: If CheckLicenseExpiry fails then program will try and get a new license when the program is next run.
  • Change: Secwin:TemplateVersion equate now generated even if Secwin is disabled.
  • Fix: In LoadUserRecordByKey method only call ErrorTrap if pTry is false
  • Fix: If online data set not set in SecwinLicense window then don't generate invalid code.
  • Fix: Clear Errorcode property if PasswordHistory fails.
Version 7.27 (24 February 2022) 
  • SecwinLicense Procedure - template changed to add option for Online Server Dataset
  • Add: DemoLicense Example
Version 7.27 (24 February 2022) 
  • NetTalk 12.36 includes web example SecwinMultiTenant (88)
  • Update to SecwinWeb procedures to simplify multi-tenant (reimport of TXA recommended.)
  • Update to documentation : Making a Multi-Tenant Web Server Program, (maybe with different data folders / databases)
  • Fix: Second factor settings were applied even if Second Factor options were off.
  • Fix: If second factor set to "either", then an error would occur if the user Phone field was empty.
  • Fix: Saving Remember me Token uses WindowsUserName so multiple users can share one machine.
Version 7.26 (11 January 2022) 
  • Change: MachineID as used in Tokens switched to using MachineId and WindowsUserName together. This allows for distinct users on the same machine (ie TS Plus etc). (REQUIRES MyTable update to version 1.22 or later.)
  • Remove:GetWindowsUserName method - method is now provided by MyTable.
  • Fix: GetControlAccess did not loop through Control queue correctly.
  • Fix (regression 7.23): AlwaysAllowWindowAccess property was being set late.
Version 7.25 (3 January 2022) 
  • Add: Support for ClassName property (REQUIRES MyTable update to version 1.20 or later.)
  • Add: Support for updating Legacy browse update buttons.
  • Change: SetWindow no longer called from BrowseClass.Updatewindow. Instead only BrowseUpdate controls are reset.
  • Fix: InterpretError calls from inside SecwinBaseClass were not flowing through Translate method.
  • Fix: If Web App set to NoLogin then user level not set correctly on login.
  • Fix: Web Settings procedure. Wasn't moving Groups to SessionQueue correctly.
Version 7.24 (27 December 2021) 
  • Fix regression: Alert(CtrlF8) was being done in CheckWindow, moved to Setwindow.
Version 7.23 (17 December 2021) 
  • Add: InterpretError passes result through Translate method.
  • Add: Calls to Analysis pass through Translate method.
  • Add: Fixed text in Login procedure passed through Translate method.
  • Change: SetWindow split into 2 parts, CheckWindow  now called before the window opens. Works around issue with Clarion not liking multiple windows opened between the call to OpenWindow and (possibly no) Accept. SetWindow remains as was for checking controls after the window has opened.
  • Change: Some template lists changed to list-box format.
  • Change: Add generated classes to SecwinSetAccessRights and SecwinGlobalSetAccessRights procedures
  • Fix: If a control had a procedure set, bubbling could override specific access rights on the control.
  • Fix: GroupsQueue in SecwinUpdateSettings get a prefix to avoid Duplicate Identifier Warning
Version 7.22 (9 December 2021) 
  • Fix: TPS Converter did not apply TableSecret before creating DSSW7 tables.
Version 7.21 (26 October 2021) 
  • Rename: CheckLicenseBubble renamed to CheckLicenseControls
  • Fix: SecwinApplyAccessLimits.BindControl allows values to be updated.
  • Fix: Some license checking if Access control was disabled
  • Fix: Test Copies code got generated in multiple places if multiple Net objects on the window.
  • Converter: Updated with Secwin 6.52 DLL's (required if you are compiling the converter)
  • Converter: Fix: email attribute not being imported
  • Converter: Fix: initial access setting for groups
  • Converter: Fix: bit number settings were misnamed.
Version 7.20 (23 September 2021) 
  • Add: Global template option to set default License Failure Behavior.
  • Add: Options to add parameters, and get return value, from Procedure Call above.
Version 7.19 (2 September 2021) 
  • Fix: WebLogin was not generating calls to SendEmail and SendSMS for Second factor logging in.
  • Fix: Embeds inside Object declarations in SecwinLogin procedure duplicated code to all the objects.
Version 7.18 (18 August 2021) 
  • Note: Updating to NetTalk 12.23 or later is required for web apps (there are some Secwin related fixes in there)
  • Add: ProcedureList object extended to support Web Bubbling across multiple DLL's.
  • Fix: SecwinCurrentUsers object is created in DataDLL and exported
Version 7.17 (17 August 2021) 
  • Note: Updating to NetTalk 12.22 or later is recommended for web apps (there are some Secwin related fixes in there as well, plus use of new icons)
  • Add: When user security settings are updated (via SecwinGlobalSetAccessRightsWeb procedure), rights for existing logged-in users are updated, without the user needing to log in again, or the server to be restarted.
  • Change: Icons on tree changed from ui-icon-check to ui-icon-access-yes and from ui-icon-close to ui-icon-access-no
  • Fix: SecwinGroupsCacheClass.LoadCache does more error checking for null or missing values.
  • Fix: When editing rights (via SecwinGlobalSetAccessRightsWeb procedure) group default rights were not taken into account.
  • Internal: SecwinCurrentUserClass gains SetRights method
  • Internal: SecwinCurrentUsersWebClass gains RightsDirty property, and UpdateUserRights method.
Version 7.16 (2 August 2021) 
  • Add: System Attributes are available using the SecwinCurrentUserClass.GetAttribute method. In other words the Current User "inherits" the System attributes.
  • Add Local Users to Active Directory Login systems via User Login Type.
  • Add: SetFileNames Procedure option to Secwin_WebHandler. This procedure is called before user authentication (for not-logged-in values) and after the user login status changes. Used for easily switching between multi-tenant databases when the login user changes.
Version 7.15 (7 June 2021) 
  • Fix: The Converter had an issue importing access to groups. The list was not "cleared" between users, so users gain access to extra groups.
Version 7.14 (24 May 2021) 
  • Installer supports Clarion 11.1
  • Change: sw:MaxColumns increased from 10 to 100 (courtesy of Meredith Mattingly)
  • Fix: CriticalSection added to SecwinCurrentUsersWebClass
  • Fix: New LDAP properties used if NetTalk 12.18 (not 12.17) installed
Version 7.13 (12 May 2021)
  • Requires: MyTable 1.13
  • Update: All calls to MyTable objects have explicit mt:label / mt:name set
  • Fix: handle cases where Blob external names are set to something different.
  • Fix: Web Browses, SQL, PROJECT all necessary fields.
Version 7.12 (11 May 2021)
  • Converter: Change: Secwin 6 users assigned to 1 or more groups now have InitialAccess set to Group.
  • Converter: Fix: All attributes and settings are lowercased.
  • Enforced: Attributes and Settings BLOBs set to lower-case JSON.
  • Change: If no GROUP is set up in the Active Directory settings then the User-In-Group test is bypassed.
  • Change: Active Directory Login: If using NetTalk 12.18 or later then Login error message is more detailed (disabled, expired, locked out, wrong password, server offline). Re-importing the NetTalk Active Directory procedures from 12.18 or later is recommended.
  • Change: If Active Directory settings are not set (host, port, domain) then Login is automatically successful (Valid user still required.)
  • Change: "Remember Login" checkbox on Login window hidden for ActiveDirectory logins. Awaiting an ActiveDirectory_ValidateUser without password procedure.
  • Fix: SetUserAccess screen had a number of visual anomalies in various edge-case setting combinations.
  • Fix: Possible fix for GPF in WebServer DeleteSession
  • Fix: Limit Copies timeout extended from 3 seconds to 13 seconds.
  • Fix: Legacy: Browse Groups: User User was "one click behind".

Version 7.11 (28 April 2021)
  • Bump: Required version of MyTable set to 1.12
  • Add: ProcedureList construction support for GlobalSetAccessWeb
  • Change: GlobalSetAccessWeb Icons changed to using NetTalk icons.
  • Update: Secwin7AccessControlWebABC.txa updated with SecwinChangePasswordWeb procedure
  • WebExample: Added to NetTalk 12 build 12.17
  • LicenseManager apps ported back to Clarion 8.
  • Fix: SecwinUpdateUsers: Even if the password was unacceptable it still got saved.
  • Fix: Couple places in the template weren't using the global glass names.
  • Fix: SecwinLicense - do not read the license on APPLY button or OK button.
Version 7.10 (22 April 2021)
  • Add: FOR the SecwinChangePasswordWeb procedure extension template
  • Change: Internal: Multi-DLL support for GlobalSetAccess reworked to support bigger app systems to avoid segdef error. Limit is now approximately 3200 security points per app rather than 3200 for the whole system.
  • Fix: If no Active Directory Group was set, then the login would fail.
  • Fix: Login as Guest, and Register buttons on login screen could appear, even if those policies were off.
  • Fix: Check New Password did not clip trailing spaces
Version 7.09 (14 April 2021)
  • Bumped: Required version of MyTable to version 1.10
  • Add: PasswordSuggestions method to SecwinCheckNewPasswordClass
  • Add: GetIP, GetWorkstation, AddOtherUser methods to SecwinCurrentUserClass.
  • Add: Copies Tab to User_Screen_Security local extension template
  • Add: CheckLicenseCopies method to SecwinApplyAccessLimitsClass
  • Add: New embed points in various routines
  • Update: UserLogin Method in SecwinCurrentUserClass with IP and Workstation parameters.
  • Update: Using ClearTable method from MyTable (version 1.10)
  • Update: Open method in SecwinSettingsRecordClass quickly exits if the file is already open.
  • Update: Various bits around the WebServer support
  • Fix: SecwinListProcedure.SetId was incorrectly called for web procedures.
  • Fix: SetAccessRightsWeb : Before saving rights, make sure RightsJson is not null.
  • Converter: Progress bar added
  • Converter: Updated to use MyTable 1.10 - especially good for BLOB support in SQL.
  • Converter: Added version resource to DLLs.
Version 7.08 (9 March 2021)
  • Add: ChainedLogin property to SecwinLoginDesktopClass
  • Add: MsSqlLegacy Example (set your connection string in sql.ini)
  • Fix: Secwin 6 "Bit Value" becomes the new "Restrict ID" and is copied across by default.
  • Fix: SQL, Legacy Apps, do not like Browses that do not have a "default key" set. In examples and TXA's, SecwinBrowseTenants and SecwinLogin procedures updated.
  • Upgrade: MyTable Version 1.08 recommended


Version 7.07 (2 March 2021)
  • Docs: Add section: Key Procedure Access Control Decisions
  • Docs: Add section: Additional Program Restrictions
  • Add: Bubbling support to RelationTree control template.
  • Add: Embed in LoginComplete method.
  • Add: Schema settings for converters (SQL only)
  • Change: Procedure name added to F8 Analysis
  • Fix: Users with "Default All" access were restricted from some procedures
  • Fix: SQL did not like ExpiryDate field in Token table being 0
  • Fix: GetAttributes returns string, but DefaultRights uses the Long equate
  • Fix: Bubbling did not take template setting "always allow access" into account
  • Fix: BindControl could happen before Init
  • Fix: AfterSave did not return sw:ok
  • MSSQL Converter : View-password button on Program Secret was pointing to the wrong control
  • SQL Converters : app= command line parameter was going to Area field.

Version 7.06 (24 February 2021)
  • SecwinConverter compiled with current Secwin build (fix Unable to EncryptString)
  • Add: Turn off license checking on procedures where it is disabled.
Version 7.05 (23 February 2021)
  • MyTable version requirement updated to MyTable 1.07
  • Fix: Template was not generating TakeEvent call in the right priority
  • Fix: ,Virtual / ,Derived errors in generated code
  • Change : More Errorcode information in file errors
  • LicenseManager (desktop) updated to support logins, Web version is in progress.
Version 7.04 (9 February 2021)
  • Released Conversion Utility for converting from DSSW tables Secwin 6 to Secwin 7.
  • Add: Template: FOR SecwinLoginProcedure: Allow Automatic Login from other EXE's
  • Update: Data shipped with examples updated to encryption version 3.
  • Fix: Various tweaks to support Licencing ON where access control is OFF.
  • Fix: SetControls did not pass controls.FEQ
  • Fix: License Expire Warning does not appear if license is blank
  • Fix: Browser buttons updated when ABC browse changes Disabled state.
  • Fix: LoginComplete did not call parent method if login type was sw:nologin


Version 7.03 ( 9 November 2020)
  • Add: DeleteToken method added to Token Class
  • Fix: All calls to OPEN tables are balanced with calls to CLOSE them when objects go out of scope.
  • Fix: Secwin2FactorRecordClass.LoadSettings, SecwinGroupsCacheClass.Free,  methods were not set as Virtual
Version 7.02 ( 2 September 2020)
  • Add: DeleteToken method added to Token Class
  • Add: LoadGroupByGuid, SaveGroup methods added to SecwinGroupRecordClass
  • Change: Second Factor Tokens Slate Time set to 15 minutes if set to 0.
  • Change: RightsNameLength bumped from max 40 chars to max 100 chars.
  • Change: Various tables Opened as needed
  • Change: Support for MyTable 1.05, so now requires _either_ Cryptonite OR NetTalk 11.41
  • Change: Desktop Logout procedure generates Token object as fully declared object.
  • Fix: SecondFactor.Validate tested SMSCodeHash in EmailCodeHash section
  • Fix: Method didn't return sw:ok
  • Fix: Email with 2nd factor included code twice
  • Fix: If 2FA was set to SMS OR Email, it behaved as if set to Sms AND Email.
  • Fix: Login as Guest could fail because userlevel moved to record field.
  • Fix: Various template code had hard coded object names, which were set as extension prompts
  • Refactor: Hierarchies for SecwinLogClass, SecwinLoginDesktopClass, SecwinLoginWebClass
Version 7.01 ( 3 June 2020)
  • Add: First release with Licensing support
  • Add: License Manager
  • Add: Logo Screen Support
  • Add: Documentation on implementing Doors.
  • Add: SecwinCurrentUserClass : GetLevelName, GetLicense, SetLicense, Refresh methods.
  • Add: SecwinApplyAccessLimitsClass: CheckLicense, CheckLicenseExpiry, CheckLicenseLevel, CheckLicenseModules, LicenseFailed, WarnImpendingExpiry, SecwinLicenseA, NeedInternetLicenseCheck, Disable, EnableAll.
  • Rename: SecwinApplyAccessLimitsClass: AccessDenied method renamed to ShowMessage.
  • Change: ds_Message added to list of procedure where UserScreenSecurity extension is auto-blocked.
  • Change: New User Groups default to Access None instead of Access All.
  • Fix: SecwinTokenRecordClass: Run method: used fixed exe name, not passed in pExe parameter.
  • Fix: If the Windows User Name did not exist as a user in the users list, then Login button could fail to appear.
  • Fix: Template used SecwinCurrentUser instead of %SecwinCurrentUserObject in various places
  • Fix: SecwinLogin procedure needed ActiveInvisible for SecwinSettings Browse set on.
  • Fix: Call to GetWorkgroup in template was incorrectly calling GetUserWorkgroup
  • Fix: Global SetAccess Rights did not save settings correctly.
Version 7.00 ( 22 May 2020)
  • Initial release
Download latest version here