Skip to content

Commit f9a22db

Browse files
authored
[OIDC] Add federated credential entities (no DB change yet) (#10252)
1 parent 088e9da commit f9a22db

4 files changed

Lines changed: 170 additions & 0 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.ComponentModel.DataAnnotations;
6+
using System.ComponentModel.DataAnnotations.Schema;
7+
8+
namespace NuGet.Services.Entities
9+
{
10+
/// <summary>
11+
/// The record of a federated credential that was accepted by a federated credential policy.
12+
/// </summary>
13+
public class FederatedCredential : IEntity
14+
{
15+
/// <summary>
16+
/// The unique key for this federated credential. Generated by the database.
17+
/// </summary>
18+
public int Key { get; set; }
19+
20+
/// <summary>
21+
/// A type enum of the <see cref="FederatedCredentialPolicy.Type"/> that accepted this federated credential.
22+
/// </summary>
23+
[Required]
24+
[Column("TypeKey")]
25+
public FederatedCredentialType Type { get; set; }
26+
27+
/// <summary>
28+
/// The key of the federated credential policy that accepted this federated credential. This does not have a
29+
/// foreign key constraint because the policy may be deleted, but the credential record should remain to ensure
30+
/// the <see cref="Identity"/> unique constraint is enforced (to prevent replay).
31+
/// </summary>
32+
public int FederatedCredentialPolicyKey { get; set; }
33+
34+
/// <summary>
35+
/// A unique identifier for the federated credential used to create this credential record. For OIDC tokens,
36+
/// this is the "jti" or "uti" claim. This must be unique to ensure tokens are not replayed.
37+
/// </summary>
38+
[StringLength(maximumLength: 64)]
39+
public string Identity { get; set; }
40+
41+
/// <summary>
42+
/// When this record was first created. The timestamp is in UTC.
43+
/// </summary>
44+
public DateTime Created { get; set; }
45+
46+
/// <summary>
47+
/// When the federated credential expires. For OIDC tokens, this will be the "exp" claim. The timestamp is in UTC.
48+
/// </summary>
49+
public DateTime Expires { get; set; }
50+
}
51+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.ComponentModel.DataAnnotations;
7+
using System.ComponentModel.DataAnnotations.Schema;
8+
9+
namespace NuGet.Services.Entities
10+
{
11+
/// <summary>
12+
/// This entity defines which federated credentials can operate on behalf of a user. The policy is used to evaluate
13+
/// an incoming federated credential and define what actions can be taken on behalf of the user.
14+
/// </summary>
15+
public class FederatedCredentialPolicy : IEntity
16+
{
17+
/// <summary>
18+
/// The unique key for this federated credential policy. Generated by the database.
19+
/// </summary>
20+
public int Key { get; set; }
21+
22+
/// <summary>
23+
/// When this entity was first created. The timestamp is in UTC.
24+
/// </summary>
25+
public DateTime Created { get; set; }
26+
27+
/// <summary>
28+
/// When this policy was last evaluated against an external credential and it matched. The timestamp is in UTC.
29+
/// </summary>
30+
public DateTime? LastMatched { get; set; }
31+
32+
/// <summary>
33+
/// A type enum to determine how the <see cref="Criteria"/> field should be interpreted.
34+
///
35+
/// Think of this field as a category of federated credential, where a given type value is specific to a
36+
/// particular identity provider (e.g. Entra ID) and specific type of credential provided by that identity
37+
/// provider (e.g. OpenID Connect token for a service principal in the app-only flow).
38+
///
39+
/// The type should be one of the values defined in <see cref="FederatedCredentialType"/>.
40+
///
41+
/// This column name ends in "Key" to allow a future where this is normalized into its own table, much like
42+
/// <see cref="Package.PackageStatusKey"/> and <see cref="Package.SemVerLevelKey"/>.
43+
/// </summary>
44+
[Required]
45+
[Column("TypeKey")]
46+
public FederatedCredentialType Type { get; set; }
47+
48+
/// <summary>
49+
/// A JSON object used to evaluate whether a token matches a user-provided pattern. Some criteria may be
50+
/// implied by the <see cref="System.Type"/> and may not be explicitly states in this field. Only criteria that varies
51+
/// from user to user or between individual cases should be expressed in this JSON object.
52+
///
53+
/// The maximum length is defined by the application and has an unbounded length in the database.
54+
/// </summary>
55+
[Required]
56+
public string Criteria { get; set; }
57+
58+
/// <summary>
59+
/// The key of the user that created this policy. If this policy was created by a site admin, this key will
60+
/// point to the user record that the site admin was acting on behalf of, not the site admin themselves.
61+
/// </summary>
62+
public int CreatedByUserKey { get; set; }
63+
64+
/// <summary>
65+
/// The key of the owner user or owner organization that this policy will create API keys scoped for. This is
66+
/// the user or organization that the user referred to by <see cref="UserKey"/> is acting on behalf of. This
67+
/// value will be used in resulting short-lived API keys in the <see cref="Scope.OwnerKey"/> value.
68+
/// </summary>
69+
public int PackageOwnerUserKey { get; set; }
70+
71+
/// <summary>
72+
/// The navigation property for the user that created the policy, defined by <see cref="CreatedByUserKey"/>.
73+
/// </summary>
74+
public virtual User CreatedBy { get; set; }
75+
76+
/// <summary>
77+
/// The navigation property for the package owner user, defined by <see cref="PackageOwnerUserKey"/>.
78+
/// </summary>
79+
public virtual User PackageOwner { get; set; }
80+
81+
/// <summary>
82+
/// The navigation property for the credentials that were allowed and created by this policy.
83+
/// </summary>
84+
public virtual ICollection<Credential> Credentials { get; set; }
85+
}
86+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace NuGet.Services.Entities
5+
{
6+
/// <summary>
7+
/// The types of federated credentials criteria that can be used to validate external credentials. This
8+
/// enum is used for the <see cref="FederatedCredentialPolicy.Type"/> property.
9+
/// </summary>
10+
public enum FederatedCredentialType
11+
{
12+
/// <summary>
13+
/// This credential type applies to Microsoft Entra ID OpenID Connect (OIDC) tokens, issued for a specific
14+
/// service principal. The service principal is identified by a tenant (directory ID) and an object ID (object
15+
/// ID). The application (client) ID is not used because the object ID uniquely identifies the service principal
16+
/// within the tenant. An object ID is required to show the service principal is provisioned within the tenant.
17+
///
18+
/// Additional validation is done on the token claims which are the same for all Entra ID tokens, such as
19+
/// subject and expiration claims.
20+
/// </summary>
21+
EntraIdServicePrincipal = 1,
22+
}
23+
}

src/NuGetGallery/App_Start/DefaultDependenciesModule.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,16 @@ protected override void Load(ContainerBuilder builder)
282282
.As<IEntityRepository<PackageRename>>()
283283
.InstancePerLifetimeScope();
284284

285+
builder.RegisterType<EntityRepository<FederatedCredentialPolicy>>()
286+
.AsSelf()
287+
.As<IEntityRepository<FederatedCredentialPolicy>>()
288+
.InstancePerLifetimeScope();
289+
290+
builder.RegisterType<EntityRepository<FederatedCredential>>()
291+
.AsSelf()
292+
.As<IEntityRepository<FederatedCredential>>()
293+
.InstancePerLifetimeScope();
294+
285295
ConfigureGalleryReadOnlyReplicaEntitiesContext(builder, loggerFactory, configuration, secretInjector, telemetryService);
286296

287297
var supportDbConnectionFactory = CreateDbConnectionFactory(

0 commit comments

Comments
 (0)