Skip to content

Commit

Permalink
merge from master
Browse files Browse the repository at this point in the history
  • Loading branch information
SondreJDigdir committed Jan 20, 2025
2 parents d9a1896 + 3768227 commit 3c1cb75
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 49 deletions.
14 changes: 7 additions & 7 deletions .github/workflows/core-cicd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: 'Dotnet restore, build & test'
Expand All @@ -27,7 +27,7 @@ jobs:
dotnet publish -c 'Release' --no-restore -o './published-app'
working-directory: 'Dan.Core'
- name: Upload artifact 'dan-core'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: 'dan-core'
path: Dan.Core/published-app
Expand All @@ -39,7 +39,7 @@ jobs:
needs: [build]
steps:
- name: 'Download artifact for dev'
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: 'dan-core'
path: './downloaded-app'
Expand All @@ -66,7 +66,7 @@ jobs:
needs: [deploy-dev]
steps:
- name: 'Download artifact for staging'
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: 'dan-core'
path: './downloaded-app'
Expand All @@ -93,7 +93,7 @@ jobs:
needs: [deploy-staging]
steps:
- name: 'Login via Azure CLI'
uses: azure/login@v1
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: 'Swap staging and production'
Expand All @@ -109,7 +109,7 @@ jobs:
needs: [swap-staging-prod]
steps:
- name: 'Download artifact for redeploy to staging'
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: 'dan-core'
path: './downloaded-app'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:

# You can pin the exact commit or the version.
# uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049
uses: SonarSource/sonarcloud-github-action@f5003fc9688ade81ce47b57a3fa97a8d3f12de4c
uses: SonarSource/sonarcloud-github-action@02ef91109b2d589e757aefcfb2854c2783fd7b19
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret)
Expand Down
11 changes: 6 additions & 5 deletions Dan.Common.UnitTest/Dan.Common.UnitTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="MSTest.TestAdapter" Version="3.5.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.5.1" />
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PackageReference Include="FluentAssertions" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="MSTest.TestAdapter" Version="3.7.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.7.1" />
<PackageReference Include="coverlet.collector" Version="6.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using Dan.Common.Extensions;
using FluentAssertions;

namespace Dan.Common.UnitTest.Extensions;

[TestClass]
public class EvidenceHarvesterRequestExtensionTests
{
[DataTestMethod]
[DataRow(1, true, 1)]
[DataRow("1", true, 1)]
[DataRow(null, false, 0)]
[DataRow(3000000000, false, 0)]
[DataRow(-3000000000, false, 0)]
public void TryGetParameter_Numbers(object value, bool expectedBool, int expectedInt)
{
// Arrange
const string parameterName = "NumberParam";
var request = new EvidenceHarvesterRequest
{
Parameters =
[
new EvidenceParameter()
{
EvidenceParamName = parameterName,
Value = value
}
]
};

// Act
var actualBool = request.TryGetParameter(parameterName, out int actualInt);

// Assert
actualBool.Should().Be(expectedBool);
actualInt.Should().Be(expectedInt);
}

[DataTestMethod]
[DataRow(1, true, 1)]
[DataRow(1.2, true, 1.2)]
[DataRow("1", true, 1)]
[DataRow("1.2", true, 1.2)]
[DataRow(null, false, 0)]
[DataRow(3000000000d, true, 3000000000)]
[DataRow(-3000000000d, true, -3000000000)]
public void TryGetParameter_Decimal(object value, bool expectedBool, double expectedTemp)
{
// Arrange
// decimal is not const so need to cast
var expectedDecimal = (decimal)expectedTemp;
const string parameterName = "NumberParam";
var request = new EvidenceHarvesterRequest
{
Parameters =
[
new EvidenceParameter()
{
EvidenceParamName = parameterName,
Value = value
}
]
};

// Act
var actualBool = request.TryGetParameter(parameterName, out decimal actualDecimal);

// Assert
actualBool.Should().Be(expectedBool);
actualDecimal.Should().Be(expectedDecimal);
}
}
8 changes: 4 additions & 4 deletions Dan.Common/Dan.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,18 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AsyncKeyedLock" Version="7.0.0" />
<PackageReference Include="AsyncKeyedLock" Version="7.1.4" />
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.22.0" />
<PackageReference Include="Microsoft.Azure.Core.NewtonsoftJson" Version="2.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.23.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="1.3.0" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.7" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.7" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.12" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.12" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />

<PackageReference Include="Polly.Caching.Distributed" Version="3.0.1" />
<PackageReference Include="Polly.Caching.Serialization.Json" Version="3.0.0" />
<PackageReference Include="StackExchange.Redis" Version="2.8.0" />
<PackageReference Include="StackExchange.Redis" Version="2.8.24" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
</ItemGroup>

Expand Down
64 changes: 61 additions & 3 deletions Dan.Common/Extensions/EvidenceHarvesterRequestExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,40 @@ public static bool TryGetParameter(this EvidenceHarvesterRequest ehr, string par
/// <returns>True if the parameter was supplied and was valid; otherwise false</returns>
public static bool TryGetParameter(this EvidenceHarvesterRequest ehr, string paramName, out int value)
{
// Can't find parameter? Return false
if (!ehr.TryGetParameter(paramName, out EvidenceParameter? parameter))
{
value = default;
return false;
}

return int.TryParse((string?)parameter?.Value, out value);
switch (parameter?.Value)
{
// Null? That's not a number, at least give a proper zero
case null:
value = default;
return false;
// If int, just return
case int intValue:
value = intValue;
return true;
// If long, return if not overflown above max int value
case long and (> int.MaxValue or < int.MinValue):
value = default;
return false;
case long longValue:
value = Convert.ToInt32(longValue);
return true;
// If string, parse
case string stringValue:
return int.TryParse(stringValue, out value);
// Otherwise return false, can't think of any other realistic value type, I dont want to do floats for
// int values due to rounding, I'd rather just throw an invalid value because don't give us a decimal
// number if we request a whole one
default:
value = default;
return false;
}
}

/// <summary>
Expand All @@ -56,8 +83,39 @@ public static bool TryGetParameter(this EvidenceHarvesterRequest ehr, string par
value = default;
return false;
}

return decimal.TryParse((string?)parameter?.Value, out value);

switch (parameter?.Value)
{
// Null? That's not a number, at least give a proper zero
case null:
value = default;
return false;
// If decimal, just return
case decimal decimalValue:
value = decimalValue;
return true;
// In case of other number values, convert
case int intValue:
value = Convert.ToDecimal(intValue);
return true;
case long longValue:
value = Convert.ToDecimal(longValue);
return true;
case float floatValue:
value = Convert.ToDecimal(floatValue);
return true;
case double doubleValue:
value = Convert.ToDecimal(doubleValue);
return true;
// If string, try to parse
case string stringValue:
return decimal.TryParse(stringValue, out value);
// Otherwise return false, can't think of any other realistic value type that could be interpreted
// as a decimal type
default:
value = default;
return false;
}
}

/// <summary>
Expand Down
7 changes: 7 additions & 0 deletions Dan.Common/Models/EvidenceCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ public class EvidenceCode
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? Timeout { get; set; }

/// <summary>
/// License of the evidence source data
/// </summary>
[DataMember(Name = "license")]
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? License { get; set; }

