Skip to content

Commit d6bce44

Browse files
authored
Merge pull request #164 from microsoftgraph/po/ExposeAuthConfigToFunctionScope
Expose AuthContext To Function Scope
2 parents bb8a5af + 080d0af commit d6bce44

File tree

17 files changed

+325
-48
lines changed

17 files changed

+325
-48
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
namespace Microsoft.Graph.Authentication.Test.Helpers
2+
{
3+
using Microsoft.Graph.PowerShell.Authentication;
4+
using System;
5+
using Xunit;
6+
public class GraphSessionTests
7+
{
8+
[Fact]
9+
public void GraphSessionShouldBeInitilizedAfterInitializerIsCalled()
10+
{
11+
GraphSession.Initialize(() => new GraphSession());
12+
13+
Assert.NotNull(GraphSession.Instance);
14+
Assert.Null(GraphSession.Instance.AuthContext);
15+
16+
// reset static instance.
17+
GraphSession.Reset();
18+
}
19+
20+
[Fact]
21+
public void ShouldOverwriteExistingGraphSession()
22+
{
23+
GraphSession.Initialize(() => new GraphSession());
24+
Guid originalSessionId = GraphSession.Instance._graphSessionId;
25+
26+
GraphSession.Initialize(() => new GraphSession(), true);
27+
28+
Assert.NotNull(GraphSession.Instance);
29+
Assert.NotEqual(originalSessionId, GraphSession.Instance._graphSessionId);
30+
31+
// reset static instance.
32+
GraphSession.Reset();
33+
}
34+
35+
[Fact]
36+
public void ShouldNotOverwriteExistingGraphSession()
37+
{
38+
GraphSession.Initialize(() => new GraphSession());
39+
Guid originalSessionId = GraphSession.Instance._graphSessionId;
40+
41+
InvalidOperationException exception = Assert.Throws<InvalidOperationException>(() => GraphSession.Initialize(() => new GraphSession()));
42+
43+
Assert.Equal("An instance of GraphSession already exists. Call Initialize(Func<GraphSession>, bool) to overwrite it.", exception.Message);
44+
Assert.NotNull(GraphSession.Instance);
45+
Assert.Equal(originalSessionId, GraphSession.Instance._graphSessionId);
46+
47+
// reset static instance.
48+
GraphSession.Reset();
49+
}
50+
}
51+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using Xunit;
2+
3+
[assembly: CollectionBehavior(DisableTestParallelization = true)]

src/Authentication/Authentication/Cmdlets/ConnectGraph.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace Microsoft.Graph.PowerShell.Authentication.Cmdlets
1616
using System.Threading.Tasks;
1717

1818
[Cmdlet(VerbsCommunications.Connect, "Graph", DefaultParameterSetName = Constants.UserParameterSet)]
19-
public class ConnectGraph : PSCmdlet
19+
public class ConnectGraph : PSCmdlet, IModuleAssemblyInitializer
2020
{
2121

2222
[Parameter(ParameterSetName = Constants.UserParameterSet, Position = 1)]
@@ -53,7 +53,7 @@ protected override void ProcessRecord()
5353
{
5454
base.ProcessRecord();
5555

56-
AuthConfig authConfig = new AuthConfig { TenantId = TenantId };
56+
IAuthContext authConfig = new AuthContext { TenantId = TenantId };
5757
CancellationToken cancellationToken = CancellationToken.None;
5858

5959
if (ParameterSetName == Constants.UserParameterSet)
@@ -117,7 +117,7 @@ protected override void ProcessRecord()
117117
authConfig.Account = jwtPayload?.Upn ?? account?.Username;
118118

119119
// Save auth config to session state.
120-
SessionState.PSVariable.Set(Constants.GraphAuthConfigId, authConfig);
120+
GraphSession.Instance.AuthContext = authConfig;
121121
}
122122
catch (AuthenticationException authEx)
123123
{
@@ -164,5 +164,13 @@ private void ThrowParameterError(string parameterName)
164164
new ArgumentException($"Must specify {parameterName}"), Guid.NewGuid().ToString(), ErrorCategory.InvalidArgument, null)
165165
);
166166
}
167+
168+
/// <summary>
169+
/// Globally initializes GraphSession.
170+
/// </summary>
171+
public void OnImport()
172+
{
173+
GraphSessionInitializer.InitializeSession();
174+
}
167175
}
168176
}

