{"id":446,"date":"2016-08-30T00:22:19","date_gmt":"2016-08-30T03:22:19","guid":{"rendered":"http:\/\/www.pascalscada.com\/?page_id=446"},"modified":"2026-09-17T09:36:51","modified_gmt":"2026-09-17T12:36:51","slug":"security-system","status":"publish","type":"page","link":"https:\/\/www.pascalscada.com\/pb\/security-system\/","title":{"rendered":"Sistema de seguran\u00e7a"},"content":{"rendered":"<h5>Introdu\u00e7\u00e3o<\/h5>\n<p>O sistema de seguran\u00e7a do PascalSCADA responde a uma pergunta simples: <strong>o usu\u00e1rio logado pode mexer neste controle?<\/strong> Ele tem tr\u00eas pe\u00e7as:<\/p>\n<ol>\n<li><strong>C\u00f3digos de seguran\u00e7a<\/strong> (<code>SecurityCode<\/code>) \u2014 textos livres que voc\u00ea atribui aos controles da <a href=\"\/pb\/hmi-control-library-hcl\/\">HCl<\/a> e \u00e0s <em>actions<\/em> de seguran\u00e7a: <code>\"setpoints\"<\/code>, <code>\"manutencao\"<\/code>, <code>\"receitas\"<\/code>. Um controle sem c\u00f3digo \u00e9 livre para todos.<\/li>\n<li><strong>O gerenciador de seguran\u00e7a<\/strong> (<code>TControlSecurityManager<\/code>) \u2014 um objeto \u00fanico por aplica\u00e7\u00e3o, criado automaticamente (voc\u00ea o acessa com <code>GetControlSecurityManager<\/code>). Todos os controles com <code>SecurityCode<\/code> se registram nele; a cada login e logout ele pergunta, para cada c\u00f3digo, se o usu\u00e1rio atual tem acesso e habilita ou desabilita os controles.<\/li>\n<li><strong>Um componente de gerenciamento de usu\u00e1rios<\/strong> (paleta <strong>PascalSCADA User Management<\/strong>) \u2014 quem de fato sabe quais usu\u00e1rios existem, valida senha e diz quais c\u00f3digos cada um pode acessar. S\u00f3 pode haver <strong>um<\/strong> por aplica\u00e7\u00e3o (o segundo levanta exce\u00e7\u00e3o ao ser criado). H\u00e1 tr\u00eas implementa\u00e7\u00f5es, descritas abaixo.<\/li>\n<\/ol>\n<p>Sem componente de gerenciamento de usu\u00e1rios, o gerenciador de seguran\u00e7a responde <em>sim<\/em> a tudo: os <code>SecurityCode<\/code> n\u00e3o t\u00eam efeito e nada fica desabilitado. Isso permite montar as telas primeiro e ligar a seguran\u00e7a depois.<\/p>\n<h5>Como funciona<\/h5>\n<pre><code>THMIEdit.SecurityCode = &quot;setpoints&quot;\n        \u2502 registra o c\u00f3digo\n        \u25bc\nTControlSecurityManager \u2500\u2500CanAccess(&quot;setpoints&quot;)?\u2500\u2500&#x25b6; TxxxUserManagement\n        \u2502                                                    \u2502\n        &#x25c0;\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 True \/ False \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n        \u25bc\nTHMIEdit.Enabled := (resposta) and (Enabled que voc\u00ea definiu)\n<\/code><\/pre>\n<ul>\n<li>Ao atribuir um <code>SecurityCode<\/code>, o controle chama <code>ValidateSecurityCode<\/code> (o gerenciador de usu\u00e1rios pode recusar c\u00f3digos desconhecidos levantando uma exce\u00e7\u00e3o) e <code>RegisterSecurityCode<\/code> (para o gerenciador poder listar os c\u00f3digos usados pela aplica\u00e7\u00e3o, o que os gerenciadores de usu\u00e1rios usam para montar as telas de permiss\u00f5es).<\/li>\n<li><strong>Login<\/strong>: <code>GetControlSecurityManager.Login<\/code> abre o di\u00e1logo padr\u00e3o de autentica\u00e7\u00e3o (usu\u00e1rio e senha, ou cart\u00e3o RFID se houver um leitor ligado). Se a senha estiver certa, o gerenciador reavalia todos os controles (<code>UpdateControls<\/code>). Depois de <code>LoginRetries<\/code> tentativas erradas o di\u00e1logo trava por <code>LoginFrozenTime<\/code> milissegundos. <code>Login(usuario, senha, uid)<\/code> faz o mesmo sem di\u00e1logo.<\/li>\n<li><strong>Logout<\/strong>: <code>GetControlSecurityManager.Logout<\/code> \u2014 todos os controles com c\u00f3digo voltam a ficar desabilitados.<\/li>\n<li>Em c\u00f3digo, <code>CanAccess(c\u00f3digo)<\/code> retorna se o usu\u00e1rio atual tem acesso, e <code>TryAccess(c\u00f3digo)<\/code> levanta uma exce\u00e7\u00e3o se n\u00e3o tiver \u2014 \u00fatil no in\u00edcio de um procedimento que n\u00e3o deve rodar sem permiss\u00e3o.<\/li>\n<\/ul>\n<p>Propriedades e eventos dispon\u00edveis em todos os gerenciadores de usu\u00e1rios:<\/p>\n<table>\n<thead>\n<tr>\n<th>Membro<\/th>\n<th>Descri\u00e7\u00e3o<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>UserLogged<\/code>, <code>CurrentUserLogin<\/code>, <code>CurrentUserName<\/code>, <code>UID<\/code>, <code>LoggedSince<\/code><\/td>\n<td>Estado do login atual (somente leitura).<\/td>\n<\/tr>\n<tr>\n<td><code>LoginRetries<\/code><\/td>\n<td>Tentativas de senha antes de travar o di\u00e1logo (0 = nunca trava).<\/td>\n<\/tr>\n<tr>\n<td><code>LoginFrozenTime<\/code><\/td>\n<td>Tempo de trava, em ms.<\/td>\n<\/tr>\n<tr>\n<td><code>ChipCardReader<\/code><\/td>\n<td>Leitor de cart\u00e3o\/RFID para login sem senha \u2014 veja <a href=\"#rfid\">Login por cart\u00e3o RFID<\/a>.<\/td>\n<\/tr>\n<tr>\n<td><code>SuccessfulLogin<\/code>, <code>FailureLogin<\/code><\/td>\n<td>Eventos de login com e sem sucesso (para auditoria, por exemplo, com o <a href=\"\/pb\/event-and-alarm-loggers\/\">registrador de eventos<\/a>).<\/td>\n<\/tr>\n<tr>\n<td><code>UserChanged<\/code><\/td>\n<td>Evento <code>(Sender, OldUsername, NewUserName)<\/code> disparado em login e logout.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h5>Escolhendo o gerenciador de usu\u00e1rios<\/h5>\n<table>\n<thead>\n<tr>\n<th>Componente<\/th>\n<th>Onde ficam os usu\u00e1rios<\/th>\n<th>Use quando\u2026<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><img decoding=\"async\" alt=\"\" src=\"https:\/\/www.pascalscada.com\/wp-content\/uploads\/2026\/09\/TCustomizedUserManagement.png\"> <code>TCustomizedUserManagement<\/code><\/td>\n<td>Onde voc\u00ea quiser: voc\u00ea responde a eventos<\/td>\n<td>tem um banco de usu\u00e1rios pr\u00f3prio, um LDAP, um arquivo \u2014 qualquer coisa. \u00c9 o mais usado.<\/td>\n<\/tr>\n<tr>\n<td><img decoding=\"async\" alt=\"\" src=\"https:\/\/www.pascalscada.com\/wp-content\/uploads\/2026\/09\/TCentralUserManagement.png\"> <code>TCentralUserManagement<\/code><\/td>\n<td>Em um servidor HTTP\/JSON na rede<\/td>\n<td>v\u00e1rias esta\u00e7\u00f5es precisam compartilhar os mesmos usu\u00e1rios e permiss\u00f5es, com login refletido em todas.<\/td>\n<\/tr>\n<tr>\n<td><img decoding=\"async\" alt=\"\" src=\"https:\/\/www.pascalscada.com\/wp-content\/uploads\/2026\/09\/TWinCCUserManagement.png\"> <code>TWinCCUserManagement<\/code><\/td>\n<td>No <em>User Administrator<\/em> do SIMATIC WinCC (s\u00f3 Windows)<\/td>\n<td>a aplica\u00e7\u00e3o roda ao lado de um WinCC e deve usar os mesmos usu\u00e1rios e autoriza\u00e7\u00f5es.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h5 id=\"TCustomizedUserManagement\">TCustomizedUserManagement<\/h5>\n<p><img decoding=\"async\" alt=\"\" src=\"https:\/\/www.pascalscada.com\/wp-content\/uploads\/2026\/09\/TCustomizedUserManagement.png\"> N\u00e3o sabe nada sobre usu\u00e1rios: delega tudo a eventos que voc\u00ea implementa. Os obrigat\u00f3rios s\u00e3o dois:<\/p>\n<pre class=\"brush: delphi; tab-size: 2; title: ; notranslate\" title=\"\">\/\/ Valida usu\u00e1rio e senha. Devolva o UID (qualquer inteiro que identifique o usu\u00e1rio)\n\/\/ e ValidUser. LoginAction \u00e9 True quando a chamada vem do di\u00e1logo de login e False\n\/\/ quando vem de uma verifica\u00e7\u00e3o de &quot;assinatura&quot; (TPascalSCADACheckSpecialTokenAction).\nprocedure TForm1.UserManagementCheckUserAndPass(user, pass: UTF8String;\n  out aUID: Integer; var ValidUser: Boolean; LoginAction: Boolean);\nbegin\n  ValidUser := Users.CheckPassword(user, pass, aUID);\nend;\n\n\/\/ Diz se o usu\u00e1rio logado tem acesso ao c\u00f3digo de seguran\u00e7a.\nprocedure TForm1.UserManagementCanAccess(securityCode: UTF8String;\n  var CanAccess: Boolean);\nbegin\n  CanAccess := Users.HasPermission(UserManagement.UID, securityCode);\nend;<\/pre>\n<p>Os demais eventos:<\/p>\n<table>\n<thead>\n<tr>\n<th>Evento<\/th>\n<th>Para qu\u00ea<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>OnUIDCanAccess(aUID, securityCode, var CanAccess)<\/code><\/td>\n<td>Como <code>OnCanAccess<\/code>, mas para um usu\u00e1rio espec\u00edfico \u2014 usado pela verifica\u00e7\u00e3o de assinatura, quando quem autoriza n\u00e3o \u00e9 quem est\u00e1 logado.<\/td>\n<\/tr>\n<tr>\n<td><code>OnGetUserName<\/code>, <code>OnGetUserLogin<\/code><\/td>\n<td>Devolvem nome e login do usu\u00e1rio atual, mostrados em <code>CurrentUserName<\/code>\/<code>CurrentUserLogin<\/code>.<\/td>\n<\/tr>\n<tr>\n<td><code>OnLogout<\/code><\/td>\n<td>Limpe o que guardou sobre o usu\u00e1rio.<\/td>\n<\/tr>\n<tr>\n<td><code>OnManageUsersAndGroups<\/code><\/td>\n<td>Abra a sua tela de cadastro de usu\u00e1rios (\u00e9 o que <code>Manage<\/code> e a <code>TPascalSCADAManageUsersAction<\/code> chamam).<\/td>\n<\/tr>\n<tr>\n<td><code>OnValidadeSecurityCode(code)<\/code><\/td>\n<td>Levante uma exce\u00e7\u00e3o se o c\u00f3digo n\u00e3o existir no seu cadastro \u2014 protege contra erros de digita\u00e7\u00e3o nos <code>SecurityCode<\/code> dos controles.<\/td>\n<\/tr>\n<tr>\n<td><code>OnRegisterSecurityCode(code)<\/code><\/td>\n<td>Chamado para cada c\u00f3digo novo que aparece na aplica\u00e7\u00e3o; grave-o no banco para ele aparecer na tela de permiss\u00f5es.<\/td>\n<\/tr>\n<tr>\n<td><code>OnCheckUserChipCard<\/code><\/td>\n<td>Valida o c\u00f3digo de um cart\u00e3o RFID lido pelo <code>ChipCardReader<\/code>.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>O exemplo <a href=\"https:\/\/github.com\/fluisgirardi\/pascalscada_v0\/tree\/master\/examples\/laz_custom_user_management\"><code>examples\/laz_custom_user_management<\/code><\/a> implementa isso com tr\u00eas usu\u00e1rios fixos em c\u00f3digo \u2014 bom para entender o mecanismo. O exemplo <a href=\"https:\/\/github.com\/fluisgirardi\/pascalscada_v0\/tree\/master\/examples\/laz_customusermanagement\"><code>examples\/laz_customusermanagement<\/code><\/a> \u00e9 uma implementa\u00e7\u00e3o completa com <strong>usu\u00e1rios, grupos e permiss\u00f5es em PostgreSQL<\/strong> (via Zeos), incluindo as telas de cadastro de usu\u00e1rios, grupos e permiss\u00f5es e o hash MD5 das senhas \u2014 o modelo de banco est\u00e1 em <code>docs\/db_user_groups_modelagem.xml<\/code>. Use-o como ponto de partida para o seu.<\/p>\n<h5 id=\"TCentralUserManagement\">TCentralUserManagement<\/h5>\n<p><img decoding=\"async\" alt=\"\" src=\"https:\/\/www.pascalscada.com\/wp-content\/uploads\/2026\/09\/TCentralUserManagement.png\"> Cliente de um <strong>servidor de autentica\u00e7\u00e3o<\/strong> HTTP: cada verifica\u00e7\u00e3o de senha e de permiss\u00e3o vira uma chamada JSON ao servidor, e uma thread acompanha o endpoint <code>userchanged<\/code> para que um login feito em uma esta\u00e7\u00e3o apare\u00e7a nas outras.<\/p>\n<table>\n<thead>\n<tr>\n<th>Propriedade<\/th>\n<th>Descri\u00e7\u00e3o<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>AuthServer<\/code>, <code>AuthServerPort<\/code>, <code>UseSSL<\/code><\/td>\n<td>Endere\u00e7o do servidor.<\/td>\n<\/tr>\n<tr>\n<td><code>UseCachedAuthorizations<\/code><\/td>\n<td>Guarda as respostas de permiss\u00e3o localmente para n\u00e3o consultar o servidor a cada <code>CanAccess<\/code>; <code>CachedUpdateCount<\/code> mostra quantas vezes o cache foi renovado.<\/td>\n<\/tr>\n<tr>\n<td><code>UseCentralUserAsLocalUser<\/code><\/td>\n<td>Se <code>True<\/code>, o usu\u00e1rio logado no servidor (por outra esta\u00e7\u00e3o) passa a ser o usu\u00e1rio desta esta\u00e7\u00e3o tamb\u00e9m \u2014 login \u00fanico na planta.<\/td>\n<\/tr>\n<tr>\n<td><code>RaiseExceptOnConnFailure<\/code><\/td>\n<td>Levanta exce\u00e7\u00e3o quando o servidor n\u00e3o responde; se <code>False<\/code>, falha silenciosamente negando acesso.<\/td>\n<\/tr>\n<tr>\n<td><code>OnUserChanged<\/code><\/td>\n<td>Notifica\u00e7\u00e3o de troca de usu\u00e1rio vinda do servidor.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>O protocolo \u00e9 simples \u2014 <code>POST<\/code> de um objeto JSON para <code>\/checkuserpwd<\/code>, <code>\/uidcanaccess<\/code>, <code>\/validadesecuritycode<\/code>, <code>\/registersecuritycode<\/code>, <code>\/enumsecuritycodes<\/code> e <code>\/userchanged<\/code>. O exemplo <strong><a href=\"https:\/\/github.com\/fluisgirardi\/pascalscada_v0\/tree\/master\/examples\/laz_security_webserver_WinCC\"><code>examples\/laz_security_webserver_WinCC<\/code><\/a><\/strong> \u00e9 um servidor pronto que exp\u00f5e o <code>TWinCCUserManagement<\/code> de uma m\u00e1quina para a rede \u2014 ou seja, todas as esta\u00e7\u00f5es PascalSCADA usam os usu\u00e1rios do WinCC \u2014 e serve de modelo para escrever um servidor sobre o seu pr\u00f3prio banco de usu\u00e1rios.<\/p>\n<h5 id=\"TWinCCUserManagement\">TWinCCUserManagement<\/h5>\n<p><img decoding=\"async\" alt=\"\" src=\"https:\/\/www.pascalscada.com\/wp-content\/uploads\/2026\/09\/TWinCCUserManagement.png\"> S\u00f3 em Windows, com o SIMATIC WinCC instalado na m\u00e1quina: usa a DLL <em>UseAdmin<\/em> do WinCC para autenticar e consultar as autoriza\u00e7\u00f5es. O di\u00e1logo de login \u00e9 o padr\u00e3o do PascalSCADA, mas usu\u00e1rio e senha s\u00e3o validados pelo WinCC; <code>Manage<\/code> abre o <em>User Administrator<\/em> do WinCC.<\/p>\n<p>Como o WinCC identifica autoriza\u00e7\u00f5es por n\u00famero (1 = <em>User administration<\/em>, 2 = <em>Value input<\/em>, 1000 = <em>System change<\/em>\u2026), a propriedade <strong><code>AuthorizationList<\/code><\/strong> mapeia cada c\u00f3digo de seguran\u00e7a do PascalSCADA para um n\u00famero, uma linha por c\u00f3digo, no formato <code>n\u00famero:c\u00f3digo<\/code>:<\/p>\n<pre><code>2:setpoints\n1000:manutencao\n1001:receitas\n<\/code><\/pre>\n<p>Um usu\u00e1rio do WinCC com a autoriza\u00e7\u00e3o 2 tem acesso a todos os controles com <code>SecurityCode = \"setpoints\"<\/code>. C\u00f3digos fora da lista s\u00e3o recusados.<\/p>\n<h5 id=\"actions\">Actions de seguran\u00e7a<\/h5>\n<p>Na aba <strong>PascalSCADA User Management<\/strong> do editor de <code>TActionList<\/code> (<em>New standard action<\/em>) h\u00e1 seis actions prontas para ligar em bot\u00f5es, itens de menu e barras de ferramentas:<\/p>\n<table>\n<thead>\n<tr>\n<th>Action<\/th>\n<th>O que faz<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>TPascalSCADALoginAction<\/code><\/td>\n<td>Abre o di\u00e1logo de login.<\/td>\n<\/tr>\n<tr>\n<td><code>TPascalSCADALogoutAction<\/code><\/td>\n<td>Faz logout. S\u00f3 fica habilitada com algu\u00e9m logado.<\/td>\n<\/tr>\n<tr>\n<td><code>TPascalSCADALogin_LogoutAction<\/code><\/td>\n<td>As duas em um bot\u00e3o s\u00f3: <code>WithoutUserLoggedInCaption<\/code>\/<code>Hint<\/code>\/<code>ImageIndex<\/code> quando ningu\u00e9m est\u00e1 logado e <code>WithUserLoggedInCaption<\/code>\/<code>Hint<\/code>\/<code>ImageIndex<\/code> quando h\u00e1 usu\u00e1rio \u2014 o bot\u00e3o troca de texto e \u00edcone sozinho.<\/td>\n<\/tr>\n<tr>\n<td><code>TPascalSCADAManageUsersAction<\/code><\/td>\n<td>Abre a tela de gerenciamento de usu\u00e1rios (<code>Manage<\/code>).<\/td>\n<\/tr>\n<tr>\n<td><code>TPascalSCADASecureAction<\/code><\/td>\n<td>Uma action comum com <code>SecurityCode<\/code>: use-a em qualquer bot\u00e3o ou menu que deva exigir permiss\u00e3o. Com <code>DisableIfNotAuthorized = True<\/code> (padr\u00e3o) ela fica desabilitada para quem n\u00e3o tem o c\u00f3digo; com <code>False<\/code> ela fica habilitada, mas <code>Execute<\/code> n\u00e3o roda o <code>OnExecute<\/code> sem permiss\u00e3o.<\/td>\n<\/tr>\n<tr>\n<td><code>TPascalSCADACheckSpecialTokenAction<\/code><\/td>\n<td><strong>Assinatura de supervisor<\/strong>: ao executar, se o usu\u00e1rio logado n\u00e3o tiver o <code>SecurityCode<\/code> (ou sempre, com <code>RequireLoginAlways = True<\/code>), abre um di\u00e1logo pedindo usu\u00e1rio e senha de <strong>outro<\/strong> usu\u00e1rio que tenha o c\u00f3digo. S\u00f3 ent\u00e3o dispara <code>OnExecute<\/code>; o login de quem autorizou fica em <code>AuthorizedBy<\/code> para voc\u00ea registrar no log. O usu\u00e1rio logado n\u00e3o muda.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><code>Hint<\/code> da action \u00e9 mostrado no di\u00e1logo de assinatura como explica\u00e7\u00e3o do que est\u00e1 sendo autorizado.<\/p>\n<h5 id=\"rfid\">Login por cart\u00e3o RFID<\/h5>\n<p>Al\u00e9m de usu\u00e1rio e senha, o di\u00e1logo de login aceita um <strong>cart\u00e3o RFID<\/strong>: o operador aproxima o cart\u00e3o e entra, sem digitar nada. Isso \u00e9 feito em duas partes \u2014 um componente leitor ligado \u00e0 propriedade <strong><code>ChipCardReader<\/code><\/strong> do gerenciador de usu\u00e1rios, e o evento que valida o c\u00f3digo do cart\u00e3o.<\/p>\n<p>O leitor pronto \u00e9 o <code>TSycRFIDReader<\/code>, do pacote <strong><code>pascalscada_externallibs_hmi<\/code><\/strong> (paleta <em>PascalSCADA User Management<\/em>), para os leitores USB <strong>Sycreader<\/strong> de 125 kHz\/13,56 MHz \u2014 os leitores baratos que se apresentam ao sistema como um teclado e &#8220;digitam&#8221; o n\u00famero do cart\u00e3o. Como o leitor \u00e9 lido pela <code>libhidapi<\/code> direto do dispositivo HID, ele <strong>n\u00e3o<\/strong> digita mais no sistema: o c\u00f3digo vai s\u00f3 para o PascalSCADA. Antes de usar:<\/p>\n<ol>\n<li><strong>S\u00f3 Linux e FreeBSD<\/strong> (macOS deve funcionar, n\u00e3o foi testado). N\u00e3o funciona no Windows \u2014 o pacote nem deve ser instalado l\u00e1.<\/li>\n<li>Instale a biblioteca: <code>sudo apt install libhidapi-dev<\/code> (Debian\/Ubuntu\/Mint). O pacote linka com <code>hidapi-libusb<\/code>; sem ela o Lazarus n\u00e3o sobe depois de instalar o pacote.<\/li>\n<li>Copie <code>src\/external_libs_hmi\/99-zzzrfid.rules<\/code> para <code>\/etc\/udev\/rules.d\/<\/code> e reconecte o leitor. A regra desliga o leitor do driver de teclado (<code>usbhid<\/code>) e d\u00e1 permiss\u00e3o de acesso ao dispositivo para qualquer usu\u00e1rio. Sem ela, o leitor continua digitando o c\u00f3digo no campo que tiver o foco e o <code>TSycRFIDReader<\/code> n\u00e3o consegue abri-lo.<\/li>\n<li>Instale o pacote <code>pascalscada_externallibs_hmi.lpk<\/code> no Lazarus.<\/li>\n<\/ol>\n<p>Propriedades do <code>TSycRFIDReader<\/code>:<\/p>\n<table>\n<thead>\n<tr>\n<th>Propriedade<\/th>\n<th>Padr\u00e3o<\/th>\n<th>Descri\u00e7\u00e3o<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>VendorID<\/code><\/td>\n<td><code>$FFFF<\/code><\/td>\n<td>VID USB do leitor.<\/td>\n<\/tr>\n<tr>\n<td><code>ProductID<\/code><\/td>\n<td><code>$0035<\/code><\/td>\n<td>PID USB do leitor.<\/td>\n<\/tr>\n<tr>\n<td><code>SerialNumber<\/code><\/td>\n<td>vazio<\/td>\n<td>N\u00famero de s\u00e9rie, para escolher um leitor espec\u00edfico quando h\u00e1 mais de um.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Os valores padr\u00e3o s\u00e3o os do Sycreader <em>SYC ID&amp;IC USB Reader<\/em>; confira os seus com <code>lsusb<\/code>. Outro leitor HID que envie o c\u00f3digo como sequ\u00eancia de teclas seguida de Enter tamb\u00e9m funciona, ajustando VID\/PID e a regra udev.<\/p>\n<p>Para ligar tudo:<\/p>\n<ol>\n<li>Solte um <code>TSycRFIDReader<\/code> no form e aponte <code>ChipCardReader<\/code> do seu gerenciador de usu\u00e1rios para ele.<\/li>\n<li>No <code>TCustomizedUserManagement<\/code>, implemente <strong><code>OnCheckUserChipCard<\/code><\/strong>:<\/li>\n<\/ol>\n<pre class=\"brush: delphi; tab-size: 2; title: ; notranslate\" title=\"\">procedure TForm1.UserManagementCheckUserChipCard(aChipCardCode: UTF8String;\n  var userlogin: UTF8String; var UserID: Integer; var ValidChipCard: Boolean;\n  LoginAction: Boolean);\nbegin\n  \/\/ aChipCardCode \u00e9 o n\u00famero impresso\/gravado no cart\u00e3o, como texto\n  ValidChipCard := Users.FindByCard(aChipCardCode, userlogin, UserID);\nend;<\/pre>\n<p>O fluxo no di\u00e1logo de login passa a ser: se h\u00e1 um cart\u00e3o sobre o leitor, aparece o aviso <em>&#8220;Remova o cart\u00e3o da leitora para continuar&#8221;<\/em>; com o leitor vazio, o di\u00e1logo abre e fica escutando \u2014 o operador pode digitar usu\u00e1rio e senha <strong>ou<\/strong> aproximar o cart\u00e3o. Um cart\u00e3o lido fecha o di\u00e1logo, chama <code>OnCheckUserChipCard<\/code> e, se v\u00e1lido, faz o login com o <code>userlogin<\/code>\/<code>UserID<\/code> devolvidos. A <a href=\"#actions\">assinatura de supervisor<\/a> (<code>TPascalSCADACheckSpecialTokenAction<\/code>) tamb\u00e9m aceita cart\u00e3o.<\/p>\n<p>Para outro tipo de leitor (serial, TCP, NFC), derive de <code>TChipCardReader<\/code> (unit <code>ChipCardReader<\/code>) e implemente <code>InitializeChipCard<\/code>, <code>ChipCardReady<\/code>, <code>IsEmptyChipCard<\/code>, <code>ChipCardRead<\/code> e <code>FinishChipCard<\/code>; o gerenciador de usu\u00e1rios n\u00e3o sabe qual leitor est\u00e1 por tr\u00e1s.<\/p>\n<h5>Passo a passo<\/h5>\n<ol>\n<li>Solte um <code>TCustomizedUserManagement<\/code> no form principal (ou em um datamodule criado antes dos forms). Implemente <code>OnCheckUserAndPass<\/code> e <code>OnCanAccess<\/code>.<\/li>\n<li>Solte um <code>TActionList<\/code>, adicione uma <code>TPascalSCADALogin_LogoutAction<\/code> e ligue-a a um bot\u00e3o da barra de ferramentas.<\/li>\n<li>Nos controles que precisam de prote\u00e7\u00e3o, preencha <code>SecurityCode<\/code> \u2014 o mesmo c\u00f3digo em v\u00e1rios controles \u00e9 o normal (<code>\"operacao\"<\/code> em todos os comandos, <code>\"setpoints\"<\/code> em todos os <code>THMIEdit<\/code> de ajuste).<\/li>\n<li>Em menus e bot\u00f5es comuns, use <code>TPascalSCADASecureAction<\/code> com o c\u00f3digo adequado.<\/li>\n<li>Para opera\u00e7\u00f5es cr\u00edticas (apagar uma receita, for\u00e7ar uma sa\u00edda), use <code>TPascalSCADACheckSpecialTokenAction<\/code> com <code>SecurityCode = \"supervisor\"<\/code>.<\/li>\n<li>Rode: tudo com c\u00f3digo come\u00e7a desabilitado; ap\u00f3s o login com um usu\u00e1rio que tenha os c\u00f3digos, os controles habilitam; o logout desabilita de novo.<\/li>\n<\/ol>\n<p>Em c\u00f3digo, quando precisar:<\/p>\n<pre class=\"brush: delphi; tab-size: 2; title: ; notranslate\" title=\"\">uses ControlSecurityManager;\n\nif GetControlSecurityManager.CanAccess(&#039;receitas&#039;) then\n  CarregarReceita;\n\nGetControlSecurityManager.TryAccess(&#039;manutencao&#039;);  \/\/ exce\u00e7\u00e3o se n\u00e3o puder<\/pre>\n<h5>Exemplos relacionados<\/h5>\n<ul>\n<li><a href=\"https:\/\/github.com\/fluisgirardi\/pascalscada_v0\/tree\/master\/examples\/laz_custom_user_management\"><code>examples\/laz_custom_user_management<\/code><\/a> \u2014 <code>TCustomizedUserManagement<\/code> m\u00ednimo, com tr\u00eas usu\u00e1rios em c\u00f3digo, <code>TPascalSCADASecureAction<\/code> em menus e <code>THMIEdit<\/code> com <code>SecurityCode<\/code>.<\/li>\n<li><a href=\"https:\/\/github.com\/fluisgirardi\/pascalscada_v0\/tree\/master\/examples\/laz_customusermanagement\"><code>examples\/laz_customusermanagement<\/code><\/a> \u2014 implementa\u00e7\u00e3o completa: usu\u00e1rios, grupos e permiss\u00f5es em PostgreSQL, com todas as telas de cadastro.<\/li>\n<li><a href=\"https:\/\/github.com\/fluisgirardi\/pascalscada_v0\/tree\/master\/examples\/laz_security_webserver_WinCC\"><code>examples\/laz_security_webserver_WinCC<\/code><\/a> \u2014 servidor HTTP que exp\u00f5e o <code>TWinCCUserManagement<\/code> para a rede, e o cliente <code>TCentralUserManagement<\/code> na pasta <code>client<\/code>.<\/li>\n<li><a href=\"https:\/\/github.com\/fluisgirardi\/pascalscada_v0\/tree\/master\/examples\/laz_user_management_with_WinCC\"><code>examples\/laz_user_management_with_WinCC<\/code><\/a> \u2014 <code>TWinCCUserManagement<\/code> direto, com <code>AuthorizationList<\/code>.<\/li>\n<\/ul>","protected":false},"excerpt":{"rendered":"<p>Introdu\u00e7\u00e3o O sistema de seguran\u00e7a do PascalSCADA responde a uma pergunta simples: o usu\u00e1rio logado pode mexer neste controle? Ele tem tr\u00eas pe\u00e7as: C\u00f3digos de seguran\u00e7a (SecurityCode) \u2014 textos livres &#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"open","ping_status":"closed","template":"template-fullwidth.php","meta":{"footnotes":""},"class_list":["post-446","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/pages\/446","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/comments?post=446"}],"version-history":[{"count":16,"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/pages\/446\/revisions"}],"predecessor-version":[{"id":1238,"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/pages\/446\/revisions\/1238"}],"wp:attachment":[{"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/media?parent=446"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}