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.
        
      
        
        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
          
            
              - Add a variable called LoginType of type LONG to the Attributes
                GROUP in the Local Data Pad of the SecwinUpdateUsers procedure.
- 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
          
            
              - Add a variable called LoginType of type LONG to the Attributes
                GROUP in the Local Data Pad of the SecwinUpdateUsersWeb
                procedure.
- Add a RADIO option to the form, on the Other tab, using this
                variable.
- 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;
            
              - Go to the SecwinUpdateUsers or SecwinUpdateUsersWeb procedure
- Add a field into the Local Data, inside the Attributes Group
- 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;
              
                - 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))
- 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;
            
            
 
            
           
        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.
- 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;
            
            
            
            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
          
            - Using your browser log onto https://api7.secwinonline.com/
- Go to the Browse / Products menu item
- 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.
            
              -  Go to the SecwinLicense procedure, to the FOR the
                  SecwinLicense Procedure Extension template. 
- Set the Online Server URL to 'https://api7.secwinonline.com'
              
- 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;
          
            - Import Secwin7.dctx into
              dictionary (\clarion\accessory\libsrc\win\secwin7.dctx)
- Import NetTalkEmail.dctx into
              dictionary (\clarion\accessory\libsrc\win\NetTalkEmail.dctx)
- Optionally import NetTalkSMS.dctx into dictionary  (\clarion\accessory\libsrc\win\NetTalkSms.dctx)
- Add Global Extensions (StringTheory, Cryptonite or NetTalk,
              MyTable, jFiles, Secwin, [desktop] Secwin Auto Populate Screen
              Security)
- If Multi-DLL then set Multi-DLL tab for all the above (excluding
              NetTalk) as appropriate.
- Import NetTalk procedures (NetTalk Global Extension, Procedures
              tab). Email, SMS and Active Directory
- [Desktop Apps] Disable User Access Control on SendEmail
                and ActiveDirectory procedures.
- [Desktop Apps] Import Secwin Desktop Procedures (Secwin
              Extension, Procedures Tab)
- [Desktop Apps] Set NetTalk procedures in Secwin Extension,
              Procedures tab.
- [Web Apps] Import Secwin Web Procedures (Secwin Extension,
              Procedures Tab)
- [Web Apps] Set optional Netalk procedures (Secwin Extension,
              Procedures Tab, NetTalk Tab)
- Set Secrets (Secwin Extension, Secrets tab)
- SendEmail procedure, update use of GLO:UserEmail
              and GLO:UserName
                [1]
- [Desktop Apps] Buttons / Menu items to Frame (Logout, Login,
              Users, Settings, GlobalSetAccess, Email Settings)
- [Desktop Apps] Add UserLogin Extension to Frame. 
- [Desktop Apps] Always Allow Access to Frame. Set Min Level of
              Frame to All.
- [Desktop Apps] On Frame, display LoggedIn Name, Level etc in
              Status bar
- [Desktop Apps] Set options on Procedures, User Screen Security,
              as desired.
- [Web Apps] Add Secwin Extension (FOR WebServer procedure) to
              WebServer procedure
- [Web Apps] Add Secwin Extension (FOR WebHandler procedure) to
              WebHandler procedure
- [Web Apps] Goto NetTalk Global Extension (Activate NetTalk Web
              Server), WebApps tab, Tick on Use Secwin.
- [Web Apps] [Optionally] add <div
                class="nt-current-user"><!-- Net:s:CurrentUser
                --></div> to xHTML on Header or Footer.
- [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.
          
            - Import the dictionary changes to the apps as normal
- 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)
- For all the apps, the Secrets on the Secwin global extension,
              Secrets Tab, must be set to the same values.
- 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;
            
              - Go to the app that contains this procedure (probably the Data
                DLL)
- Go to the SecwinGlobalSetAccessRights procedure, to the
                template extensions
- Go to the FOR the SecwinGlobalSetAccessRights Procedure
                extension template
- Go to the Apps tab
- 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.
          
            - Open your Dictionary
- Click on the dropdown next to the  Import/Export button.
 
  
- From the drop down select Import Dictionary from Text
- 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;
          
            - Secrets Tab: Program Secret
- 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
            
              -  If you don't already have them then import the EmailSettings
                  and EmailLog tables into
                your dictionary. 
-  Make sure the Global Extension, Activate NetTalk,
                has been added to your app.
-  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
-  Inside the SendEmail
                procedure replace;
 GLO:UserEmail with 
                  SecwinCurrentUser.GetEmail() and
 GLO:UserName with 
                  SecwinCurrentUser.GetName()
 
  
-  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
            
              -  Make sure the Global Extension, Activate NetTalk,
                has been added to your app.
-  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
-  Go to the Global Secwin Extension in the application, to the
                Procedures tab, and select the name of the SendSMS
                  procedure.
-  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
            
              - Make sure the Global Extension, Activate NetTalk,
                has been added to your app.
- 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
- Go to the Global Secwin Extension in the application, to the
                Procedures tab, and select the names of the two Active Directory
                procedures.
- 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
              
                - Go to the Global Extensions for the APP.
- Highlight the Activate CapeSoft Secwin
                  item in the extensions list
- Click the Insert button
- 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;
            
              - Go to the procedure in the application tree
- Go to the User Screen Security extension.
- Go to the Licensing / Expiry tab
- 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.
            
            
              - 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.
- 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. 
 
   
- 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. 
 
   
- In the same procedure, on the Secwin User Screen Security
                extension, Licensing / Copies tab, tick on the option to Test to
                License Copies here.
- Set the NetClient Object name to the same you specified in
                step 2 above. This is typically LimitCopies. 
- 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. 
         
       
       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;
        
          -  If you are using Secwin for Licensing, then the records in the  SecwinSettings table are important.
-  If you are using Secwin for Access Control then the records in
            the  SecwinSettings AND SecwinUsers
              tables are important. Being able to empty the Users table,
            but not the Settings table leads to extra complexities. 
        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 SuperFileThe 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 SuperFileThe 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). | 
          
        
       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 
          
            
              - 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()
- 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()
- 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.
- 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.
- The RowHash (and all the other hash fields) should not be
                touched.
- The UserLevel field should contain one of the following
                equates; sw:sysadmin, sw:administrator,
                sw:operator, sw:guest,
                sw:nolevel
- The UserWorkGroup field is the same as the WorkGroup field in
                Secwin 6.
- 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;
          
            - Secwin data tables - these are shared across all the tenants.
- System configuration tables - these are shared across all the
              tenants - in this example the emailSettings, emailLog, smsSettings
              and smsLog tables fall into this category.
- 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. 
          
            - 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
- 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.
- Add variables used in step 1 to Dictionary Globals list. Make
              sure the Tenant variables are THREADed.
Next are changes to the Application.
          
            - 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.
 
- 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.
- 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...
- In the WebHandler procedure, Secwin FOR_WebHandler Extension,
              Options tab, select the SetFileNames procedure.
- 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
 
- 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.
- 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.
- 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.
- 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.
- Repeat steps 11 and 12 for the SecwinBrowseGroupsWeb and
              SecwinUpdateGroupsWeb procedures.
Running the App for the First Time
          
            
              -  Click the Login button. There are no users at this point and
                no policies, so you will be logged in as a SysAdmin. 
- 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".) 
- 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
          
            
              - Login as the SysAdmin User. Go to the Security menu, to the
                Browse Tenants option. Click the Insert button to add a tenant.
- 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. 
- Set the Data Path and Data Secret for this tenant. 
- On the Login Policies tab Set the login type to (at least)
                Program Login.
- 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.)
            
            
          
         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.