src/Authentication/Authentication/Cmdlets/DisconnectGraph.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
namespace Microsoft.Graph.PowerShell.Authentication.Cmdlets
55
{
66
using Microsoft.Graph.PowerShell.Authentication.Helpers;
7-
using Microsoft.Graph.PowerShell.Authentication.Models;
87
using System;
98
using System.Management.Automation;
109
[Cmdlet(VerbsCommunications.Disconnect, "Graph")]
@@ -24,15 +23,15 @@ protected override void ProcessRecord()
2423
{
2524
base.ProcessRecord();
2625

27-
AuthConfig authConfig = SessionState.PSVariable.GetValue(Constants.GraphAuthConfigId) as AuthConfig;
26+
IAuthContext authConfig = GraphSession.Instance.AuthContext;
2827

2928
if (authConfig == null)
3029
ThrowTerminatingError(
3130
new ErrorRecord(new System.Exception("No application to sign out from."), Guid.NewGuid().ToString(), ErrorCategory.InvalidArgument, null));
3231

3332
AuthenticationHelpers.Logout(authConfig);
3433

35-
SessionState.PSVariable.Remove(Constants.GraphAuthConfigId);
34+
GraphSession.Instance.AuthContext = null;
3635
}
3736

3837
protected override void StopProcessing()

src/Authentication/Authentication/Cmdlets/GetMGContext.cs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,10 @@
44

55
namespace Microsoft.Graph.PowerShell.Authentication.Cmdlets
66
{
7-
using Microsoft.Graph.Auth;
8-
using Microsoft.Graph.PowerShell.Authentication.Helpers;
9-
using Microsoft.Graph.PowerShell.Authentication.Models;
10-
using System;
11-
using System.Collections.Generic;
127
using System.Management.Automation;
13-
using System.Net.Http;
14-
using System.Threading;
15-
using System.Threading.Tasks;
168

179
[Cmdlet(VerbsCommon.Get, "MgContext", DefaultParameterSetName = Constants.UserParameterSet)]
18-
[OutputType(typeof(AuthConfig))]
10+
[OutputType(typeof(IAuthContext))]
1911
public class GetMGContext: PSCmdlet
2012
{
2113
protected override void BeginProcessing()
@@ -26,11 +18,8 @@ protected override void BeginProcessing()
2618
protected override void ProcessRecord()
2719
{
2820
base.ProcessRecord();
29-
// Get auth config from session state.
30-
PSVariable graphAuthVariable = SessionState.PSVariable.Get(Constants.GraphAuthConfigId);
31-
AuthConfig authConfig = graphAuthVariable?.Value as AuthConfig;
32-
Invoke<AuthConfig>();
33-
WriteObject(authConfig as AuthConfig);
21+
IAuthContext authConfig = GraphSession.Instance.AuthContext;
22+
WriteObject(authConfig as IAuthContext);
3423
}
3524

3625
protected override void EndProcessing()
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// ------------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
3+
// ------------------------------------------------------------------------------
4+
5+
namespace Microsoft.Graph.PowerShell.Authentication
6+
{
7+
using System;
8+
using System.Threading;
9+
/// <summary>
10+
/// Contains methods to create, modify or obtain a thread safe static instance of <see cref="GraphSession"/>.
11+
/// </summary>
12+
public class GraphSession : IGraphSession
13+
{
14+
static GraphSession _instance;
15+
static bool _initialized = false;
16+
static ReaderWriterLockSlim sessionLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
17+
internal Guid _graphSessionId;
18+
19+
/// <summary>
20+
/// Gets or Sets <see cref="IAuthContext"/>.
21+
/// </summary>
22+
public IAuthContext AuthContext { get; set; }
23+
24+
/// <summary>
25+
/// Gets an instance of <see cref="GraphSession"/>.
26+
/// </summary>
27+
public static GraphSession Instance
28+
{
29+
get
30+
{
31+
try
32+
{
33+
sessionLock.EnterReadLock();
34+
try
35+
{
36+
if (null == _instance)
37+
{
38+
throw new InvalidOperationException(ErrorConstants.Codes.SessionNotInitialized);
39+
}
40+
return _instance;
41+
}
42+
finally
43+
{
44+
sessionLock.ExitReadLock();
45+
}
46+
}
47+
catch (LockRecursionException lockException)
48+
{
49+
throw new InvalidOperationException(ErrorConstants.Codes.SessionLockReadRecursion, lockException);
50+
}
51+
catch (ObjectDisposedException disposedException)
52+
{
53+
throw new InvalidOperationException(ErrorConstants.Codes.SessionLockReadDisposed, disposedException);
54+
}
55+
}
56+
}
57+
58+
/// <summary>
59+
/// Creates a new GraphSession.
60+
/// </summary>
61+
public GraphSession()
62+
{
63+
_graphSessionId = Guid.NewGuid();
64+
}
65+
66+
/// <summary>
67+
/// Initialize <see cref="GraphSession"/>.
68+
/// </summary>
69+
/// <param name="instanceCreator">A func to create an instance.</param>
70+
/// <param name="overwrite">If true, overwrite the current instance. Otherwise do not initialize.</param>
71+
public static void Initialize(Func<GraphSession> instanceCreator, bool overwrite)
72+
{
73+
try
74+
{
75+
sessionLock.EnterWriteLock();
76+
try
77+
{
78+
if (overwrite || !_initialized)
79+
{
80+
_instance = instanceCreator();
81+
_initialized = true;
82+
}
83+
else
84+
{
85+
throw new InvalidOperationException(string.Format(ErrorConstants.Message.InstanceExists, nameof(GraphSession), "Initialize(Func<GraphSession>, bool)"));
86+
}
87+
}
88+
finally
89+
{
90+
sessionLock.ExitWriteLock();
91+
}
92+
}
93+
catch (LockRecursionException lockException)
94+
{
95+
throw new InvalidOperationException(ErrorConstants.Codes.SessionLockWriteRecursion, lockException);
96+
}
97+
catch (ObjectDisposedException disposedException)
98+
{
99+
throw new InvalidOperationException(ErrorConstants.Codes.SessionLockWriteDisposed, disposedException);
100+
}
101+
}
102+
103+
/// <summary>
104+
/// Initialize the current instance if none exists.
105+
/// </summary>
106+
/// <param name="instanceCreator">A func to create an instance.</param>
107+
public static void Initialize(Func<GraphSession> instanceCreator)
108+
{
109+
Initialize(instanceCreator, false);
110+
}
111+
112+
/// <summary>
113+
/// Modify the current instance of <see cref="GraphSession"/>.
114+
/// </summary>
115+
/// <param name="modifier">A func to modify the <see cref="GraphSession"/> instance.</param>
116+
public static void Modify(Action<GraphSession> modifier)
117+
{
118+
try
119+
{
120+
sessionLock.EnterWriteLock();
121+
try
122+
{
123+
modifier(_instance);
124+
}
125+
finally
126+
{
127+
sessionLock.ExitWriteLock();
128+
}
129+
}
130+
catch (LockRecursionException lockException)
131+
{
132+
throw new InvalidOperationException(ErrorConstants.Codes.SessionLockWriteRecursion, lockException);
133+
}
134+
catch (ObjectDisposedException disposedException)
135+
{
136+
throw new InvalidOperationException(ErrorConstants.Codes.SessionLockWriteDisposed, disposedException);
137+
}
138+
}
139+
140+
/// <summary>
141+
/// Resets the current instance of <see cref="GraphSession"/> to initial state.
142+
/// </summary>
143+
internal static void Reset()
144+
{
145+
try
146+
{
147+
sessionLock.EnterWriteLock();
148+
try
149+
{
150+
_instance = null;
151+
_initialized = false;
152+
}
153+
finally
154+
{
155+
sessionLock.ExitWriteLock();
156+
}
157+
}
158+
catch (LockRecursionException lockException)
159+
{
160+
throw new InvalidOperationException(ErrorConstants.Codes.SessionLockWriteRecursion, lockException);
161+
}
162+
catch (ObjectDisposedException disposedException)
163+
{
164+
throw new InvalidOperationException(ErrorConstants.Codes.SessionLockWriteDisposed, disposedException);
165+
}
166+
}
167+
}
168+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// ------------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
3+
// ------------------------------------------------------------------------------
4+
5+
namespace Microsoft.Graph.PowerShell.Authentication
6+
{
7+
public static class GraphSessionInitializer
8+
{
9+
/// <summary>
10+
/// Initializes <see cref="GraphSession"/>.
11+
/// </summary>
12+
public static void InitializeSession()
13+
{
14+
GraphSession.Initialize(() => CreateInstance());
15+
}
16+
17+
/// <summary>
18+
/// Creates a new instance of a <see cref="GraphSession"/>.
19+
/// </summary>
20+
/// <returns><see cref="GraphSession"/></returns>
21+
internal static GraphSession CreateInstance()
22+
{
23+
// This can be used to initialize GraphSession from a file in the future.
24+
return new GraphSession();
25+
}
26+
}
27+
}

src/Authentication/Authentication/ErrorConstants.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,19 @@ public static class ErrorConstants
77
{
88
internal static class Codes
99
{
10+
internal const string SessionNotInitialized = "sessionNotInitialized";
11+
internal const string SessionLockReadRecursion = "sessionLockReadRecursion";
12+
internal const string SessionLockReadDisposed = "sessionLockReadDisposed";
13+
internal const string SessionLockWriteDisposed = "sessionLockWriteDisposed";
14+
internal const string SessionLockWriteRecursion = "sessionLockWriteRecursion";
1015
internal const string InvalidJWT = "invalidJWT";
1116
}
1217

1318
internal static class Message
1419
{
1520
internal const string InvalidJWT = "Invalid JWT access token.";
21+
internal const string MissingAuthContext = "Authentication needed, call Connect-Graph.";
22+
internal const string InstanceExists = "An instance of {0} already exists. Call {1} to overwrite it.";
1623
}
1724
}
1825
}

src/Authentication/Authentication/Helpers/AuthenticationHelpers.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
namespace Microsoft.Graph.PowerShell.Authentication.Helpers
55
{
66
using Microsoft.Graph.Auth;
7-
using Microsoft.Graph.PowerShell.Authentication.Models;
87
using Microsoft.Graph.PowerShell.Authentication.TokenCache;
98
using Microsoft.Identity.Client;
109
using System;
@@ -16,7 +15,7 @@ internal static class AuthenticationHelpers
1615
{
1716
private static readonly object FileLock = new object();
1817

19-
internal static IAuthenticationProvider GetAuthProvider(AuthConfig authConfig)
18+
internal static IAuthenticationProvider GetAuthProvider(IAuthContext authConfig)
2019
{
2120
if (authConfig.AuthType == AuthenticationType.Delegated)
2221
{
@@ -43,7 +42,7 @@ internal static IAuthenticationProvider GetAuthProvider(AuthConfig authConfig)
4342
}
4443
}
4544

46-
internal static void Logout(AuthConfig authConfig)
45+
internal static void Logout(IAuthContext authConfig)
4746
{
4847
lock (FileLock)
4948
{

0 commit comments

Comments
 (0)