Sie sind auf Seite 1von 18

2010

Carbonum Labs
http://www.carbonum.com
Copyright (c) 2009-2014

[ACCESS CONTROL SERVICE]


Creating an Access Control Service with Port Manager 2010.
ACCESS CONTROL SERVICE

Access Control Service


In this example we will build a basic windows service that interfaces with some card readers to
grant or deny access to the costumer facilities. The card readers use a simple protocol to exchange
messages and commands through a TCP connection.

NOTE: All specific details regarding the hardware in this example are fictional; we will
emulate the hardware using the testing tool.
As we will see, the goal of the system is to perform a validation, making sure the card being read
at a given location has access to that specific location before opening the door.

The protocol
The protocol used by the readers is a simple text based protocol. Each reader has two configurable
TCP ports, TCP port 1 (which defaults to 12000) listens for incoming connections and is used to
send commands to the reader. In the other hand, TCP port 2 (which defaults to 12100) is used by
the reader to push messages into an external system (in this case our service).

Each message starts with the string "MSG.", followed by either of the tree possible message types:

• Out
• In
• Read

After the message type comes the ReaderID which is always a six digit number (each reader has a
unique ID within the whole organization). Also each message can include other data that gives the
system the information required to perform actions or fully interpret an event. Finally all messages
need to be terminated with the CR/LF characters.

The commands supported by each reader are the following:

MSG.Out.<ReaderID>.<OUTPUT#><CR><LF>

This command instructs the reader to send a signal through the specified digital output.
Each reader has two digital outputs. Output 00 is wired to open its corresponding door,
while output 01 is wired to turn a red light and a buzzer on for a short duration.

Examples:

MSG.Out.001221.01\r\n
MSG.Out.001221.00\r\n

Page 2
ACCESS CONTROL SERVICE

MSG.In.<ReaderID>.<INPUT#><CR><LF>

This message is sent from the reader to the service whenever there is an incoming digital
signal in the given input pin, there's two possible input pins, pin 00 and 01. In this example
inputs are not taken into account.

Examples:

MSG.In.001221.01\r\n
MSG.In.001221.00\r\n

MSG.Read.<ReaderID>.<CardID><CR><LF>

This message is sent from the reader to the service whenever a card is read. The message
includes the ID of the card being read, which is composed by eight digits (this is granted to
be a unique ID within the whole organization).

Examples:

MSG.Read.001221.00034690\r\n
MSG.Read.001221.00034704\r\n

Command Acknowledge

Whenever the system receives a message from the reader, it needs to reply with an acknowledge
message which has the following format:

ACK.<ReaderID>\r\n

Also when the application sends a command to the reader, the reader will answer with an
Acknowledge in the same format.

Acknowledge messages are always sent through the port the original message was received. For
instance if you send a command you can expect the acknowledge in the command port. Likewise,
if the reader pushes a message into the system using the message port, the reader will wait for the
acknowledge in that same port.

Reader Configuration

In case you are wondering, each reader has a programming interface (web based) which allows to
configure things like it's ID, administrative password, IP address (both the reader's IP and the IP of
the system that will handle the reader’s messages), the TCP ports being used for pushing messages
and receiving commands, etc. This configuration needs to be carried out before installing the
hardware.

Page 3
ACCESS CONTROL SERVICE

The application
The general idea in this application is to associate each reader ID to specific locations within the
organization, for instance the entrance to the parking lot, entrance to the main building or
entrance to the site, just to mention some examples. Also the application will need to associate
each employee with a CardID. Finally there's the need to have a permission catalog indicating
which employees (Card IDs) have access to which locations (Reader IDs).

In order to avoid getting this example more complex than need be, we are going to avoid all the
details of the backend and the underlying administrative functions like reader catalog, cards,
locations, employee and permission catalogs; since those details are outside the scope of the
PortManager component our validation function will simply generate a random number in order
to determine if a CardID has access to any given location (ReaderID):

private bool HasAccess(string readerid, string cardid)


{
Random rnd = new Random();
return (rnd.Next(0, 10) > 3);
}

To start building the application we need to create a standard windows service project, and add
the PortManager component to the service design view (Figure 1). Next you can right click the
component and select the “Port Configuration” option from the contextual menu. In this
configuration form, create a Tcp Server Port and name it something like "InputPort" (Figure 2).
This port will handle all the incoming messages from all the readers in the organization.

Page 4
ACCESS CONTROL SERVICE

Figure 1: Design view of the windows service.

Page 5
ACCESS CONTROL SERVICE

Figure 2: Create a server port called “InputPort”.

Make sure you add the following message delimiter: Start = MSG., End = \r\n
as shown in figure 2.

After that, we will need to add as many client ports as readers we have in the organization. Since
there is no way to know this information before hand, we will add these ports at runtime when
the service starts. These ports will be named following a specific convention:
“Reader<ReaderID>".

