Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data / System / Data / Common / NameValuePermission.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="NameValuePermission.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8
9 namespace System.Data.Common {
10
11     using System.Collections;
12     using System.Data.Common;
13     using System.Diagnostics;
14     using System.Globalization;
15     using System.Security;
16     using System.Security.Permissions;
17     using System.Text;
18
19     [Serializable] // MDAC 83147
20     sealed internal class NameValuePermission : IComparable {
21         // reused as both key and value nodes
22         // key nodes link to value nodes
23         // value nodes link to key nodes
24         private string _value;
25
26         // value node with (null != _restrictions) are allowed to match connection strings
27         private DBConnectionString _entry;
28
29         private NameValuePermission[] _tree; // with branches
30
31         static internal readonly NameValuePermission Default = null;// = new NameValuePermission(String.Empty, new string[] { "File Name" }, KeyRestrictionBehavior.AllowOnly);
32
33         internal NameValuePermission() { // root node
34         }
35
36         private NameValuePermission(string keyword) {
37             _value = keyword;
38        }
39
40         private NameValuePermission(string value, DBConnectionString entry) {
41             _value = value;
42             _entry = entry;
43         }
44
45         private NameValuePermission(NameValuePermission permit) { // deep-copy
46             _value = permit._value;
47             _entry = permit._entry;
48             _tree = permit._tree;
49             if (null != _tree) {
50                 NameValuePermission[] tree = (_tree.Clone() as NameValuePermission[]);
51                 for(int i = 0; i < tree.Length; ++i) {
52                     if (null != tree[i]) { // WebData 98488
53                         tree[i] = tree[i].CopyNameValue(); // deep copy
54                     }
55                 }
56                 _tree = tree;
57             }
58         }
59
60         int IComparable.CompareTo(object a) {
61             return StringComparer.Ordinal.Compare(_value, ((NameValuePermission)a)._value);
62         }
63
64         static internal void AddEntry(NameValuePermission kvtree, ArrayList entries, DBConnectionString entry) {
65             Debug.Assert(null != entry, "null DBConnectionString");
66
67             if (null != entry.KeyChain) {
68                 for(NameValuePair keychain = entry.KeyChain; null != keychain; keychain = keychain.Next) {
69                     NameValuePermission kv;
70
71                     kv = kvtree.CheckKeyForValue(keychain.Name);
72                     if (null == kv) {
73                         kv = new NameValuePermission(keychain.Name);
74                         kvtree.Add(kv); // add directly into live tree
75                     }
76                     kvtree = kv;
77
78                     kv = kvtree.CheckKeyForValue(keychain.Value);
79                     if (null == kv) {
80                         DBConnectionString insertValue = ((null != keychain.Next) ? null : entry);
81                         kv = new NameValuePermission(keychain.Value, insertValue);
82                         kvtree.Add(kv); // add directly into live tree
83                         if (null != insertValue) {
84                             entries.Add(insertValue);
85                         }
86                     }
87                     else if (null == keychain.Next) { // shorter chain potential
88                         if (null != kv._entry) {
89                             Debug.Assert(entries.Contains(kv._entry), "entries doesn't contain entry");
90                             entries.Remove(kv._entry);
91                             kv._entry = kv._entry.Intersect(entry); // union new restrictions into existing tree
92                         }
93                         else {
94                             kv._entry = entry;
95                         }
96                         entries.Add(kv._entry);
97                     }
98                     kvtree = kv;
99                 }
100             }
101             else { // global restrictions, MDAC 84443
102                 DBConnectionString kentry = kvtree._entry;
103                 if (null != kentry) {
104                     Debug.Assert(entries.Contains(kentry), "entries doesn't contain entry");
105                     entries.Remove(kentry);
106                     kvtree._entry = kentry.Intersect(entry);
107                 }
108                 else {
109                     kvtree._entry = entry;
110                 }
111                 entries.Add(kvtree._entry);
112             }
113         }
114
115         internal void Intersect(ArrayList entries, NameValuePermission target) {
116             if (null == target) {
117                 _tree = null;
118                 _entry = null;
119             }
120             else {
121                 if (null != _entry) {
122                     entries.Remove(_entry);
123                     _entry = _entry.Intersect(target._entry);
124                     entries.Add(_entry);
125                 }
126                 else if (null != target._entry) {
127                     _entry = target._entry.Intersect(null);
128                     entries.Add(_entry);
129                 }
130
131                 if (null != _tree) {
132                     int count = _tree.Length;
133                     for(int i = 0; i < _tree.Length; ++i) {
134                         NameValuePermission kvtree = target.CheckKeyForValue(_tree[i]._value);
135                         if (null != kvtree) { // does target tree contain our value
136                             _tree[i].Intersect(entries, kvtree);
137                         }
138                         else {
139                             _tree[i] = null;
140                             --count;
141                         }
142                     }
143                     if (0 == count) {
144                         _tree = null;
145                     }
146                     else if (count < _tree.Length) {
147                         NameValuePermission[] kvtree = new NameValuePermission[count];
148                         for (int i = 0, j = 0; i < _tree.Length; ++i) {
149                             if(null != _tree[i]) {
150                                 kvtree[j++] = _tree[i];
151                             }
152                         }
153                         _tree = kvtree;
154                     }
155                 }
156             }
157         }
158
159         private void Add(NameValuePermission permit) {
160             NameValuePermission[] tree = _tree;
161             int length = ((null != tree) ? tree.Length : 0);
162             NameValuePermission[] newtree = new NameValuePermission[1+length];
163             for(int i = 0; i < newtree.Length-1; ++i) {
164                 newtree[i] = tree[i];
165             }
166             newtree[length] = permit;
167             Array.Sort(newtree);
168             _tree = newtree;
169         }
170
171         internal bool CheckValueForKeyPermit(DBConnectionString parsetable) {
172             if (null == parsetable) {
173                 return false;
174             }
175             bool hasMatch = false;
176             NameValuePermission[] keytree = _tree; // _tree won't mutate but Add will replace it
177             if (null != keytree) {
178                 hasMatch = parsetable.IsEmpty; // MDAC 86773
179                 if (!hasMatch) {
180
181                     // which key do we follow the key-value chain on
182                     for (int i = 0; i < keytree.Length; ++i) {
183                         NameValuePermission permitKey = keytree[i];
184                         if (null != permitKey) {
185                             string keyword = permitKey._value;
186 #if DEBUG
187                             Debug.Assert(null == permitKey._entry, "key member has no restrictions");
188 #endif
189                             if (parsetable.ContainsKey(keyword)) {
190                                 string valueInQuestion = (string)parsetable[keyword];
191
192                                 // keyword is restricted to certain values
193                                 NameValuePermission permitValue = permitKey.CheckKeyForValue(valueInQuestion);
194                                 if (null != permitValue) {
195                                     //value does match - continue the chain down that branch
196                                     if (permitValue.CheckValueForKeyPermit(parsetable)) {
197                                         hasMatch = true;
198                                         // adding a break statement is tempting, but wrong
199                                         // user can safetly extend their restrictions for current rule to include missing keyword
200                                         // i.e. Add("provider=sqloledb;integrated security=sspi", "data provider=", KeyRestrictionBehavior.AllowOnly);
201                                         // i.e. Add("data provider=msdatashape;provider=sqloledb;integrated security=sspi", "", KeyRestrictionBehavior.AllowOnly);
202                                     }
203                                     else { // failed branch checking
204                                         return false;
205                                     }
206                                 }
207                                 else { // value doesn't match to expected values - fail here
208                                     return false;
209                                 }
210                             }
211                         }
212                         // else try next keyword
213                     }
214                 }
215                 // partial chain match, either leaf-node by shorter chain or fail mid-chain if (null == _restrictions)
216             }
217
218             DBConnectionString entry = _entry;
219             if (null != entry) {
220                 // also checking !hasMatch is tempting, but wrong
221                 // user can safetly extend their restrictions for current rule to include missing keyword
222                 // i.e. Add("provider=sqloledb;integrated security=sspi", "data provider=", KeyRestrictionBehavior.AllowOnly);
223                 // i.e. Add("provider=sqloledb;", "integrated security=;", KeyRestrictionBehavior.AllowOnly);
224                 hasMatch = entry.IsSupersetOf(parsetable);
225             }
226
227             return hasMatch; // mid-chain failure
228         }
229
230         private NameValuePermission CheckKeyForValue(string keyInQuestion) {
231             NameValuePermission[] valuetree = _tree; // _tree won't mutate but Add will replace it
232             if (null != valuetree) {
233                 for (int i = 0; i < valuetree.Length; ++i) {
234                     NameValuePermission permitValue = valuetree[i];
235                     if (String.Equals(keyInQuestion, permitValue._value, StringComparison.OrdinalIgnoreCase)) {
236                         return permitValue;
237                     }
238                 }
239             }
240             return null;
241         }
242
243         internal NameValuePermission CopyNameValue() {
244             return new NameValuePermission(this);
245         }
246     }
247 }