├── .gitattributes ├── .gitignore ├── AadGraphApiHelper.sln ├── AadGraphApiHelper ├── AadEnvironment.cs ├── AadEnvironmentSet.cs ├── AadGraphApiHelper.csproj ├── AadGraphApiUrlFormatter.cs ├── AddTenantCredentialForm.Designer.cs ├── AddTenantCredentialForm.cs ├── AddTenantCredentialForm.resx ├── ApiVersionSet.cs ├── App.config ├── ApplicationType.cs ├── CollectionExtensions.cs ├── GraphApiEntityType.cs ├── GraphApiProperty.cs ├── GraphApiUrlBuilder.cs ├── GraphApiUrlFilterComponent.cs ├── IGraphApiUrlFormatter.cs ├── IStore.cs ├── JsonText.cs ├── MSGraphApiUrlFormatter.cs ├── MainForm.Designer.cs ├── MainForm.cs ├── MainForm.resx ├── Names.cs ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── QueryBuilderForm.Designer.cs ├── QueryBuilderForm.cs ├── QueryBuilderForm.resx ├── RegistryStore.cs ├── Request.cs ├── RequestHistoryManager.cs ├── RequestHistoryObject.cs ├── RequestHistoryWindow.Designer.cs ├── RequestHistoryWindow.cs ├── RequestHistoryWindow.resx ├── ResponseTable.cs ├── StringResources.Designer.cs ├── StringResources.resx ├── TenantCredential.cs ├── TenantCredentialSet.cs ├── Token.cs └── packages.config └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # Roslyn cache directories 20 | *.ide/ 21 | 22 | # MSTest test Results 23 | [Tt]est[Rr]esult*/ 24 | [Bb]uild[Ll]og.* 25 | 26 | #NUNIT 27 | *.VisualState.xml 28 | TestResult.xml 29 | 30 | # Build Results of an ATL Project 31 | [Dd]ebugPS/ 32 | [Rr]eleasePS/ 33 | dlldata.c 34 | 35 | *_i.c 36 | *_p.c 37 | *_i.h 38 | *.ilk 39 | *.meta 40 | *.obj 41 | *.pch 42 | *.pdb 43 | *.pgc 44 | *.pgd 45 | *.rsp 46 | *.sbr 47 | *.tlb 48 | *.tli 49 | *.tlh 50 | *.tmp 51 | *.tmp_proj 52 | *.log 53 | *.vspscc 54 | *.vssscc 55 | .builds 56 | *.pidb 57 | *.svclog 58 | *.scc 59 | 60 | # Chutzpah Test files 61 | _Chutzpah* 62 | 63 | # Visual C++ cache files 64 | ipch/ 65 | *.aps 66 | *.ncb 67 | *.opensdf 68 | *.sdf 69 | *.cachefile 70 | 71 | # Visual Studio profiler 72 | *.psess 73 | *.vsp 74 | *.vspx 75 | 76 | # TFS 2012 Local Workspace 77 | $tf/ 78 | 79 | # Guidance Automation Toolkit 80 | *.gpState 81 | 82 | # ReSharper is a .NET coding add-in 83 | _ReSharper*/ 84 | *.[Rr]e[Ss]harper 85 | *.DotSettings.user 86 | 87 | # JustCode is a .NET coding addin-in 88 | .JustCode 89 | 90 | # TeamCity is a build add-in 91 | _TeamCity* 92 | 93 | # DotCover is a Code Coverage Tool 94 | *.dotCover 95 | 96 | # NCrunch 97 | _NCrunch_* 98 | .*crunch*.local.xml 99 | 100 | # MightyMoose 101 | *.mm.* 102 | AutoTest.Net/ 103 | 104 | # Web workbench (sass) 105 | .sass-cache/ 106 | 107 | # Installshield output folder 108 | [Ee]xpress/ 109 | 110 | # DocProject is a documentation generator add-in 111 | DocProject/buildhelp/ 112 | DocProject/Help/*.HxT 113 | DocProject/Help/*.HxC 114 | DocProject/Help/*.hhc 115 | DocProject/Help/*.hhk 116 | DocProject/Help/*.hhp 117 | DocProject/Help/Html2 118 | DocProject/Help/html 119 | 120 | # Click-Once directory 121 | publish/ 122 | 123 | # Publish Web Output 124 | *.[Pp]ublish.xml 125 | *.azurePubxml 126 | ## TODO: Comment the next line if you want to checkin your 127 | ## web deploy settings but do note that will include unencrypted 128 | ## passwords 129 | #*.pubxml 130 | 131 | # NuGet Packages Directory 132 | packages/* 133 | ## TODO: If the tool you use requires repositories.config 134 | ## uncomment the next line 135 | #!packages/repositories.config 136 | 137 | # Enable "build/" folder in the NuGet Packages folder since 138 | # NuGet packages use it for MSBuild targets. 139 | # This line needs to be after the ignore of the build folder 140 | # (and the packages folder if the line above has been uncommented) 141 | !packages/build/ 142 | 143 | # Windows Azure Build Output 144 | csx/ 145 | *.build.csdef 146 | 147 | # Windows Store app package directory 148 | AppPackages/ 149 | 150 | # Others 151 | sql/ 152 | *.Cache 153 | ClientBin/ 154 | [Ss]tyle[Cc]op.* 155 | ~$* 156 | *~ 157 | *.dbmdl 158 | *.dbproj.schemaview 159 | *.pfx 160 | *.publishsettings 161 | node_modules/ 162 | 163 | # RIA/Silverlight projects 164 | Generated_Code/ 165 | 166 | # Backup & report files from converting an old project file 167 | # to a newer Visual Studio version. Backup files are not needed, 168 | # because we have git ;-) 169 | _UpgradeReport_Files/ 170 | Backup*/ 171 | UpgradeLog*.XML 172 | UpgradeLog*.htm 173 | 174 | # SQL Server files 175 | *.mdf 176 | *.ldf 177 | 178 | # Business Intelligence projects 179 | *.rdl.data 180 | *.bim.layout 181 | *.bim_*.settings 182 | 183 | # Microsoft Fakes 184 | FakesAssemblies/ 185 | 186 | # LightSwitch generated files 187 | GeneratedArtifacts/ 188 | _Pvt_Extensions/ 189 | ModelManifest.xml -------------------------------------------------------------------------------- /AadGraphApiHelper.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AadGraphApiHelper", "AadGraphApiHelper\AadGraphApiHelper.csproj", "{995E32EB-09D4-42C9-B487-027522DDEAB1}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {995E32EB-09D4-42C9-B487-027522DDEAB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {995E32EB-09D4-42C9-B487-027522DDEAB1}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {995E32EB-09D4-42C9-B487-027522DDEAB1}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {995E32EB-09D4-42C9-B487-027522DDEAB1}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /AadGraphApiHelper/AadEnvironment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | 5 | namespace AadGraphApiHelper 6 | { 7 | internal class AadEnvironment : IEquatable, IComparable 8 | { 9 | private const string UrlFormat = @"https://{0}/{1}"; 10 | 11 | private readonly IReadOnlyList apiVersions; 12 | 13 | public AadEnvironment( 14 | string registryName, 15 | string loginEndpoint, 16 | string graphApiEndpoint, 17 | IList apiVersions, 18 | IGraphApiUrlFormatter urlFormatter) 19 | { 20 | this.RegistryName = registryName; 21 | this.LoginEndpoint = loginEndpoint; 22 | this.GraphApiEndpoint = graphApiEndpoint; 23 | this.UrlFormatter = urlFormatter; 24 | this.apiVersions = new List(apiVersions); 25 | } 26 | 27 | public string DisplayName => this.GraphApiEndpoint; 28 | 29 | public string RegistryName { get; private set; } 30 | 31 | public string LoginEndpoint { get; private set; } 32 | 33 | public string GraphApiEndpoint { get; private set; } 34 | 35 | public IGraphApiUrlFormatter UrlFormatter { get; private set; } 36 | 37 | public string GraphResourceUrl 38 | { 39 | get 40 | { 41 | return String.Format(CultureInfo.InvariantCulture, @"https://{0}", this.GraphApiEndpoint); 42 | } 43 | } 44 | 45 | public string GetLoginUrl(string tenant) 46 | { 47 | return String.Format(UrlFormat, this.LoginEndpoint, tenant); 48 | } 49 | 50 | public string GetTenantGraphUrl(string tenant) 51 | { 52 | return String.Format(UrlFormat, this.GraphApiEndpoint, tenant); 53 | } 54 | 55 | public IReadOnlyList ApiVersions => this.apiVersions; 56 | 57 | /// 58 | /// Determines whether the specified is equal to the current . 59 | /// 60 | /// 61 | /// true if the specified object is equal to the current object; otherwise, false. 62 | /// 63 | /// The object to compare with the current object. 64 | public override bool Equals(object obj) 65 | { 66 | if (obj == null) 67 | { 68 | return false; 69 | } 70 | 71 | return this.Equals(obj as AadEnvironment); 72 | } 73 | 74 | /// 75 | /// Indicates whether the current object is equal to another object of the same type. 76 | /// 77 | /// 78 | /// true if the current object is equal to the parameter; otherwise, false. 79 | /// 80 | /// An object to compare with this object. 81 | public bool Equals(AadEnvironment other) 82 | { 83 | if (other == null) 84 | { 85 | return false; 86 | } 87 | 88 | return this.GraphApiEndpoint.Equals(other.GraphApiEndpoint, StringComparison.OrdinalIgnoreCase); 89 | } 90 | 91 | /// 92 | /// Serves as a hash function for a particular type. 93 | /// 94 | /// 95 | /// A hash code for the current . 96 | /// 97 | public override int GetHashCode() 98 | { 99 | return this.GraphApiEndpoint.ToUpperInvariant().GetHashCode(); 100 | } 101 | 102 | /// 103 | /// Compares the current object with another object of the same type. 104 | /// 105 | /// 106 | /// A value that indicates the relative order of the objects being compared. The return value has the following meanings: 107 | /// Value Meaning Less than zero This object is less than the parameter. 108 | /// Zero This object is equal to . 109 | /// Greater than zero This object is greater than . 110 | /// 111 | /// An object to compare with this object. 112 | public int CompareTo(AadEnvironment other) 113 | { 114 | if (other == null) 115 | { 116 | return -1; 117 | } 118 | 119 | return this.GraphApiEndpoint.ToUpperInvariant().CompareTo(other.GraphApiEndpoint.ToUpperInvariant()); 120 | } 121 | 122 | /// 123 | /// Returns a string that represents the current object. 124 | /// 125 | /// 126 | /// A string that represents the current object. 127 | /// 128 | public override string ToString() 129 | { 130 | return this.GraphApiEndpoint; 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /AadGraphApiHelper/AadEnvironmentSet.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace AadGraphApiHelper 4 | { 5 | internal class AadEnvironmentSet : List 6 | { 7 | private static readonly IList aadGraphApiVersions = new[] { @"1.6", @"1.61", @"beta" }; 8 | 9 | private static readonly IList msGraphApiVersions = new[] { @"v1.0", @"beta" }; 10 | 11 | public AadEnvironmentSet() 12 | { 13 | this.Add(MSGraphProduction); 14 | this.Add(AadGraphProduction); 15 | this.Add(MSGraphPreProduction); 16 | this.Add(AadGraphPreProduction); 17 | } 18 | 19 | 20 | public static AadEnvironment AadGraphProduction 21 | { 22 | get 23 | { 24 | return new AadEnvironment( 25 | @"Production", 26 | @"login.windows.net", 27 | @"graph.windows.net", 28 | aadGraphApiVersions, 29 | new AadGraphApiUrlFormatter()); 30 | } 31 | } 32 | public static AadEnvironment MSGraphProduction 33 | { 34 | get 35 | { 36 | return new AadEnvironment( 37 | @"Production", 38 | @"login.windows.net", 39 | @"graph.microsoft.com", 40 | msGraphApiVersions, 41 | new MSGraphApiUrlFormatter()); 42 | } 43 | } 44 | 45 | public static AadEnvironment AadGraphPreProduction 46 | { 47 | get 48 | { 49 | return new AadEnvironment( 50 | @"PPE", 51 | @"login.windows-ppe.net", 52 | @"graph.ppe.windows.net", 53 | aadGraphApiVersions, 54 | new AadGraphApiUrlFormatter()); 55 | } 56 | } 57 | 58 | public static AadEnvironment MSGraphPreProduction 59 | { 60 | get 61 | { 62 | return new AadEnvironment( 63 | @"PPE", 64 | @"login.windows-ppe.net", 65 | @"graph.microsoft-ppe.com", 66 | msGraphApiVersions, 67 | new MSGraphApiUrlFormatter()); 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /AadGraphApiHelper/AadGraphApiHelper.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {995E32EB-09D4-42C9-B487-027522DDEAB1} 8 | WinExe 9 | Properties 10 | AadGraphApiHelper 11 | AadGraphApiHelper 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | TRACE;DEBUG 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.16.204221202\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll 37 | True 38 | 39 | 40 | ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.16.204221202\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll 41 | 42 | 43 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 44 | True 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Form 71 | 72 | 73 | QueryBuilderForm.cs 74 | 75 | 76 | 77 | 78 | Form 79 | 80 | 81 | RequestHistoryWindow.cs 82 | 83 | 84 | 85 | Code 86 | 87 | 88 | Form 89 | 90 | 91 | AddTenantCredentialForm.cs 92 | 93 | 94 | 95 | 96 | 97 | Form 98 | 99 | 100 | MainForm.cs 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | True 111 | True 112 | StringResources.resx 113 | 114 | 115 | 116 | 117 | AddTenantCredentialForm.cs 118 | 119 | 120 | MainForm.cs 121 | 122 | 123 | ResXFileCodeGenerator 124 | Resources.Designer.cs 125 | Designer 126 | 127 | 128 | True 129 | Resources.resx 130 | 131 | 132 | QueryBuilderForm.cs 133 | 134 | 135 | RequestHistoryWindow.cs 136 | 137 | 138 | ResXFileCodeGenerator 139 | StringResources.Designer.cs 140 | 141 | 142 | 143 | SettingsSingleFileGenerator 144 | Settings.Designer.cs 145 | 146 | 147 | True 148 | Settings.settings 149 | True 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 169 | -------------------------------------------------------------------------------- /AadGraphApiHelper/AadGraphApiUrlFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace AadGraphApiHelper 6 | { 7 | internal class AadGraphApiUrlFormatter : IGraphApiUrlFormatter 8 | { 9 | public string FormatUrl(GraphApiUrlBuilder builder) 10 | { 11 | if (builder.Environment == null || 12 | builder.TenantCredential == null || 13 | String.IsNullOrWhiteSpace(builder.ResourceFirst) || 14 | String.IsNullOrWhiteSpace(builder.ApiVersion)) 15 | { 16 | return null; 17 | } 18 | 19 | UriBuilder uriBuilder = new UriBuilder(builder.Environment.GraphResourceUrl); 20 | uriBuilder.Path = builder.TenantCredential.Tenant + '/' + builder.ResourceFirst; 21 | if (!String.IsNullOrWhiteSpace(builder.ResourceId)) 22 | { 23 | uriBuilder.Path += '/' + builder.ResourceId; 24 | } 25 | 26 | if (!String.IsNullOrWhiteSpace(builder.ResourceSecond)) 27 | { 28 | uriBuilder.Path += '/' + builder.ResourceSecond; 29 | } 30 | 31 | IDictionary queryPairs = new Dictionary(); 32 | 33 | string filterQuery = this.CreateFilterQuery(builder); 34 | if (!String.IsNullOrWhiteSpace(filterQuery)) 35 | { 36 | queryPairs["$filter"] = filterQuery; 37 | } 38 | 39 | queryPairs["api-version"] = builder.ApiVersion; 40 | 41 | uriBuilder.Query = ConvertQueryCollection(queryPairs); 42 | uriBuilder.Port = -1; 43 | return uriBuilder.ToString(); 44 | } 45 | 46 | private static string ConvertQueryCollection(IDictionary queryPairs) 47 | { 48 | if (queryPairs == null || queryPairs.Count == 0) 49 | { 50 | return null; 51 | } 52 | 53 | StringBuilder stringBuilder = new StringBuilder(); 54 | foreach (KeyValuePair pair in queryPairs) 55 | { 56 | if (stringBuilder.Length > 0) 57 | { 58 | stringBuilder.Append(@"&"); 59 | } 60 | 61 | stringBuilder.AppendFormat("{0}={1}", pair.Key, pair.Value); 62 | } 63 | 64 | return stringBuilder.ToString(); 65 | } 66 | 67 | private string CreateFilterQuery(GraphApiUrlBuilder builder) 68 | { 69 | const string LogicalOperatorFormat = @" {0} "; 70 | const string AnyEqString = @"{0}/any(p:p {1} '{2}')"; 71 | const string FunctionOperator = @"{0}({1},'{2}')"; 72 | const string OperatorForStrings = @"{0} {1} '{2}'"; 73 | const string OperatorForGuids = @"{0} {1} '{2}'"; 74 | const string OperatorForOthers = @"{0} {1} {2}"; 75 | StringBuilder filterQueryBuilder = new StringBuilder(); 76 | foreach (GraphApiUrlFilterComponent filterComponent in builder.FilterComponents) 77 | { 78 | string andOr = filterComponent.LogicalOperator; 79 | string op = filterComponent.ComparisonOperator; 80 | GraphApiProperty property = filterComponent.Property; 81 | string value = filterComponent.Value; 82 | 83 | if (filterQueryBuilder.Length > 0) 84 | { 85 | filterQueryBuilder.AppendFormat(LogicalOperatorFormat, andOr); 86 | } 87 | 88 | if (op.Equals(Names.StartsWithOperator, StringComparison.OrdinalIgnoreCase)) 89 | { 90 | filterQueryBuilder.AppendFormat(FunctionOperator, Names.StartsWithOperator, property.Name, value); 91 | } 92 | else if (op.Equals(Names.AnyEqualsOperator, StringComparison.OrdinalIgnoreCase)) 93 | { 94 | filterQueryBuilder.AppendFormat(AnyEqString, property.Name, Names.EqualToOperator, value); 95 | } 96 | else if (property.Type == typeof(Guid)) 97 | { 98 | filterQueryBuilder.AppendFormat(OperatorForGuids, property.Name, op, value); 99 | } 100 | else if (property.Type == typeof(bool) || property.Type == typeof(int)) 101 | { 102 | filterQueryBuilder.AppendFormat(OperatorForOthers, property.Name, op, value); 103 | } 104 | else 105 | { 106 | filterQueryBuilder.AppendFormat(OperatorForStrings, property.Name, op, value); 107 | } 108 | } 109 | 110 | return filterQueryBuilder.ToString(); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /AadGraphApiHelper/AddTenantCredentialForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace AadGraphApiHelper 2 | { 3 | partial class AddTenantCredentialForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.clientIdLabel = new System.Windows.Forms.Label(); 32 | this.clientIdTextBox = new System.Windows.Forms.TextBox(); 33 | this.keyOrReplyUrlLabel = new System.Windows.Forms.Label(); 34 | this.keyOrReplyUrlTextBox = new System.Windows.Forms.TextBox(); 35 | this.tenantLabel = new System.Windows.Forms.Label(); 36 | this.addButton = new System.Windows.Forms.Button(); 37 | this.cancelButton = new System.Windows.Forms.Button(); 38 | this.tenantTextBox = new System.Windows.Forms.TextBox(); 39 | this.saveCredentialCheckBox = new System.Windows.Forms.CheckBox(); 40 | this.webAppRadioButton = new System.Windows.Forms.RadioButton(); 41 | this.nativeAppRadioButton = new System.Windows.Forms.RadioButton(); 42 | this.authenticationTypeGroupBox = new System.Windows.Forms.GroupBox(); 43 | this.label1 = new System.Windows.Forms.Label(); 44 | this.aliasTextBox = new System.Windows.Forms.TextBox(); 45 | this.authenticationTypeGroupBox.SuspendLayout(); 46 | this.SuspendLayout(); 47 | // 48 | // clientIdLabel 49 | // 50 | this.clientIdLabel.AutoSize = true; 51 | this.clientIdLabel.Location = new System.Drawing.Point(14, 58); 52 | this.clientIdLabel.Name = "clientIdLabel"; 53 | this.clientIdLabel.Size = new System.Drawing.Size(65, 19); 54 | this.clientIdLabel.TabIndex = 10; 55 | this.clientIdLabel.Text = "Client &ID:"; 56 | // 57 | // clientIdTextBox 58 | // 59 | this.clientIdTextBox.Location = new System.Drawing.Point(119, 54); 60 | this.clientIdTextBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 61 | this.clientIdTextBox.Name = "clientIdTextBox"; 62 | this.clientIdTextBox.Size = new System.Drawing.Size(517, 25); 63 | this.clientIdTextBox.TabIndex = 11; 64 | this.clientIdTextBox.TextChanged += new System.EventHandler(this.clientIdTextBox_TextChanged); 65 | // 66 | // keyOrReplyUrlLabel 67 | // 68 | this.keyOrReplyUrlLabel.AutoSize = true; 69 | this.keyOrReplyUrlLabel.Enabled = false; 70 | this.keyOrReplyUrlLabel.Location = new System.Drawing.Point(14, 264); 71 | this.keyOrReplyUrlLabel.Name = "keyOrReplyUrlLabel"; 72 | this.keyOrReplyUrlLabel.Size = new System.Drawing.Size(34, 19); 73 | this.keyOrReplyUrlLabel.TabIndex = 20; 74 | this.keyOrReplyUrlLabel.Text = "&Key:"; 75 | // 76 | // keyOrReplyUrlTextBox 77 | // 78 | this.keyOrReplyUrlTextBox.Enabled = false; 79 | this.keyOrReplyUrlTextBox.Location = new System.Drawing.Point(119, 258); 80 | this.keyOrReplyUrlTextBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 81 | this.keyOrReplyUrlTextBox.Name = "keyOrReplyUrlTextBox"; 82 | this.keyOrReplyUrlTextBox.Size = new System.Drawing.Size(506, 25); 83 | this.keyOrReplyUrlTextBox.TabIndex = 21; 84 | this.keyOrReplyUrlTextBox.TextChanged += new System.EventHandler(this.clientKeyTextBox_TextChanged); 85 | // 86 | // tenantLabel 87 | // 88 | this.tenantLabel.AutoSize = true; 89 | this.tenantLabel.Location = new System.Drawing.Point(14, 21); 90 | this.tenantLabel.Name = "tenantLabel"; 91 | this.tenantLabel.Size = new System.Drawing.Size(53, 19); 92 | this.tenantLabel.TabIndex = 0; 93 | this.tenantLabel.Text = "&Tenant:"; 94 | // 95 | // addButton 96 | // 97 | this.addButton.Enabled = false; 98 | this.addButton.Location = new System.Drawing.Point(445, 292); 99 | this.addButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 100 | this.addButton.Name = "addButton"; 101 | this.addButton.Size = new System.Drawing.Size(87, 30); 102 | this.addButton.TabIndex = 30; 103 | this.addButton.Text = "&Add"; 104 | this.addButton.UseVisualStyleBackColor = true; 105 | this.addButton.Click += new System.EventHandler(this.addButton_Click); 106 | // 107 | // cancelButton 108 | // 109 | this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; 110 | this.cancelButton.Location = new System.Drawing.Point(538, 292); 111 | this.cancelButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 112 | this.cancelButton.Name = "cancelButton"; 113 | this.cancelButton.Size = new System.Drawing.Size(87, 30); 114 | this.cancelButton.TabIndex = 31; 115 | this.cancelButton.Text = "Cancel"; 116 | this.cancelButton.UseVisualStyleBackColor = true; 117 | this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); 118 | // 119 | // tenantTextBox 120 | // 121 | this.tenantTextBox.Location = new System.Drawing.Point(119, 17); 122 | this.tenantTextBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 123 | this.tenantTextBox.Name = "tenantTextBox"; 124 | this.tenantTextBox.Size = new System.Drawing.Size(517, 25); 125 | this.tenantTextBox.TabIndex = 1; 126 | this.tenantTextBox.Text = ".onmicrosoft.com"; 127 | this.tenantTextBox.TextChanged += new System.EventHandler(this.tenantTextBox_TextChanged); 128 | // 129 | // saveCredentialCheckBox 130 | // 131 | this.saveCredentialCheckBox.AutoSize = true; 132 | this.saveCredentialCheckBox.Checked = true; 133 | this.saveCredentialCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; 134 | this.saveCredentialCheckBox.Enabled = false; 135 | this.saveCredentialCheckBox.Location = new System.Drawing.Point(79, 297); 136 | this.saveCredentialCheckBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 137 | this.saveCredentialCheckBox.Name = "saveCredentialCheckBox"; 138 | this.saveCredentialCheckBox.Size = new System.Drawing.Size(342, 23); 139 | this.saveCredentialCheckBox.TabIndex = 28; 140 | this.saveCredentialCheckBox.Text = "&Save Credential (Key is stored encrypted in registry)"; 141 | this.saveCredentialCheckBox.UseVisualStyleBackColor = true; 142 | // 143 | // webAppRadioButton 144 | // 145 | this.webAppRadioButton.AutoSize = true; 146 | this.webAppRadioButton.Location = new System.Drawing.Point(7, 25); 147 | this.webAppRadioButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 148 | this.webAppRadioButton.Name = "webAppRadioButton"; 149 | this.webAppRadioButton.Size = new System.Drawing.Size(83, 23); 150 | this.webAppRadioButton.TabIndex = 32; 151 | this.webAppRadioButton.TabStop = true; 152 | this.webAppRadioButton.Text = "Web App"; 153 | this.webAppRadioButton.UseVisualStyleBackColor = true; 154 | this.webAppRadioButton.CheckedChanged += new System.EventHandler(this.webAppRadioButton_CheckedChanged); 155 | // 156 | // nativeAppRadioButton 157 | // 158 | this.nativeAppRadioButton.AutoSize = true; 159 | this.nativeAppRadioButton.Location = new System.Drawing.Point(7, 55); 160 | this.nativeAppRadioButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 161 | this.nativeAppRadioButton.Name = "nativeAppRadioButton"; 162 | this.nativeAppRadioButton.Size = new System.Drawing.Size(95, 23); 163 | this.nativeAppRadioButton.TabIndex = 33; 164 | this.nativeAppRadioButton.TabStop = true; 165 | this.nativeAppRadioButton.Text = "Native App"; 166 | this.nativeAppRadioButton.UseVisualStyleBackColor = true; 167 | this.nativeAppRadioButton.CheckedChanged += new System.EventHandler(this.nativeAppRadioButton_CheckedChanged); 168 | // 169 | // authenticationTypeGroupBox 170 | // 171 | this.authenticationTypeGroupBox.Controls.Add(this.nativeAppRadioButton); 172 | this.authenticationTypeGroupBox.Controls.Add(this.webAppRadioButton); 173 | this.authenticationTypeGroupBox.Location = new System.Drawing.Point(18, 145); 174 | this.authenticationTypeGroupBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 175 | this.authenticationTypeGroupBox.Name = "authenticationTypeGroupBox"; 176 | this.authenticationTypeGroupBox.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4); 177 | this.authenticationTypeGroupBox.Size = new System.Drawing.Size(607, 92); 178 | this.authenticationTypeGroupBox.TabIndex = 34; 179 | this.authenticationTypeGroupBox.TabStop = false; 180 | this.authenticationTypeGroupBox.Text = "Type of App"; 181 | // 182 | // label1 183 | // 184 | this.label1.AutoSize = true; 185 | this.label1.Location = new System.Drawing.Point(14, 103); 186 | this.label1.Name = "label1"; 187 | this.label1.Size = new System.Drawing.Size(99, 19); 188 | this.label1.TabIndex = 35; 189 | this.label1.Text = "Alias (optional)"; 190 | // 191 | // aliasTextBox 192 | // 193 | this.aliasTextBox.Location = new System.Drawing.Point(119, 97); 194 | this.aliasTextBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 195 | this.aliasTextBox.Name = "aliasTextBox"; 196 | this.aliasTextBox.Size = new System.Drawing.Size(517, 25); 197 | this.aliasTextBox.TabIndex = 36; 198 | // 199 | // AddTenantCredentialForm 200 | // 201 | this.AcceptButton = this.addButton; 202 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); 203 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 204 | this.CancelButton = this.cancelButton; 205 | this.ClientSize = new System.Drawing.Size(648, 353); 206 | this.ControlBox = false; 207 | this.Controls.Add(this.aliasTextBox); 208 | this.Controls.Add(this.label1); 209 | this.Controls.Add(this.authenticationTypeGroupBox); 210 | this.Controls.Add(this.saveCredentialCheckBox); 211 | this.Controls.Add(this.tenantTextBox); 212 | this.Controls.Add(this.cancelButton); 213 | this.Controls.Add(this.addButton); 214 | this.Controls.Add(this.keyOrReplyUrlTextBox); 215 | this.Controls.Add(this.keyOrReplyUrlLabel); 216 | this.Controls.Add(this.clientIdTextBox); 217 | this.Controls.Add(this.clientIdLabel); 218 | this.Controls.Add(this.tenantLabel); 219 | this.Font = new System.Drawing.Font("Segoe UI", 10F); 220 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 221 | this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 222 | this.MaximizeBox = false; 223 | this.Name = "AddTenantCredentialForm"; 224 | this.ShowInTaskbar = false; 225 | this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; 226 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 227 | this.Text = "Add Tenant Credential"; 228 | this.authenticationTypeGroupBox.ResumeLayout(false); 229 | this.authenticationTypeGroupBox.PerformLayout(); 230 | this.ResumeLayout(false); 231 | this.PerformLayout(); 232 | 233 | } 234 | 235 | #endregion 236 | 237 | private System.Windows.Forms.Label clientIdLabel; 238 | private System.Windows.Forms.TextBox clientIdTextBox; 239 | private System.Windows.Forms.Label keyOrReplyUrlLabel; 240 | private System.Windows.Forms.TextBox keyOrReplyUrlTextBox; 241 | private System.Windows.Forms.Label tenantLabel; 242 | private System.Windows.Forms.Button addButton; 243 | private System.Windows.Forms.Button cancelButton; 244 | private System.Windows.Forms.TextBox tenantTextBox; 245 | private System.Windows.Forms.CheckBox saveCredentialCheckBox; 246 | private System.Windows.Forms.RadioButton webAppRadioButton; 247 | private System.Windows.Forms.RadioButton nativeAppRadioButton; 248 | private System.Windows.Forms.GroupBox authenticationTypeGroupBox; 249 | private System.Windows.Forms.Label label1; 250 | private System.Windows.Forms.TextBox aliasTextBox; 251 | } 252 | } -------------------------------------------------------------------------------- /AadGraphApiHelper/AddTenantCredentialForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Windows.Forms; 4 | 5 | namespace AadGraphApiHelper 6 | { 7 | public partial class AddTenantCredentialForm : Form 8 | { 9 | private MainForm mainForm; 10 | 11 | private readonly Color errorColor = Color.LightCoral; 12 | 13 | private readonly Color defaultControlColor = SystemColors.Window; 14 | 15 | public AddTenantCredentialForm(MainForm mainForm) 16 | { 17 | this.InitializeComponent(); 18 | this.mainForm = mainForm; 19 | } 20 | 21 | private void addButton_Click(object sender, EventArgs e) 22 | { 23 | ApplicationType applicationType = ApplicationType.Web; 24 | 25 | if (this.nativeAppRadioButton.Checked) 26 | { 27 | applicationType = ApplicationType.Native; 28 | } 29 | 30 | TenantCredential tenantCredential = new TenantCredential( 31 | (AadEnvironment)this.mainForm.EnvironmentComboBox.SelectedItem, 32 | this.tenantTextBox.Text.Trim().ToLowerInvariant(), 33 | this.clientIdTextBox.Text.Trim().ToLowerInvariant(), 34 | applicationType, 35 | aliasTextBox.Text); 36 | 37 | if (applicationType == ApplicationType.Native) 38 | { 39 | tenantCredential.ReplyUrl = new Uri(this.keyOrReplyUrlTextBox.Text); 40 | } 41 | else 42 | { 43 | tenantCredential.EncryptAndSetKey(this.keyOrReplyUrlTextBox.Text.Trim()); 44 | } 45 | 46 | this.mainForm.TenantCredentialComboBox.Items.Add(tenantCredential); 47 | this.mainForm.TenantCredentialComboBox.SelectedItem = tenantCredential; 48 | 49 | if (this.nativeAppRadioButton.Checked || (this.webAppRadioButton.Checked && this.saveCredentialCheckBox.Checked)) 50 | { 51 | this.mainForm.Store.Store(tenantCredential); 52 | } 53 | 54 | this.Close(); 55 | } 56 | 57 | private void cancelButton_Click(object sender, EventArgs e) 58 | { 59 | this.Close(); 60 | } 61 | 62 | private void tenantTextBox_TextChanged(object sender, EventArgs e) 63 | { 64 | this.SetAddButtonState(); 65 | } 66 | 67 | private void clientIdTextBox_TextChanged(object sender, EventArgs e) 68 | { 69 | this.SetAddButtonState(); 70 | } 71 | 72 | private void clientKeyTextBox_TextChanged(object sender, EventArgs e) 73 | { 74 | this.SetAddButtonState(); 75 | } 76 | 77 | private void SetAddButtonState() 78 | { 79 | this.tenantTextBox.BackColor = String.IsNullOrWhiteSpace(this.tenantTextBox.Text) ? this.errorColor : this.defaultControlColor; 80 | 81 | if (!String.IsNullOrEmpty(this.clientIdTextBox.Text)) 82 | { 83 | this.clientIdTextBox.BackColor = this.IsClientIdValid() ? this.defaultControlColor : this.errorColor; 84 | } 85 | 86 | if (this.keyOrReplyUrlTextBox.Enabled && !String.IsNullOrEmpty(this.keyOrReplyUrlTextBox.Text)) 87 | { 88 | this.keyOrReplyUrlTextBox.BackColor = this.IsKeyOrReplyUrlValid() ? this.defaultControlColor : this.errorColor; 89 | } 90 | 91 | if (!String.IsNullOrWhiteSpace(this.tenantTextBox.Text) && 92 | this.IsClientIdValid() && 93 | this.IsKeyOrReplyUrlValid() && 94 | (this.nativeAppRadioButton.Checked || this.webAppRadioButton.Checked)) 95 | { 96 | this.addButton.Enabled = true; 97 | } 98 | else 99 | { 100 | this.addButton.Enabled = false; 101 | } 102 | } 103 | 104 | private void webAppRadioButton_CheckedChanged(object sender, EventArgs e) 105 | { 106 | this.keyOrReplyUrlLabel.Text = StringResources.ClientKeyLabel; 107 | this.EnableControls(); 108 | this.SetAddButtonState(); 109 | } 110 | 111 | private void nativeAppRadioButton_CheckedChanged(object sender, EventArgs e) 112 | { 113 | this.keyOrReplyUrlLabel.Text = StringResources.ClientReplyUrlLabel; 114 | this.EnableControls(); 115 | this.SetAddButtonState(); 116 | } 117 | 118 | private void EnableControls() 119 | { 120 | this.keyOrReplyUrlLabel.Enabled = true; 121 | this.keyOrReplyUrlTextBox.Enabled = true; 122 | this.saveCredentialCheckBox.Enabled = !this.nativeAppRadioButton.Checked; 123 | } 124 | 125 | private bool IsClientIdValid() 126 | { 127 | Guid ignored; 128 | return Guid.TryParse(this.clientIdTextBox.Text, out ignored); 129 | } 130 | 131 | private bool IsKeyOrReplyUrlValid() 132 | { 133 | if (this.webAppRadioButton.Checked) 134 | { 135 | return true; 136 | } 137 | else 138 | { 139 | return this.IsReplyUrlValid(); 140 | } 141 | } 142 | 143 | private bool IsReplyUrlValid() 144 | { 145 | Uri uriResult; 146 | return Uri.TryCreate(this.keyOrReplyUrlTextBox.Text, UriKind.Absolute, out uriResult) && uriResult.Scheme == Uri.UriSchemeHttp | 147 | Uri.TryCreate(this.keyOrReplyUrlTextBox.Text, UriKind.Absolute, out uriResult) && uriResult.Scheme == Uri.UriSchemeHttps; 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /AadGraphApiHelper/AddTenantCredentialForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /AadGraphApiHelper/ApiVersionSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace AadGraphApiHelper 5 | { 6 | internal class ApiVersionSet : SortedSet 7 | { 8 | internal ApiVersionSet() : base(StringComparer.OrdinalIgnoreCase) 9 | { 10 | this.Add(@"1.5"); 11 | this.Add(@"1.6"); 12 | this.Add(@"beta"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /AadGraphApiHelper/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /AadGraphApiHelper/ApplicationType.cs: -------------------------------------------------------------------------------- 1 | namespace AadGraphApiHelper 2 | { 3 | internal enum ApplicationType 4 | { 5 | Unknown = 0, 6 | 7 | Native = 1, 8 | 9 | Web = 2 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /AadGraphApiHelper/CollectionExtensions.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Collections.ObjectModel; 6 | using System.Linq; 7 | 8 | namespace AadGraphApiHelper 9 | { 10 | internal static class CollectionExtensions 11 | { 12 | /// 13 | /// Casts down each item in the provided enumerable to create a collection that is strongly typed. 14 | /// 15 | /// The enumeration to convert. 16 | /// An enumeration that has items converted to one of the supported types. 17 | /// true if the conversion succeeded, false otherwise. 18 | /// 19 | /// There are cases when a enuerable of typed objects is contains in a non-generic enumerable or an enumerable of objects. For 20 | /// example, a collection of strings { "a", "b" } may be available as an or . 21 | /// This method iterates through every item in the collection and attempts to cast it down to the type of the items if possible. 22 | /// Thus, { "a", "b" } will be created as a and returned. If all items are not of the same 23 | /// type, then the conversion is not performed and the method returns false. 24 | /// 25 | public static bool TryConvertToTypedCollection(this IEnumerable enumerable, out ICollection convertedObject) 26 | { 27 | if (enumerable == null) 28 | { 29 | convertedObject = null; 30 | return false; 31 | } 32 | 33 | ICollection objectCollection = (ICollection) enumerable as Collection ?? 34 | (ICollection) enumerable.Cast().ToList(); 35 | 36 | if (objectCollection.Count == 0) 37 | { 38 | convertedObject = objectCollection; 39 | return true; 40 | } 41 | 42 | Type firstItemType = null; 43 | IList typedCollection = null; 44 | 45 | foreach (object item in objectCollection) 46 | { 47 | try 48 | { 49 | if (firstItemType == null) 50 | { 51 | // Create a list of the type of the first item 52 | firstItemType = item.GetType(); 53 | Type genericType = typeof(List<>).MakeGenericType(new[] {firstItemType}); 54 | typedCollection = (IList) Activator.CreateInstance(genericType); 55 | } 56 | else if (item.GetType() != firstItemType) 57 | { 58 | // Essentially we're dealing with a collection that has items with different types, e.g. { "name", 2, true }. 59 | // Instead of casting this out to object type, we just do not cast since the purpose of the method is to cast down. 60 | convertedObject = null; 61 | return false; 62 | } 63 | 64 | typedCollection.Add(Convert.ChangeType(item, firstItemType)); 65 | } 66 | catch (InvalidCastException) 67 | { 68 | convertedObject = null; 69 | return false; 70 | } 71 | } 72 | 73 | convertedObject = typedCollection; 74 | return true; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /AadGraphApiHelper/GraphApiEntityType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace AadGraphApiHelper 5 | { 6 | // Ideally we would get this from the service metadata. However, certain 7 | // information, such as which properties are filterable, is not available 8 | // in the service metadata, and needs to be hardcoded. Then, instead of 9 | // splitting information (i.e. some hardcoded, and some 10 | // retrieved from metadata), all the information is hardcoded here. 11 | // 12 | // TODO: The only place where getting the service metadata will be useful 13 | // would be to determine which properties are available in which api versions. 14 | internal class GraphApiEntityType 15 | { 16 | // TODO: Merge navigation properties into the entity, after entity types are added for all objects 17 | private static readonly IDictionary> navigationProperties = new Dictionary>(StringComparer.OrdinalIgnoreCase) 18 | { 19 | { 20 | Names.Applications, new HashSet 21 | { 22 | Names.Owners, 23 | Names.OwnersLinks, 24 | Names.ExtensionProperties 25 | } 26 | }, 27 | { 28 | Names.Contacts, new HashSet 29 | { 30 | Names.Manager, 31 | Names.ManagerLinks, 32 | Names.DirectReports, 33 | Names.DirectReportsLinks, 34 | Names.MemberOf, 35 | Names.MemberOfLinks 36 | } 37 | }, 38 | { 39 | Names.Contracts, new HashSet() 40 | }, 41 | { 42 | Names.Devices, new HashSet 43 | { 44 | Names.RegisteredOwners, 45 | Names.RegisteredOwnersLinks, 46 | Names.RegisteredUsers, 47 | Names.RegisteredUsersLinks 48 | } 49 | }, 50 | { 51 | Names.DirectoryRoles, new HashSet 52 | { 53 | Names.Members, 54 | Names.MembersLinks 55 | } 56 | }, 57 | { 58 | Names.DirectoryRoleTemplates, new HashSet() 59 | }, 60 | { 61 | Names.Groups, new HashSet 62 | { 63 | Names.Members, 64 | Names.MembersLinks, 65 | Names.MemberOf, 66 | Names.MemberOfLinks, 67 | Names.Owners, 68 | Names.OwnersLinks, 69 | Names.AppRoleAssignments, 70 | Names.AppRoleAssignmentsLinks 71 | } 72 | }, 73 | { 74 | Names.OAuth2PermissionGrants, new HashSet() 75 | }, 76 | { 77 | Names.ServicePrincipals, new HashSet 78 | { 79 | Names.AppRoleAssignedTo, 80 | Names.AppRolesAssignedToLinks, 81 | Names.AppRoleAssignments, 82 | Names.AppRoleAssignmentsLinks, 83 | Names.CreatedObjects, 84 | Names.CreatedObjectsLinks, 85 | Names.MemberOf, 86 | Names.MemberOfLinks, 87 | Names.OAuth2PermissionGrants, 88 | Names.OAuth2PermissionGrantsLinks, 89 | Names.OwnedObjects, 90 | Names.OwnedObjectsLinks, 91 | Names.Owners, 92 | Names.OwnersLinks 93 | } 94 | }, 95 | { 96 | Names.SubscribedSkus, new HashSet() 97 | }, 98 | { 99 | Names.TenantDetails, new HashSet() 100 | }, 101 | { 102 | Names.Users, new HashSet 103 | { 104 | Names.Manager, 105 | Names.ManagerLinks, 106 | Names.DirectReports, 107 | Names.DirectReportsLinks, 108 | Names.MemberOf, 109 | Names.MemberOfLinks, 110 | Names.OwnedDevices, 111 | Names.OwnedDevicesLinks, 112 | Names.RegisteredDevices, 113 | Names.RegisteredDevicesLinks, 114 | Names.CreatedObjects, 115 | Names.CreatedObjectsLinks, 116 | Names.OwnedObjects, 117 | Names.OwnedObjectsLinks, 118 | Names.AppRoleAssignments, 119 | Names.AppRoleAssignmentsLinks, 120 | Names.OAuth2PermissionGrants, 121 | Names.OAuth2PermissionGrantsLinks, 122 | Names.CheckMemberGroups, 123 | Names.GetMemberGroups 124 | } 125 | } 126 | }; 127 | 128 | private static readonly IList entities = new List 129 | { 130 | new GraphApiEntityType(Names.Applications) 131 | { 132 | Properties = new List 133 | { 134 | new GraphApiProperty(Names.AppId, typeof(string), true), 135 | new GraphApiProperty(Names.AvailableToOtherTenants, typeof(bool), true), 136 | new GraphApiProperty(Names.DisplayName, typeof(string), true), 137 | new GraphApiProperty(Names.ErrorUrl, typeof(string), false), 138 | new GraphApiProperty(Names.GroupMembershipClaims, typeof(string), false), 139 | new GraphApiProperty(Names.HomePage, typeof(string), false), 140 | new GraphApiProperty(Names.IdentifierUris, typeof(ICollection), true), 141 | new GraphApiProperty(Names.LogoutUrl, typeof(string), false), 142 | new GraphApiProperty(Names.OAuth2AllowImplicitFlow, typeof(bool), false), 143 | new GraphApiProperty(Names.OAuth2AllowUrlPathMatching, typeof(bool), false), 144 | new GraphApiProperty(Names.OAuth2RequiredResponse, typeof(Guid), false), 145 | new GraphApiProperty(Names.ObjectId, typeof(Guid), true), 146 | new GraphApiProperty(Names.ObjectType, typeof(string), false), 147 | new GraphApiProperty(Names.PublicClient, typeof(bool), false), 148 | new GraphApiProperty(Names.ReplyUrls, typeof(ICollection), false), 149 | new GraphApiProperty(Names.SamlMetadataUrl, typeof(string), false) 150 | } 151 | }, 152 | new GraphApiEntityType(Names.Groups) 153 | { 154 | Properties = new List 155 | { 156 | new GraphApiProperty(Names.Description, typeof(string), false), 157 | new GraphApiProperty(Names.DirSyncEnabled, typeof(bool), true), 158 | new GraphApiProperty(Names.DisplayName, typeof(string), true), 159 | new GraphApiProperty(Names.LastDirSyncTime, typeof(DateTime), false), // TODO: make it enabled and add search for date/times 160 | new GraphApiProperty(Names.Mail, typeof(string), true), 161 | new GraphApiProperty(Names.MailEnabled, typeof(bool), false), 162 | new GraphApiProperty(Names.MailNickName, typeof(string), true), 163 | new GraphApiProperty(Names.ObjectId, typeof(Guid), true), 164 | new GraphApiProperty(Names.ObjectType, typeof(string), false), 165 | new GraphApiProperty(Names.OnPremisesSecurityIdentifier, typeof(string), false), 166 | new GraphApiProperty(Names.ProxyAddresses, typeof(ICollection), true), 167 | new GraphApiProperty(Names.SecurityEnabled, typeof(bool), false) 168 | } 169 | }, 170 | new GraphApiEntityType(Names.ServicePrincipals) 171 | { 172 | Properties = new List 173 | { 174 | new GraphApiProperty(Names.AccountEnabled, typeof(bool), true), 175 | new GraphApiProperty(Names.AppDisplayName, typeof(string), false), 176 | new GraphApiProperty(Names.AppId, typeof(string), true), 177 | new GraphApiProperty(Names.AppOwnerTenantId, typeof(Guid), false), 178 | new GraphApiProperty(Names.AppRoleAssignmentRequired, typeof(bool), false), 179 | new GraphApiProperty(Names.DisplayName, typeof(string), true), 180 | new GraphApiProperty(Names.ErrorUrl, typeof(string), false), 181 | new GraphApiProperty(Names.HomePage, typeof(string), false), 182 | new GraphApiProperty(Names.LogoutUrl, typeof(string), false), 183 | new GraphApiProperty(Names.OAuth2Permissions, typeof(bool), false), 184 | new GraphApiProperty(Names.ObjectId, typeof(Guid), true), 185 | new GraphApiProperty(Names.ObjectType, typeof(string), false), 186 | new GraphApiProperty(Names.PreferredTokenSigningKeyThumbprint, typeof(string), false), 187 | new GraphApiProperty(Names.PublisherName, typeof(string), true), 188 | new GraphApiProperty(Names.ReplyUrls, typeof(ICollection), false), 189 | new GraphApiProperty(Names.SamlMetadataUrl, typeof(string), false), 190 | new GraphApiProperty(Names.ServicePrincipalNames, typeof(ICollection), true), 191 | new GraphApiProperty(Names.Tags, typeof(ICollection), true) 192 | } 193 | }, 194 | new GraphApiEntityType(Names.Users) 195 | { 196 | Properties = new List 197 | { 198 | new GraphApiProperty(Names.AccountEnabled, typeof(bool), true), 199 | new GraphApiProperty(Names.City, typeof(string), true), 200 | new GraphApiProperty(Names.Country, typeof(string), true), 201 | new GraphApiProperty(Names.DeletionTimeStamp, typeof(DateTime), false), 202 | new GraphApiProperty(Names.Department, typeof(string), true), 203 | new GraphApiProperty(Names.DirSyncEnabled, typeof(bool), true), 204 | new GraphApiProperty(Names.DisplayName, typeof(string), true), 205 | new GraphApiProperty(Names.FacsimileTelephoneNumber, typeof(string), false), 206 | new GraphApiProperty(Names.GivenName, typeof(string), true), 207 | new GraphApiProperty(Names.ImmutableId, typeof(string), true), 208 | new GraphApiProperty(Names.JobTitle, typeof(string), true), 209 | new GraphApiProperty(Names.LastDirSyncTime, typeof(DateTime), false), // TODO: Make it enabled and add search for date/times 210 | new GraphApiProperty(Names.Mail, typeof(string), true), 211 | new GraphApiProperty(Names.MailNickName, typeof(string), true), 212 | new GraphApiProperty(Names.Mobile, typeof(string), false), 213 | new GraphApiProperty(Names.ObjectId, typeof(Guid), true), 214 | new GraphApiProperty(Names.ObjectType, typeof(string), false), 215 | new GraphApiProperty(Names.OnPremisesSecurityIdentifier, typeof(string), false), 216 | new GraphApiProperty(Names.OtherMails, typeof(ICollection), true), 217 | new GraphApiProperty(Names.PasswordPolicies, typeof(string), false), 218 | new GraphApiProperty(Names.PhysicalDeliveryOfficeName, typeof(string), false), 219 | new GraphApiProperty(Names.PostalCode, typeof(string), false), 220 | new GraphApiProperty(Names.PreferredLanguage, typeof(string), false), 221 | new GraphApiProperty(Names.ProxyAddresses, typeof(ICollection), true), 222 | new GraphApiProperty(Names.SipProxyAddress, typeof(string), false), 223 | new GraphApiProperty(Names.State, typeof(string), true), 224 | new GraphApiProperty(Names.StreetAddress, typeof(string), false), 225 | new GraphApiProperty(Names.Surname, typeof(string), true), 226 | new GraphApiProperty(Names.TelephoneNumber, typeof(string), false), 227 | new GraphApiProperty(Names.UsageLocation, typeof(string), true), 228 | new GraphApiProperty(Names.UserPrincipalName, typeof(string), true), 229 | new GraphApiProperty(Names.UserType, typeof(string), true) 230 | } 231 | } 232 | }; 233 | 234 | private GraphApiEntityType(string name) 235 | { 236 | if (String.IsNullOrEmpty(name)) 237 | { 238 | throw new ArgumentException(StringResources.NameCannotBeNullOrWhiteSpace, "name"); 239 | } 240 | 241 | this.Name = name; 242 | } 243 | 244 | public string Name { get; private set; } 245 | 246 | public static ICollection Entities 247 | { 248 | get { return entities; } 249 | } 250 | 251 | public IReadOnlyCollection Properties { get; private set; } 252 | 253 | // TODO: Merge navigation properties into the entity, after entity types are added for all objects 254 | internal static IDictionary> NavigationProperties 255 | { 256 | get { return navigationProperties; } 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /AadGraphApiHelper/GraphApiProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace AadGraphApiHelper 5 | { 6 | internal class GraphApiProperty 7 | { 8 | private static IDictionary comparisonOperatorByType = new Dictionary 9 | { 10 | { typeof(string), new[] { Names.StartsWithOperator, Names.EqualToOperator, Names.GreaterThanOrEqualToOperator, Names.LessThanOrEqualToOperator } }, 11 | { typeof(ICollection), new[] { Names.AnyEqualsOperator } }, 12 | { typeof(bool), new[] { Names.EqualToOperator } }, 13 | { typeof(Guid), new[] { Names.EqualToOperator } }, 14 | { typeof(object), new[] { Names.EqualToOperator, Names.GreaterThanOrEqualToOperator, Names.LessThanOrEqualToOperator } } 15 | }; 16 | 17 | public string Name { get; private set; } 18 | 19 | public Type Type { get; private set; } 20 | 21 | public bool Filterable { get; private set; } 22 | 23 | internal GraphApiProperty(string name, Type type, bool filterable) 24 | { 25 | if (String.IsNullOrEmpty(name)) 26 | { 27 | throw new ArgumentException(StringResources.NameCannotBeNullOrWhiteSpace, "name"); 28 | } 29 | 30 | if (type == null) 31 | { 32 | throw new ArgumentNullException("type"); 33 | } 34 | 35 | this.Name = name; 36 | this.Type = type; 37 | this.Filterable = filterable; 38 | } 39 | 40 | /// 41 | /// Returns a string that represents the current object. 42 | /// 43 | /// 44 | /// A string that represents the current object. 45 | /// 46 | public override string ToString() 47 | { 48 | return this.Name; 49 | } 50 | 51 | public string[] GetAllowedComparisonOperators() 52 | { 53 | string[] operators; 54 | if (comparisonOperatorByType.TryGetValue(this.Type, out operators)) 55 | { 56 | return operators; 57 | } 58 | 59 | return comparisonOperatorByType[typeof(object)]; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /AadGraphApiHelper/GraphApiUrlBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace AadGraphApiHelper 5 | { 6 | internal class GraphApiUrlBuilder 7 | { 8 | public string ResourceFirst { get; set; } 9 | 10 | public string ResourceId { get; set; } 11 | 12 | public string ResourceSecond { get; set; } 13 | 14 | public string ApiVersion { get; set; } 15 | 16 | public AadEnvironment Environment { get; set; } 17 | 18 | public TenantCredential TenantCredential { get; set; } 19 | 20 | private IList filterComponents = new List(); 21 | 22 | public IList FilterComponents 23 | { 24 | get { return this.filterComponents; } 25 | } 26 | 27 | public string CreateUrl() 28 | { 29 | IGraphApiUrlFormatter formatter = this.Environment?.UrlFormatter; 30 | 31 | return formatter?.FormatUrl(this) ?? String.Empty; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /AadGraphApiHelper/GraphApiUrlFilterComponent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace AadGraphApiHelper 5 | { 6 | internal class GraphApiUrlFilterComponent 7 | { 8 | private static string[] allowedLogicalOperators = { Names.AndOperator, Names.OrOperator }; 9 | 10 | public static string[] AllowedLogicalOperators 11 | { 12 | get { return allowedLogicalOperators; } 13 | } 14 | 15 | public string LogicalOperator { get; set; } 16 | 17 | public GraphApiProperty Property { get; set; } 18 | 19 | public string ComparisonOperator { get; set; } 20 | 21 | public string Value { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /AadGraphApiHelper/IGraphApiUrlFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace AadGraphApiHelper 2 | { 3 | internal interface IGraphApiUrlFormatter 4 | { 5 | string FormatUrl(GraphApiUrlBuilder builder); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /AadGraphApiHelper/IStore.cs: -------------------------------------------------------------------------------- 1 | namespace AadGraphApiHelper 2 | { 3 | internal interface IStore 4 | { 5 | void Store(AadEnvironment environment); 6 | 7 | AadEnvironmentSet GetAadEnvironments(); 8 | 9 | void Store(TenantCredential tenantCredential); 10 | 11 | TenantCredentialSet GetTenantCredentials(AadEnvironment environment); 12 | 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// true if delete successful 18 | bool Delete(TenantCredential tenantCredential); 19 | 20 | void Store(string apiVersion); 21 | 22 | ApiVersionSet GetApiVersions(); 23 | 24 | void SavePreference(string key, object value); 25 | 26 | T GetPreference(string key); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /AadGraphApiHelper/JsonText.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text; 3 | 4 | namespace AadGraphApiHelper 5 | { 6 | class JsonText 7 | { 8 | private const string IndentString = " "; 9 | 10 | public static string Format(string str) 11 | { 12 | var indent = 0; 13 | var quoted = false; 14 | var sb = new StringBuilder(); 15 | for (var i = 0; i < str.Length; i++) 16 | { 17 | var ch = str[i]; 18 | switch (ch) 19 | { 20 | case '{': 21 | case '[': 22 | sb.Append(ch); 23 | if (!quoted) 24 | { 25 | sb.AppendLine(); 26 | ++indent; 27 | for (int j = 0; j < indent; j++) sb.Append(IndentString); 28 | } 29 | break; 30 | case '}': 31 | case ']': 32 | if (!quoted) 33 | { 34 | sb.AppendLine(); 35 | --indent; 36 | for (int j = 0; j < indent; j++) sb.Append(IndentString); 37 | } 38 | sb.Append(ch); 39 | break; 40 | case '"': 41 | sb.Append(ch); 42 | bool escaped = false; 43 | var index = i; 44 | while (index > 0 && str[--index] == '\\') 45 | escaped = !escaped; 46 | if (!escaped) 47 | quoted = !quoted; 48 | break; 49 | case ',': 50 | sb.Append(ch); 51 | if (!quoted) 52 | { 53 | sb.AppendLine(); 54 | for (int j = 0; j < indent; j++) sb.Append(IndentString); 55 | } 56 | break; 57 | case ':': 58 | sb.Append(ch); 59 | if (!quoted) 60 | sb.Append(" "); 61 | break; 62 | default: 63 | sb.Append(ch); 64 | break; 65 | } 66 | } 67 | return sb.ToString(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /AadGraphApiHelper/MSGraphApiUrlFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace AadGraphApiHelper 6 | { 7 | internal class MSGraphApiUrlFormatter : IGraphApiUrlFormatter 8 | { 9 | public string FormatUrl(GraphApiUrlBuilder builder) 10 | { 11 | if (builder.Environment == null || 12 | builder.TenantCredential == null || 13 | String.IsNullOrWhiteSpace(builder.ResourceFirst) || 14 | String.IsNullOrWhiteSpace(builder.ApiVersion)) 15 | { 16 | return null; 17 | } 18 | 19 | UriBuilder uriBuilder = new UriBuilder(builder.Environment.GraphResourceUrl); 20 | uriBuilder.Path = builder.ApiVersion + '/' + builder.ResourceFirst; 21 | if (!String.IsNullOrWhiteSpace(builder.ResourceId)) 22 | { 23 | uriBuilder.Path += '/' + builder.ResourceId; 24 | } 25 | 26 | if (!String.IsNullOrWhiteSpace(builder.ResourceSecond)) 27 | { 28 | uriBuilder.Path += '/' + builder.ResourceSecond; 29 | } 30 | 31 | IDictionary queryPairs = new Dictionary(); 32 | 33 | string filterQuery = this.CreateFilterQuery(builder); 34 | if (!String.IsNullOrWhiteSpace(filterQuery)) 35 | { 36 | queryPairs["$filter"] = filterQuery; 37 | } 38 | 39 | uriBuilder.Query = ConvertQueryCollection(queryPairs); 40 | uriBuilder.Port = -1; 41 | return uriBuilder.ToString(); 42 | } 43 | 44 | private static string ConvertQueryCollection(IDictionary queryPairs) 45 | { 46 | if (queryPairs == null || queryPairs.Count == 0) 47 | { 48 | return null; 49 | } 50 | 51 | StringBuilder stringBuilder = new StringBuilder(); 52 | foreach (KeyValuePair pair in queryPairs) 53 | { 54 | if (stringBuilder.Length > 0) 55 | { 56 | stringBuilder.Append(@"&"); 57 | } 58 | 59 | stringBuilder.AppendFormat("{0}={1}", pair.Key, pair.Value); 60 | } 61 | 62 | return stringBuilder.ToString(); 63 | } 64 | 65 | private string CreateFilterQuery(GraphApiUrlBuilder builder) 66 | { 67 | const string LogicalOperatorFormat = @" {0} "; 68 | const string AnyEqString = @"{0}/any(p:p {1} '{2}')"; 69 | const string FunctionOperator = @"{0}({1},'{2}')"; 70 | const string OperatorForStrings = @"{0} {1} '{2}'"; 71 | const string OperatorForGuids = @"{0} {1} '{2}'"; 72 | const string OperatorForOthers = @"{0} {1} {2}"; 73 | StringBuilder filterQueryBuilder = new StringBuilder(); 74 | foreach (GraphApiUrlFilterComponent filterComponent in builder.FilterComponents) 75 | { 76 | string andOr = filterComponent.LogicalOperator; 77 | string op = filterComponent.ComparisonOperator; 78 | GraphApiProperty property = filterComponent.Property; 79 | string value = filterComponent.Value; 80 | 81 | if (filterQueryBuilder.Length > 0) 82 | { 83 | filterQueryBuilder.AppendFormat(LogicalOperatorFormat, andOr); 84 | } 85 | 86 | if (op.Equals(Names.StartsWithOperator, StringComparison.OrdinalIgnoreCase)) 87 | { 88 | filterQueryBuilder.AppendFormat(FunctionOperator, Names.StartsWithOperator, property.Name, value); 89 | } 90 | else if (op.Equals(Names.AnyEqualsOperator, StringComparison.OrdinalIgnoreCase)) 91 | { 92 | filterQueryBuilder.AppendFormat(AnyEqString, property.Name, Names.EqualToOperator, value); 93 | } 94 | else if (property.Type == typeof(Guid)) 95 | { 96 | filterQueryBuilder.AppendFormat(OperatorForGuids, property.Name, op, value); 97 | } 98 | else if (property.Type == typeof(bool) || property.Type == typeof(int)) 99 | { 100 | filterQueryBuilder.AppendFormat(OperatorForOthers, property.Name, op, value); 101 | } 102 | else 103 | { 104 | filterQueryBuilder.AppendFormat(OperatorForStrings, property.Name, op, value); 105 | } 106 | } 107 | 108 | return filterQueryBuilder.ToString(); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /AadGraphApiHelper/MainForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 375, 17 122 | 123 | 124 | 609, 17 125 | 126 | 127 | 152, 17 128 | 129 | 130 | 17, 17 131 | 132 | 133 | 472, 17 134 | 135 | 136 | 67 137 | 138 | -------------------------------------------------------------------------------- /AadGraphApiHelper/Names.cs: -------------------------------------------------------------------------------- 1 | namespace AadGraphApiHelper 2 | { 3 | internal class Names 4 | { 5 | // Entity types 6 | public const string Applications = @"applications"; 7 | public const string Contacts = @"contacts"; 8 | public const string Contracts = @"contracts"; 9 | public const string Devices = @"devices"; 10 | public const string DirectoryRoles = @"directoryRoles"; 11 | public const string DirectoryRoleTemplates = @"directoryRoleTemplates"; 12 | public const string ExtensionProperties = @"extensionProperties"; 13 | public const string Groups = @"groups"; 14 | public const string Metadata = @"$metadata"; 15 | public const string ObjectType = @"objectType"; 16 | public const string ServicePrincipals = @"servicePrincipals"; 17 | public const string TenantDetails = @"tenantDetails"; 18 | public const string Users = @"users"; 19 | public const string SubscribedSkus = @"subscribedSkus"; 20 | 21 | // Functions 22 | public const string GetAvailableExtensionProperties = @"getAvailableExtensionProperties"; 23 | public const string GetMemberGroups = @"getMemberGroups"; 24 | public const string CheckMemberGroups = @"CheckMemberGroups"; 25 | 26 | // Navigation properties 27 | public const string AppRoleAssignedTo = @"appRoleAssignedTo"; 28 | public const string AppRolesAssignedToLinks = @"$links/appRoleAssignedTo"; 29 | public const string AppRoleAssignments = @"appRoleAssignments"; 30 | public const string AppRoleAssignmentsLinks = @"$links/appRoleAssignments"; 31 | public const string CreatedObjects = @"createdObjects"; 32 | public const string CreatedObjectsLinks = @"$links/createdObjects"; 33 | public const string DirectReports = @"directReports"; 34 | public const string DirectReportsLinks = @"$links/directReports"; 35 | public const string Manager = @"manager"; 36 | public const string ManagerLinks = @"$links/manager"; 37 | public const string MemberOf = @"memberOf"; 38 | public const string MemberOfLinks = @"$links/memberOf"; 39 | public const string Members = @"members"; 40 | public const string MembersLinks = @"$links/members"; 41 | public const string OAuth2PermissionGrants = @"oauth2PermissionGrants"; 42 | public const string OAuth2PermissionGrantsLinks = @"$links/oauth2PermissionGrants"; 43 | public const string OwnedDevices = @"ownedDevices"; 44 | public const string OwnedDevicesLinks = @"$links/ownedDevices"; 45 | public const string OwnedObjects = @"ownedObjects"; 46 | public const string OwnedObjectsLinks = @"$links/ownedObjects"; 47 | public const string Owners = @"owners"; 48 | public const string OwnersLinks = @"$links/owners"; 49 | public const string RegisteredDevices = @"registeredDevices"; 50 | public const string RegisteredDevicesLinks = @"$links/registeredDevices"; 51 | public const string RegisteredOwners = @"registeredOwners"; 52 | public const string RegisteredOwnersLinks = @"$links/registeredOwners"; 53 | public const string RegisteredUsers = @"registeredUsers"; 54 | public const string RegisteredUsersLinks = @"$links/registeredUsers"; 55 | 56 | // Built-in properties applicable to multiple objects 57 | public const string ObjectId = @"objectId"; 58 | public const string Id = @"id"; 59 | public const string DisplayName = @"displayName"; 60 | 61 | // Build-in user properties 62 | public const string AccountEnabled = @"accountEnabled"; 63 | public const string AssignedLicenses = @"assignedLicenses"; 64 | public const string AssignedPlans = @"assignedPlans"; 65 | public const string City = @"city"; 66 | public const string Country = @"country"; 67 | public const string DeletionTimeStamp = @"deletionTimeStamp"; 68 | public const string Department = @"department"; 69 | public const string DirSyncEnabled = @"dirSyncEnabled"; 70 | public const string FacsimileTelephoneNumber = @"facsimileTelephoneNumber"; 71 | public const string GivenName = @"givenName"; 72 | public const string ImmutableId = @"immutableId"; 73 | public const string JobTitle = @"jobTitle"; 74 | public const string LastDirSyncTime = @"lastDirSyncTime"; 75 | public const string Mail = @"mail"; 76 | public const string MailNickName = @"mailNickName"; 77 | public const string Mobile = @"mobile"; 78 | public const string OnPremisesSecurityIdentifier = @"onPremisesSecurityIdentifier"; 79 | public const string OtherMails = @"otherMails"; 80 | public const string PasswordPolicies = @"passwordPolicies"; 81 | public const string PasswordProfile = @"passwordProfile"; 82 | public const string PhysicalDeliveryOfficeName = @"physicalDeliveryOfficeName"; 83 | public const string PostalCode = @"postalCode"; 84 | public const string PreferredLanguage = @"preferredLanguage"; 85 | public const string ProvisionedPlans = @"provisionedPlans"; 86 | public const string ProxyAddresses = @"proxyAddresses"; 87 | public const string SipProxyAddress = @"sipProxyAddress"; 88 | public const string State = @"state"; 89 | public const string StreetAddress = @"streetAddress"; 90 | public const string Surname = @"surname"; 91 | public const string TelephoneNumber = @"telephoneNumber"; 92 | public const string ThumbnailPhoto = @"thumbnailPhoto"; 93 | public const string UsageLocation = @"usageLocation"; 94 | public const string UserPrincipalName = @"userPrincipalName"; 95 | public const string UserType = @"userType"; 96 | 97 | // Built-in application/servicePrincipal properties 98 | public const string AppDisplayName = @"appDisplayName"; 99 | public const string AppOwnerTenantId = @"appOwnerTenantId"; 100 | public const string AppRoleAssignmentRequired = @"appRoleAssignmentRequired"; 101 | public const string OAuth2Permissions = @"oauth2Permissions"; 102 | public const string PreferredTokenSigningKeyThumbprint = @"preferredTokenSigningKeyThumbprint"; 103 | public const string PublisherName = @"publisherName"; 104 | public const string ServicePrincipalNames = @"servicePrincipalNames"; 105 | public const string Tags = @"tags"; 106 | public const string AppId = @"appId"; 107 | public const string AppRoles = @"appRoles"; 108 | public const string AvailableToOtherTenants = @"availableToOtherTenants"; 109 | public const string ErrorUrl = @"errorUrl"; 110 | public const string GroupMembershipClaims = @"groupMembershipClaims"; 111 | public const string HomePage = @"homePage"; 112 | public const string IdentifierUris = @"identifierUris"; 113 | public const string LogoutUrl = @"logoutUrl"; 114 | public const string OAuth2AllowImplicitFlow = @"oauth2AllowImplicitFlow"; 115 | public const string OAuth2AllowUrlPathMatching = @"oauth2AllowUrlPathMatching"; 116 | public const string OAuth2RequiredResponse = @"oauth2RequiredResponse"; 117 | public const string PublicClient = @"publicClient"; 118 | public const string ReplyUrls = @"replyUrls"; 119 | public const string SamlMetadataUrl = @"samlMetadataUrl"; 120 | 121 | // Built-in group/directoryRole propeties 122 | public const string Description = @"description"; 123 | public const string MailEnabled = @"mailEnabled"; 124 | public const string SecurityEnabled = @"securityEnabled"; 125 | 126 | // Filter query operators 127 | public const string AndOperator = @"and"; 128 | public const string OrOperator = @"or"; 129 | 130 | public const string StartsWithOperator = @"startswith"; 131 | public const string EqualToOperator = @"eq"; 132 | public const string GreaterThanOrEqualToOperator = @"ge"; 133 | public const string LessThanOrEqualToOperator = @"le"; 134 | public const string AnyEqualsOperator = @"any (eq)"; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /AadGraphApiHelper/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace AadGraphApiHelper 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// The main entry point for the application. 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new MainForm()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /AadGraphApiHelper/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("AadGraphApiHelper")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("AadGraphApiHelper")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("9a6eb0dc-b64f-49ee-913b-ec9aee3fe5c9")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /AadGraphApiHelper/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.34209 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Microsoft.Azure.ActiveDirectory.GraphApiBuddy.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AadGraphApiHelper.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /AadGraphApiHelper/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /AadGraphApiHelper/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.34209 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Microsoft.Azure.ActiveDirectory.GraphApiBuddy.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /AadGraphApiHelper/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AadGraphApiHelper/QueryBuilderForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace AadGraphApiHelper 2 | { 3 | partial class QueryBuilderForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.queryGridView = new System.Windows.Forms.DataGridView(); 32 | this.cancelButton = new System.Windows.Forms.Button(); 33 | this.createRequestButton = new System.Windows.Forms.Button(); 34 | this.resourceLabel = new System.Windows.Forms.Label(); 35 | this.resourceComboBox = new System.Windows.Forms.ComboBox(); 36 | this.createAndExecuteButton = new System.Windows.Forms.Button(); 37 | ((System.ComponentModel.ISupportInitialize)(this.queryGridView)).BeginInit(); 38 | this.SuspendLayout(); 39 | // 40 | // queryGridView 41 | // 42 | this.queryGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; 43 | this.queryGridView.Location = new System.Drawing.Point(0, 45); 44 | this.queryGridView.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 45 | this.queryGridView.Name = "queryGridView"; 46 | this.queryGridView.Size = new System.Drawing.Size(984, 331); 47 | this.queryGridView.TabIndex = 10; 48 | // 49 | // cancelButton 50 | // 51 | this.cancelButton.CausesValidation = false; 52 | this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; 53 | this.cancelButton.Location = new System.Drawing.Point(847, 383); 54 | this.cancelButton.Name = "cancelButton"; 55 | this.cancelButton.Size = new System.Drawing.Size(125, 27); 56 | this.cancelButton.TabIndex = 22; 57 | this.cancelButton.Text = "Cancel"; 58 | this.cancelButton.UseVisualStyleBackColor = true; 59 | this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); 60 | // 61 | // createRequestButton 62 | // 63 | this.createRequestButton.DialogResult = System.Windows.Forms.DialogResult.No; 64 | this.createRequestButton.Location = new System.Drawing.Point(485, 383); 65 | this.createRequestButton.Name = "createRequestButton"; 66 | this.createRequestButton.Size = new System.Drawing.Size(175, 27); 67 | this.createRequestButton.TabIndex = 21; 68 | this.createRequestButton.Text = "Create request"; 69 | this.createRequestButton.UseVisualStyleBackColor = true; 70 | this.createRequestButton.Click += new System.EventHandler(this.createRequestButton_Click); 71 | // 72 | // resourceLabel 73 | // 74 | this.resourceLabel.AutoSize = true; 75 | this.resourceLabel.Location = new System.Drawing.Point(13, 13); 76 | this.resourceLabel.Name = "resourceLabel"; 77 | this.resourceLabel.Size = new System.Drawing.Size(67, 19); 78 | this.resourceLabel.TabIndex = 1; 79 | this.resourceLabel.Text = "Resource:"; 80 | // 81 | // resourceComboBox 82 | // 83 | this.resourceComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 84 | this.resourceComboBox.FormattingEnabled = true; 85 | this.resourceComboBox.Location = new System.Drawing.Point(101, 10); 86 | this.resourceComboBox.Name = "resourceComboBox"; 87 | this.resourceComboBox.Size = new System.Drawing.Size(229, 25); 88 | this.resourceComboBox.TabIndex = 2; 89 | this.resourceComboBox.SelectedIndexChanged += new System.EventHandler(this.resourceComboBox_SelectedIndexChanged); 90 | // 91 | // createAndExecuteButton 92 | // 93 | this.createAndExecuteButton.DialogResult = System.Windows.Forms.DialogResult.Yes; 94 | this.createAndExecuteButton.Location = new System.Drawing.Point(666, 383); 95 | this.createAndExecuteButton.Name = "createAndExecuteButton"; 96 | this.createAndExecuteButton.Size = new System.Drawing.Size(175, 27); 97 | this.createAndExecuteButton.TabIndex = 23; 98 | this.createAndExecuteButton.Text = "Create and execute"; 99 | this.createAndExecuteButton.UseVisualStyleBackColor = true; 100 | this.createAndExecuteButton.Click += new System.EventHandler(this.createAndExecuteButton_Click); 101 | // 102 | // QueryBuilderForm 103 | // 104 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); 105 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 106 | this.CancelButton = this.cancelButton; 107 | this.ClientSize = new System.Drawing.Size(984, 418); 108 | this.Controls.Add(this.createAndExecuteButton); 109 | this.Controls.Add(this.resourceComboBox); 110 | this.Controls.Add(this.resourceLabel); 111 | this.Controls.Add(this.createRequestButton); 112 | this.Controls.Add(this.cancelButton); 113 | this.Controls.Add(this.queryGridView); 114 | this.Font = new System.Drawing.Font("Segoe UI", 10F); 115 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 116 | this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 117 | this.MaximizeBox = false; 118 | this.MinimizeBox = false; 119 | this.Name = "QueryBuilderForm"; 120 | this.ShowInTaskbar = false; 121 | this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; 122 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 123 | this.Text = "Query Builder"; 124 | this.Load += new System.EventHandler(this.QueryBuilderForm_Load); 125 | ((System.ComponentModel.ISupportInitialize)(this.queryGridView)).EndInit(); 126 | this.ResumeLayout(false); 127 | this.PerformLayout(); 128 | 129 | } 130 | 131 | #endregion 132 | 133 | private System.Windows.Forms.DataGridView queryGridView; 134 | private System.Windows.Forms.Button cancelButton; 135 | private System.Windows.Forms.Button createRequestButton; 136 | private System.Windows.Forms.Label resourceLabel; 137 | private System.Windows.Forms.ComboBox resourceComboBox; 138 | private System.Windows.Forms.Button createAndExecuteButton; 139 | 140 | } 141 | } -------------------------------------------------------------------------------- /AadGraphApiHelper/QueryBuilderForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace AadGraphApiHelper 7 | { 8 | public partial class QueryBuilderForm : Form 9 | { 10 | private const string LogicalOperatorColumnName = "And/Or"; 11 | 12 | private const string PropertyColumnName = "Property"; 13 | 14 | private const string ComparisonOperatorColumnName = "Operator"; 15 | 16 | private const string ValueColumnName = "Value"; 17 | 18 | private readonly int allowedWidth; 19 | 20 | private DataGridViewComboBoxColumn propertyColumn; 21 | 22 | public QueryBuilderForm() 23 | { 24 | this.InitializeComponent(); 25 | this.allowedWidth = this.Width; 26 | } 27 | 28 | internal GraphApiUrlBuilder UrlBuilder { get; set; } 29 | 30 | private void QueryBuilderForm_Load(object sender, EventArgs args) 31 | { 32 | this.resourceComboBox.Items.AddRange(GraphApiEntityType.Entities.Select(e => e.Name).ToArray()); 33 | DataGridViewComboBoxColumn andOrColumn = new DataGridViewComboBoxColumn(); 34 | andOrColumn.Name = LogicalOperatorColumnName; 35 | andOrColumn.FlatStyle = FlatStyle.Flat; 36 | andOrColumn.Items.AddRange(GraphApiUrlFilterComponent.AllowedLogicalOperators); 37 | this.queryGridView.Columns.Add(andOrColumn); 38 | 39 | this.propertyColumn = new DataGridViewComboBoxColumn(); 40 | this.propertyColumn.Name = PropertyColumnName; 41 | this.propertyColumn.FlatStyle = FlatStyle.Flat; 42 | this.propertyColumn.Width = (this.allowedWidth - andOrColumn.Width) / 4; 43 | 44 | this.queryGridView.Columns.Add(this.propertyColumn); 45 | 46 | DataGridViewComboBoxColumn operatorColumn = new DataGridViewComboBoxColumn(); 47 | operatorColumn.Name = ComparisonOperatorColumnName; 48 | operatorColumn.FlatStyle = FlatStyle.Flat; 49 | operatorColumn.Width = (this.allowedWidth - andOrColumn.Width - this.propertyColumn.Width) / 4; 50 | this.queryGridView.Columns.Add(operatorColumn); 51 | 52 | DataGridViewTextBoxColumn valueColumn = new DataGridViewTextBoxColumn(); 53 | valueColumn.Name = ValueColumnName; 54 | valueColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 55 | this.queryGridView.Columns.Add(valueColumn); 56 | 57 | this.queryGridView.EnableHeadersVisualStyles = false; 58 | this.queryGridView.ColumnHeadersDefaultCellStyle.ForeColor = Color.Gray; 59 | this.queryGridView.ColumnHeadersDefaultCellStyle.BackColor = Color.FromArgb(0xf7, 0xf7, 0xf7); 60 | 61 | DataGridViewCell firstAndOrColumn = this.queryGridView.Rows[0].Cells[LogicalOperatorColumnName]; 62 | firstAndOrColumn.ReadOnly = true; 63 | firstAndOrColumn.Style = new DataGridViewCellStyle { BackColor = Color.LightGray }; 64 | this.queryGridView.CurrentCell = this.queryGridView.Rows[0].Cells[PropertyColumnName]; 65 | this.queryGridView.RowValidating += this.queryGridView_RowValidating; 66 | 67 | this.queryGridView.CellValueChanged += this.queryGridView_CellValueChanged; 68 | 69 | this.PopulateDataGridViewFromUrlBuilder(); 70 | } 71 | 72 | private void cancelButton_Click(object sender, EventArgs e) 73 | { 74 | this.Close(); 75 | } 76 | 77 | private void queryGridView_CellValueChanged(object sender, DataGridViewCellEventArgs args) 78 | { 79 | if (args.ColumnIndex == this.queryGridView.Columns[PropertyColumnName].Index) 80 | { 81 | DataGridViewRow row = this.queryGridView.Rows[args.RowIndex]; 82 | this.SetComparisonOperators(row); 83 | } 84 | } 85 | 86 | private void queryGridView_RowValidating(object sender, DataGridViewCellCancelEventArgs args) 87 | { 88 | DataGridViewRow row = this.queryGridView.Rows[args.RowIndex]; 89 | string andOr = row.Cells[LogicalOperatorColumnName].Value as string; 90 | string op = row.Cells[ComparisonOperatorColumnName].Value as string; 91 | string property = row.Cells[PropertyColumnName].Value as string; 92 | string value = row.Cells[ValueColumnName].Value as string; 93 | 94 | if (args.RowIndex != this.queryGridView.Rows.Count - 1) 95 | { 96 | if (args.RowIndex != 0 && String.IsNullOrWhiteSpace(andOr)) 97 | { 98 | row.Cells[LogicalOperatorColumnName].Style = new DataGridViewCellStyle { BackColor = Color.LightCoral }; 99 | args.Cancel = true; 100 | } 101 | 102 | if (String.IsNullOrWhiteSpace(property)) 103 | { 104 | row.Cells[PropertyColumnName].Style = new DataGridViewCellStyle { BackColor = Color.LightCoral }; 105 | args.Cancel = true; 106 | } 107 | 108 | if (String.IsNullOrWhiteSpace(op)) 109 | { 110 | row.Cells[ComparisonOperatorColumnName].Style = new DataGridViewCellStyle { BackColor = Color.LightCoral }; 111 | args.Cancel = true; 112 | } 113 | 114 | if (String.IsNullOrWhiteSpace(value)) 115 | { 116 | row.Cells[ValueColumnName].Style = new DataGridViewCellStyle { BackColor = Color.LightCoral }; 117 | args.Cancel = true; 118 | } 119 | } 120 | 121 | if (args.Cancel == false) 122 | { 123 | row.Cells[LogicalOperatorColumnName].Style = new DataGridViewCellStyle { BackColor = Color.White }; 124 | row.Cells[PropertyColumnName].Style = new DataGridViewCellStyle { BackColor = Color.White }; 125 | row.Cells[ComparisonOperatorColumnName].Style = new DataGridViewCellStyle { BackColor = Color.White }; 126 | row.Cells[ValueColumnName].Style = new DataGridViewCellStyle { BackColor = Color.White }; 127 | } 128 | } 129 | 130 | private void createRequestButton_Click(object sender, EventArgs e) 131 | { 132 | this.UpdateUrlBuilder(); 133 | this.Close(); 134 | } 135 | 136 | private void createAndExecuteButton_Click(object sender, EventArgs e) 137 | { 138 | this.UpdateUrlBuilder(); 139 | this.Close(); 140 | } 141 | 142 | private void UpdateUrlBuilder() 143 | { 144 | this.UrlBuilder.ResourceFirst = this.resourceComboBox.Text; 145 | this.UrlBuilder.ResourceSecond = null; 146 | this.UrlBuilder.ResourceId = null; 147 | 148 | this.UrlBuilder.FilterComponents.Clear(); 149 | for (int index = 0; index < this.queryGridView.Rows.Count; index++) 150 | { 151 | DataGridViewRow row = this.queryGridView.Rows[index]; 152 | if (row.IsNewRow) 153 | { 154 | continue; 155 | } 156 | 157 | string logicalOperator = row.Cells[LogicalOperatorColumnName].Value as string; 158 | string comparisonOperator = row.Cells[ComparisonOperatorColumnName].Value as string; 159 | string propertyName = row.Cells[PropertyColumnName].Value as string; 160 | string value = row.Cells[ValueColumnName].Value as string; 161 | 162 | if ((index != 0 && String.IsNullOrWhiteSpace(logicalOperator) || 163 | String.IsNullOrWhiteSpace(comparisonOperator) || 164 | String.IsNullOrWhiteSpace(propertyName) || 165 | String.IsNullOrWhiteSpace(value))) 166 | { 167 | return; 168 | } 169 | 170 | GraphApiEntityType entity = GetEntityType(this.resourceComboBox.Text); 171 | if (entity == null) 172 | { 173 | return; 174 | } 175 | 176 | GraphApiProperty property = entity.Properties.SingleOrDefault(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); 177 | if (property == null) 178 | { 179 | return; 180 | } 181 | 182 | GraphApiUrlFilterComponent filterComponent = new GraphApiUrlFilterComponent 183 | { 184 | LogicalOperator = index > 0 ? logicalOperator : null, 185 | Property = property, 186 | ComparisonOperator = comparisonOperator, 187 | Value = value 188 | }; 189 | 190 | this.UrlBuilder.FilterComponents.Add(filterComponent); 191 | } 192 | } 193 | 194 | private void PopulateDataGridViewFromUrlBuilder() 195 | { 196 | if (this.resourceComboBox.Items.Contains(this.UrlBuilder.ResourceFirst)) 197 | { 198 | this.resourceComboBox.Text = this.UrlBuilder.ResourceFirst; 199 | } 200 | 201 | this.AssignProperties(); 202 | 203 | foreach (GraphApiUrlFilterComponent filterComponent in this.UrlBuilder.FilterComponents) 204 | { 205 | int rowIndex = this.queryGridView.Rows.Add(); 206 | DataGridViewRow row = this.queryGridView.Rows[rowIndex]; 207 | row.Cells[LogicalOperatorColumnName].Value = filterComponent.LogicalOperator; 208 | row.Cells[PropertyColumnName].Value = filterComponent.Property.Name; 209 | this.SetComparisonOperators(row); 210 | row.Cells[ComparisonOperatorColumnName].Value = filterComponent.ComparisonOperator; 211 | row.Cells[ValueColumnName].Value = filterComponent.Value; 212 | } 213 | } 214 | 215 | private void SetComparisonOperators(DataGridViewRow row) 216 | { 217 | string propertyName = (string)row.Cells[PropertyColumnName].Value; 218 | 219 | GraphApiEntityType entity = GetEntityType(this.resourceComboBox.Text); 220 | GraphApiProperty property = entity.Properties.Single(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); 221 | DataGridViewComboBoxCell operatorCell = (DataGridViewComboBoxCell)row.Cells[ComparisonOperatorColumnName]; 222 | string operatorValue = operatorCell.Value as string; 223 | operatorCell.Items.Clear(); 224 | string[] operators = property.GetAllowedComparisonOperators(); 225 | operatorCell.Items.AddRange(operators); 226 | 227 | if (!operators.Contains(operatorValue)) 228 | { 229 | operatorCell.Value = null; 230 | } 231 | } 232 | 233 | private void resourceComboBox_SelectedIndexChanged(object sender, EventArgs args) 234 | { 235 | this.AssignProperties(); 236 | } 237 | 238 | private void AssignProperties() 239 | { 240 | this.propertyColumn.Items.Clear(); 241 | GraphApiEntityType entity = GetEntityType(this.resourceComboBox.Text); 242 | 243 | if (entity == null) 244 | { 245 | return; 246 | } 247 | 248 | this.propertyColumn.Items.AddRange(entity.Properties.Where(p => p.Filterable).Select(p => p.Name).ToArray()); 249 | } 250 | 251 | private static GraphApiEntityType GetEntityType(string entityName) 252 | { 253 | if (String.IsNullOrWhiteSpace(entityName)) 254 | { 255 | return null; 256 | } 257 | 258 | return GraphApiEntityType.Entities.SingleOrDefault(e => e.Name.Equals(entityName, StringComparison.OrdinalIgnoreCase)); 259 | } 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /AadGraphApiHelper/QueryBuilderForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /AadGraphApiHelper/RegistryStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Win32; 3 | 4 | namespace AadGraphApiHelper 5 | { 6 | internal class RegistryStore : IStore 7 | { 8 | private const string RegistryRoot = @"Software\Microsoft\AadGraphApiBuddy"; 9 | 10 | private const string EnvironmentsRoot = RegistryRoot + @"\Environments"; 11 | 12 | private const string ApiVersionRoot = RegistryRoot + @"\ApiVersions"; 13 | 14 | private const string LoginEndpointKey = @"LoginEndpoint"; 15 | 16 | private const string GraphApiEndpointKey = @"GraphApiEndpoint"; 17 | 18 | private const string EncryptedKeyKey = @"EncryptedKey"; 19 | 20 | private const string ApplicationTypeKey = @"ApplicationType"; 21 | 22 | private const string AliasKey = @"Alias"; 23 | 24 | private const string ReplyUrlKey = @"ReplyUrl"; 25 | 26 | public void Store(AadEnvironment environment) 27 | { 28 | string environmentPath = GetEnvironmentPath(environment); 29 | using (RegistryKey registryKey = Registry.CurrentUser.CreateSubKey(environmentPath)) 30 | { 31 | if (registryKey == null) 32 | { 33 | return; 34 | } 35 | 36 | registryKey.SetValue(LoginEndpointKey, environment.LoginEndpoint); 37 | registryKey.SetValue(GraphApiEndpointKey, environment.GraphApiEndpoint); 38 | } 39 | } 40 | 41 | public void Delete(AadEnvironment environment) 42 | { 43 | } 44 | 45 | public AadEnvironmentSet GetAadEnvironments() 46 | { 47 | return new AadEnvironmentSet(); 48 | 49 | //using (RegistryKey environmentsRootKey = Registry.CurrentUser.CreateSubKey(EnvironmentsRoot)) 50 | //{ 51 | // if (environmentsRootKey == null) 52 | // { 53 | // return environments; 54 | // } 55 | 56 | // foreach (string environmentName in environmentsRootKey.GetSubKeyNames()) 57 | // { 58 | // if (environmentName.Equals(AadEnvironment.MSGraphProduction.RegistryName, StringComparison.OrdinalIgnoreCase) || 59 | // environmentName.Equals(AadEnvironment.AadGraphProduction.RegistryName, StringComparison.OrdinalIgnoreCase) || 60 | // environmentName.Equals(AadEnvironment.AadGraphPreProduction.RegistryName, StringComparison.OrdinalIgnoreCase)) 61 | // { 62 | // // These keys are created to store tenants/credentials, but we do not add them since they have 63 | // // been added already. Their endpoints are not read from the registry. 64 | // continue; 65 | // } 66 | 67 | // using (RegistryKey environmentKey = environmentsRootKey.OpenSubKey(environmentName)) 68 | // { 69 | // if (environmentKey == null) 70 | // { 71 | // continue; 72 | // } 73 | 74 | // string loginEndpoint = environmentKey.GetValue(LoginEndpointKey) as string; 75 | // string graphApiEndpoint = environmentKey.GetValue(GraphApiEndpointKey) as string; 76 | 77 | // if (String.IsNullOrWhiteSpace(loginEndpoint) || String.IsNullOrWhiteSpace(graphApiEndpoint)) 78 | // { 79 | // continue; 80 | // } 81 | 82 | // environments.Add(new AadEnvironment(environmentName, loginEndpoint.Trim(), graphApiEndpoint.Trim())); 83 | // } 84 | // } 85 | //} 86 | 87 | //return environments; 88 | } 89 | 90 | public void Store(TenantCredential tenantCredential) 91 | { 92 | string credentialPath = GetCredentialPath(tenantCredential); 93 | 94 | using (RegistryKey credentialKey = Registry.CurrentUser.CreateSubKey(credentialPath)) 95 | { 96 | if (credentialKey == null) 97 | { 98 | return; 99 | } 100 | 101 | credentialKey.SetValue(ApplicationTypeKey, tenantCredential.ApplicationType.ToString()); 102 | credentialKey.SetValue(AliasKey, tenantCredential.Alias); 103 | 104 | if (tenantCredential.ApplicationType == ApplicationType.Native) 105 | { 106 | credentialKey.SetValue(ReplyUrlKey, tenantCredential.ReplyUrl); 107 | } 108 | else 109 | { 110 | credentialKey.SetValue(EncryptedKeyKey, tenantCredential.EncryptedKey); 111 | } 112 | } 113 | } 114 | 115 | public bool Delete(TenantCredential tbdTenantCredential) 116 | { 117 | string environmentPath = GetEnvironmentPath(tbdTenantCredential.Environment); 118 | using (RegistryKey environmentKey = Registry.CurrentUser.OpenSubKey(environmentPath, true)) 119 | { 120 | if (environmentKey == null) 121 | { 122 | return false; 123 | } 124 | 125 | foreach (string tenant in environmentKey.GetSubKeyNames()) 126 | { 127 | using (RegistryKey tenantKey = environmentKey.OpenSubKey(tenant, true)) 128 | { 129 | bool found = false; 130 | if (tenantKey == null) 131 | { 132 | continue; 133 | } 134 | 135 | foreach (string clientId in tenantKey.GetSubKeyNames()) 136 | { 137 | using (RegistryKey clientIdKey = tenantKey.OpenSubKey(clientId, true)) 138 | { 139 | if (String.IsNullOrWhiteSpace(clientId)) 140 | { 141 | continue; 142 | } 143 | 144 | if (clientIdKey == null) 145 | { 146 | continue; 147 | } 148 | 149 | string applicationTypeString = clientIdKey.GetValue(ApplicationTypeKey) as string; 150 | ApplicationType applicationType; 151 | if (!Enum.TryParse(applicationTypeString, true, out applicationType)) 152 | { 153 | continue; 154 | } 155 | 156 | string alias = clientIdKey.GetValue(AliasKey) as string; 157 | 158 | TenantCredential credential; 159 | try 160 | { 161 | credential = new TenantCredential(tbdTenantCredential.Environment, tenant, clientId, applicationType, alias); 162 | } 163 | catch (Exception) 164 | { 165 | // Bad key, can't decrypt :-( 166 | continue; 167 | } 168 | 169 | switch (applicationType) 170 | { 171 | case ApplicationType.Native: 172 | string replyUrl = clientIdKey.GetValue(ReplyUrlKey) as string; 173 | if (string.IsNullOrWhiteSpace(replyUrl)) 174 | { 175 | continue; 176 | } 177 | 178 | credential.ReplyUrl = new Uri(replyUrl); 179 | break; 180 | 181 | case ApplicationType.Web: 182 | byte[] encryptedKey = clientIdKey.GetValue(EncryptedKeyKey) as byte[]; 183 | if (encryptedKey == null || encryptedKey.Length < 16) 184 | { 185 | continue; 186 | } 187 | 188 | credential.EncryptedKey = encryptedKey; 189 | break; 190 | 191 | default: 192 | // unknown application type :-( 193 | continue; 194 | } 195 | 196 | 197 | if (tbdTenantCredential.Equals(credential)) 198 | { 199 | found = true; 200 | } 201 | } 202 | 203 | if (found) 204 | { 205 | break; 206 | } 207 | } 208 | 209 | if (found) 210 | { 211 | tenantKey.DeleteSubKey(tbdTenantCredential.ClientId); 212 | return true; 213 | } 214 | } 215 | } 216 | } 217 | return false; 218 | } 219 | 220 | public TenantCredentialSet GetTenantCredentials(AadEnvironment environment) 221 | { 222 | TenantCredentialSet tenantCredentials = new TenantCredentialSet(); 223 | string environmentPath = GetEnvironmentPath(environment); 224 | using (RegistryKey environmentKey = Registry.CurrentUser.OpenSubKey(environmentPath)) 225 | { 226 | if (environmentKey == null) 227 | { 228 | return tenantCredentials; 229 | } 230 | 231 | foreach (string tenant in environmentKey.GetSubKeyNames()) 232 | { 233 | using (RegistryKey tenantKey = environmentKey.OpenSubKey(tenant)) 234 | { 235 | if (tenantKey == null) 236 | { 237 | continue; 238 | } 239 | 240 | foreach (string clientId in tenantKey.GetSubKeyNames()) 241 | { 242 | using (RegistryKey clientIdKey = tenantKey.OpenSubKey(clientId)) 243 | { 244 | if (String.IsNullOrWhiteSpace(clientId)) 245 | { 246 | continue; 247 | } 248 | 249 | if (clientIdKey == null) 250 | { 251 | continue; 252 | } 253 | 254 | string applicationTypeString = clientIdKey.GetValue(ApplicationTypeKey) as string; 255 | ApplicationType applicationType; 256 | if (!Enum.TryParse(applicationTypeString, true, out applicationType)) 257 | { 258 | continue; 259 | } 260 | 261 | string alias = clientIdKey.GetValue(AliasKey) as string; 262 | 263 | TenantCredential credential; 264 | try 265 | { 266 | credential = new TenantCredential(environment, tenant, clientId, applicationType, alias); 267 | } 268 | catch (Exception) 269 | { 270 | // Bad key, can't decrypt :-( 271 | continue; 272 | } 273 | 274 | switch (applicationType) 275 | { 276 | case ApplicationType.Native: 277 | string replyUrl = clientIdKey.GetValue(ReplyUrlKey) as string; 278 | if (string.IsNullOrWhiteSpace(replyUrl)) 279 | { 280 | continue; 281 | } 282 | 283 | credential.ReplyUrl = new Uri(replyUrl); 284 | break; 285 | 286 | case ApplicationType.Web: 287 | byte[] encryptedKey = clientIdKey.GetValue(EncryptedKeyKey) as byte[]; 288 | if (encryptedKey == null || encryptedKey.Length < 16) 289 | { 290 | continue; 291 | } 292 | 293 | credential.EncryptedKey = encryptedKey; 294 | break; 295 | 296 | default: 297 | // unknown application type :-( 298 | continue; 299 | } 300 | 301 | tenantCredentials.Add(credential); 302 | } 303 | } 304 | } 305 | } 306 | } 307 | 308 | return tenantCredentials; 309 | } 310 | 311 | public void Store(string apiVersion) 312 | { 313 | using (RegistryKey apiVersionKey = Registry.CurrentUser.CreateSubKey(ApiVersionRoot)) 314 | { 315 | if (apiVersionKey == null) 316 | { 317 | return; 318 | } 319 | 320 | apiVersionKey.SetValue(apiVersion, String.Empty); 321 | } 322 | } 323 | 324 | public ApiVersionSet GetApiVersions() 325 | { 326 | ApiVersionSet apiVersionCollection = new ApiVersionSet(); 327 | using (RegistryKey apiVersionKey = Registry.CurrentUser.OpenSubKey(ApiVersionRoot)) 328 | { 329 | if (apiVersionKey == null) 330 | { 331 | return apiVersionCollection; 332 | } 333 | 334 | foreach (string apiVersion in apiVersionKey.GetValueNames()) 335 | { 336 | if (!apiVersionCollection.Contains(apiVersion)) 337 | { 338 | apiVersionCollection.Add(apiVersion); 339 | } 340 | } 341 | } 342 | 343 | return apiVersionCollection; 344 | } 345 | 346 | public void SavePreference(string key, object value) 347 | { 348 | using (RegistryKey preferenceKey = Registry.CurrentUser.CreateSubKey(RegistryRoot)) 349 | { 350 | if (preferenceKey == null) 351 | { 352 | return; 353 | } 354 | 355 | preferenceKey.SetValue(key, value); 356 | } 357 | } 358 | 359 | public T GetPreference(string key) 360 | { 361 | using (RegistryKey preferenceKey = Registry.CurrentUser.OpenSubKey(RegistryRoot)) 362 | { 363 | if (preferenceKey == null) 364 | { 365 | return default(T); 366 | } 367 | 368 | object value = preferenceKey.GetValue(key); 369 | if (value == null) 370 | { 371 | return default(T); 372 | } 373 | 374 | if (value is T) 375 | { 376 | return (T)value; 377 | } 378 | 379 | return default(T); 380 | } 381 | } 382 | 383 | private static string GetEnvironmentPath(AadEnvironment environment) 384 | { 385 | return EnvironmentsRoot + '\\' + environment.RegistryName; 386 | } 387 | 388 | private static string GetTenantPath(AadEnvironment environment, string tenant) 389 | { 390 | string environmentPath = GetEnvironmentPath(environment); 391 | return environmentPath + '\\' + tenant; 392 | } 393 | 394 | private static string GetCredentialPath(TenantCredential tenantCredential) 395 | { 396 | string tenantPath = GetTenantPath(tenantCredential.Environment, tenantCredential.Tenant); 397 | return tenantPath + '\\' + tenantCredential.ClientId; 398 | } 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /AadGraphApiHelper/Request.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace AadGraphApiHelper 10 | { 11 | internal class Request 12 | { 13 | internal string AccessToken { get; set; } 14 | 15 | internal Request(string accessToken) 16 | { 17 | this.AccessToken = accessToken; 18 | } 19 | 20 | internal HttpStatusCode Send(string method, string url, string body, out string response) 21 | { 22 | WebRequest request = WebRequest.Create(url); 23 | request.Headers.Add("Authorization", this.AccessToken); 24 | request.ContentType = @"application/json"; 25 | request.Method = method.ToUpperInvariant(); 26 | 27 | if (!String.IsNullOrWhiteSpace(body)) 28 | { 29 | using (StreamWriter writer = new StreamWriter(request.GetRequestStream())) 30 | { 31 | writer.Write(body); 32 | } 33 | } 34 | 35 | try 36 | { 37 | using (WebResponse webResponse = request.GetResponse()) 38 | { 39 | HttpWebResponse httpWebResponse = webResponse as HttpWebResponse; 40 | 41 | if (httpWebResponse == null) 42 | { 43 | throw new InvalidOperationException("Web response is not an http response."); 44 | } 45 | 46 | using (Stream stream = webResponse.GetResponseStream()) 47 | { 48 | using (StreamReader streamReader = new StreamReader(stream, Encoding.UTF8)) 49 | { 50 | response = streamReader.ReadToEnd(); 51 | } 52 | } 53 | 54 | return httpWebResponse.StatusCode; 55 | } 56 | } 57 | catch (WebException exception) 58 | { 59 | Stream stream = exception.Response.GetResponseStream(); 60 | string errorResponseText; 61 | using (StreamReader reader = new StreamReader(stream)) 62 | { 63 | response = reader.ReadToEnd(); 64 | } 65 | 66 | HttpWebResponse httpWebResponse = (HttpWebResponse)exception.Response; 67 | return httpWebResponse.StatusCode; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /AadGraphApiHelper/RequestHistoryManager.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace AadGraphApiHelper 11 | { 12 | class RequestHistoryManager 13 | { 14 | private static RequestHistoryManager s_RequestHistoryManager = new RequestHistoryManager(); 15 | public List RequestHistoryObjects 16 | { 17 | get;set; 18 | } 19 | 20 | public RequestHistoryManager() 21 | { 22 | if (File.Exists(StorageFileName)) 23 | { 24 | string json = File.ReadAllText(StorageFileName); 25 | RequestHistoryObjects = JsonConvert.DeserializeObject>(json); 26 | 27 | // temp code to remove duplicates if present during load 28 | var temp = new List(); 29 | foreach (var requestHistoryObject in RequestHistoryObjects) 30 | { 31 | if (!temp.Exists(x => x.Url == requestHistoryObject.Url)) 32 | temp.Add(requestHistoryObject); 33 | } 34 | 35 | RequestHistoryObjects = temp; 36 | } 37 | else 38 | { 39 | RequestHistoryObjects = new List(); 40 | } 41 | } 42 | 43 | public static RequestHistoryManager Instance 44 | { 45 | get { return s_RequestHistoryManager; } 46 | } 47 | 48 | public void AddRequest(string method, string url, string body) 49 | { 50 | if (RequestHistoryObjects.Exists(x => x.Url.Equals(url))) 51 | return; 52 | RequestHistoryObjects.Add(new RequestHistoryObject(method, url, body)); 53 | SaveHistoryToDisk(); 54 | } 55 | 56 | public void SaveHistoryToDisk() 57 | { 58 | string json = JsonConvert.SerializeObject(RequestHistoryObjects); 59 | 60 | if (!Directory.Exists(StorageDir)) 61 | { 62 | Directory.CreateDirectory(StorageDir); 63 | } 64 | 65 | File.WriteAllText(StorageFileName, json); 66 | } 67 | 68 | private string StorageFileName 69 | { 70 | get 71 | { 72 | string filePath = Path.Combine(StorageDir, "history.json"); 73 | return filePath; 74 | } 75 | } 76 | 77 | private string StorageDir 78 | { 79 | get 80 | { 81 | string dirPath = Path.Combine( 82 | Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AADGraphHelper"); 83 | return dirPath; 84 | } 85 | } 86 | } 87 | 88 | 89 | } 90 | -------------------------------------------------------------------------------- /AadGraphApiHelper/RequestHistoryObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace AadGraphApiHelper 8 | { 9 | class RequestHistoryObject: IEquatable 10 | { 11 | public RequestHistoryObject(string method, string url, string body) 12 | { 13 | Method = method; 14 | Url = url; 15 | this.Body = body; 16 | } 17 | 18 | public string Method { get; set; } 19 | public string Url { get; set; } 20 | public string Body { get; set; } 21 | 22 | 23 | public bool Equals(RequestHistoryObject other) 24 | { 25 | return other.Body.Equals(Body) 26 | && other.Url.Equals(Url) 27 | && other.Method.Equals(Method); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /AadGraphApiHelper/RequestHistoryWindow.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace AadGraphApiHelper 2 | { 3 | partial class RequestHistoryWindow 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.dataGridView1 = new System.Windows.Forms.DataGridView(); 32 | ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); 33 | this.SuspendLayout(); 34 | // 35 | // dataGridView1 36 | // 37 | this.dataGridView1.AllowUserToAddRows = false; 38 | this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; 39 | this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill; 40 | this.dataGridView1.Location = new System.Drawing.Point(0, 0); 41 | this.dataGridView1.Name = "dataGridView1"; 42 | this.dataGridView1.ReadOnly = true; 43 | this.dataGridView1.Size = new System.Drawing.Size(1005, 288); 44 | this.dataGridView1.TabIndex = 0; 45 | this.dataGridView1.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellClick); 46 | // 47 | // RequestHistoryWindow 48 | // 49 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 50 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 51 | this.ClientSize = new System.Drawing.Size(1005, 288); 52 | this.Controls.Add(this.dataGridView1); 53 | this.Name = "RequestHistoryWindow"; 54 | this.Text = "RequestHistoryWindow"; 55 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.RequestHistoryWindow_FormClosing); 56 | this.Load += new System.EventHandler(this.RequestHistoryWindow_Load); 57 | ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); 58 | this.ResumeLayout(false); 59 | 60 | } 61 | 62 | #endregion 63 | 64 | private System.Windows.Forms.DataGridView dataGridView1; 65 | } 66 | } -------------------------------------------------------------------------------- /AadGraphApiHelper/RequestHistoryWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Diagnostics; 6 | using System.Drawing; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | 12 | namespace AadGraphApiHelper 13 | { 14 | public partial class RequestHistoryWindow : Form 15 | { 16 | BindingSource mBindingSource = new BindingSource(); 17 | 18 | public int RowSelected { get; set; } 19 | 20 | public RequestHistoryWindow() 21 | { 22 | InitializeComponent(); 23 | } 24 | 25 | // A copy of RequestHistoryObjects to allow modification and cancellation 26 | internal List ViewRequestHistoryObjects { get; private set; } 27 | 28 | private void RequestHistoryWindow_Load(object sender, EventArgs e) 29 | { 30 | mBindingSource.DataSource = typeof(RequestHistoryObject); 31 | 32 | ViewRequestHistoryObjects = RequestHistoryManager.Instance.RequestHistoryObjects.ToList(); 33 | mBindingSource.DataSource = ViewRequestHistoryObjects; 34 | 35 | dataGridView1.DataSource = mBindingSource; 36 | dataGridView1.AutoGenerateColumns = true; 37 | 38 | for (int i = 0; i < dataGridView1.Columns.Count; i++) 39 | { 40 | dataGridView1.Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 41 | dataGridView1.Columns[i].SortMode = DataGridViewColumnSortMode.Automatic; 42 | } 43 | 44 | dataGridView1.Columns[0].MinimumWidth = 100; 45 | dataGridView1.Columns[0].FillWeight = 1; 46 | 47 | dataGridView1.Columns[1].MinimumWidth = 200; 48 | dataGridView1.Columns[1].FillWeight = 8; 49 | 50 | dataGridView1.Columns[2].MinimumWidth = 100; 51 | dataGridView1.Columns[2].FillWeight = 4; 52 | } 53 | 54 | //private void FillDataFromSource() 55 | //{ 56 | // foreach (var item in RequestHistoryManager.Instance.RequestHistoryObjects) 57 | // { 58 | // mBindingSource.Add(item); 59 | // } 60 | //} 61 | 62 | private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) 63 | { 64 | this.RowSelected = e.RowIndex; 65 | this.DialogResult = DialogResult.OK; 66 | this.Close(); 67 | } 68 | 69 | private void RequestHistoryWindow_FormClosing(object sender, FormClosingEventArgs e) 70 | { 71 | //if (!ViewRequestHistoryObjects.Equals(RequestHistoryManager.Instance.RequestHistoryObjects)) 72 | if (!AreListEqual(ViewRequestHistoryObjects, RequestHistoryManager.Instance.RequestHistoryObjects)) 73 | { 74 | DialogResult dr = MessageBox.Show( 75 | "You made some changes to history, do you want to save them?", 76 | "Confirm", 77 | MessageBoxButtons.YesNoCancel); 78 | switch (dr) 79 | { 80 | case DialogResult.Yes: 81 | RequestHistoryManager.Instance.RequestHistoryObjects = ViewRequestHistoryObjects.ToList(); 82 | break; 83 | 84 | case DialogResult.No: 85 | break; 86 | 87 | case DialogResult.Cancel: 88 | e.Cancel = true; 89 | break; 90 | } 91 | } 92 | } 93 | 94 | internal bool AreListEqual(List list1, List list2) 95 | { 96 | return list1.Count == list2.Count // assumes unique values in each list 97 | && new HashSet(list1).SetEquals(list2); 98 | 99 | //return Ids.Count == XmlIds.Count && 100 | // Ids.All(XmlIds.Contains) && 101 | // XmlIds.All(Ids.Contains); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /AadGraphApiHelper/RequestHistoryWindow.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /AadGraphApiHelper/ResponseTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.Windows.Forms; 6 | using System.Web.Script.Serialization; 7 | 8 | namespace AadGraphApiHelper 9 | { 10 | internal class ResponseTable 11 | { 12 | private const string ODataMetadataKey = @"odata.metadata"; 13 | 14 | private const string ODataErrorKey = @"odata.error"; 15 | 16 | private const string ODataContextKey = @"@odata.context"; 17 | 18 | private const string EntityKey = @"$entity"; 19 | 20 | //private const string MetadataKey = @"$metadata#"; 21 | 22 | private const string ODataMetadataElementKey = @"@Element"; 23 | 24 | private const string ODataTypeKey = @"odata.type"; 25 | 26 | private readonly DataGridView dataGridView; 27 | 28 | private IDictionary deserializedObject; 29 | 30 | private string jsonText; 31 | 32 | public ResponseTable(DataGridView gridView) 33 | { 34 | this.dataGridView = gridView; 35 | } 36 | 37 | public string JsonText 38 | { 39 | get 40 | { 41 | return this.jsonText; 42 | } 43 | } 44 | 45 | public bool IsFormattable { get; private set; } 46 | 47 | public bool ShowNullValues { get; set; } 48 | 49 | public void SetJsonText(string text) 50 | { 51 | this.dataGridView.Hide(); 52 | 53 | this.jsonText = text; 54 | 55 | try 56 | { 57 | JavaScriptSerializer serializer = new JavaScriptSerializer(); 58 | this.deserializedObject = serializer.Deserialize>(this.jsonText); 59 | } 60 | catch (Exception) 61 | { 62 | this.IsFormattable = false; 63 | return; 64 | } 65 | 66 | if (this.deserializedObject.ContainsKey(ODataMetadataKey) || 67 | this.deserializedObject.ContainsKey(ODataContextKey)) 68 | { 69 | this.IsFormattable = true; 70 | return; 71 | } 72 | 73 | this.IsFormattable = false; 74 | } 75 | 76 | public void Clear() 77 | { 78 | this.dataGridView.Hide(); 79 | this.dataGridView.Rows.Clear(); 80 | this.dataGridView.Columns.Clear(); 81 | } 82 | 83 | public void CreateResponseTable() 84 | { 85 | this.dataGridView.SuspendLayout(); 86 | this.Clear(); 87 | 88 | if (this.deserializedObject == null || this.IsFormattable == false) 89 | { 90 | return; 91 | } 92 | 93 | object contextOrMetadataObject; 94 | if (!this.deserializedObject.TryGetValue(ODataContextKey, out contextOrMetadataObject) && 95 | !this.deserializedObject.TryGetValue(ODataMetadataKey, out contextOrMetadataObject)) 96 | { 97 | return; 98 | } 99 | 100 | string contextOrMetadata = contextOrMetadataObject as string; 101 | 102 | if (String.IsNullOrWhiteSpace(contextOrMetadata)) 103 | { 104 | return; 105 | } 106 | 107 | if (contextOrMetadata.Contains(ODataMetadataElementKey) || contextOrMetadata.Contains(EntityKey)) 108 | { 109 | this.FormatAsObject(); 110 | } 111 | else 112 | { 113 | this.FormatAsCollection(); 114 | } 115 | 116 | this.dataGridView.ResumeLayout(); 117 | this.dataGridView.Show(); 118 | } 119 | 120 | public string GetCellValueOfSelectedCell() 121 | { 122 | if (this.dataGridView.CurrentCell == null) 123 | { 124 | return null; 125 | } 126 | 127 | return this.dataGridView.CurrentCell.Value as string; 128 | } 129 | 130 | public string GetCellValueFromSelectedRow(string columnName) 131 | { 132 | DataGridViewRow row; 133 | if (this.dataGridView.SelectedRows.Count == 1) 134 | { 135 | row = this.dataGridView.SelectedRows[0]; 136 | } 137 | else if (this.dataGridView.CurrentCell != null) 138 | { 139 | row = this.dataGridView.CurrentCell.OwningRow; 140 | } 141 | else 142 | { 143 | return null; 144 | } 145 | 146 | try 147 | { 148 | return row.Cells[columnName].Value as string; 149 | } 150 | catch (ArgumentException) 151 | { 152 | return null; 153 | } 154 | } 155 | 156 | private void FormatAsObject() 157 | { 158 | this.dataGridView.ColumnCount = 2; 159 | this.dataGridView.Columns[0].Name = "Property"; 160 | this.dataGridView.Columns[1].Name = "Value"; 161 | this.dataGridView.Columns[1].DefaultCellStyle.WrapMode = DataGridViewTriState.True; 162 | 163 | foreach (KeyValuePair property in this.deserializedObject) 164 | { 165 | if (property.Value == null) 166 | { 167 | if (this.ShowNullValues) 168 | { 169 | this.dataGridView.Rows.Add(property.Key, StringResources.NullValue); 170 | } 171 | } 172 | else if (property.Value is ValueType || property.Value is String) 173 | { 174 | this.dataGridView.Rows.Add(property.Key, property.Value); 175 | } 176 | else if (property.Value is IEnumerable) 177 | { 178 | ICollection collection; 179 | 180 | if (((IEnumerable)property.Value).TryConvertToTypedCollection(out collection) && collection.Count > 0 && 181 | (collection is IEnumerable || collection is IEnumerable)) 182 | { 183 | string value = String.Empty; 184 | foreach (object o in collection) 185 | { 186 | if (!String.IsNullOrWhiteSpace(value)) 187 | { 188 | value += Environment.NewLine; 189 | } 190 | 191 | value += o.ToString(); 192 | } 193 | 194 | this.dataGridView.Rows.Add(property.Key, value); 195 | } 196 | else 197 | { 198 | string value = String.Empty; 199 | bool itemAdded = false; 200 | int itemCount = 0; 201 | 202 | foreach (object obj in (IEnumerable)property.Value) 203 | { 204 | itemCount++; 205 | if (obj is IDictionary) 206 | { 207 | if (itemAdded) 208 | { 209 | value += Environment.NewLine; 210 | } 211 | 212 | value += @"{ "; 213 | foreach (DictionaryEntry entry in (IDictionary)obj) 214 | { 215 | if (entry.Value == null && this.ShowNullValues == false) 216 | { 217 | continue; 218 | } 219 | 220 | value += itemAdded ? @", " : String.Empty; 221 | value += String.Format(CultureInfo.InvariantCulture, 222 | @"""{0}"": ""{1}""", 223 | entry.Key, 224 | entry.Value ?? StringResources.NullValue); 225 | itemAdded = true; 226 | } 227 | 228 | value += @" }"; 229 | } 230 | } 231 | 232 | value = itemCount == 0 ? StringResources.EmptyCollectionValue : value; 233 | this.dataGridView.Rows.Add(property.Key, value); 234 | } 235 | } 236 | } 237 | } 238 | 239 | private void FormatAsCollection() 240 | { 241 | ArrayList itemCollection = this.deserializedObject[@"value"] as ArrayList; 242 | if (itemCollection == null || itemCollection.Count == 0) 243 | { 244 | return; 245 | } 246 | 247 | IDictionary columnNameByIndex = new Dictionary(); 248 | 249 | foreach (IDictionary item in itemCollection) 250 | { 251 | foreach (KeyValuePair property in item) 252 | { 253 | if (IncludeProperty(property)) 254 | { 255 | if (!columnNameByIndex.ContainsKey(property.Key)) 256 | { 257 | columnNameByIndex.Add(property.Key, columnNameByIndex.Count); 258 | } 259 | } 260 | } 261 | } 262 | 263 | this.dataGridView.ColumnCount = columnNameByIndex.Count; 264 | foreach (KeyValuePair columnPair in columnNameByIndex) 265 | { 266 | this.dataGridView.Columns[columnPair.Value].Name = columnPair.Key; 267 | } 268 | 269 | foreach (IDictionary item in itemCollection) 270 | { 271 | object[] values = new object[columnNameByIndex.Count]; 272 | foreach (KeyValuePair property in item) 273 | { 274 | if (IncludeProperty(property)) 275 | { 276 | values[columnNameByIndex[property.Key]] = property.Value; 277 | } 278 | } 279 | 280 | this.dataGridView.Rows.Add(values); 281 | } 282 | } 283 | 284 | private static bool IncludeProperty(KeyValuePair property) 285 | { 286 | if (property.Key.Equals(ODataTypeKey, StringComparison.OrdinalIgnoreCase)) 287 | { 288 | return false; 289 | } 290 | 291 | if (property.Value is ValueType || property.Value is String) 292 | { 293 | return true; 294 | } 295 | 296 | return false; 297 | } 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /AadGraphApiHelper/StringResources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.34209 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace AadGraphApiHelper { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class StringResources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal StringResources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AadGraphApiHelper.StringResources", typeof(StringResources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to The client id cannot be null or only white space.. 65 | /// 66 | internal static string ClientIdCannotBeNullOrWhiteSpace { 67 | get { 68 | return ResourceManager.GetString("ClientIdCannotBeNullOrWhiteSpace", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to The client id value must be a guid.. 74 | /// 75 | internal static string ClientIdMustBeAGuid { 76 | get { 77 | return ResourceManager.GetString("ClientIdMustBeAGuid", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Looks up a localized string similar to Key:. 83 | /// 84 | internal static string ClientKeyLabel { 85 | get { 86 | return ResourceManager.GetString("ClientKeyLabel", resourceCulture); 87 | } 88 | } 89 | 90 | /// 91 | /// Looks up a localized string similar to Reply Url:. 92 | /// 93 | internal static string ClientReplyUrlLabel { 94 | get { 95 | return ResourceManager.GetString("ClientReplyUrlLabel", resourceCulture); 96 | } 97 | } 98 | 99 | /// 100 | /// Looks up a localized string similar to (complex object not shown). 101 | /// 102 | internal static string ComplexValue { 103 | get { 104 | return ResourceManager.GetString("ComplexValue", resourceCulture); 105 | } 106 | } 107 | 108 | /// 109 | /// Looks up a localized string similar to (none). 110 | /// 111 | internal static string EmptyCollectionValue { 112 | get { 113 | return ResourceManager.GetString("EmptyCollectionValue", resourceCulture); 114 | } 115 | } 116 | 117 | /// 118 | /// Looks up a localized string similar to The environment parameter cannot be null.. 119 | /// 120 | internal static string EnvironmentCannotBeNull { 121 | get { 122 | return ResourceManager.GetString("EnvironmentCannotBeNull", resourceCulture); 123 | } 124 | } 125 | 126 | /// 127 | /// Looks up a localized string similar to Get app. &token. 128 | /// 129 | internal static string GetAppTokenText { 130 | get { 131 | return ResourceManager.GetString("GetAppTokenText", resourceCulture); 132 | } 133 | } 134 | 135 | /// 136 | /// Looks up a localized string similar to Get user &token. 137 | /// 138 | internal static string GetUserTokenText { 139 | get { 140 | return ResourceManager.GetString("GetUserTokenText", resourceCulture); 141 | } 142 | } 143 | 144 | /// 145 | /// Looks up a localized string similar to Key can only be set for a web application.. 146 | /// 147 | internal static string KeyCannotBeSet { 148 | get { 149 | return ResourceManager.GetString("KeyCannotBeSet", resourceCulture); 150 | } 151 | } 152 | 153 | /// 154 | /// Looks up a localized string similar to <Add...>. 155 | /// 156 | internal static string ManageItem { 157 | get { 158 | return ResourceManager.GetString("ManageItem", resourceCulture); 159 | } 160 | } 161 | 162 | /// 163 | /// Looks up a localized string similar to The name parameter cannot be null or white space.. 164 | /// 165 | internal static string NameCannotBeNullOrWhiteSpace { 166 | get { 167 | return ResourceManager.GetString("NameCannotBeNullOrWhiteSpace", resourceCulture); 168 | } 169 | } 170 | 171 | /// 172 | /// Looks up a localized string similar to (null). 173 | /// 174 | internal static string NullValue { 175 | get { 176 | return ResourceManager.GetString("NullValue", resourceCulture); 177 | } 178 | } 179 | 180 | /// 181 | /// Looks up a localized string similar to Reply URL can only be set for a native client application.. 182 | /// 183 | internal static string ReplyUrlCannotBeSet { 184 | get { 185 | return ResourceManager.GetString("ReplyUrlCannotBeSet", resourceCulture); 186 | } 187 | } 188 | 189 | /// 190 | /// Looks up a localized string similar to The tenant cannot be null or only white space.. 191 | /// 192 | internal static string TenantCannotBeNullOrWhiteSpace { 193 | get { 194 | return ResourceManager.GetString("TenantCannotBeNullOrWhiteSpace", resourceCulture); 195 | } 196 | } 197 | 198 | /// 199 | /// Looks up a localized string similar to Error returned by token endpoint. 200 | /// 201 | internal static string TokenErrorTitle { 202 | get { 203 | return ResourceManager.GetString("TokenErrorTitle", resourceCulture); 204 | } 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /AadGraphApiHelper/StringResources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | The client id cannot be null or only white space. 122 | 123 | 124 | The client id value must be a guid. 125 | 126 | 127 | Key: 128 | 129 | 130 | Reply Url: 131 | 132 | 133 | (complex object not shown) 134 | 135 | 136 | (none) 137 | 138 | 139 | The environment parameter cannot be null. 140 | 141 | 142 | Get app. &token 143 | 144 | 145 | Get user &token 146 | 147 | 148 | Key can only be set for a web application. 149 | 150 | 151 | <Add...> 152 | 153 | 154 | The name parameter cannot be null or white space. 155 | 156 | 157 | (null) 158 | 159 | 160 | Reply URL can only be set for a native client application. 161 | 162 | 163 | The tenant cannot be null or only white space. 164 | 165 | 166 | Error returned by token endpoint 167 | 168 | -------------------------------------------------------------------------------- /AadGraphApiHelper/TenantCredential.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | using System.Text; 4 | using System.Linq; 5 | 6 | namespace AadGraphApiHelper 7 | { 8 | internal class TenantCredential : IEquatable, IComparable 9 | { 10 | private Uri replyUrl; 11 | 12 | public TenantCredential(AadEnvironment environment, string tenant, string clientId, ApplicationType applicationType, string alias) 13 | { 14 | if (environment == null) 15 | { 16 | throw new ArgumentException(StringResources.EnvironmentCannotBeNull); 17 | } 18 | 19 | if (String.IsNullOrEmpty(tenant)) 20 | { 21 | throw new ArgumentException(StringResources.TenantCannotBeNullOrWhiteSpace); 22 | } 23 | 24 | if (String.IsNullOrWhiteSpace(clientId)) 25 | { 26 | throw new ArgumentException(StringResources.ClientIdCannotBeNullOrWhiteSpace); 27 | } 28 | 29 | Guid ignored; 30 | if (!Guid.TryParse(clientId, out ignored)) 31 | { 32 | throw new ArgumentException(StringResources.ClientIdMustBeAGuid); 33 | } 34 | 35 | this.Environment = environment; 36 | this.Tenant = tenant; 37 | this.ClientId = clientId; 38 | this.ApplicationType = applicationType; 39 | this.Alias = alias; 40 | } 41 | 42 | public AadEnvironment Environment { get; private set; } 43 | 44 | public string Tenant { get; private set; } 45 | 46 | public ApplicationType ApplicationType { get; private set; } 47 | 48 | public byte[] EncryptedKey { get; set; } 49 | 50 | public string ClientId { get; set; } 51 | 52 | public Uri ReplyUrl 53 | { 54 | get 55 | { 56 | return this.replyUrl; 57 | } 58 | 59 | set 60 | { 61 | if (value == null) 62 | { 63 | this.replyUrl = null; 64 | return; 65 | } 66 | 67 | if (this.ApplicationType != ApplicationType.Native) 68 | { 69 | throw new ArgumentException(StringResources.ReplyUrlCannotBeSet); 70 | } 71 | 72 | this.replyUrl = value; 73 | } 74 | } 75 | 76 | public string Alias { get; set; } 77 | 78 | public string GetDecryptedKey() 79 | { 80 | byte[] plainBytes = ProtectedData.Unprotect(this.EncryptedKey, null, DataProtectionScope.CurrentUser); 81 | return Encoding.UTF8.GetString(plainBytes); 82 | } 83 | 84 | public void EncryptAndSetKey(string key) 85 | { 86 | if (key == null) 87 | { 88 | this.EncryptedKey = null; 89 | return; 90 | } 91 | 92 | if (this.ApplicationType != ApplicationType.Web) 93 | { 94 | throw new ArgumentException(StringResources.KeyCannotBeSet); 95 | } 96 | 97 | byte[] plainBytes = Encoding.UTF8.GetBytes(key); 98 | this.EncryptedKey = ProtectedData.Protect(plainBytes, null, DataProtectionScope.CurrentUser); 99 | } 100 | 101 | /// 102 | /// Determines whether the specified is equal to the current . 103 | /// 104 | /// 105 | /// true if the specified object is equal to the current object; otherwise, false. 106 | /// 107 | /// The object to compare with the current object. 108 | public override bool Equals(object obj) 109 | { 110 | if (obj == null) 111 | { 112 | return false; 113 | } 114 | 115 | return this.Equals(obj as TenantCredential); 116 | } 117 | 118 | /// 119 | /// Indicates whether the current object is equal to another object of the same type. 120 | /// 121 | /// 122 | /// true if the current object is equal to the parameter; otherwise, false. 123 | /// 124 | /// An object to compare with this object. 125 | public bool Equals(TenantCredential other) 126 | { 127 | if (other == null) 128 | { 129 | return false; 130 | } 131 | 132 | bool areEqual = this.Tenant.Equals(other.Tenant, StringComparison.OrdinalIgnoreCase) && 133 | this.ClientId.Equals(other.ClientId, StringComparison.OrdinalIgnoreCase) && 134 | this.ApplicationType == other.ApplicationType && 135 | String.Equals(this.Alias, other.Alias, StringComparison.OrdinalIgnoreCase); 136 | 137 | if (!areEqual) 138 | { 139 | return false; 140 | } 141 | 142 | if (this.ApplicationType == ApplicationType.Native) 143 | { 144 | return this.ReplyUrl.Equals(other.ReplyUrl); 145 | } 146 | 147 | if ((this.EncryptedKey == null && other.EncryptedKey != null) || 148 | (this.EncryptedKey != null && other.EncryptedKey == null)) 149 | { 150 | return false; 151 | } 152 | 153 | if (this.EncryptedKey == null || other.EncryptedKey == null) 154 | { 155 | return true; 156 | } 157 | 158 | return this.EncryptedKey.SequenceEqual(other.EncryptedKey); 159 | } 160 | 161 | /// 162 | /// Serves as a hash function for a particular type. 163 | /// 164 | /// 165 | /// A hash code for the current . 166 | /// 167 | public override int GetHashCode() 168 | { 169 | int hashCode = (this.Tenant + "::" + this.ClientId).GetHashCode() ^ this.ApplicationType.GetHashCode(); 170 | 171 | if (this.ApplicationType == ApplicationType.Native) 172 | { 173 | return hashCode ^ this.ReplyUrl.GetHashCode(); 174 | } 175 | 176 | return hashCode ^ this.EncryptedKey.GetHashCode(); 177 | } 178 | 179 | /// 180 | /// Compares the current object with another object of the same type. 181 | /// 182 | /// 183 | /// A value that indicates the relative order of the objects being compared. The return value has the following meanings: 184 | /// Value Meaning Less than zero This object is less than the parameter. 185 | /// Zero This object is equal to . 186 | /// Greater than zero This object is greater than . 187 | /// 188 | /// An object to compare with this object. 189 | public int CompareTo(TenantCredential other) 190 | { 191 | if (other == null) 192 | { 193 | return 1; 194 | } 195 | 196 | // Compare value is used primarily for sorting purposes, and by using ToString(), we ensure that if representation is 197 | // changed in the future, CompareTo will be reflective of it. 198 | int compareValue = this.ToString().ToUpperInvariant().CompareTo(other.ToString().ToUpperInvariant()); 199 | return compareValue; 200 | } 201 | 202 | /// 203 | /// Returns a string that represents the current object. 204 | /// 205 | /// 206 | /// A string that represents the current object. 207 | /// 208 | public override string ToString() 209 | { 210 | string alias = string.IsNullOrEmpty(this.Alias) ? "" : this.Alias + ": "; 211 | return alias + this.Tenant + @" (" + this.ClientId + @")"; 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /AadGraphApiHelper/TenantCredentialSet.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace AadGraphApiHelper 4 | { 5 | internal class TenantCredentialSet : SortedSet 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /AadGraphApiHelper/Token.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using Microsoft.IdentityModel.Clients.ActiveDirectory; 4 | 5 | namespace AadGraphApiHelper 6 | { 7 | internal class Token 8 | { 9 | private static TenantCredential lastUserTenantCredential = null; 10 | 11 | internal static string GetApplicationToken(TenantCredential tenantCredential) 12 | { 13 | string loginUrl = tenantCredential.Environment.GetLoginUrl(tenantCredential.Tenant); 14 | AuthenticationContext ac = new AuthenticationContext(loginUrl, false); 15 | ClientCredential clientCredential = new ClientCredential(tenantCredential.ClientId, tenantCredential.GetDecryptedKey()); 16 | AuthenticationResult authenticationResult = ac.AcquireToken(tenantCredential.Environment.GraphResourceUrl, clientCredential); 17 | return authenticationResult.CreateAuthorizationHeader(); 18 | } 19 | 20 | internal static string GetUserToken(TenantCredential tenantCredential) 21 | { 22 | string loginUrl = tenantCredential.Environment.GetLoginUrl(tenantCredential.Tenant); 23 | AuthenticationContext ac = new AuthenticationContext(loginUrl, false); 24 | 25 | var promptBehaviour = PromptBehavior.Auto; 26 | 27 | if (lastUserTenantCredential != null && !lastUserTenantCredential.Tenant.Equals(tenantCredential.Tenant)) 28 | { 29 | promptBehaviour = PromptBehavior.Always; 30 | } 31 | 32 | lastUserTenantCredential = tenantCredential; 33 | 34 | AuthenticationResult authenticationResult = ac.AcquireToken( 35 | tenantCredential.Environment.GraphResourceUrl, 36 | tenantCredential.ClientId, 37 | tenantCredential.ReplyUrl, 38 | promptBehaviour); 39 | 40 | return authenticationResult.CreateAuthorizationHeader(); 41 | 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /AadGraphApiHelper/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AadGraphHelper 2 | This tool let's you, a developer, create request for AAD Graph API, issue the request and get the response. 3 | The responses can be viewed in a table format (instead of raw JSON) which makes visualization easier, although 4 | only simple non-null values are exposed in the table. The requests can be saved as templates that can be opened 5 | up later, and issued against different tenants. Finally, it also has a filter query builder that can let you 6 | compose a search for users, convert it into a filter query string that can be issued as a request. 7 | 8 | To get started: 9 | 10 | 1. Create an application in Azure Active Directory (https://manage.windowsazure.com), and give it at least the Directory Read permissions. 11 | 12 | a. Create a web application if you want to store the key and authentication to not require manual entry of credentials. Such an application cannot access all properties (e.g. oauth2PermissionGrants), or perform certain operations (e.g. delete a user). 13 | 14 | b. If you need to perform those operations, create a native application. You will be required to provide user credentials on every authentication, but it will allow administrator operations. 15 | 16 | 2. Click on the "Releases" tab in this GIT repository (at the top), and download the latest release. 17 | 18 | 3. Execute the AadGraphHelper.exe from the downloaded zip file. 19 | 20 | 4. In the tool, select Environment as "Production" and click "Add" in the Tenant/Credential list. 21 | 22 | 5. Add the credentials for the application that you created in step 1. 23 | 24 | 6. Click on the "Get Token" button. 25 | 26 | 7. Now you can select the method e.g. GET), resource (e.g. users) and click "Execute" button. This will issue the very first request. Once it executes successfully, play around and have fun. 27 | 28 | --------------------------------------------------------------------------------