-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/ginger analytics config page b #3895
Changes from 12 commits
6e5b481
176fb9e
b77a921
0eea4fb
f720eac
1e6fde5
630b928
f8b11ef
58c683c
ae27951
dcf60eb
7ffaece
c146ab2
543277e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
#region License | ||
/* | ||
Copyright © 2014-2024 European Support Limited | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License") | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
#endregion | ||
|
||
using Ginger.Configurations; | ||
using Amdocs.Ginger.Common; | ||
using amdocs.ginger.GingerCoreNET; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using System; | ||
using IdentityModel.Client; | ||
using System.IdentityModel.Tokens.Jwt; | ||
|
||
namespace Ginger.ExternalConfigurations | ||
{ | ||
public class GingerAnalyticsAPI | ||
Check warning on line 30 in Ginger/Ginger/ExternalConfigurations/GingerAnalyticsAPI.cs Codacy Production / Codacy Static Code AnalysisGinger/Ginger/ExternalConfigurations/GingerAnalyticsAPI.cs#L30
|
||
{ | ||
public static DateTime validTo = DateTime.MinValue; | ||
Check failure on line 32 in Ginger/Ginger/ExternalConfigurations/GingerAnalyticsAPI.cs Codacy Production / Codacy Static Code AnalysisGinger/Ginger/ExternalConfigurations/GingerAnalyticsAPI.cs#L32
|
||
public static GingerAnalyticsConfiguration gingerAnalyticsUserConfig = | ||
Check failure on line 33 in Ginger/Ginger/ExternalConfigurations/GingerAnalyticsAPI.cs Codacy Production / Codacy Static Code AnalysisGinger/Ginger/ExternalConfigurations/GingerAnalyticsAPI.cs#L33
|
||
WorkSpace.Instance.SolutionRepository.GetAllRepositoryItems<GingerAnalyticsConfiguration>().Count == 0 ? new GingerAnalyticsConfiguration() : WorkSpace.Instance.SolutionRepository.GetFirstRepositoryItem<GingerAnalyticsConfiguration>(); | ||
|
||
|
||
public static async Task<bool> RequestToken(string clientId, string clientSecret, string address) | ||
{ | ||
try | ||
{ | ||
HttpClientHandler handler = new HttpClientHandler() { UseProxy = false }; | ||
|
||
using (var client = new HttpClient(handler)) | ||
{ | ||
|
||
var disco = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest | ||
{ | ||
Address = address, | ||
Policy = | ||
{ | ||
RequireHttps = true, | ||
ValidateIssuerName = true | ||
} | ||
}); | ||
|
||
if (disco.IsError) | ||
{ | ||
Reporter.ToLog(eLogLevel.ERROR, $"Discovery document error: {disco.Error}"); | ||
return false; | ||
} | ||
|
||
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest | ||
{ | ||
Address = disco.TokenEndpoint, | ||
ClientId = clientId, | ||
ClientSecret = clientSecret | ||
}); | ||
|
||
if (tokenResponse.IsError) | ||
{ | ||
Reporter.ToLog(eLogLevel.ERROR, $"Token request error: {tokenResponse.Error}"); | ||
return false; | ||
} | ||
|
||
validTo = DateTime.UtcNow.AddMinutes(60); | ||
gingerAnalyticsUserConfig.Token = tokenResponse.AccessToken; | ||
return true; | ||
} | ||
} | ||
catch (HttpRequestException httpEx) | ||
{ | ||
Reporter.ToLog(eLogLevel.ERROR, "HTTP request failed", httpEx); | ||
return false; | ||
} | ||
catch (Exception ex) | ||
{ | ||
Reporter.ToLog(eLogLevel.ERROR, "Unexpected error during token request", ex); | ||
return false; | ||
} | ||
} | ||
|
||
public static bool IsTokenValid() | ||
{ | ||
try | ||
{ | ||
if (string.IsNullOrEmpty(gingerAnalyticsUserConfig.Token) || gingerAnalyticsUserConfig.Token.Split('.').Length != 3) | ||
{ | ||
return false; | ||
} | ||
|
||
var handler = new JwtSecurityTokenHandler(); | ||
var jwtToken = handler.ReadJwtToken(gingerAnalyticsUserConfig.Token); | ||
validTo = jwtToken.ValidTo; | ||
if (DateTime.UtcNow < validTo) | ||
{ | ||
return true; | ||
} | ||
else | ||
{ | ||
return false; | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
Reporter.ToLog(eLogLevel.ERROR, "Error occured in validate token", ex); | ||
return false; | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<UserControlsLib:GingerUIPage x:Class="Ginger.ExternalConfigurations.GingerAnalyticsConfigurationPage" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
xmlns:local="clr-namespace:Ginger.ExternalConfigurations" | ||
xmlns:usercontrols="clr-namespace:Amdocs.Ginger.UserControls" | ||
xmlns:UserControlsLib="clr-namespace:Ginger.UserControlsLib" | ||
xmlns:Activities="clr-namespace:Ginger.BusinessFlowWindows" | ||
mc:Ignorable="d" | ||
d:DesignHeight="450" d:DesignWidth="800" | ||
Title="GingerAnalyticsConfigurationPage"> | ||
|
||
<DockPanel Background="{StaticResource $BackgroundColor_White}"> | ||
<Grid> | ||
<Grid.RowDefinitions> | ||
<RowDefinition Height="30"/> | ||
<RowDefinition Height="1*"/> | ||
</Grid.RowDefinitions> | ||
<StackPanel Orientation="Horizontal" Grid.Row="0"> | ||
<Label Content="Ginger Analytics Configuration" Style="{StaticResource $HorizontalExpanderLabelStyle}"/> | ||
<usercontrols:ImageMakerControl SetAsFontImageWithSize="16" ToolTip="Enterprise Feature" ImageType="Building" Width="20" Height="16" Foreground="{StaticResource $BackgroundColor_Black}" /> | ||
</StackPanel> | ||
<StackPanel Orientation="Vertical" Grid.Row="1"> | ||
<Grid Margin="10,10,0,0" x:Name="xAnalyticsGrid" > | ||
<Grid.RowDefinitions> | ||
<RowDefinition Height="50"/> | ||
<RowDefinition Height="50"/> | ||
<RowDefinition Height="50"/> | ||
<RowDefinition Height="50"/> | ||
</Grid.RowDefinitions> | ||
<Grid.ColumnDefinitions> | ||
<ColumnDefinition Width="230"/> | ||
<ColumnDefinition Width="350*"/> | ||
<ColumnDefinition/> | ||
</Grid.ColumnDefinitions> | ||
<StackPanel Orientation="Horizontal" Grid.Column="0" Grid.Row="0"> | ||
<Label x:Name="xGAAURLLabel" Content="Ginger Analytics Account URL:" Style="{StaticResource @InputFieldLabelStyle}" VerticalAlignment="Center" FontSize="12"/> | ||
<Label x:Name="xGAAURLLabelValidation" Content="*" Style="{StaticResource @InputFieldLabelStyle}" VerticalAlignment="Center" Foreground="Red" FontWeight="Bold" FontSize="12"/> | ||
</StackPanel> | ||
<Activities:UCValueExpression x:Name="xGAAURLTextBox" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Left" VerticalAlignment="Center" ToolTip="Ginger Analytics Account URL" Margin="10,0,0,0" Width="400"/> | ||
|
||
<StackPanel Orientation="Horizontal" Grid.Column="0" Grid.Row="1"> | ||
<Label x:Name="xISURLLabel" Content="Identity Service URL:" Style="{StaticResource @InputFieldLabelStyle}" VerticalAlignment="Center" FontSize="12"/> | ||
<Label x:Name="xISURLLabelValidation" Content="*" Style="{StaticResource @InputFieldLabelStyle}" VerticalAlignment="Center" Foreground="Red" FontWeight="Bold" FontSize="12"/> | ||
</StackPanel> | ||
<Activities:UCValueExpression x:Name="xISURLTextBox" Grid.Column="1" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Center" ToolTip="Identiry Service URL" Margin="10,0,0,0" Width="400"/> | ||
|
||
<StackPanel Orientation="Horizontal" Grid.Column="0" Grid.Row="2"> | ||
<Label x:Name="xClientIdLabel" Content="Client Id:" Style="{StaticResource @InputFieldLabelStyle}" VerticalAlignment="Center" FontSize="12"/> | ||
<Label x:Name="xClientIdLabelValidation" Content="*" Style="{StaticResource @InputFieldLabelStyle}" VerticalAlignment="Center" Foreground="Red" FontWeight="Bold" FontSize="12"/> | ||
</StackPanel> | ||
<Activities:UCValueExpression x:Name="xClientIdTextBox" Grid.Column="1" Grid.Row="2" HorizontalAlignment="Left" VerticalAlignment="Center" ToolTip="Client Id" Margin="10,0,0,0" Width="400" LostKeyboardFocus="xClientIdTextBox_LostKeyboardFocus"/> | ||
|
||
<StackPanel Orientation="Horizontal" Grid.Column="0" Grid.Row="3"> | ||
<Label x:Name="xClientSecretLabel" Content="Client Secret:" Style="{StaticResource @InputFieldLabelStyle}" VerticalAlignment="Center" FontSize="12"/> | ||
<Label x:Name="xClientSecretValidation" Content="*" Style="{StaticResource @InputFieldLabelStyle}" VerticalAlignment="Center" Foreground="Red" FontWeight="Bold" FontSize="12"/> | ||
</StackPanel> | ||
<Activities:UCValueExpression x:Name="xClientSecretTextBox" Grid.Column="1" Grid.Row="3" HorizontalAlignment="Left" VerticalAlignment="Center" ToolTip="Client Secret" Margin="10,0,0,0" Width="400" LostKeyboardFocus="xClientSecretTextBox_LostKeyboardFocus"/> | ||
</Grid> | ||
<Grid> | ||
<Button x:Name="xTestConBtn" IsEnabled="True" Click="xTestConBtn_Click" Content="Test Connection" Width="100" Background="White" Style="{StaticResource $InputButtonStyle}" Margin="10,15,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" /> | ||
<usercontrols:ImageMakerControl x:Name="xProcessingImage" ImageType="Processing" Margin="120,10,0,0" HorizontalAlignment="Left" Height="30" Width="20" Visibility="Hidden"></usercontrols:ImageMakerControl> | ||
|
||
</Grid> | ||
</StackPanel> | ||
</Grid> | ||
</DockPanel> | ||
</UserControlsLib:GingerUIPage> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
#region License | ||
/* | ||
Copyright © 2014-2024 European Support Limited | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License") | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
#endregion | ||
|
||
using Ginger.Configurations; | ||
using Ginger.UserControlsLib; | ||
using Amdocs.Ginger.Common; | ||
using System.Windows; | ||
using GingerCore; | ||
using amdocs.ginger.GingerCoreNET; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using System; | ||
using IdentityModel.Client; | ||
using Ginger.ValidationRules; | ||
using System.IdentityModel.Tokens.Jwt; | ||
using System.Linq; | ||
using Microsoft.IdentityModel.Tokens; | ||
using System.Text; | ||
|
||
namespace Ginger.ExternalConfigurations | ||
{ | ||
/// <summary> | ||
/// Interaction logic for GingerAnalyticsConfigurationPage.xaml | ||
/// </summary> | ||
public partial class GingerAnalyticsConfigurationPage : GingerUIPage | ||
{ | ||
public GingerAnalyticsConfiguration gingerAnalyticsUserConfig; | ||
public GingerAnalyticsAPI analyticsAPI; | ||
public GingerAnalyticsConfigurationPage() | ||
{ | ||
InitializeComponent(); | ||
Init(); | ||
} | ||
private void Init() | ||
{ | ||
analyticsAPI = new GingerAnalyticsAPI(); | ||
gingerAnalyticsUserConfig = WorkSpace.Instance.SolutionRepository.GetAllRepositoryItems<GingerAnalyticsConfiguration>().Count == 0 ? new GingerAnalyticsConfiguration() : WorkSpace.Instance.SolutionRepository.GetFirstRepositoryItem<GingerAnalyticsConfiguration>(); | ||
gingerAnalyticsUserConfig.StartDirtyTracking(); | ||
SetControls(); | ||
|
||
} | ||
|
||
private void SetControls() | ||
{ | ||
Context mContext = new(); | ||
|
||
xGAAURLTextBox.Init(mContext, gingerAnalyticsUserConfig, nameof(GingerAnalyticsConfiguration.AccountUrl)); | ||
xISURLTextBox.Init(mContext, gingerAnalyticsUserConfig, nameof(GingerAnalyticsConfiguration.IdentityServiceURL)); | ||
xClientIdTextBox.Init(mContext, gingerAnalyticsUserConfig, nameof(GingerAnalyticsConfiguration.ClientId)); | ||
xClientSecretTextBox.Init(mContext, gingerAnalyticsUserConfig, nameof(GingerAnalyticsConfiguration.ClientSecret)); | ||
ApplyValidationRules(); | ||
|
||
} | ||
|
||
private void ApplyValidationRules() | ||
{ | ||
// check if fields have been populated (font-end validation) | ||
xGAAURLTextBox.ValueTextBox.AddValidationRule(new ValidateEmptyValue("Report URL cannot be empty")); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check if report in error message is correct cause no report url is being used. |
||
xISURLTextBox.ValueTextBox.AddValidationRule(new ValidateEmptyValue("Identitiy Service URL cannot be empty")); | ||
xClientIdTextBox.ValueTextBox.AddValidationRule(new ValidateEmptyValue("ClientID cannot be empty")); | ||
xClientSecretTextBox.ValueTextBox.AddValidationRule(new ValidateEmptyValue("ClientSecret cannot be empty")); | ||
} | ||
|
||
private async void xTestConBtn_Click(object sender, RoutedEventArgs e) | ||
{ | ||
ShowLoader(); | ||
xTestConBtn.IsEnabled = false; | ||
if (AreRequiredFieldsEmpty()) | ||
{ | ||
Reporter.ToUser(eUserMsgKey.RequiredFieldsEmpty); | ||
HideLoader(); | ||
xTestConBtn.IsEnabled = true; | ||
return; | ||
} | ||
|
||
GingerCoreNET.GeneralLib.General.CreateGingerAnalyticsConfiguration(gingerAnalyticsUserConfig); | ||
|
||
if (GingerAnalyticsAPI.IsTokenValid()) | ||
{ | ||
Reporter.ToUser(eUserMsgKey.GingerAnalyticsConnectionSuccess); | ||
HideLoader(); | ||
xTestConBtn.IsEnabled = true; | ||
return; | ||
} | ||
|
||
bool isAuthorized = await HandleTokenAuthorization(); | ||
ShowConnectionResult(isAuthorized); | ||
HideLoader(); | ||
xTestConBtn.IsEnabled = true; | ||
} | ||
|
||
public bool AreRequiredFieldsEmpty() | ||
{ | ||
return string.IsNullOrEmpty(gingerAnalyticsUserConfig.AccountUrl) | ||
|| string.IsNullOrEmpty(gingerAnalyticsUserConfig.IdentityServiceURL) | ||
|| string.IsNullOrEmpty(gingerAnalyticsUserConfig.ClientId) | ||
|| string.IsNullOrEmpty(gingerAnalyticsUserConfig.ClientSecret); | ||
} | ||
|
||
public async Task<bool> HandleTokenAuthorization() | ||
{ | ||
if (string.IsNullOrEmpty(gingerAnalyticsUserConfig.Token)) | ||
{ | ||
return await GingerAnalyticsAPI.RequestToken(ValueExpression.PasswordCalculation(gingerAnalyticsUserConfig.ClientId), | ||
ValueExpression.PasswordCalculation(gingerAnalyticsUserConfig.ClientSecret), | ||
ValueExpression.PasswordCalculation(gingerAnalyticsUserConfig.IdentityServiceURL)); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
public static void ShowConnectionResult(bool isAuthorized) | ||
{ | ||
if (isAuthorized) | ||
{ | ||
Reporter.ToUser(eUserMsgKey.GingerAnalyticsConnectionSuccess); | ||
} | ||
else | ||
{ | ||
Reporter.ToUser(eUserMsgKey.GingerAnalyticsConnectionFail); | ||
} | ||
} | ||
|
||
|
||
private void xClientSecretTextBox_LostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e) | ||
{ | ||
if (!EncryptionHandler.IsStringEncrypted(xClientSecretTextBox.ValueTextBox.Text)) | ||
{ | ||
xClientSecretTextBox.ValueTextBox.Text = ValueExpression.IsThisAValueExpression(xClientSecretTextBox.ValueTextBox.Text) ? xClientSecretTextBox.ValueTextBox.Text : EncryptionHandler.EncryptwithKey(xClientSecretTextBox.ValueTextBox.Text); | ||
|
||
} | ||
} | ||
|
||
private void xClientIdTextBox_LostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e) | ||
{ | ||
if (!EncryptionHandler.IsStringEncrypted(xClientIdTextBox.ValueTextBox.Text)) | ||
{ | ||
xClientIdTextBox.ValueTextBox.Text = ValueExpression.IsThisAValueExpression(xClientIdTextBox.ValueTextBox.Text) ? xClientIdTextBox.ValueTextBox.Text : EncryptionHandler.EncryptwithKey(xClientIdTextBox.ValueTextBox.Text); | ||
|
||
} | ||
} | ||
Comment on lines
+140
to
+156
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Encrypt sensitive data appropriately and handle potential security issues. The encryption logic in
Add error handling around the encryption logic to ensure the application remains stable and secure if encryption fails. |
||
|
||
|
||
|
||
|
||
public void HideLoader() | ||
{ | ||
this.Dispatcher.Invoke(() => | ||
{ | ||
xProcessingImage.Visibility = Visibility.Hidden; | ||
}); | ||
} | ||
|
||
public void ShowLoader() | ||
{ | ||
this.Dispatcher.Invoke(() => | ||
{ | ||
xProcessingImage.Visibility = Visibility.Visible; | ||
}); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Address static analysis hints.
Codacy suggests changing the visibility of
validTo
andgingerAnalyticsUserConfig
or making themconst
orreadonly
. While making themconst
orreadonly
is not desirable in this case, changing their visibility toprivate
and providing thread-safe access methods could help ensure thread safety and encapsulation.Consider the following changes:
validTo
andgingerAnalyticsUserConfig
toprivate
.Tools
GitHub Check: Codacy Static Code Analysis