Tuesday 22 May 2018

basic application monitor


apparently monitoring is important.

As a developer I like to reinvent the wheel so here's the basic application monitor.

A windows service runs a heap of monitors based on 3 types; website monitor checks for a good http response code, db monitor runs a small select query, healthchecker requests some json from the app (this is a custom thing we do on the apps so the app can report how healthy it is). The service then outputs it all into a big json file on the filesystem. The website then reads the json and displays it.

There's no notifications, there's no history, there's no interface to add more monitors (you have to add via json configuration). The goal is to keep it as simple as possible and be a snapshot of what is happening right now.









garfield & 404 pages

have I put these here already? who knows. Dear Garfield boss, please don't sue me.



nz geohunter - game based on googlemaps api

A game based on googlemaps api.



The difficulty increases when you get three right in a row and decreases when you get one wrong. Harder = smaller population centers, easier = cities.

google maps - how to get co-ordinates for drawing buildings

This will let you draw shapes on the satellite picture and then output the lat lng coordinates of each point of the line. Those co-ordinates can then be saved and used by something else to draw shapes on maps.

This needs the drawing library! Make sure it is on the end of the link to the google maps api e.g. <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=drawing">

Open up the dev tools and after drawing a rectangle, set of lines or a point it should output the co-ordinates to the console.

<body>
    <div id="map"></div>
    <script>

        function initMap() {
            var uluru = {
                lat: -39.072474,
                lng: 174.055992
            };
            var map = new google.maps.Map(document.getElementById('map'), {
                center: uluru,
                zoom: 17,
                mapTypeId: 'satellite'
            });
           
            // this creates the base options for the drawings (polygons/buildings)
            var drawingManager = new google.maps.drawing.DrawingManager({
                drawingMode: google.maps.drawing.OverlayType.MARKER,
                drawingControl: true,
                drawingControlOptions: {
                    position: google.maps.ControlPosition.TOP_CENTER,
                    drawingModes: ['marker', 'circle', 'polygon', 'polyline', 'rectangle']
                },
                markerOptions: {
                    icon: 'https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png'
                },
                rectangleOptions: {
                    fillOpacity: 0
                },
                circleOptions: {
                    //fillColor: '#ffff00',
                    fillOpacity: 1,
                    strokeWeight: 5,
                    clickable: false,
                    editable: true,
                    zIndex: 1
                }
            });
            drawingManager.setMap(map);

            // this just outputs the coordinates once a polygon is drawn on the screen
            google.maps.event.addListener(drawingManager, 'overlaycomplete', function (event) {
                console.log(event.type);
                if (event.type == 'rectangle') {
                    var ne = event.overlay.getBounds().getNorthEast();
                    var sw = event.overlay.getBounds().getSouthWest();

                    var bounds = "ne: " + ne.lat() + ", sw: " + sw.lat() + ", ne: " + ne.lng() + ", sw: " + sw.lng();
                    console.log(bounds);
                }
                if (event.type == 'polyline') {
                    //var radius = event.overlay.getRadius();
                    var coordinatesArray = event.overlay.getPath().getArray();
                    var allCoordinates = "";
                    for (var i = 0; i < coordinatesArray.length; i++) {
                        //console.log("new google.maps.LatLng(" + coordinatesArray[i].lat() + ", " + coordinatesArray[i].lng() + "),");
                        var coord = "{lat: " + coordinatesArray[i].lat() + ", lng: " + coordinatesArray[i].lng() + "},";
                        allCoordinates = allCoordinates + coord;
                    }
                    console.log(allCoordinates);
                }
                if (event.type == 'marker') {
                    var lat = event.overlay.getPosition().lat();
                    var lng = event.overlay.getPosition().lng();
                    var coord = "{lat: " + lat + ", lng: " + lng + "},";
                    console.log(coord);
                }
               
            });
        }
    </script>
    <script src="https://maps.googleapis.com/maps/api/js?key=[your key here]&libraries=drawing&callback=initMap" async defer></script>
</body>

wow. After you've got the coords and drawn some polygons based off them you can edit the lines by setting editable to true and adding a listener to the path you've created and output the new co-ordinates. This allows for fine tuning of the points of the polygon.

                // this is just used when editing the buildings - will output the new paths
                buildingDefinition.getPath().addListener('set_at', function(index, event) {
                    var coordinatesArray = this.getArray();
                    var allCoordinates = "";
                    for (var i = 0; i < coordinatesArray.length; i++) {
                        var coord = "{lat: " + coordinatesArray[i].lat() + ", lng: " + coordinatesArray[i].lng() + "},";
                        allCoordinates = allCoordinates + coord;
                    }
                    console.log(allCoordinates);
                });



HTML map of the hospital, using Google maps. This has a custom overlay (background) which shows the roads internal to the hospital. Different types of parking is hightlighed and bus stops and entrances to the hospital are shown. A list of services can be clicked on which will highlight the building they are in and center the map on that location.



Clicking a building will show details:




Doing whatever:



'Ambulance house' and 'Helicopter house' may or may not be the offical names of the buildings.

Building co-ordinates are stored in this format in sepearte js files:
var buildings = [];
var ict = {
    title: "ICT Services",
    key: "itServices",
    titleCoordinates: { lat: -39.0747770079001, lng: 174.0569919347763 },
    description: "Applications, Business Intellignence, Reporting and Development",
    coordinates: [{
        lat: -39.07470621091739,
        lng: 174.05701607465744
    }, {
        lat: -39.07475202073784,
        lng: 174.0570965409279
    }, {
        lat: -39.074962329077316,
        lng: 174.05688732862473
    }, {
        lat: -39.07491027261602,
        lng: 174.0568122267723
    }]
}

buildings.push(ict);
etc - add more buildings as required

This mob of co-ordinates is just referenced in the HTML
    <script type="text/javascript" src="buildingPersistance.js"></script>
    <script type="text/javascript" src="otherPersistance.js"></script>

Ok.
You have to sign up with google api and get a map key. Cram the script tag in your html:

    <script src="https://maps.googleapis.com/maps/api/js?key=[your key here]"></script>

Hook up an initialisation method to run when the maps stuff is loaded

        google.maps.event.addDomListener(window, 'load', initMap);

You need an initialisation method

        function initMap() {
            var uluru = {
                lat: -39.072474,
                lng: 174.055992
            };
            map = new google.maps.Map(document.getElementById('map'), {
                center: uluru,
                zoom: 17,
                styles: [{
                    featureType: 'poi',
                    stylers: [{
                        visibility: 'off'
                    }]
                }, {
                    featureType: 'transit',
                    elementType: 'labels.icon',
                    stylers: [{
                        visibility: 'off'
                    }]
                }]
            });

            // draw any buildings we have
            drawBuildings(map, buildings);
etc ...

When creating the map object the center will set the start location of the map
Styles defines what objects you want to show on the map (a heap of stuff shows by default)

Draw the things you want

        function drawBuildings(map, buildings) {
            for (var i = 0; i < buildings.length; i++) {
                var building = buildings[i];
                var buildingCoords = [];
                for (var c = 0; c < building.coordinates.length; c++) {
                    var coordinate = building.coordinates[c];
                    buildingCoords.push(new google.maps.LatLng(coordinate.lat, coordinate.lng));
                }
                var strokeColor = building.strokeColor ? building.strokeColor : '#000';
                var fillColor = building.fillColor ? building.fillColor : '#000';
                var buildingDefinition = new google.maps.Polygon({
                    paths: buildingCoords,
                    draggable: false,
                    editable: false,
                    strokeColor: strokeColor,
                    strokeOpacity: 0.8,
                    strokeWeight: 2,
                    fillColor: fillColor,
                    fillOpacity: 0.35,
                    originalStrokeColor: strokeColor,
                    originalFillColor: fillColor,
                    key: building.key,
                    description: building.description,
                    title: building.title
                });
                buildingDefinition.setMap(map);


Adding a popup for a building

                // build the popup information if we have a description for this building
                if (buildingDefinition.description) {
                    buildingDefinition.addListener('click', function(event) {
                        var contentString = '<div class="info-list-title">' + this.title + "</div>" + this.description;
                        infowindow.setContent(contentString);
                        infowindow.setPosition(event.latLng);
                        infowindow.open(map);
                    });
                }

Creating a label for a building (using maplabel-compiled.js from ... somewhere?)

                // create a label for this building
                if (building.title && building.titleCoordinates) {
                    var mapLabel = new MapLabel({
                        text: building.title,
                        position: new google.maps.LatLng(building.titleCoordinates.lat, building.titleCoordinates.lng),
                        map: map,
                        fontSize: 9
                    });
                    mapLabels.push(mapLabel);
                }







Async example


Here's a clue. The parent method needs the async keyword, the methods that are running asynchronously (called from the parent) don't. Weird.


A method that runs a heap of methods asynchronously. This guy needs the async keyword in the method signature:


        public async Task<List<RunResult>> RunMonitors(List<IMonitor> monitors)
        {
            var results = new List<RunResult>();
            try
            {
                var tasks = new List<Task<RunResult>>();
                foreach (var monitor in monitors)
                {
                    _logger.Debug($"About to run monitor {monitor.Name()}");

                    var newTask = Task.Run(() => monitor.Run());

                    tasks.Add(newTask);
                }

                _logger.Debug($"About to wait for monitor to complete; started {tasks.Count} tasks");
                await Task.WhenAll(tasks);

                _logger.Debug($"Monitors have completed; going to collect results");
                foreach (var task in tasks)
                {
                    results.Add(task.Result);
                }
            } catch (Exception ex)
            {
                _logger.Error($"Error while running monitors: {ex}");
            }
            return results;
        }

This bit starts the tasks running:
                foreach (var monitor in monitors)
                {
                    _logger.Debug($"About to run monitor {monitor.Name()}");

                    var newTask = Task.Run(() => monitor.Run());

                    tasks.Add(newTask);
                }

This bit causes everything to wait until all the tasks have completed:
                await Task.WhenAll(tasks);

This bit gets the results from each of the tasks
                _logger.Debug($"Monitors have completed; going to collect results");
                foreach (var task in tasks)
                {
                    results.Add(task.Result);
                }



Example of monitor run method with return type of Task<[an object]>. This guy doesn't need the async keyword (even tho it is running asynchronously with all its buddies).

        public Task<RunResult> Run()
        {
            DateTime started = DateTime.Now;
            var result = new RunResult
            {
                MonitorType = _type,
                Name = _name,
                RequestedAt = started
            };
            try
            {
                HttpResponseMessage response = _client.GetAsync(_url).Result;

                string responseString = response.Content.ReadAsStringAsync().Result;
                result.RunStatus = response.IsSuccessStatusCode ? Lookups.StatusLookup.Success : Lookups.StatusLookup.Failure;
                result.Response = response.IsSuccessStatusCode ? null : responseString;
                _logger.Info($"WebSiteMonitor: {_name} got a IsSuccessStatusCode of {response.IsSuccessStatusCode} and response of: {result.Response}");

            } catch(Exception ex)
            {
                _logger.Error($"WebSiteMonitor: {_name} caused an exception of {ex}");
                result.RunStatus = Lookups.StatusLookup.Failure;
                result.Response = ex.Message;
            }
            result.CompletedAt = DateTime.Now;
            return Task.FromResult(result);
        }


Example of how the parent method is called (just normal):

                var monitors = monitorManager.GetMonitors(config);
                var results = engine.RunMonitors(monitors).Result; // this method has the async keyword
                monitorManager.SaveRunResults(results);

Wednesday 9 May 2018

Adding FormsAuthentication to validate against AD to a MVC website with no auth


You could use the new identity stuff. lol.



Update web.config to say it's formsauth, stick a path to an action that will deal with the login

  <system.web>
    <authentication mode="Forms">
      <forms loginUrl="~/Account/LogOn"/>
    </authentication>
    <authorization>
      <deny users="?" />
    </authorization>

Create a controller for the above path

    public class AccountController : Controller
    {
        public ActionResult LogOn()
        {
            return View();
        }

Create a corresponding view and a model to hold logon details

viewmodel:

    public class LogOnModel
    {
        [Required]
        [Display(Name = "User name")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }

    }

view:

@model Mixr.Web.Models.LogOnModel
@{
    ViewBag.Title = "Log On";
}


<div class="logonPage">
    <div class="logonContainer">
        <h2>Log On</h2>
        <p>

        </p>

        <div class="logonForm">
            @Html.ValidationSummary(true, "Login was unsuccessful. Please correct the errors and try again.")

            <form action="~/Account/LogOn" method="post">
                <fieldset>
                    <legend>Please enter your user name and password:</legend>

                    <div class="labelAndField">
                        <div class="editor-label">
                            @Html.LabelFor(m => m.UserName)
                        </div>
                        <div class="editor-field">
                            @Html.TextBoxFor(m => m.UserName)
                            @Html.ValidationMessageFor(m => m.UserName)
                        </div>
                    </div>
                    <div class="labelAndField">
                        <div class="editor-label">
                            @Html.LabelFor(m => m.Password)
                        </div>
                        <div class="editor-field">
                            @Html.PasswordFor(m => m.Password)
                            @Html.ValidationMessageFor(m => m.Password)
                        </div>
                    </div>

                    <input class="logonButton" type="submit" value="Log On" />
                </fieldset>
            </form>

        </div>
    </div>
</div>


go back to your controller and create a method to recieve the post:

        [HttpPost]
        public ActionResult LogOn(LogOnModel model)
        {
            if (ModelState.IsValid)
            {
                if (Membership.ValidateUser(model.UserName, model.Password))
                {
                    FormsAuthentication.SetAuthCookie(model.UserName, false);
                    return RedirectToAction("Index", "Home");
                }
                ModelState.AddModelError("", "The user name or password provided is incorrect.");
            }

            return View(model);
        }

wow. great. How does this know where your AD is?
Go to the web.config and in system.web add a AD membership provider:

    <membership defaultProvider="AspNetActiveDirectoryMembershipProvider">
      <providers>
        <clear />
        <add name="AspNetActiveDirectoryMembershipProvider" connectionStringName="ADService" type="System.Web.Security.ActiveDirectoryMembershipProvider,System.Web, Version=4.0.0.0, Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a" attributeMapUsername="sAMAccountName" />
      </providers>
    </membership>


The above expects an entry in your connectionStrings section:

  <connectionStrings>
        <add name="ADService" connectionString="LDAP://wowgreat.co.nz:389" />
  </connectionStrings>


if you're feeling fancy give the user some way to logout

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();

            return RedirectToAction("Index", "Home");
        }

Great. now they're authenticated. now you have to do some authorization.