In this way sending messages through the command ports will be a simple matter of accessing
each port in the Ports collection by its name.

Also, since we said we would avoid the details of the backend, the list of the reader IDs in this
example will be loaded from a file instead of a database, see code below:

private void CreateCommandPorts()


{
ClientPort p;
string readerid, ip;
int port;
string[] lines = File.ReadAllLines("readers.txt");
foreach (string line in lines)
{
string[] info = line.Split(new char[] { ',' });
readerid = info[0].Trim();
ip = info[1].Trim();
port = Convert.ToInt32(info[2].Trim());
p = new ClientPort("Reader" + readerid,
InterfaceType.Tcp, ip, port);
portManager.Ports.Add(p);
}
}

NOTE: Make sure each line in the file contains the following information:
ReaderID, IP, TcpPort\r\n

ReaderID must be a six digit number, IP and TcpPort must be any valid IP:Port you intend to
connect while executing the application (You can use either IPv4 or IPv6 addresses).

Finally we need to wire all together, to do that we need to write the OnReceiveMessage event
handler of the Server Port. To create this handler, select the PortManager component in your

Page 6
ACCESS CONTROL SERVICE

design view, right click it, and select the Port Configuration option from the contextual menu that
appears. Once the configuration window is shown, select the server port we created before and
click on its OnReceiveMessage button (Figure 3). This action will create an empty event handler
and associate this event handler to the appropriate component in your designer.

Figure 3. Add the OnReceiveMessage event handler.

After that you can fill up the event handler with the following code:

private void InputPort_OnReceiveMessage(IComPort sender, PortMessageEventArgs e)


{
string[] parsedMsg = e.Message.Split(new char[] { '.', '\r', '\n' },
StringSplitOptions.RemoveEmptyEntries);
string command = parsedMsg[1];
string readerid = parsedMsg[2];
//Send an acknowledge thru the same cannection we received this message
sender.SendMessage(e.Connection, "ACK_" + readerid + "\r\n");
//Only handle "Read" messages...
if (command.ToLower() == "read")
{
string cardid = parsedMsg[3];
if (HasAccess(readerid, cardid))
{
if (portManager.Ports.Contains("Reader" + readerid))
{
//Open door by issuing the corresponding command to the reader
portManager.Ports["Reader" + readerid].SendMessage("MSG.Out." +
readerid + ".00\r\n");
}
else
{
//port is not registered in the app...
//handle error maybe by wirting an event to the event log
}
}

Page 7
ACCESS CONTROL SERVICE

else
{
if (portManager.Ports.Contains("Reader" + readerid))
{
//Sound buzzer by issuing the corresponding command...
portManager.Ports["Reader" + readerid].SendMessage("MSG.Out." +
readerid + ".01\r\n");
}
else
{
//port is not registered in the app...
//handle error maybe by wirting an event to the event log
}
}
}
}

Notice that when we receive a message in the server port we send an acknowledge to the
corresponding active connection, and then we process the rest of the transaction. This is
important because the reader expects this acknowledge before receiving any other command.

We don't really have to handle the incoming acknowledges the reader sends us after issuing a
command, we are ignoring this messages simply by not adding the message delimiter for the
acknowledge message (ACK., \r\n) and not defining any handlers for those ports. The Port
Manager will automatically discard any message that is non conformant to the list of delimiters or
if there is no handler to any of its “Receive” events.

Note: In a previous example (chat example) we managed messages without delimiters using the
OnReceiveData event handler. Refer to that example if message delimiters are not usable in your
application. Also note that you should avoid having many delimiters in a single port, there may be
performance penalties that become more noticeable the more delimiters you add to any given
port.

Now in order to facilitate the testing of our service we are going to build a simple testing harness
to execute as a windows application or as a windows service based on a defined symbol. To do
that we need to modify our Program.cs source file as follows:

#define RunAsWindowsApp

using System.Collections.Generic;
using System.ServiceProcess;
using System.Text;
using System.Windows.Forms;

namespace Acs
{
static class Program
{
/// <summary>
/// The main entry point for the application.

Page 8
ACCESS CONTROL SERVICE

/// </summary>
static void Main()
{
#if RunAsWindowsApp
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new WinForm());
#else
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new WinService() };
ServiceBase.Run(ServicesToRun);
#endif
}
}
}

Also we need to add a new Windows Form called WinForm with the layout shown in figure 4:

Figure 4: Testing windows form.

And add the following code in this form:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Acs
{
public partial class WinForm : Form
{
WinService service;

public WinForm()
{
InitializeComponent();
service = new WinService();
}

Page 9
ACCESS CONTROL SERVICE

private void btnStart_Click(object sender, EventArgs e)


{
btnStart.Enabled = false;
service.Start();
btnStop.Enabled = true;
lStatus.Text = "Started...";
}

private void btnStop_Click(object sender, EventArgs e)


{
btnStop.Enabled = false;
service.Stop();
btnStart.Enabled = true;
lStatus.Text = "Stoped...";
}
}
}