/// <summary>
/// Optional setting for aliases. Key is service context, value is Dataset name
/// Allows for the same dataset to be shared between service contexts with different names.
Expand Down
7 changes: 5 additions & 2 deletions Dan.Common/Models/ServiceContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ namespace Dan.Common.Models;
[DataContract]
public class ServiceContext
{
[DataMember(Name = "Name")]
[DataMember(Name = "name")]
public string Name { get; set; } = string.Empty;

[DataMember(Name = "Id")]
[DataMember(Name = "id")]
public string Id { get; set; } = string.Empty;

[DataMember(Name = "description")]
public string? Description { get; set; }

[DataMember(Name = "validLanguages")]
public List<string> ValidLanguages { get; set; } = new();
Expand Down
14 changes: 7 additions & 7 deletions Dan.Core.UnitTest/Dan.Core.UnitTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="7.0.0"/>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="MSTest.TestAdapter" Version="3.5.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.5.1" />
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PackageReference Include="FluentAssertions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="MSTest.TestAdapter" Version="3.7.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.7.1" />
<PackageReference Include="coverlet.collector" Version="6.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
14 changes: 7 additions & 7 deletions Dan.Core/Dan.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.12.0" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.6.0" />
<PackageReference Include="Azure.Identity" Version="1.13.2" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.7.0" />
<PackageReference Include="GitInfo" Version="3.3.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand All @@ -49,12 +49,12 @@
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Timer" Version="4.3.1" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.17.4" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.0.1" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.12" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.1" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.3.0" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.3.0" />
<PackageReference Include="Polly" Version="8.4.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.0.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" />

<!-- WCF related things -->
<PackageReference Include="System.Private.ServiceModel" Version="4.10.3" />
Expand Down
23 changes: 11 additions & 12 deletions Dan.Core/Middleware/AuthenticationMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class AuthenticationMiddleware : IFunctionsWorkerMiddleware
public const string AuthorizationHeader = "X-NADOBE-AUTHORIZATION";
public const string AuthorizationHeaderLocal = "Authorization";
public const string DefaultScope = "altinn:dataaltinnno";
public const string NewScopeRoot = "dan:";

private static readonly object CmLockMaskinporten = new();
private static readonly object CmLockAltinnPlatform = new();
Expand Down Expand Up @@ -113,7 +114,7 @@ public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next
var accessTokenJwt = headerValues.First();
accessTokenJwt = Jwt.RemoveBearer(accessTokenJwt);
var claimsPrincipal = await ValidateJwt(accessTokenJwt);
if (ValidateScopes(claimsPrincipal, DefaultScope))
if (ValidateScopes(claimsPrincipal))
{
orgNumber = claimsPrincipal.GetOrganizationNumberClaim();
scopes = claimsPrincipal.GetScopes()!.ToList();
Expand Down Expand Up @@ -201,26 +202,24 @@ private async Task<ClaimsPrincipal> ValidateJwt(string token)
}
}

private bool ValidateScopes(ClaimsPrincipal claimsPrincipal, string requiredScopes)
private bool ValidateScopes(ClaimsPrincipal claimsPrincipal)
{
var requiredScopeList = requiredScopes.Split(',');
var principalScopeList = claimsPrincipal.GetScopes();
if (principalScopeList == null)
{
return false;
}

foreach (var requiredScope in requiredScopeList)
// Replaced with StartsWith to allow new scope root and removed foreach
// Old: Note that this had .Contains does a substring match. This means that a requirement for
// eg. altinn:somescope will be satisfied by altinn:somescope/foo or any scope containing the substring
// "altinn:somescope". As ":" is not a valid subscope character in Maskinporten, this ought to be
// safe as it cannot be abused by something like "difi:altinn:somescope"
if (!principalScopeList.Any(x => x.StartsWith(DefaultScope) || x.StartsWith(NewScopeRoot)))
{
// Note that this use of .Contains does a substring match. This means that a requirement for
// eg. altinn:somescope will be satisfied by altinn:somescope/foo or any scope containing the substring
// "altinn:somescope". As ":" is not a valid subscope character in Maskinporten, this ought to be
// safe as it cannot be abused by something like "difi:altinn:somescope"
if (!principalScopeList.Any(x => x.Contains(requiredScope)))
{
return false;
}
return false;
}


return true;
}
Expand Down
Loading

0 comments on commit 3c1cb75

Please sign in to comment.