1
+ // Licensed to the .NET Foundation under one or more agreements.
2
+ // The .NET Foundation licenses this file to you under the MIT license.
3
+ // See the LICENSE file in the project root for more information.
4
+
5
+ using System . Text . Json ;
6
+ using System . Text . Json . Serialization ;
7
+ using System . Xml ;
8
+
9
+ namespace DevProxy . Plugins . MinimalPermissions ;
10
+
11
+ enum ActionType
12
+ {
13
+ ObjectPath ,
14
+ Query ,
15
+ SetProperty
16
+ }
17
+
18
+ enum AccessType
19
+ {
20
+ Delegated ,
21
+ Application
22
+ }
23
+
24
+ static class CsomParser
25
+ {
26
+ private static readonly JsonSerializerOptions options = new ( )
27
+ {
28
+ PropertyNameCaseInsensitive = true ,
29
+ Converters =
30
+ {
31
+ new JsonStringEnumConverter ( JsonNamingPolicy . CamelCase )
32
+ }
33
+ } ;
34
+
35
+ private static string ? GetTypeName ( string objectPathId , XmlDocument doc , XmlNamespaceManager nsManager , CsomTypesDefinition typesDefinition )
36
+ {
37
+ var objectPath = doc . SelectSingleNode ( $ "//ns:ObjectPaths/*[@Id='{ objectPathId } ']", nsManager ) ;
38
+ if ( objectPath == null )
39
+ {
40
+ return null ;
41
+ }
42
+
43
+ if ( objectPath . Name == "Constructor" ||
44
+ objectPath . Name == "StaticProperty" )
45
+ {
46
+ var typeIdAttr = objectPath . Attributes ? [ "TypeId" ] ;
47
+ if ( typeIdAttr != null )
48
+ {
49
+ var typeId = typeIdAttr . Value . Trim ( '{' , '}' ) ;
50
+ if ( typesDefinition ? . Types ? . TryGetValue ( typeId , out string ? typeName ) == true )
51
+ {
52
+ if ( objectPath . Name == "StaticProperty" )
53
+ {
54
+ var nameAttr = objectPath . Attributes ? [ "Name" ] ;
55
+ if ( nameAttr != null )
56
+ {
57
+ return $ "{ typeName } .{ nameAttr . Value } ";
58
+ }
59
+ }
60
+ return typeName ;
61
+ }
62
+ else
63
+ {
64
+ return null ;
65
+ }
66
+ }
67
+ return null ;
68
+ }
69
+
70
+ var parentIdAttr = objectPath . Attributes ? [ "ParentId" ] ;
71
+ if ( parentIdAttr == null )
72
+ {
73
+ return null ;
74
+ }
75
+ var parentId = parentIdAttr . Value ;
76
+
77
+ return GetTypeName ( parentId , doc , nsManager , typesDefinition ) ;
78
+ }
79
+
80
+ private static string ? GetObjectPathName ( string objectPathId , ActionType actionType , XmlDocument doc , XmlNamespaceManager nsManager , CsomTypesDefinition typesDefinition )
81
+ {
82
+ var objectPath = doc . SelectSingleNode ( $ "//ns:ObjectPaths/*[@Id='{ objectPathId } ']", nsManager ) ;
83
+ if ( objectPath == null )
84
+ {
85
+ return null ;
86
+ }
87
+
88
+ var typeName = GetTypeName ( objectPathId , doc , nsManager , typesDefinition ) ;
89
+ if ( typeName == null )
90
+ {
91
+ return null ;
92
+ }
93
+
94
+ if ( objectPath . Name == "Constructor" )
95
+ {
96
+ var suffix = actionType == ActionType . Query ? "query" : "ctor" ;
97
+ return $ "{ typeName } .{ suffix } ";
98
+ }
99
+
100
+ if ( objectPath . Name == "Method" )
101
+ {
102
+ var nameAttr = objectPath . Attributes ? [ "Name" ] ;
103
+ if ( nameAttr == null )
104
+ {
105
+ return null ;
106
+ }
107
+ var methodName = actionType == ActionType . Query ? "query" : nameAttr . Value ;
108
+ return $ "{ typeName } .{ methodName } ";
109
+ }
110
+
111
+ if ( objectPath . Name == "Property" )
112
+ {
113
+ var nameAttr = objectPath . Attributes ? [ "Name" ] ;
114
+ if ( nameAttr == null )
115
+ {
116
+ return null ;
117
+ }
118
+ if ( typesDefinition ? . ReturnTypes ? . TryGetValue ( $ "{ typeName } .{ nameAttr . Value } ", out string ? returnType ) == true )
119
+ {
120
+ var methodName = actionType == ActionType . SetProperty ? "setProperty" : nameAttr . Value ;
121
+ return $ "{ returnType } .{ methodName } ";
122
+ }
123
+ else
124
+ {
125
+ return $ "{ typeName } .{ nameAttr . Value } ";
126
+ }
127
+ }
128
+
129
+ return null ;
130
+ }
131
+
132
+ private static ActionType GetActionType ( string actionName )
133
+ {
134
+ return actionName switch
135
+ {
136
+ "ObjectPath" => ActionType . ObjectPath ,
137
+ "Query" => ActionType . Query ,
138
+ "SetProperty" => ActionType . SetProperty ,
139
+ _ => throw new ArgumentOutOfRangeException ( nameof ( actionName ) , $ "Unknown action type: { actionName } ")
140
+ } ;
141
+ }
142
+
143
+ public static ( IEnumerable < string > Actions , IEnumerable < string > Errors ) GetActions ( string xml , CsomTypesDefinition typesDefinition )
144
+ {
145
+ if ( typesDefinition ? . Types == null || string . IsNullOrEmpty ( xml ) )
146
+ {
147
+ return ( [ ] , [ ] ) ;
148
+ }
149
+
150
+ var actions = new List < string > ( ) ;
151
+ var errors = new List < string > ( ) ;
152
+
153
+ try
154
+ {
155
+ // Load the XML
156
+ var doc = new XmlDocument ( ) ;
157
+ doc . LoadXml ( xml ) ;
158
+
159
+ var nsManager = new XmlNamespaceManager ( doc . NameTable ) ;
160
+ var defaultNamespace = doc . DocumentElement ? . NamespaceURI ?? string . Empty ;
161
+ if ( ! string . IsNullOrEmpty ( defaultNamespace ) )
162
+ {
163
+ nsManager . AddNamespace ( "ns" , defaultNamespace ) ;
164
+ }
165
+
166
+ // Get the Actions element
167
+ var actionsNode = doc . SelectSingleNode ( "//ns:Actions" , nsManager ) ;
168
+ if ( actionsNode == null )
169
+ {
170
+ errors . Add ( "Actions node not found in XML." ) ;
171
+ // If Actions node is not found, return empty list
172
+ return ( actions , errors ) ;
173
+ }
174
+
175
+ // Process all child Action elements
176
+ foreach ( XmlNode actionNode in actionsNode . ChildNodes )
177
+ {
178
+ var actionType = GetActionType ( actionNode . Name ) ;
179
+
180
+ // Extract ObjectPathId attribute
181
+ var objectPathIdAttr = actionNode . Attributes ? [ "ObjectPathId" ] ;
182
+ if ( objectPathIdAttr == null )
183
+ {
184
+ errors . Add ( $ "ObjectPathId attribute not found for action: { actionNode . OuterXml } ") ;
185
+ continue ;
186
+ }
187
+
188
+ var objectPathId = objectPathIdAttr . Value ;
189
+
190
+ var type = GetObjectPathName ( objectPathId , actionType , doc , nsManager , typesDefinition ) ;
191
+
192
+ if ( type != null )
193
+ {
194
+ actions . Add ( type ) ;
195
+ }
196
+ }
197
+ }
198
+ catch ( Exception ex )
199
+ {
200
+ Console . WriteLine ( $ "Error parsing XML: { ex . Message } ") ;
201
+ }
202
+
203
+ return ( actions , errors ) ;
204
+ }
205
+
206
+ public static ( string [ ] MinimalScopes , string [ ] UnmatchedOperations ) GetMinimalScopes ( IEnumerable < string > actions , AccessType accessType , CsomTypesDefinition typesDefinition )
207
+ {
208
+ var operationsAndScopes = typesDefinition ? . Actions
209
+ ? . Where ( o => o . Value . Delegated != null || o . Value . Application != null )
210
+ . ToDictionary (
211
+ o => o . Key ,
212
+ o => accessType == AccessType . Delegated ? o . Value . Delegated : o . Value . Application
213
+ ) ;
214
+ return MinimalPermissionsUtils . GetMinimalScopes ( [ .. actions ] , operationsAndScopes ! ) ;
215
+ }
216
+ }
0 commit comments