NOTE: The complete code for this example can be found in the examples folder of your installation
route (look for the AccessControlService folder).

Testing the service.


Before we test the service we need to perform some configuration of our PortManager
component. In the design view, select the portManager object and show its properties. We need
to set the MonitoringEnabled property to true, the MonitoringPassword property to anything you
like (in this example we used the word “password”), and the MonitoringPort to any available Tcp
port you like, by default we use the tcp port 35100. See Figure 5.

Page 10
ACCESS CONTROL SERVICE

Figure 5. Final adjustments before testing.

Once this changes are done save and rebuild your solution. Remember you can change the values
in these properties at any time, even run time; however if you do so at run time when your service
is already running you will need to call the Stop method and then Start in order for those changes
to take effect.

Next thing to do is run the application and click the start button. At this point you might get a
warning from your firewall or antivirus software since we are starting several tcp listeners (one for
your InputPort, and one for your remote monitoring port). If you want to avoid these messages,
configure your firewall before hand to allow access to the corresponding tcp ports. At any rate,
make sure you allow access to the requested resources; otherwise all inbound/outbound traffic
will be blocked.

To verify our service is up and running we can start the PortMonitor application installed alongside
your component. There should be a shortcut in your programs menu or you can browse to your
installation folder and execute It manually (see figure 6).

Page 11
ACCESS CONTROL SERVICE

Figure 6. Execute the PortMonitor application.


In the Port Monitor, select the “Connection/Connect” option and fill up the required information
according to the values you configured in the MonitoringPassword and MonitoringPort properties
of the portManager component (see figure 7).

Figure 7. Connect the PortMonitor to the Access Control Service.

Click ok to connect to the service, you should see something like what is shown in figure 8:

Page 12
ACCESS CONTROL SERVICE

Figure 8. Now we are connected to the service and remotely monitoring port activity.

In order to further test our new service we need to emulate one (or more) of our readers. Take for
instance “Reader001201”. Our service is trying to connect to the “localhost:23000” IP end point,
which represents the command port for this reader, but currently there is nothing there (since the
readers are not installed). The command port of the reader can be emulated by creating a new Tcp
Server test tab, to do so, select the Testing/Tcp Server option from the main menu of the Port
Monitor application. The port used for this reader is 23000, so fill up this information and click the
start button (see figure 9).

Again you might get a warning from your antivirus/firewall software asking you to grant access to
this port. Make sure you allow the application to open the corresponding listener, otherwise this
port will be blocked and the example won’t work.

After a few seconds, a new active connection should become available in the list to the left (as
shown in figure 9), this connection comes from the Access Control Service. Remember this port is
used to send commands to the reader, so there is nothing we can do with this port right now.

Page 13
ACCESS CONTROL SERVICE

Figure 9. Create and start a new TcpServer to emulate the command port of a reader.

The next step would be to emulate the message port of the reader. Remember the reader after
being configured and installed will try to connect to our service, and that our service is listening for
incoming connections in the tcp port 12100. So in order to emulate the message port, we can
create a new tab using the “Testing/Tcp Client” option of the main menu in the Port monitor
application. We need to input “localhost” in the IP field and “12100” in the port field, and then
click connect (see figure 10).

Page 14
ACCESS CONTROL SERVICE

Figure 10. Add a new Tcp Client tab to emulate the message port of a reader.

After connecting successfully, we can go to our “Port Monitor” tab and see we are indeed
connected to the InputPort (see figure 11).

Page 15
ACCESS CONTROL SERVICE

Figure 11. As soon as we connect to our listener, we can see a new active connection.

In the Port Monitor, select the Tcp Client tab again and input the following message:

MSG.Read.001201.00034690\r\n

The reader id must be 001201, otherwise the commands will not be directed to the right port. The
cardid does not matter since our HasAccess function will not validate this information any way.
Click the Send button and you will receive the corresponding Acknowledge, also you will see in the
command port of the reader (our Tcp Server – 23000 tab) the command that was issued to either
open the door or sound the buzzer (see Figures 12 and 13).

Page 16
ACCESS CONTROL SERVICE

Figure 12. After sending a valid message, the service answers with an acknowledge.

Page 17
ACCESS CONTROL SERVICE

Figure 13. You can also see that the service sent a command to the reader’s command port, in this
case the random function determined that the door should remain closed and the buzzer and
semaphore should be turned on.

You can keep testing the application in this way, try creating new tabs to emulate the remaining
readers. Send invalid messages, test disconnections, etc. However, take into account that
exception handling goes beyond the scope of this article, if you start disconnecting ports you will
start getting unhandled exceptions in your Access Control Service since we did not include any
exception handling there. That’s a problem you can take care of easily, so feel free to experiment.

Page 18

Das könnte Ihnen auch gefallen