diff --git a/GpioController/Authorization/ConditionalJwtAuthorization.cs b/GpioController/Authorization/ConditionalJwtAuthorization.cs new file mode 100644 index 0000000..6aa1e1a --- /dev/null +++ b/GpioController/Authorization/ConditionalJwtAuthorization.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Authorization; + +namespace GpioController.Authorization; + +public class ConditionalAuthRequirement : IAuthorizationRequirement { } + +public class ConditionalJwtAuthorization(IConfiguration config) : AuthorizationHandler +{ + protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, + ConditionalAuthRequirement requirement) + { + var requireAuth = config.GetValue("Authorization:Enabled"); + + if (!requireAuth || (context.User.Identity?.IsAuthenticated ?? false)) + { + context.Succeed(requirement); + } + + return Task.CompletedTask; + } +} + + + diff --git a/GpioController/Commands/GpioSetCommand.cs b/GpioController/Commands/GpioSetCommand.cs index 830dc3b..8a2ed08 100644 --- a/GpioController/Commands/GpioSetCommand.cs +++ b/GpioController/Commands/GpioSetCommand.cs @@ -29,6 +29,8 @@ protected override void RunOptionalPostCommandLogic(GpioSetRequest request, Gpio for (var timesRepeated = 1; timesRepeated < request.Options?.RepeatTimes*2; timesRepeated++) { + if (request.CancellationToken.IsCancellationRequested) + break; Thread.Sleep(sleepTime); RunOpposite(request); } diff --git a/GpioController/Commands/Request/GpioSetRequest.cs b/GpioController/Commands/Request/GpioSetRequest.cs index e8d7397..b180b66 100644 --- a/GpioController/Commands/Request/GpioSetRequest.cs +++ b/GpioController/Commands/Request/GpioSetRequest.cs @@ -6,6 +6,7 @@ public class GpioSetRequest : Request public required IEnumerable Gpios { get; set; } public required string State { get; set; } public OptionalSettings? Options { get; set; } + public CancellationToken CancellationToken { get; set; } } public class OptionalSettings diff --git a/GpioController/Commands/Results/GpioInfoResult.cs b/GpioController/Commands/Results/GpioInfoResult.cs index 499caeb..c81e759 100644 --- a/GpioController/Commands/Results/GpioInfoResult.cs +++ b/GpioController/Commands/Results/GpioInfoResult.cs @@ -4,5 +4,5 @@ namespace GpioController.Commands.Results; public class GpioInfoResult : Result { - public IEnumerable Result { get; set; } + public required IEnumerable Result { get; set; } } \ No newline at end of file diff --git a/GpioController/Commands/Results/GpioSetResult.cs b/GpioController/Commands/Results/GpioSetResult.cs index c3bb925..225b1e1 100644 --- a/GpioController/Commands/Results/GpioSetResult.cs +++ b/GpioController/Commands/Results/GpioSetResult.cs @@ -2,5 +2,5 @@ namespace GpioController.Commands.Results; public class GpioSetResult : Result { - public string Result { get; set; } + public required string Result { get; set; } } \ No newline at end of file diff --git a/GpioController/Controllers/GpioController.cs b/GpioController/Controllers/GpioController.cs index 1dcab33..d16d902 100644 --- a/GpioController/Controllers/GpioController.cs +++ b/GpioController/Controllers/GpioController.cs @@ -8,22 +8,29 @@ namespace GpioController.Controllers; [ApiController] [Route("sbc")] +[Authorize(Policy = "ConditionalPolicy")] public class GpioController(IOptions authorizationSettings, IGpioService gpioService) : SecureController(authorizationSettings) { - [AllowAnonymous] [HttpGet] [Route("chipsets/gpios")] public IActionResult Get() { + if (!IsAuthorized()) + return Unauthorized(); + var gpios = gpioService.GetGpios(); - return Ok(gpios); + var filteredResults = gpioService.OrderResultsByFilter(gpios); + + return Ok(filteredResults); } - [AllowAnonymous] [HttpGet] [Route("chipsets/{chipsetId}/gpios/{gpioId}")] public IActionResult GetById([FromRoute] int chipsetId, [FromRoute] int gpioId) { + if (!IsAuthorized()) + return Unauthorized(); + var gpio = gpioService.GetGpioById(chipsetId, gpioId); return Ok(gpio); } diff --git a/GpioController/Controllers/SecureController.cs b/GpioController/Controllers/SecureController.cs index 74f5205..de8608c 100644 --- a/GpioController/Controllers/SecureController.cs +++ b/GpioController/Controllers/SecureController.cs @@ -5,22 +5,26 @@ namespace GpioController.Controllers; -public class SecureController : ControllerBase +public class SecureController(IOptions authorizationSettings) : ControllerBase { - private readonly AuthorizationSettings authorizationSettings; + private readonly AuthorizationSettings authorizationSettings = authorizationSettings.Value; - public SecureController(IOptions authorizationSettings) + protected bool IsAuthorized() { - this.authorizationSettings = authorizationSettings.Value; + return !authorizationSettings.Enabled || IsEmailClaimAuthorized(); } - protected bool IsAuthorized() + private bool IsEmailClaimAuthorized() { + const string validationType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"; + var userIdentity = HttpContext.User.Identity as ClaimsIdentity; + var emailCliam = - userIdentity?.Claims?.FirstOrDefault(claim => claim.Type == authorizationSettings?.ValidationType); + userIdentity?.Claims?.FirstOrDefault(claim => claim.Type == validationType); var currentUserEmail = emailCliam?.Value ?? string.Empty; + var isAuthorized = authorizationSettings?.AuthorizedEmails?.Contains(currentUserEmail) ?? false; return isAuthorized; diff --git a/GpioController/Controllers/StateController.cs b/GpioController/Controllers/StateController.cs index c6087fa..b2b0522 100644 --- a/GpioController/Controllers/StateController.cs +++ b/GpioController/Controllers/StateController.cs @@ -9,29 +9,26 @@ namespace GpioController.Controllers; [ApiController] [Route("sbc")] -public class StateController(IOptions authorizationSettings, IStateService stateService) : SecureController(authorizationSettings) +[Authorize(Policy = "ConditionalPolicy")] +public class StateController(IOptions authorizationSettings, IStateService stateService, ITokenManagementService tokenManagementService) : SecureController(authorizationSettings) { - [AllowAnonymous] - // [Authorize] [HttpGet] [Route("chipsets/{chipsetId}/gpios/{gpioId}/state")] public IActionResult GetById([FromRoute] int chipsetId, [FromRoute] int gpioId) { - // if (!IsAuthorized()) - // return Unauthorized(); + if (!IsAuthorized()) + return Unauthorized(); var result = stateService.GetStateByGpioId(chipsetId, gpioId); return Ok(result); } - [AllowAnonymous] - // [Authorize] [HttpPost] [Route("chipsets/{chipsetId}/gpios/{gpioId}/state/{state}")] public IActionResult UpdateSingleStateById([FromRoute] int chipsetId, [FromRoute] int gpioId, [FromRoute] string state) { - // if (!IsAuthorized()) - // return Unauthorized(); + if (!IsAuthorized()) + return Unauthorized(); stateService.UpdateSingleState(new GpioSetRequest { @@ -43,15 +40,14 @@ public IActionResult UpdateSingleStateById([FromRoute] int chipsetId, [FromRoute return NoContent(); } - [AllowAnonymous] - // [Authorize] [HttpPost] [Route("chipsets/gpios/state")] public IActionResult UpdateMultipleStatesByRequest([FromRoute] int chipsetId, [FromRoute] int gpioId, [FromBody] IEnumerable updateRequests) { - // if (!IsAuthorized()) - // return Unauthorized(); + if (!IsAuthorized()) + return Unauthorized(); + tokenManagementService.CancelAll(); stateService.UpdateMultipleStates(updateRequests); return NoContent(); diff --git a/GpioController/Models/ActiveTask.cs b/GpioController/Models/ActiveTask.cs new file mode 100644 index 0000000..9c21a7f --- /dev/null +++ b/GpioController/Models/ActiveTask.cs @@ -0,0 +1,9 @@ +using GpioController.Commands.Request; + +namespace GpioController.Models; + +public class ActiveTask +{ + public required CancellationTokenSource TokenSource { get; set; } + public required GpioSetRequest ActiveRequest { get; set; } +} \ No newline at end of file diff --git a/GpioController/Models/AuthorizationSettings.cs b/GpioController/Models/AuthorizationSettings.cs index f7084cd..9119519 100644 --- a/GpioController/Models/AuthorizationSettings.cs +++ b/GpioController/Models/AuthorizationSettings.cs @@ -2,6 +2,6 @@ namespace GpioController.Models; public class AuthorizationSettings { - public required string ValidationType { get; set; } + public required bool Enabled { get; set; } public required List AuthorizedEmails { get; set; } } \ No newline at end of file diff --git a/GpioController/Models/FilterSettings.cs b/GpioController/Models/FilterSettings.cs new file mode 100644 index 0000000..e5e44d2 --- /dev/null +++ b/GpioController/Models/FilterSettings.cs @@ -0,0 +1,7 @@ +namespace GpioController.Models; + +public class FilterSettings +{ + public required List AllowOnlyTheseChipsets { get; set; } + public required List AllowOnlyTheseGpios { get; set; } +} \ No newline at end of file diff --git a/GpioController/Models/Gpio.cs b/GpioController/Models/Gpio.cs index dc9310b..2eaff28 100644 --- a/GpioController/Models/Gpio.cs +++ b/GpioController/Models/Gpio.cs @@ -4,5 +4,5 @@ public class Gpio { public int Chipset { get; set; } public int Id { get; set; } - public string Name { get; set; } + public string? Name { get; set; } } \ No newline at end of file diff --git a/GpioController/Program.cs b/GpioController/Program.cs index 0d942ca..383701b 100644 --- a/GpioController/Program.cs +++ b/GpioController/Program.cs @@ -1,3 +1,4 @@ +using GpioController.Authorization; using GpioController.Commands; using GpioController.Commands.Request; using GpioController.Commands.Results; @@ -7,6 +8,7 @@ using GpioController.Parsers; using GpioController.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; namespace GpioController; @@ -19,18 +21,49 @@ public static void Main(string[] args) builder.Services.Configure( builder.Configuration.GetSection("Authorization") ); + + builder.Services.Configure( + builder.Configuration.GetSection("Filters") + ); + + var requireAuth = builder.Configuration.GetValue("Authorization:Enabled"); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient, InfoParser>(); - builder.Services.AddTransient, UpdateParser>(); - builder.Services.AddTransient, ReadParser>(); - builder.Services.AddTransient, GpioInfoCommand>(); - builder.Services.AddTransient, GpioReadCommand>(); - builder.Services.AddTransient, GpioSetCommand>(); - builder.Services.AddSingleton(provider => new CommandFactory(provider)); + if (requireAuth) + { + var authorizedCorsOrigin = builder.Configuration + .GetSection("Authorization:AuthorizedCorsOrigins") + .Get(); + + builder.Services.AddCors(options => + { + options.AddPolicy("AuthorizedCorsPolicy", policy => + { + policy + .WithOrigins(authorizedCorsOrigin ?? []) + .AllowCredentials() + .AllowAnyHeader() + .WithMethods("GET", "POST", "OPTIONS"); + }); + }); + } + else + { + builder.Services.AddCors(options => + { + options.AddPolicy("AuthorizedCorsPolicy", policy => + { + policy + .AllowAnyOrigin() + .AllowAnyHeader() + .WithMethods("GET", "POST", "OPTIONS"); + }); + }); + } + builder.Services.AddAuthorizationBuilder() + .AddPolicy("ConditionalPolicy", policy => + policy.Requirements.Add(new ConditionalAuthRequirement())); + builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { @@ -44,10 +77,24 @@ public static void Main(string[] args) }; }); + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + builder.Services.AddTransient, InfoParser>(); + builder.Services.AddTransient, UpdateParser>(); + builder.Services.AddTransient, ReadParser>(); + builder.Services.AddTransient, GpioInfoCommand>(); + builder.Services.AddTransient, GpioReadCommand>(); + builder.Services.AddTransient, GpioSetCommand>(); + builder.Services.AddSingleton(provider => new CommandFactory(provider)); + builder.Services.AddControllers(); var app = builder.Build(); - + + app.UseCors("AuthorizedCorsPolicy"); app.UseMiddleware(); app.UseAuthorization(); app.UseHttpsRedirection(); diff --git a/GpioController/Services/GpioService.cs b/GpioController/Services/GpioService.cs index b807897..9aa2f4f 100644 --- a/GpioController/Services/GpioService.cs +++ b/GpioController/Services/GpioService.cs @@ -1,23 +1,44 @@ using GpioController.Commands.Request; using GpioController.Commands.Results; using GpioController.Exceptions; -using GpioController.Extensions; using GpioController.Factories; using GpioController.Models; +using Microsoft.Extensions.Options; namespace GpioController.Services; -public class GpioService(ICommandFactory commandFactory) : IGpioService +public class GpioService(ICommandFactory commandFactory, IOptions filterSettings) : IGpioService { public IEnumerable GetGpios() { + var gpioFilter = filterSettings.Value; var command = commandFactory.GetCommand(); var gpios = command.Execute(new GpioInfoRequest()); + var results = gpios?.Result ?? []; if (!gpios?.Result.Any() ?? false) throw new NoGpiosFoundException(); + + if (results.Any() && gpioFilter.AllowOnlyTheseChipsets.Any()) + results = results.Where(gpio => gpioFilter.AllowOnlyTheseChipsets.Contains(gpio.Chipset)); + + if (results.Any() && gpioFilter.AllowOnlyTheseGpios.Any()) + results = results.Where(gpio => gpioFilter.AllowOnlyTheseGpios.Contains(gpio.Id)); - return gpios.Result; + return results; + } + + public IEnumerable OrderResultsByFilter(IEnumerable results) + { + var filter = filterSettings?.Value?.AllowOnlyTheseGpios ?? []; + + if (results.Any() && filter.Any()) + return filter + .Select(id => results.FirstOrDefault(p => p.Id == id)) + .Where(p => p != null) + .ToList(); + + return results; } public Gpio GetGpioById(int chipsetId, int gpioId) diff --git a/GpioController/Services/IGpioService.cs b/GpioController/Services/IGpioService.cs index af29d99..78b898b 100644 --- a/GpioController/Services/IGpioService.cs +++ b/GpioController/Services/IGpioService.cs @@ -7,4 +7,5 @@ public interface IGpioService { IEnumerable GetGpios(); Gpio GetGpioById(int chipsetId, int gpioId); + IEnumerable OrderResultsByFilter(IEnumerable results); } \ No newline at end of file diff --git a/GpioController/Services/ITokenManagementService.cs b/GpioController/Services/ITokenManagementService.cs new file mode 100644 index 0000000..5906c0a --- /dev/null +++ b/GpioController/Services/ITokenManagementService.cs @@ -0,0 +1,9 @@ +using GpioController.Commands.Request; + +namespace GpioController.Services; + +public interface ITokenManagementService +{ + public void CancelAll(); + public CancellationToken CreateToken(GpioSetRequest request); +} \ No newline at end of file diff --git a/GpioController/Services/StateService.cs b/GpioController/Services/StateService.cs index b536456..55174ef 100644 --- a/GpioController/Services/StateService.cs +++ b/GpioController/Services/StateService.cs @@ -7,7 +7,7 @@ namespace GpioController.Services; -public class StateService(ICommandFactory commandFactory, IGpioService gpioService) : IStateService +public class StateService(ICommandFactory commandFactory, IGpioService gpioService, ITokenManagementService tokenManagementService) : IStateService { public GpioReadResult GetStateByGpioId(int chipsetId, int gpioId) { @@ -38,7 +38,8 @@ public void UpdateMultipleStates(IEnumerable updateRequests) new Action(() => { foreach (var request in updateRequests) - { + { + request.CancellationToken = tokenManagementService.CreateToken(request); var command = commandFactory.GetCommand(); command.Execute(request); } diff --git a/GpioController/Services/TokenManagementService.cs b/GpioController/Services/TokenManagementService.cs new file mode 100644 index 0000000..cad6ac8 --- /dev/null +++ b/GpioController/Services/TokenManagementService.cs @@ -0,0 +1,59 @@ +using GpioController.Commands.Request; +using GpioController.Commands.Results; +using GpioController.Factories; +using GpioController.Models; + +namespace GpioController.Services; + +public class TokenManagementService(ICommandFactory commandFactory) : ITokenManagementService +{ + private readonly List activeTokenSources = new(); + private readonly Lock lockingMechanism = new(); + + public CancellationToken CreateToken(GpioSetRequest request) + { + var cts = new CancellationTokenSource(); + + lock (lockingMechanism) + { + activeTokenSources.Add(new ActiveTask + { + TokenSource = cts, + ActiveRequest = request + + }); + + PruneCancelled(); + } + + return cts.Token; + } + + public void CancelAll() + { + lock (lockingMechanism) + { + foreach (var cts in activeTokenSources.Where(cts => !cts.TokenSource.IsCancellationRequested)) + { + cts.TokenSource.Cancel(); + RunOppositeRequestToTurnOffAnyActiveGpios(cts.ActiveRequest); + Thread.Sleep(50); + } + + activeTokenSources.Clear(); + } + } + + private void RunOppositeRequestToTurnOffAnyActiveGpios(GpioSetRequest request) + { + request.State = State.ParseOpposite(request.State).ToString(); + request.Options = null; + var command = commandFactory.GetCommand(); + command.Execute(request); + } + + private void PruneCancelled() + { + activeTokenSources.RemoveAll(cts => cts.TokenSource.IsCancellationRequested); + } +} \ No newline at end of file diff --git a/GpioController/appsettings.json b/GpioController/appsettings.json index 88527c9..67ed923 100644 --- a/GpioController/appsettings.json +++ b/GpioController/appsettings.json @@ -6,17 +6,15 @@ } } }, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*", "Authorization": { - "ValidationType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", + "Enabled": true, "AuthorizedEmails": [ - "your-email@email.com" - ] + "yourEmail@gmail.com" + ], + "AuthorizedCorsOrigins": ["http://yourProductionWebsite:3000", "http://localhost.com:3000"] + }, + "Filters": { + "AllowOnlyTheseChipsets": [], + "AllowOnlyTheseGpios": [] } -} +} \ No newline at end of file diff --git a/GpioControllerTests/Commands/GpioSetCommandTests.cs b/GpioControllerTests/Commands/GpioSetCommandTests.cs index 5724fca..91d121f 100644 --- a/GpioControllerTests/Commands/GpioSetCommandTests.cs +++ b/GpioControllerTests/Commands/GpioSetCommandTests.cs @@ -2,6 +2,7 @@ using GpioController.Commands; using GpioController.Commands.Request; using GpioController.Commands.Results; +using GpioController.Extensions; using GpioController.Parsers; using GpioController.Services; @@ -89,4 +90,37 @@ public void Execute_ShouldRunTerminalCommandCorrectly_WithRequestWithoutAnyOptio A.CallTo(() => terminalService.RunCommand(A._)) .MustHaveHappenedANumberOfTimesMatching(times => times == 1); } + + [Fact] + public void Execute_ShouldExitEarly_WhenTokenIsCancelled() + { + var sut = GetSystemUnderTest(); + var tokenSource = new CancellationTokenSource(); + + var request = new GpioSetRequest + { + Chipset = 1, + Gpios = [81], + State = "Low", + Options = new OptionalSettings + { + Milliseconds = 500, + RepeatTimes = 3 + } + }; + + request.CancellationToken = tokenSource.Token; + + new Action(() => + { + sut.Execute(request); + }).StartOnBackgroundThread(); + + Thread.Sleep(50); + + tokenSource.Cancel(); + + A.CallTo(() => terminalService.RunCommand(A._)) + .MustHaveHappenedANumberOfTimesMatching(times => times == 1); + } } \ No newline at end of file diff --git a/GpioControllerTests/Services/GpioServiceTests.cs b/GpioControllerTests/Services/GpioServiceTests.cs new file mode 100644 index 0000000..8e2d124 --- /dev/null +++ b/GpioControllerTests/Services/GpioServiceTests.cs @@ -0,0 +1,114 @@ +using FakeItEasy; +using FluentAssertions; +using GpioController.Commands; +using GpioController.Commands.Request; +using GpioController.Commands.Results; +using GpioController.Factories; +using GpioController.Models; +using GpioController.Services; +using Microsoft.Extensions.Options; + +namespace GpioControllerTests.Services; + +public class GpioServiceTests +{ + private ICommandFactory commandFactory; + private IOptions filterSettings; + private ICommand command; + + public GpioService GetSystemUnderTest() + { + command = A.Fake>(); + commandFactory = A.Fake(); + filterSettings = A.Fake>(); + + return new GpioService(commandFactory, filterSettings); + } + + [Fact] + public void GetGpios_ShouldReturnResult_WhenFactoryAdCommandFound() + { + var sut = GetSystemUnderTest(); + + A.CallTo(() => filterSettings.Value).Returns(new FilterSettings + { + AllowOnlyTheseChipsets = [], + AllowOnlyTheseGpios = [] + }); + + var expectedResult = new GpioInfoResult + { + Result = new List + { + new() + { + Chipset = 1, + Id = 95, + Name = "Gpio 95" + }, + new() + { + Chipset = 1, + Id = 80, + Name = "Gpio 80" + }, + new() + { + Chipset = 1, + Id = 81, + Name = "Gpio 81" + } + } + }; + + A.CallTo(() => commandFactory.GetCommand()).Returns(command); + + A.CallTo(() => command.Execute(A._)).Returns(expectedResult); + + var results = sut.GetGpios(); + + results.Should().BeEquivalentTo(expectedResult.Result); + + + } + + [Fact] + public void OrderResultsByFilter_ShouldSortResults_WhenFilterIsPresent() + { + var sut = GetSystemUnderTest(); + + A.CallTo(() => filterSettings.Value).Returns(new FilterSettings + { + AllowOnlyTheseChipsets = [1], + AllowOnlyTheseGpios = [81,95,80,79,94,93] + }); + + var data = new List + { + new() + { + Chipset = 1, + Id = 95, + Name = "Gpio 95" + }, + new() + { + Chipset = 1, + Id = 80, + Name = "Gpio 80" + }, + new() + { + Chipset = 1, + Id = 81, + Name = "Gpio 81" + } + }; + + var result = sut.OrderResultsByFilter(data).ToArray(); + + result[0].Id.Should().Be(81); + result[1].Id.Should().Be(95); + result[2].Id.Should().Be(80); + } +} \ No newline at end of file diff --git a/Installation/Install.sh b/Installation/Install.sh index fdd1c60..a6c4414 100644 --- a/Installation/Install.sh +++ b/Installation/Install.sh @@ -3,12 +3,12 @@ ARCHITECTURE=$(uname -m) case $ARCHITECTURE in x86_64) echo "64-bit architecture detected." - URL="https://github.com/SparkyCoder/Gpio_Controller_Api/raw/refs/heads/main/Installation/linux-x64/gpio-controller-api-1.3.deb" + URL="https://github.com/SparkyCoder/Gpio_Controller_Api/raw/refs/heads/main/Installation/linux-x64/gpio-controller-api-1.4.deb" ;; aarch64) echo "ARM 64-bit architecture detected." - URL="https://github.com/SparkyCoder/Gpio_Controller_Api/raw/refs/heads/main/Installation/linux-arm64/gpio-controller-api-1.3.deb" + URL="https://github.com/SparkyCoder/Gpio_Controller_Api/raw/refs/heads/main/Installation/linux-arm64/gpio-controller-api-1.4.deb" ;; *) echo "Unknown architecture: $ARCHITECTURE" @@ -20,6 +20,6 @@ TEMP_DEB="$(mktemp)" && wget -O "$TEMP_DEB" "$URL" && sudo dpkg -i "$TEMP_DEB" && rm -f "$TEMP_DEB" && -cd /opt/gpio-controller-api-1.3 && +cd /opt/gpio-controller-api-1.4 && sudo ./GpioController diff --git a/Installation/linux-arm64/gpio-controller-api-1.4.deb b/Installation/linux-arm64/gpio-controller-api-1.4.deb new file mode 100644 index 0000000..9cb58d1 Binary files /dev/null and b/Installation/linux-arm64/gpio-controller-api-1.4.deb differ diff --git a/Installation/linux-x64/gpio-controller-api-1.4.deb b/Installation/linux-x64/gpio-controller-api-1.4.deb new file mode 100644 index 0000000..8acf789 Binary files /dev/null and b/Installation/linux-x64/gpio-controller-api-1.4.deb differ diff --git a/README.md b/README.md index 917ec05..f53f57a 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ No coding required. Just follow the installation steps @@ -127,7 +127,7 @@ No coding required. Just follow the installation stepsinstallation steps @@ -159,7 +159,7 @@ No coding required. Just follow the installation steps @@ -173,7 +173,7 @@ No coding required. Just follow the installation steps @@ -182,29 +182,36 @@ No coding required. Just follow the installation stepsinstallation steps - ## Settings -To update your API settings, refer to the [AppSettings](https://github.com/SparkyCoder/Gpio_Controller_Api/blob/main/GpioController/appsettings.json) file in your optional installs directory: `/opt/gpio-controller-api-1.3`. +To update your API settings, refer to the [AppSettings](https://github.com/SparkyCoder/Gpio_Controller_Api/blob/main/GpioController/appsettings.json) file in your optional installs directory: `/opt/gpio-controller-api-1.4`. + +#### Kestral: +- `Kestral:Endpoints:Http:Url` - This is the endpoint your API will bind to.
+ By default it's set to 0:0:0:0:3005. Meaning it will bind to http://localhost:3005 and http://your.ip.address:3005. + +#### Authorization: +- `Authorization:Enabled` - allows values "true" or "false". + - False: Allows access to any CORS origins and headers. Recommended only for developement. + - True: Requires a Google JWT to be authorized. It also requires the following authorization settings to be filled. + + +- `Authorization:AuthorizedEmails` - Restricts entry to only the following Google users.
+ - Example value: `["yourEmailHere@gmail.com, anotherEmailHere@gmail.com]` + + + +- `Authorization:AuthorizedCorsOrigins` - Restricts CORS origins. For security purposes only requests with the listed origins are allowed through. + +#### Filters: + +- `Filters:AllowOnlyTheseChipsets` - `GET /sbc/chipsets/gpios` will usually return all Chipsets from your board. Some projects only require a subset of these. Adding IDs here filters results. + - Example: `[1]` + -- Your API defaults to Port 3005. This can be updated in your AppSettings. -- Your IP binds to 0:0:0:0 which means it will be accessible on any IP address associated with your SBC. Use `ifconfig` to see that information. -- If you install the secure version (see below) update the AuthorizedEmails section to include your email. This will give you access to your API. +- `Filters:AllowOnlyTheseGpios` - `GET /sbc/chipsets/gpios` will usually return all GPIOs from your board. Some projects only require a subset of these. Adding IDs here filters results. + - Example: `[91, 92, 81,95,80,79,94,93]` -- Warning!
If you expose your IP and Port to the public (By adding a rule to your router / firewall) it is highly recommended to install the Secure API version which requires a Google JWT Bearer Token for authorization. Without it, anybody can call your API. +Important!
If you expose your IP and Port to the public (By adding a rule to your router / firewall) it is highly recommended to set `Authorization:Enabled` to `true`. Without it, anybody can call your API.
@@ -232,7 +260,7 @@ To update your API settings, refer to the [AppSettings](https://github.com/Spark - [x] add to list available GPIOs - [x] Add endpoint to Update GPIO state - [x] Add endpoint to read GPIO values -- [ ] Add secure endpoints debian package for Google JWT Auth +- [x] Add secure endpoints for Google JWT Auth - [ ] Add additional chipset architectures - [ ] linux-muscl-64 - [ ] linux-arm @@ -247,7 +275,7 @@ See the [open issues](https://github.com/SparkyCoder/Gpio_Controller_Api/issues) ## Contributing If you have a suggestion that would make this API better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". -Don't forget to give the project a star! Thanks again! +

Don't forget to give the project a star! Thanks again!

(back to top)