Endpoint authorization

Tokens are validated for some of the endpoints. Need to add other measures to rule out bogus tokens.
This commit is contained in:
Kun Deng
2022-08-26 18:01:29 -04:00
parent 2cc13ac9bb
commit d48716a54f
7 changed files with 221 additions and 12 deletions
+16 -4
View File
@@ -220,8 +220,9 @@ namespace Icarus.Controllers.Managers
song.SongDirectory = _tempDirectoryRoot; song.SongDirectory = _tempDirectoryRoot;
song.DateCreated = DateTime.Now; song.DateCreated = DateTime.Now;
var tempPath = song.SongPath();
using (var filestream = new FileStream(song.SongPath(), FileMode.Create)) using (var filestream = new FileStream(tempPath, FileMode.Create))
{ {
_logger.Info("Saving song to temporary directory"); _logger.Info("Saving song to temporary directory");
await songFile.CopyToAsync(filestream); await songFile.CopyToAsync(filestream);
@@ -246,11 +247,22 @@ namespace Icarus.Controllers.Managers
using (var fileStream = new FileStream(filePath, FileMode.Create)) using (var fileStream = new FileStream(filePath, FileMode.Create))
{ {
var songBytes = await System.IO.File.ReadAllBytesAsync(song.SongPath()); var songBytes = await System.IO.File.ReadAllBytesAsync(tempPath);
try
{
if (!System.IO.File.Exists(filePath))
{
await System.IO.File.WriteAllBytesAsync(filePath, songBytes); await System.IO.File.WriteAllBytesAsync(filePath, songBytes);
System.IO.File.Delete(song.SongPath()); System.IO.File.Delete(tempPath);
song.SongDirectory = dirMgr.SongDirectory; }
}
catch (Exception ex)
{
var msg = ex.Message;
_logger.Error($"An error occurred: {msg}");
}
// song.SongDirectory = dirMgr.SongDirectory;
_logger.Info("Song successfully saved to filesystem"); _logger.Info("Song successfully saved to filesystem");
} }
+63 -3
View File
@@ -20,13 +20,26 @@ using Icarus.Models;
namespace Icarus.Controllers.Managers namespace Icarus.Controllers.Managers
{ {
#region Classes
/**
{
"scope":"download:songs read:song_details upload:songs delete:songs read:albums read:artists update:songs stream:songs read:genre read:year download:cover_art",
"exp":1661616071,
"aud":"http://localhost:5002",
"iss":"http://localhost:5002",
"iat":1661529671
}
*/
public class TokenManager : BaseManager public class TokenManager : BaseManager
{ {
#region Fields #region Fields
private string _clientId; private string _clientId;
private string _clientSecret; private string _clientSecret;
private string _privateKeyPath; private string _privateKeyPath;
private string _privateKey;
private string _publicKeyPath; private string _publicKeyPath;
private string _publicKey;
private string _audience; private string _audience;
private string _grantType; private string _grantType;
private string _url; private string _url;
@@ -70,7 +83,7 @@ namespace Icarus.Controllers.Managers
_logger.Info("Deserializing response"); _logger.Info("Deserializing response");
var tokenResult = JsonConvert var tokenResult = JsonConvert
.DeserializeObject<Token>(response.Content); .DeserializeObject<TokenTierOne>(response.Content);
_logger.Info("Response deserialized"); _logger.Info("Response deserialized");
return new LoginResult return new LoginResult
@@ -84,7 +97,7 @@ namespace Icarus.Controllers.Managers
public LoginResult LogIn(User user) public LoginResult LogIn(User user)
{ {
var tokenResult = new Token(); var tokenResult = new TokenTierOne();
tokenResult.TokenType = "Jwt"; tokenResult.TokenType = "Jwt";
var privateKey = ReadKeyContent(_privateKeyPath).Result; var privateKey = ReadKeyContent(_privateKeyPath).Result;
@@ -110,6 +123,50 @@ namespace Icarus.Controllers.Managers
}; };
} }
public bool IsTokenValid(string scope, string accessToken)
{
var result = false;
var token = DecodeToken(accessToken);
if (token == null)
{
return result;
}
result = (!token.TokenExpired() && token.ContainsScope(scope)) ? true : false;
// What would make a token valid?
// 1. The expiration date must be before the current date
// 2. The desired scope must be part of the scopes within the access token
// 3. Must be able to be decoded
return result;
}
public Token? DecodeToken(string accessToken)
{
var rsaParams = GetRSAPublic(_publicKey);
Token tok = null;
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(rsaParams);
IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
var algorithm = new JWT.Algorithms.RS256Algorithm(rsa);
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
var json = decoder.Decode(accessToken);
tok = JsonConvert.DeserializeObject<Token>(json);
}
return tok;
}
private string AllScopes() private string AllScopes()
{ {
@@ -269,6 +326,8 @@ namespace Icarus.Controllers.Managers
_url = $"https://{_config["Auth0:Domain"]}"; _url = $"https://{_config["Auth0:Domain"]}";
_privateKeyPath = _config["RSAKeys:PrivateKeyPath"]; _privateKeyPath = _config["RSAKeys:PrivateKeyPath"];
_publicKeyPath = _config["RSAKeys:PublicKeyPath"]; _publicKeyPath = _config["RSAKeys:PublicKeyPath"];
_privateKey = System.IO.File.ReadAllText(_privateKeyPath);
_publicKey = System.IO.File.ReadAllText(_publicKeyPath);
PrintCredentials(); PrintCredentials();
} }
@@ -299,7 +358,7 @@ namespace Icarus.Controllers.Managers
[JsonProperty("grant_type")] [JsonProperty("grant_type")]
public string GrantType { get; set; } public string GrantType { get; set; }
} }
private class Token private class TokenTierOne
{ {
[JsonProperty("access_token")] [JsonProperty("access_token")]
public string AccessToken { get; set; } public string AccessToken { get; set; }
@@ -310,4 +369,5 @@ namespace Icarus.Controllers.Managers
} }
#endregion #endregion
} }
#endregion
} }
+33 -1
View File
@@ -43,10 +43,40 @@ namespace Icarus.Controllers.V1
#endregion #endregion
#region Methods
private string ParseBearerTokenFromHeader()
{
var token = string.Empty;
const string tokenType = "Bearer";
var req = Request;
var auth = req.Headers.Authorization;
var val = auth.ToString();
if (val.Contains(tokenType) && val.Split(" ").Count() > 1)
{
var split = val.Split(" ");
token = split[1];
}
return token;
}
#region HTTP Endpoints
[HttpGet] [HttpGet]
[Authorize("read:song_details")] // [Authorize("read:song_details")]
public IActionResult Get() public IActionResult Get()
{ {
var token = ParseBearerTokenFromHeader();
var tokMgr = new TokenManager(_config);
if (!tokMgr.IsTokenValid("read:song_details", token))
{
return StatusCode(401, "Not allowed");
}
List<Song> songs = new List<Song>(); List<Song> songs = new List<Song>();
Console.WriteLine("Attemtping to retrieve songs"); Console.WriteLine("Attemtping to retrieve songs");
_logger.LogInformation("Attempting to retrieve songs"); _logger.LogInformation("Attempting to retrieve songs");
@@ -100,5 +130,7 @@ namespace Icarus.Controllers.V1
return Ok(songRes); return Ok(songRes);
} }
#endregion
#endregion
} }
} }
+30 -1
View File
@@ -47,6 +47,27 @@ namespace Icarus.Controllers.V1
#endregion #endregion
private string ParseBearerTokenFromHeader()
{
var token = string.Empty;
const string tokenType = "Bearer";
const string otherTokenType = "Jwt";
var req = Request;
var auth = req.Headers.Authorization;
var val = auth.ToString();
if (val.Contains(tokenType) && val.Split(" ").Count() > 1 ||
val.Contains(otherTokenType) && val.Split(" ").Count() > 1)
{
var split = val.Split(" ");
token = split[1];
}
return token;
}
[HttpGet("download/{id}")] [HttpGet("download/{id}")]
[Route("private-scoped")] [Route("private-scoped")]
@@ -112,9 +133,17 @@ namespace Icarus.Controllers.V1
// //
[HttpPost("upload/with/data")] [HttpPost("upload/with/data")]
[Route("private-scoped")] [Route("private-scoped")]
[Authorize("upload:songs")] // [Authorize("upload:songs")]
public async Task<IActionResult> Post ([FromForm] UploadSongWithDataForm up) public async Task<IActionResult> Post ([FromForm] UploadSongWithDataForm up)
{ {
var token = ParseBearerTokenFromHeader();
var tokMgr = new TokenManager(_config);
if (!tokMgr.IsTokenValid("upload:songs", token))
{
return StatusCode(401, "Not allowed");
}
try try
{ {
if (up.SongData.Length > 0 && up.CoverArtData.Length > 0 && !string.IsNullOrEmpty(up.SongFile)) if (up.SongData.Length > 0 && up.CoverArtData.Length > 0 && !string.IsNullOrEmpty(up.SongFile))
+31 -1
View File
@@ -13,6 +13,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Icarus.Models; using Icarus.Models;
using Icarus.Controllers.Managers;
using Icarus.Database.Contexts; using Icarus.Database.Contexts;
namespace Icarus.Controllers.V1 namespace Icarus.Controllers.V1
@@ -42,11 +43,40 @@ namespace Icarus.Controllers.V1
#endregion #endregion
private string ParseBearerTokenFromHeader()
{
var token = string.Empty;
const string tokenType = "Bearer";
var req = Request;
var auth = req.Headers.Authorization;
var val = auth.ToString();
if (val.Contains(tokenType) && val.Split(" ").Count() > 1)
{
var split = val.Split(" ");
token = split[1];
}
return token;
}
#region HTTP endpoints #region HTTP endpoints
[HttpGet("{id}")] [HttpGet("{id}")]
[Authorize("stream:songs")] // [Authorize("stream:songs")]
public async Task<IActionResult> Get(int id) public async Task<IActionResult> Get(int id)
{ {
var token = ParseBearerTokenFromHeader();
var tokMgr = new TokenManager(_config);
/**
if (!tokMgr.IsTokenValid("stream:songs", token))
{
return StatusCode(401, "Not allowed");
}
*/
var context = new SongContext(_config.GetConnectionString("DefaultConnection")); var context = new SongContext(_config.GetConnectionString("DefaultConnection"));
var song = context.Songs.FirstOrDefault(sng => sng.SongID == id); var song = context.Songs.FirstOrDefault(sng => sng.SongID == id);
+46
View File
@@ -0,0 +1,46 @@
using System;
using System.Linq;
using Newtonsoft.Json;
namespace Icarus.Models
{
public class Token
{
#region Properties
[JsonProperty("scope")]
public string Scope { get; set; }
[JsonProperty("exp")]
public int Expiration { get; set; }
[JsonProperty("aud")]
public string Audience { get; set; }
[JsonProperty("iss")]
public string Issuer { get; set; }
[JsonProperty("iat")]
public int Issued { get; set; }
#endregion
#region Methods
public bool TokenExpired()
{
var result = false;
var currentDate = DateTime.Now;
var currentDateInSeconds = Math.Floor((currentDate - DateTime.UnixEpoch).TotalSeconds);
result = (currentDateInSeconds >= Expiration) ? true : false;
return result;
}
public bool ContainsScope(string desiredScope)
{
var result = false;
result = Scope.Contains(desiredScope);
return result;
}
#endregion
}
}
+1 -1
View File
@@ -151,6 +151,7 @@ namespace Icarus
var domain = $"https://{auth_id}/"; var domain = $"https://{auth_id}/";
var audience = Configuration["Auth0:ApiIdentifier"]; var audience = Configuration["Auth0:ApiIdentifier"];
/**
services services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => .AddJwtBearer(options =>
@@ -159,7 +160,6 @@ namespace Icarus
options.Audience = audience; options.Audience = audience;
}); });
/**
services.AddAuthorization(options => services.AddAuthorization(options =>
{ {
options.AddPolicy("download:songs", policy => options.AddPolicy("download:songs", policy =>