@@ -5,35 +5,177 @@ we aim to store this data using a the concept of `Contexts` that are stored loca
5
5
developers separate user and module data from the module code, so that modules can be created in a way where users can resume from where they left off
6
6
without having to reconfigure the module or log in to services that support refreshing sessions with data you can store, i.e., refresh tokens.
7
7
8
+ ## What is a ` Context ` ?
9
+
8
10
The consept of ` Contexts ` is built on top of the functionality provided by the ` Microsoft.PowerShell.SecretManagement ` and
9
11
` Microsoft.PowerShell.SecretStore ` modules. The ` Context ` module manages a set of ` secrets ` that is stored in a ` SecretVault ` instance. A context in
10
- this case is a collection of secrets and data that is combined to represent a context for a module or a user.
11
-
12
- ## What is a ` Context ` ?
12
+ this case is a data structure that supports secrets and regular datatypes converted to a modified JSON structure and stored as a string based secret
13
+ in the ` SecretStore ` . The ` Context ` is stored in the ` SecretVault ` as a secret with the name ` Context:<ContextId> ` .
13
14
14
- A ` Context ` is collection of a name, data and secrets. A context must always have a name and the type of data you can store is:
15
+ The context is stored as compressed JSON and could look something like the examples below. These are the same data but one shows the JSON structure
16
+ that is stored in the ` SecretStore ` and the other shows the same data as a ` PSCustomObject ` that could be used in a PowerShell script.
15
17
16
- - Byte[ ]
17
- - String
18
- - SecureString
19
- - PSCredential
20
- - Hashtable
18
+ <details >
19
+ <summary >PSCustomObject</summary >
21
20
22
- The context is stored as hashtable and could look something like this:
21
+ Typical the first input to a context (altho it can also be a hashtable or any other object type that converts with JSON)
23
22
24
23
``` pwsh
25
- @{
26
- Name = "GitHub" # Required: Used to store the context in the vault.
27
- AccessToken = "123456",
28
- AccessTokenExpirationDate = '2021-12-31T23:59:59'
29
- RefreshToken = '654321'
30
- RefreshTokenExpirationDate = '2021-12-31T23:59:59'
31
- APIVersion = 'v3'
32
- APIHost = 'https://api.github.com'
33
- ClientId = '123456'
34
- Scope = 'repo, user'
24
+ [PSCustomObject]@{
25
+ Username = 'john_doe'
26
+ AuthToken = 'ghp_12345ABCDE67890FGHIJ' | ConvertTo-SecureString -AsPlainText -Force #gitleaks:allow
27
+ LoginTime = Get-Date
28
+ IsTwoFactorAuth = $true
29
+ TwoFactorMethods = @('TOTP', 'SMS')
30
+ LastLoginAttempts = @(
31
+ [PSCustomObject]@{
32
+ Timestamp = (Get-Date).AddHours(-1)
33
+ IP = '192.168.1.101' | ConvertTo-SecureString -AsPlainText -Force
34
+ Success = $true
35
+ },
36
+ [PSCustomObject]@{
37
+ Timestamp = (Get-Date).AddDays(-1)
38
+ IP = '203.0.113.5' | ConvertTo-SecureString -AsPlainText -Force
39
+ Success = $false
40
+ }
41
+ )
42
+ UserPreferences = @{
43
+ Theme = 'dark'
44
+ DefaultBranch = 'main'
45
+ Notifications = [PSCustomObject]@{
46
+ Email = $true
47
+ Push = $false
48
+ SMS = $true
49
+ }
50
+ CodeReview = @('PR Comments', 'Inline Suggestions')
51
+ }
52
+ Repositories = @(
53
+ [PSCustomObject]@{
54
+ Name = 'Repo1'
55
+ IsPrivate = $true
56
+ CreatedDate = (Get-Date).AddMonths(-6)
57
+ Stars = 42
58
+ Languages = @('Python', 'JavaScript')
59
+ },
60
+ [PSCustomObject]@{
61
+ Name = 'Repo2'
62
+ IsPrivate = $false
63
+ CreatedDate = (Get-Date).AddYears(-1)
64
+ Stars = 130
65
+ Languages = @('C#', 'HTML', 'CSS')
66
+ }
67
+ )
68
+ AccessScopes = @('repo', 'user', 'gist', 'admin:org')
69
+ ApiRateLimits = [PSCustomObject]@{
70
+ Limit = 5000
71
+ Remaining = 4985
72
+ ResetTime = (Get-Date).AddMinutes(30)
73
+ }
74
+ SessionMetaData = [PSCustomObject]@{
75
+ SessionID = 'sess_abc123'
76
+ Device = 'Windows-PC'
77
+ Location = [PSCustomObject]@{
78
+ Country = 'USA'
79
+ City = 'New York'
80
+ }
81
+ BrowserInfo = [PSCustomObject]@{
82
+ Name = 'Chrome'
83
+ Version = '118.0.1'
84
+ }
85
+ }
86
+ }
87
+ ```
88
+ </details >
89
+
90
+ <details >
91
+ <summary >JSON</summary >
92
+
93
+ This is same as what is stored, except that this is an uncomressed version for readability.
94
+
95
+ ``` json
96
+ {
97
+ "Username" : " john_doe" ,
98
+ "AuthToken" : " [SECURESTRING]ghp_12345ABCDE67890FGHIJ" ,
99
+ "LoginTime" : " 2024-11-21T21:16:56.2518249+01:00" ,
100
+ "IsTwoFactorAuth" : true ,
101
+ "TwoFactorMethods" : [
102
+ " TOTP" ,
103
+ " SMS"
104
+ ],
105
+ "LastLoginAttempts" : [
106
+ {
107
+ "Timestamp" : " 2024-11-21T20:16:56.2518510+01:00" ,
108
+ "IP" : " [SECURESTRING]192.168.1.101" ,
109
+ "Success" : true
110
+ },
111
+ {
112
+ "Timestamp" : " 2024-11-20T21:16:56.2529436+01:00" ,
113
+ "IP" : " [SECURESTRING]203.0.113.5" ,
114
+ "Success" : false
115
+ }
116
+ ],
117
+ "UserPreferences" : {
118
+ "Theme" : " dark" ,
119
+ "DefaultBranch" : " main" ,
120
+ "Notifications" : {
121
+ "Email" : true ,
122
+ "Push" : false ,
123
+ "SMS" : true
124
+ },
125
+ "CodeReview" : [
126
+ " PR Comments" ,
127
+ " Inline Suggestions"
128
+ ]
129
+ },
130
+ "Repositories" : [
131
+ {
132
+ "Name" : " Repo1" ,
133
+ "IsPrivate" : true ,
134
+ "CreatedDate" : " 2024-05-21T21:16:56.2540703+02:00" ,
135
+ "Stars" : 42 ,
136
+ "Languages" : [
137
+ " Python" ,
138
+ " JavaScript"
139
+ ]
140
+ },
141
+ {
142
+ "Name" : " Repo2" ,
143
+ "IsPrivate" : false ,
144
+ "CreatedDate" : " 2023-11-21T21:16:56.2545789+01:00" ,
145
+ "Stars" : 130 ,
146
+ "Languages" : [
147
+ " C#" ,
148
+ " HTML" ,
149
+ " CSS"
150
+ ]
151
+ }
152
+ ],
153
+ "AccessScopes" : [
154
+ " repo" ,
155
+ " user" ,
156
+ " gist" ,
157
+ " admin:org"
158
+ ],
159
+ "ApiRateLimits" : {
160
+ "Limit" : 5000 ,
161
+ "Remaining" : 4985 ,
162
+ "ResetTime" : " 2024-11-21T21:46:56.2550348+01:00"
163
+ },
164
+ "SessionMetaData" : {
165
+ "SessionID" : " sess_abc123" ,
166
+ "Device" : " Windows-PC" ,
167
+ "Location" : {
168
+ "Country" : " USA" ,
169
+ "City" : " New York"
170
+ },
171
+ "BrowserInfo" : {
172
+ "Name" : " Chrome" ,
173
+ "Version" : " 118.0.1"
174
+ }
175
+ }
35
176
}
36
177
```
178
+ </details >
37
179
38
180
## Prerequisites
39
181
@@ -69,17 +211,17 @@ this module. The context for the module is stored in the `SecretVault` as a secr
69
211
70
212
To store user data, the module developer can create a new context that defines a "namespace" for the user configuration. So let's say a developer has
71
213
implemented this for the ` GitHub ` module, a user would log in using their details. The module would call upon ` Context ` functionality to create a new
72
- context under the ` GitHub ` context .
214
+ context under the ` GitHub ` namespace .
73
215
74
216
Imagine a user called ` BobMarley ` logs in to the ` GitHub ` module. The following would exist in the context:
75
217
76
218
- ` Context:GitHub ` containing module configuration, like default user, host, and client ID to use if not otherwise specified.
77
- - ` Context:GitHub. BobMarley ` containing user configuration, details about the user, secrets and default values for API calls etc.
219
+ - ` Context:GitHub/ BobMarley ` containing user configuration, details about the user, secrets and default values for API calls etc.
78
220
79
221
Let's say the person also has another account on ` GitHub ` called ` RastaBlasta ` . After logging on with the second account, the following context would
80
222
also exist in the context:
81
223
82
- - ` Context:GitHub. RastaBlasta ` containing user configuration, details about the user, secrets and default values for API calls etc.
224
+ - ` Context:GitHub/ RastaBlasta ` containing user configuration, details about the user, secrets and default values for API calls etc.
83
225
84
226
With this the module developer could allow users to set default context, and store a key of the name of that context in the module context. This way
85
227
the module could automatically log in the user to the correct account when the module is loaded. The user could also switch between accounts by
@@ -89,7 +231,7 @@ changing the default context.
89
231
90
232
To set up a new module to use the ` Context ` module, the following steps should be taken:
91
233
92
- 1 . Create a new context for the module -> ` Set-Context -Name 'GitHub' ` during the module initialization.
234
+ 1 . Create a new context for the module -> ` Set-Context -ID 'GitHub' -Context @{ ... } ` during the module initialization.
93
235
94
236
` src\variable\private\Config.ps1 `
95
237
``` pwsh
@@ -100,10 +242,12 @@ $script:Config = @{
100
242
101
243
` src\loader.ps1 `
102
244
``` pwsh
103
- Write-Verbose "Initialized secret vault [$($script:Config.VaultName)] of type [$($script:Config.VaultType)]"
104
245
### This is the context config for this module
105
246
$contextParams = @{
106
- Name = $script:Config.Name
247
+ ID = 'GitHub'
248
+ Context = @{
249
+ Name = 'GitHub'
250
+ }
107
251
}
108
252
try {
109
253
Set-Context @contextParams
@@ -113,10 +257,10 @@ try {
113
257
}
114
258
```
115
259
116
- 2 . Add some module configuration -> ` Set-ContextSetting -Context 'GitHub' -Name 'ClientId' -Value '123456' `
117
- 3 . Get the module configuration -> ` Get-ContextSetting -Context 'GitHub' -Name 'ClientId' ` -> ` 123456 `
118
- - ` Get-ContextSettign -Context 'GitHub' ` -> Returns all module configuration for the ` GitHub ` context.
119
- 4 . Remove the module configuration -> ` Remove-ContextSetting -Context 'GitHub' -Name 'ClientId' `
260
+ 2 . Add some module configuration -> ` Set-ContextSetting -ID 'GitHub' -Name 'ClientId' -Value '123456' `
261
+ 3 . Get the module configuration -> ` Get-ContextSetting -ID 'GitHub' -Name 'ClientId' ` -> ` 123456 `
262
+ - ` Get-ContextSettign -ID 'GitHub' ` -> Returns all module configuration for the ` GitHub ` context.
263
+ 4 . Remove the module configuration -> ` Remove-ContextSetting -ID 'GitHub' -Name 'ClientId' `
120
264
121
265
### Setup for a New Context
122
266
@@ -132,11 +276,12 @@ To set up a new context for a user, the following steps should be taken:
132
276
- ` Get-<ModuleName>ContextSetting ` that uses ` Get-ContextSetting `
133
277
- ` Remove-<ModuleName>ContextSetting ` that uses ` Remove-ContextSetting `
134
278
135
- 2 . Create a new context for the user -> ` Set-Context -Context 'GitHub.BobMarley' ` -> Context ` GitHub.BobMarley ` is created.
136
- 3 . Add some user configuration -> ` Set-ContextSetting -Context 'GitHub.BobMarley.AccessToken' -Name 'Secret' -Value '123456' ` ->
137
- Secret ` GitHub.BobMarley.AccessToken ` is created.
138
- 4 . Get the user configuration -> ` Get-ContextSetting -Context 'GitHub.BobMarley.AccessToken' -Name 'Secret' -AsPlainText ` -> ` 123456 `
139
- 5 . Remove the user configuration -> ` Remove-Context -Name 'GitHub.BobMarley.AccessToken' ` -> Secret ` GitHub.BobMarley.AccessToken ` is removed.
279
+ 2 . Create a new context for the user -> ` Set-Context -ID 'GitHub.BobMarley' ` -> Context ` GitHub/BobMarley ` is created.
280
+ 3 . Add some user configuration -> ` Set-ContextSetting -ID 'GitHub.BobMarley' -Name 'AccessToken' -Value 'qweqweqwe' ` ->
281
+ Secret ` GitHub.BobMarley ` is created with a JSON structure containing the ` AccessToken ` secret.
282
+ 4 . Get the user configuration -> ` Get-ContextSetting -Context 'GitHub/BobMarley' -Name 'AccessToken' ` -> ` qweqweqwe `
283
+ 5 . Remove the user configuration -> ` Remove-Context -ID 'GitHub/BobMarley' -Name 'AccessToken ` -> Secret ` GitHub/BobMarley ` is opened, the property
284
+ called ` AccessToken ` is removed, the context gets stored again.
140
285
141
286
## Contributing
142
287
0 commit comments