diff --git a/misc/create_schema.sql b/misc/create_schema.sql index 8df7d30..41b4378 100644 --- a/misc/create_schema.sql +++ b/misc/create_schema.sql @@ -101,7 +101,6 @@ CREATE TABLE `participant` ( `street` VARCHAR(128) NULL DEFAULT NULL, `postal_code` VARCHAR(5) NULL DEFAULT NULL, `city` VARCHAR(64) NULL DEFAULT NULL, - `roles` INT(10) UNSIGNED NULL DEFAULT 0 COMMENT 'Bitarray of assigned roles', `flags` INT(10) UNSIGNED NULL DEFAULT NULL, `created` DATETIME NULL DEFAULT current_timestamp(), `modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(), @@ -175,6 +174,8 @@ CREATE TABLE `role` ( `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(50) NOT NULL DEFAULT '0' COMMENT 'unique role name', `description` VARCHAR(255) NULL DEFAULT '0' COMMENT 'role description', + `created` DATETIME NULL DEFAULT current_timestamp(), + `modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(), PRIMARY KEY (`id`), UNIQUE INDEX `name` (`name`) ) @@ -185,8 +186,10 @@ ENGINE=InnoDB CREATE TABLE `securable` ( - `id` INT(10) UNSIGNED NOT NULL, + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(50) NOT NULL DEFAULT '', + `created` DATETIME NULL DEFAULT current_timestamp(), + `modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(), PRIMARY KEY (`id`), UNIQUE INDEX `name` (`name`) ) @@ -199,6 +202,8 @@ CREATE TABLE `user_role_map` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` INT(10) UNSIGNED NOT NULL DEFAULT 0, `role_id` INT(10) UNSIGNED NOT NULL DEFAULT 0, + `created` DATETIME NULL DEFAULT current_timestamp(), + `modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(), PRIMARY KEY (`id`), INDEX `FK_USER_ROLE` (`user_id`), INDEX `FK_ROLE_USER` (`role_id`), @@ -211,9 +216,11 @@ ENGINE=InnoDB ; CREATE TABLE `role_securable_map` ( - `id` INT(10) UNSIGNED NOT NULL, + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `role_id` INT(10) UNSIGNED NOT NULL, `securable_id` INT(10) UNSIGNED NOT NULL, + `created` DATETIME NULL DEFAULT current_timestamp(), + `modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(), PRIMARY KEY (`id`), INDEX `FK_ROLE_SECURABLE` (`role_id`), INDEX `FK_SECURABLE_ROLE` (`securable_id`), diff --git a/src/RoleEditor/App.config b/src/RoleEditor/App.config new file mode 100644 index 0000000..49cc43e --- /dev/null +++ b/src/RoleEditor/App.config @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/RoleEditor/App.xaml b/src/RoleEditor/App.xaml new file mode 100644 index 0000000..7f5413e --- /dev/null +++ b/src/RoleEditor/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/src/RoleEditor/App.xaml.cs b/src/RoleEditor/App.xaml.cs new file mode 100644 index 0000000..6d8bf95 --- /dev/null +++ b/src/RoleEditor/App.xaml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace RoleEditor +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/src/RoleEditor/AssemblyInfo.cs b/src/RoleEditor/AssemblyInfo.cs new file mode 100644 index 0000000..8b5504e --- /dev/null +++ b/src/RoleEditor/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/src/RoleEditor/MainWindow.xaml b/src/RoleEditor/MainWindow.xaml new file mode 100644 index 0000000..a8662ba --- /dev/null +++ b/src/RoleEditor/MainWindow.xaml @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RoleEditor/MainWindow.xaml.cs b/src/RoleEditor/MainWindow.xaml.cs new file mode 100644 index 0000000..ea7d758 --- /dev/null +++ b/src/RoleEditor/MainWindow.xaml.cs @@ -0,0 +1,413 @@ + +using System; +using System.Collections.ObjectModel; +using System.Security.Cryptography; +using System.Text; +using System.Windows; +using System.Windows.Controls; + +using brecal.model; +using brecal.mysql; + +namespace RoleEditor +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + #region private fields + + private readonly ObservableCollection _participants = new ObservableCollection(); + private readonly ObservableCollection _roles = new ObservableCollection(); + private readonly ObservableCollection _securables = new ObservableCollection(); + private readonly ObservableCollection _users = new ObservableCollection(); + private readonly ObservableCollection _assignedRoles = new ObservableCollection(); + private readonly ObservableCollection _assignedSecurables = new ObservableCollection(); + private DBManager _dbManager; + + #endregion + + #region Construction / Loading + + public MainWindow() + { + InitializeComponent(); + _dbManager = new(); + } + + private async void Window_Loaded(object sender, RoutedEventArgs e) + { + // try database connection + try + { + // load all participants + foreach(Participant p in await Participant.LoadAll(_dbManager)) + _participants.Add(p); + this.listBoxParticipant.ItemsSource = _participants; + + // load all roles + foreach(Role r in await Role.LoadAll(_dbManager)) + _roles.Add(r); + this.listBoxRoles.ItemsSource = _roles; + + // load all securables + foreach(Securable s in await Securable.LoadAll(_dbManager)) + _securables.Add(s); + this.listBoxSecurables.ItemsSource = _securables; + + // set other item sources (filled later after selection) + this.listBoxUser.ItemsSource = _users; + this.listBoxRoleSecurables.ItemsSource = _assignedSecurables; + this.listBoxUserRoles.ItemsSource = _assignedRoles; + } + catch (Exception ex) + { + MessageBox.Show($"Database connection couldn't be established: {ex.Message}"); + this.Close(); + } + } + + #endregion + + #region button callbacks + + private async void buttonParticipantSave_Click(object sender, RoutedEventArgs e) + { + Participant? p = this.listBoxParticipant.SelectedItem as Participant; + if (p != null) + { + p.Name = this.textBoxParticipantName.Text.Trim(); + p.Street = this.textBoxParticipantStreet.Text.Trim(); + p.PostalCode = this.textBoxParticipantPostalCode.Text.Trim(); + p.City = this.textBoxParticipantCity.Text.Trim(); + await p.Save(_dbManager); + this.listBoxParticipant.ItemsSource = null; + this.listBoxParticipant.ItemsSource = _users; + this.listBoxParticipant.SelectedItem = p; + } + } + + private async void buttonUserSave_Click(object sender, RoutedEventArgs e) + { + User? u = this.listBoxUser.SelectedItem as User; + if(u != null) + { + u.Firstname = this.textBoxUserFirstName.Text.Trim(); + u.Lastname = this.textBoxUserLastName.Text.Trim(); + u.Username = this.textBoxUserUserName.Text.Trim(); + if(this.textBoxUserPassword.Text.Trim().Length > 0 ) + { + var data = Encoding.UTF8.GetBytes(this.textBoxUserPassword.Text.Trim()); + using SHA512 sha = SHA512.Create(); + byte[] hashedInputBytes = sha.ComputeHash(data); + var hashedInputStringBuilder = new StringBuilder(128); + foreach (var b in hashedInputBytes) + hashedInputStringBuilder.Append(b.ToString("X2")); + u.PasswordHash = hashedInputStringBuilder.ToString(); + } + u.APIKey = this.textBoxUserAPIKey.Text.Trim(); + await u.Save(_dbManager); + this.listBoxUser.ItemsSource = null; + this.listBoxUser.ItemsSource = _users; + this.listBoxUser.SelectedItem = u; + } + } + + private async void buttonAddRole_Click(object sender, RoutedEventArgs e) + { + Role? r = this.listBoxRoles.SelectedItem as Role; + User? u = this.listBoxUser.SelectedItem as User; + if((r != null) && (u != null)) + { + // test if assignment is already present + bool foundMatchingAssignment = false; + foreach(RoleAssignment ra in _assignedRoles) + { + if((ra.UserId == u.Id) && (ra.RoleId == r.Id)) + { + foundMatchingAssignment = true; + break; + } + } + + if(!foundMatchingAssignment) + { + RoleAssignment ra = new RoleAssignment(); + ra.UserId = (int) u.Id; + ra.RoleId = (int) r.Id; + ra.AssignedRole = r; + ra.AssignedUser = u; + await ra.Save(_dbManager); + _assignedRoles.Add(ra); + } + } + } + + private async void buttonRemoveRole_Click(object sender, RoutedEventArgs e) + { + // remove role from user + RoleAssignment? ra = this.listBoxUserRoles.SelectedItem as RoleAssignment; + if(ra != null) + { + await ra.Delete(_dbManager); + if(_assignedRoles.Contains(ra)) + _assignedRoles.Remove(ra); + } + } + + private async void buttonAddSecurable_Click(object sender, RoutedEventArgs e) + { + if ((this.listBoxRoles.SelectedItem is Role r) && (this.listBoxSecurables.SelectedItem is Securable s)) + { + // test if assignment is already present + bool foundMatchingAssignment = false; + foreach (SecurableAssignment sa in _assignedSecurables) + { + if ((sa.SecurableId == s.Id) && (sa.RoleId == r.Id)) + { + foundMatchingAssignment = true; + break; + } + } + + if (!foundMatchingAssignment) + { + SecurableAssignment sa = new SecurableAssignment(); + sa.SecurableId = (int)s.Id; + sa.RoleId = (int)r.Id; + sa.AssignedRole = r; + sa.AssignedSecurable = s; + await sa.Save(_dbManager); + _assignedSecurables.Add(sa); + } + } + } + + private async void buttonRemoveSecurable_Click(object sender, RoutedEventArgs e) + { + SecurableAssignment? sa = this.listBoxRoleSecurables.SelectedItem as SecurableAssignment; + if(sa != null) + { + await sa.Delete(_dbManager); + if (_assignedSecurables.Contains(sa)) + _assignedSecurables.Remove(sa); + } + } + + private async void buttonSaveSecurable_Click(object sender, RoutedEventArgs e) + { + Securable? s = this.listBoxSecurables.SelectedItem as Securable; + if(s != null) + { + s.Name = this.textBoxSecurableName.Text.Trim(); + await s.Save(_dbManager); + this.listBoxSecurables.ItemsSource = null; + this.listBoxSecurables.ItemsSource = _securables; + this.listBoxSecurables.SelectedItem = s; + } + } + + private async void buttonSaveRole_Click(object sender, RoutedEventArgs e) + { + Role? r = this.listBoxRoles.SelectedItem as Role; + if(r != null) + { + r.Name = this.textBoxRoleName.Text.Trim(); + r.Description = this.textBoxRoleDescription.Text.Trim(); + await r.Save(_dbManager); + this.listBoxRoles.ItemsSource = null; + this.listBoxRoles.ItemsSource = _roles; + this.listBoxRoles.SelectedItem = r; + } + } + + #endregion + + #region listbox selection callbacks + + private async void listBoxParticipant_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + Participant? p = this.listBoxParticipant.SelectedItem as Participant; + + this.textBoxParticipantName.Text = (p != null) ? p.Name : string.Empty; + this.textBoxParticipantStreet.Text = (p != null) ? p.Street : string.Empty; + this.textBoxParticipantPostalCode.Text = (p != null) ? p.PostalCode : string.Empty; + this.textBoxParticipantCity.Text = (p != null) ? p.City : string.Empty; + // this.checkboxParticipantActive.Checked = (p != null) ? p. + this.textBoxParticipantCreated.Text = (p != null) ? p.Created.ToString() : string.Empty; + this.textBoxParticipantModified.Text = (p != null) ? p.Modified.ToString() : string.Empty; + + // -> load users for this participant selection + this._users.Clear(); + foreach (User u in await User.LoadForParticipant(p, _dbManager)) + _users.Add(u); + } + + private async void listBoxRoles_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + Role? r = this.listBoxRoles.SelectedItem as Role; + this.textBoxRoleName.Text = (r != null) ? r.Name : string.Empty; + this.textBoxRoleDescription.Text = (r != null) ? r.Description : string.Empty; + + _assignedSecurables.Clear(); + if (r != null) + { + // load assigned securables + foreach (SecurableAssignment sa in await SecurableAssignment.LoadForRole(r, _dbManager)) + { + foreach (Securable s in this._securables) + { + if (sa.SecurableId == s.Id) + { + sa.AssignedSecurable = s; + break; + } + } + _assignedSecurables.Add(sa); + } + } + } + + private async void listBoxUser_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + User? u = this.listBoxUser.SelectedItem as User; + this.textBoxUserFirstName.Text = (u != null) ? u.Firstname : string.Empty; + this.textBoxUserLastName.Text = (u != null) ? u.Lastname : string.Empty; + this.textBoxUserUserName.Text = (u != null) ? u.Username : string.Empty; + this.textBoxUserAPIKey.Text = (u != null) ? u.APIKey : string.Empty; + this.textBoxUserCreated.Text = (u != null) ? u.Created.ToString() : string.Empty; + this.textBoxUserModified.Text = (u != null) ? u.Modified.ToString() : string.Empty; + this.textBoxUserPassword.Text = string.Empty; + + _assignedRoles.Clear(); + + if (u != null) + { + // load roles assigned to user + foreach (RoleAssignment ra in await RoleAssignment.LoadForUser(u, _dbManager)) + { + foreach (Role r in this._roles) + { + if (ra.RoleId == r.Id) + { + ra.AssignedRole = r; + break; + } + } + _assignedRoles.Add(ra); + } + } + } + + private void listBoxSecurables_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + Securable? s = this.listBoxSecurables.SelectedItem as Securable; + this.textBoxSecurableName.Text = (s != null) ? s.Name : string.Empty; + } + + #endregion + + #region menuitem callbacks + + private async void menuItemDeleteParticipant_Click(object sender, RoutedEventArgs e) + { + try + { + if(this.listBoxParticipant.SelectedItem is Participant p) + { + await p.Delete(_dbManager); + this._participants.Remove(p); + } + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + private void menuItemNewParticipant_Click(object sender, RoutedEventArgs e) + { + Participant p = new(); + this._participants.Add(p); + this.listBoxParticipant.SelectedItem = p; + } + + private void menuItemNewUser_Click(object sender, RoutedEventArgs e) + { + Participant? p = this.listBoxParticipant.SelectedItem as Participant; + if(p != null) + { + User u = new(); + u.Participant_Id = p.Id; + _users.Add(u); + this.listBoxUser.SelectedItem = u; + } + } + + private async void menuItemDeleteUser_Click(object sender, RoutedEventArgs e) + { + try + { + if (this.listBoxUser.SelectedItem is User u) + { + await u.Delete(_dbManager); + this._users.Remove(u); + } + } + catch(Exception ex) + { + MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + private void menuItemNewRole_Click(object sender, RoutedEventArgs e) + { + Role r = new(); + this._roles.Add(r); + this.listBoxRoles.SelectedItem = r; + } + + private async void menuItemDeleteRole_Click(object sender, RoutedEventArgs e) + { + try + { + if (this.listBoxRoles.SelectedItem is Role r) + { + await r.Delete(_dbManager); + this._roles.Remove(r); + } + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + private void menuItemNewSecurable_Click(object sender, RoutedEventArgs e) + { + Securable s = new Securable(); + _securables.Add(s); + this.listBoxSecurables.SelectedItem = s; + } + + private async void menuItemDeleteSecurable_Click(object sender, RoutedEventArgs e) + { + try + { + if (this.listBoxSecurables.SelectedItem is Securable s) + { + await s.Delete(_dbManager); + this._securables.Remove(s); + } + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + #endregion + + } +} diff --git a/src/RoleEditor/Resources.Designer.cs b/src/RoleEditor/Resources.Designer.cs new file mode 100644 index 0000000..b62f446 --- /dev/null +++ b/src/RoleEditor/Resources.Designer.cs @@ -0,0 +1,173 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RoleEditor { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RoleEditor.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] about { + get { + object obj = ResourceManager.GetObject("about", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] add { + get { + object obj = ResourceManager.GetObject("add", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] arrow_left_green { + get { + object obj = ResourceManager.GetObject("arrow_left_green", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] businessman { + get { + object obj = ResourceManager.GetObject("businessman", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] delete2 { + get { + object obj = ResourceManager.GetObject("delete2", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] disk_blue { + get { + object obj = ResourceManager.GetObject("disk_blue", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] id_card { + get { + object obj = ResourceManager.GetObject("id_card", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] key1 { + get { + object obj = ResourceManager.GetObject("key1", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] key1_add { + get { + object obj = ResourceManager.GetObject("key1_add", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] lock_preferences { + get { + object obj = ResourceManager.GetObject("lock_preferences", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] safe { + get { + object obj = ResourceManager.GetObject("safe", resourceCulture); + return ((byte[])(obj)); + } + } + } +} diff --git a/src/RoleEditor/Resources.resx b/src/RoleEditor/Resources.resx new file mode 100644 index 0000000..2d7da8b --- /dev/null +++ b/src/RoleEditor/Resources.resx @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Resources\about.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Resources\add.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Resources\arrow_left_green.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Resources\businessman.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Resources\delete2.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Resources\disk_blue.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Resources\id_card.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Resources\key1.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Resources\key1_add.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Resources\lock_preferences.ico;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Resources\safe.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/RoleEditor/Resources/about.png b/src/RoleEditor/Resources/about.png new file mode 100644 index 0000000..c2738bf Binary files /dev/null and b/src/RoleEditor/Resources/about.png differ diff --git a/src/RoleEditor/Resources/add.png b/src/RoleEditor/Resources/add.png new file mode 100644 index 0000000..a871081 Binary files /dev/null and b/src/RoleEditor/Resources/add.png differ diff --git a/src/RoleEditor/Resources/arrow_left_green.png b/src/RoleEditor/Resources/arrow_left_green.png new file mode 100644 index 0000000..39eb6e6 Binary files /dev/null and b/src/RoleEditor/Resources/arrow_left_green.png differ diff --git a/src/RoleEditor/Resources/businessman.png b/src/RoleEditor/Resources/businessman.png new file mode 100644 index 0000000..0e7499a Binary files /dev/null and b/src/RoleEditor/Resources/businessman.png differ diff --git a/src/RoleEditor/Resources/delete2.png b/src/RoleEditor/Resources/delete2.png new file mode 100644 index 0000000..32a7f9e Binary files /dev/null and b/src/RoleEditor/Resources/delete2.png differ diff --git a/src/RoleEditor/Resources/disk_blue.png b/src/RoleEditor/Resources/disk_blue.png new file mode 100644 index 0000000..1abc0e7 Binary files /dev/null and b/src/RoleEditor/Resources/disk_blue.png differ diff --git a/src/RoleEditor/Resources/id_card.png b/src/RoleEditor/Resources/id_card.png new file mode 100644 index 0000000..70e78af Binary files /dev/null and b/src/RoleEditor/Resources/id_card.png differ diff --git a/src/RoleEditor/Resources/key1.png b/src/RoleEditor/Resources/key1.png new file mode 100644 index 0000000..53d2a24 Binary files /dev/null and b/src/RoleEditor/Resources/key1.png differ diff --git a/src/RoleEditor/Resources/key1_add.png b/src/RoleEditor/Resources/key1_add.png new file mode 100644 index 0000000..06057ef Binary files /dev/null and b/src/RoleEditor/Resources/key1_add.png differ diff --git a/src/RoleEditor/Resources/lock_preferences.ico b/src/RoleEditor/Resources/lock_preferences.ico new file mode 100644 index 0000000..4128cdb Binary files /dev/null and b/src/RoleEditor/Resources/lock_preferences.ico differ diff --git a/src/RoleEditor/Resources/safe.png b/src/RoleEditor/Resources/safe.png new file mode 100644 index 0000000..2bd364d Binary files /dev/null and b/src/RoleEditor/Resources/safe.png differ diff --git a/src/RoleEditor/RoleEditor.csproj b/src/RoleEditor/RoleEditor.csproj new file mode 100644 index 0000000..936e6bb --- /dev/null +++ b/src/RoleEditor/RoleEditor.csproj @@ -0,0 +1,61 @@ + + + + WinExe + net6.0-windows + enable + true + Resources\lock_preferences.ico + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + diff --git a/src/RoleEditor/RoleEditor.sln b/src/RoleEditor/RoleEditor.sln new file mode 100644 index 0000000..6b98d6e --- /dev/null +++ b/src/RoleEditor/RoleEditor.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33516.290 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RoleEditor", "RoleEditor.csproj", "{8A8CB0D3-7728-468E-AB11-E811BA5B5BC0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "brecal.model", "..\brecal.model\brecal.model.csproj", "{F3BC5ADC-BF57-47DC-A5D5-CC4A13857DEE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "brecal.mysql", "..\brecal.mysql\brecal.mysql.csproj", "{E88F908B-48C9-46BD-A3AE-C36FBE9EDF1F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8A8CB0D3-7728-468E-AB11-E811BA5B5BC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A8CB0D3-7728-468E-AB11-E811BA5B5BC0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A8CB0D3-7728-468E-AB11-E811BA5B5BC0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A8CB0D3-7728-468E-AB11-E811BA5B5BC0}.Release|Any CPU.Build.0 = Release|Any CPU + {F3BC5ADC-BF57-47DC-A5D5-CC4A13857DEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F3BC5ADC-BF57-47DC-A5D5-CC4A13857DEE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3BC5ADC-BF57-47DC-A5D5-CC4A13857DEE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3BC5ADC-BF57-47DC-A5D5-CC4A13857DEE}.Release|Any CPU.Build.0 = Release|Any CPU + {E88F908B-48C9-46BD-A3AE-C36FBE9EDF1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E88F908B-48C9-46BD-A3AE-C36FBE9EDF1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E88F908B-48C9-46BD-A3AE-C36FBE9EDF1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E88F908B-48C9-46BD-A3AE-C36FBE9EDF1F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {12E46C08-C5A8-46E8-8A8C-04BA6F197DCB} + EndGlobalSection +EndGlobal diff --git a/src/brecal.model/DbEntity.cs b/src/brecal.model/DbEntity.cs new file mode 100644 index 0000000..e341bf7 --- /dev/null +++ b/src/brecal.model/DbEntity.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace brecal.model +{ + public abstract class DbEntity + { + /// + /// DB primary key + /// + public uint Id { get; set; } + + /// + /// Creation timestamp, if null record is unsaved + /// + public DateTime? Created { get; set; } + + /// + /// Modified timestamp, if null record was never modified + /// + public DateTime? Modified { get; set; } + + /// + /// Set query and cmd parameters for an update query + /// + /// CMD created by DB manager + public abstract void SetUpdate(IDbCommand cmd); + + /// + /// set query and cmd parameters for a create query + /// + /// CMD created by DB manager + public abstract void SetCreate(IDbCommand cmd); + + /// + /// set query and cmd parameters for a delete query + /// + /// CMD created by DB manager + public abstract void SetDelete(IDbCommand cmd); + + /// + /// Each database entity must be able to save itself to the database + /// + public async Task Save(IDBManager manager) + { + if (this.Created.HasValue) + { + await manager.ExecuteNonQuery(this.SetUpdate); + } + else + { + this.Id = (uint)await manager.ExecuteNonQuery(this.SetCreate); + } + } + + /// + /// Each entity must be able to delete itself + /// + public async Task Delete(IDBManager manager) + { + await manager.ExecuteNonQuery(this.SetDelete); + } + + } +} diff --git a/src/brecal.model/IDBManager.cs b/src/brecal.model/IDBManager.cs new file mode 100644 index 0000000..1fad343 --- /dev/null +++ b/src/brecal.model/IDBManager.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace brecal.model +{ + public interface IDBManager + { + + delegate List LoadFunc(T entity); + + delegate void QueryFunc(IDbCommand cmd, params object?[] args); + + Task> Load(QueryFunc prepareAction, LoadFunc loadAction, params object?[] args); + + Task ExecuteScalar(Action prepareAction); + + Task ExecuteNonQuery(Action prepareAction); + + } +} diff --git a/src/brecal.model/Participant.cs b/src/brecal.model/Participant.cs new file mode 100644 index 0000000..e2a1d88 --- /dev/null +++ b/src/brecal.model/Participant.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace brecal.model +{ + public class Participant : DbEntity + { + #region Properties + + public string? Name { get; set; } + + public string? Street { get; set; } + + public string? PostalCode { get; set; } + + public string? City { get; set; } + + public uint Flags { get; set; } + + #endregion + + #region public static methods + + public static async Task> LoadAll(IDBManager manager) + { + List loadResultList = await manager.Load(SetLoadQuery, LoadElems); + List result = new(); + foreach (Participant p in loadResultList.Cast()) + result.Add(p); + return result; + } + + public static List LoadElems(IDataReader reader) + { + List result = new List(); + while (reader.Read()) + { + Participant p = new(); + p.Id = (uint)reader.GetInt32(0); + if (!reader.IsDBNull(1)) p.Name = reader.GetString(1); + if (!reader.IsDBNull(2)) p.Street = reader.GetString(2); + if (!reader.IsDBNull(3)) p.PostalCode = reader.GetString(3); + if (!reader.IsDBNull(4)) p.City = reader.GetString(4); + if (!reader.IsDBNull(5)) p.Flags = (uint)reader.GetInt32(5); + if (!reader.IsDBNull(6)) p.Created = reader.GetDateTime(6); + if (!reader.IsDBNull(7)) p.Modified = reader.GetDateTime(7); + result.Add(p); + } + return result; + } + + public static void SetLoadQuery(IDbCommand cmd, params object?[] list) + { + cmd.CommandText = "SELECT id, name, street, postal_code, city, flags, created, modified FROM participant"; + } + + #endregion + + #region abstract method implementation + + public override void SetUpdate(IDbCommand cmd) + { + cmd.CommandText = "UPDATE participant set name = @NAME, street = @STREET, postal_code = @POSTAL_CODE, city = @CITY, flags = @FLAGS WHERE id = @ID"; + this.SetParameters(cmd); + } + + public override void SetCreate(IDbCommand cmd) + { + cmd.CommandText = "INSERT INTO participant (name, street, postal_code, city, flags) VALUES ( @NAME, @STREET, @POSTAL_CODE, @CITY, @FLAGS)"; + this.SetParameters(cmd); + } + + public override void SetDelete(IDbCommand cmd) + { + cmd.CommandText = "DELETE FROM participant WHERE id = @ID"; + + IDataParameter idParam = cmd.CreateParameter(); + idParam.ParameterName = "ID"; + idParam.Value = this.Id; + cmd.Parameters.Add(idParam); + } + + #endregion + + #region private methods + + private void SetParameters(IDbCommand cmd) + { + IDbDataParameter name = cmd.CreateParameter(); + name.ParameterName = "NAME"; + name.Value = this.Name; + cmd.Parameters.Add(name); + + IDbDataParameter street = cmd.CreateParameter(); + street.ParameterName = "STREET"; + street.Value = this.Street; + cmd.Parameters.Add(street); + + IDataParameter postal_code = cmd.CreateParameter(); + postal_code.ParameterName = "POSTAL_CODE"; + postal_code.Value = this.PostalCode; + cmd.Parameters.Add(postal_code); + + IDataParameter city = cmd.CreateParameter(); + city.ParameterName = "CITY"; + city.Value = this.City; + cmd.Parameters.Add(city); + + IDataParameter flags = cmd.CreateParameter(); + flags.ParameterName = "FLAGS"; + flags.Value = this.Flags; + cmd.Parameters.Add(flags); + + IDataParameter idParam = cmd.CreateParameter(); + idParam.ParameterName = "ID"; + idParam.Value = this.Id; + cmd.Parameters.Add(idParam); + } + + #endregion + + #region overrides + + public override string ToString() + { + return this.Name ?? $"{base.Id} - {this.GetType().Name}"; + } + + #endregion + + } +} diff --git a/src/brecal.model/Role.cs b/src/brecal.model/Role.cs new file mode 100644 index 0000000..c85802b --- /dev/null +++ b/src/brecal.model/Role.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace brecal.model +{ + public class Role : DbEntity + { + + #region Properties + + public string? Name { get; set; } + + public string? Description { get; set; } + + #endregion + + #region public static methods + + public static async Task> LoadAll(IDBManager manager) + { + List loadResultList = await manager.Load(SetLoadQuery, LoadElems); + List result = new(); + foreach (Role r in loadResultList.Cast()) + result.Add(r); + return result; + } + + public static void SetLoadQuery(IDbCommand cmd, params object?[] list) + { + cmd.CommandText = "SELECT id, name, description, created, modified FROM role"; + } + + public static List LoadElems(IDataReader reader) + { + List result = new List(); + while (reader.Read()) + { + Role r = new(); + r.Id = (uint)reader.GetInt32(0); + if (!reader.IsDBNull(1)) r.Name = reader.GetString(1); + if (!reader.IsDBNull(2)) r.Description = reader.GetString(2); + if (!reader.IsDBNull(3)) r.Created = reader.GetDateTime(3); + if (!reader.IsDBNull(4)) r.Modified = reader.GetDateTime(4); + result.Add(r); + } + return result; + } + + #endregion + + #region overrides + + public override void SetUpdate(IDbCommand cmd) + { + cmd.CommandText = "UPDATE role set name = @NAME, description = @DESC WHERE id = @ID"; + this.SetParameters(cmd); + } + + public override void SetCreate(IDbCommand cmd) + { + cmd.CommandText = "INSERT INTO role (name, description) VALUES ( @NAME, @DESC)"; + this.SetParameters(cmd); + } + + public override void SetDelete(IDbCommand cmd) + { + cmd.CommandText = "DELETE FROM role WHERE id = @ID"; + + IDataParameter idParam = cmd.CreateParameter(); + idParam.ParameterName = "ID"; + idParam.Value = this.Id; + cmd.Parameters.Add(idParam); + } + + public override string ToString() + { + return this.Name ?? $"{base.Id} - {this.GetType().Name}"; + } + + #endregion + + #region private methods + + private void SetParameters(IDbCommand cmd) + { + IDbDataParameter name = cmd.CreateParameter(); + name.ParameterName = "NAME"; + name.Value = this.Name; + cmd.Parameters.Add(name); + + IDbDataParameter desc = cmd.CreateParameter(); + desc.ParameterName = "DESC"; + desc.Value = this.Description; + cmd.Parameters.Add(desc); + + IDataParameter idParam = cmd.CreateParameter(); + idParam.ParameterName = "ID"; + idParam.Value = this.Id; + cmd.Parameters.Add(idParam); + } + + #endregion + + } +} diff --git a/src/brecal.model/RoleAssignment.cs b/src/brecal.model/RoleAssignment.cs new file mode 100644 index 0000000..26ea20d --- /dev/null +++ b/src/brecal.model/RoleAssignment.cs @@ -0,0 +1,110 @@ +using System.Data; + +namespace brecal.model +{ + public class RoleAssignment : DbEntity + { + + #region Properties + + public int? UserId { get; set; } + + public int? RoleId { get; set; } + + public Role? AssignedRole { get; set; } + + public User? AssignedUser { get; set; } + + #endregion + + #region public static methods + + public static async Task> LoadForUser(User? u, IDBManager manager) + { + List loadResultList = await manager.Load(SetLoadQuery, LoadElems, args: u); + List result = new(); + foreach (RoleAssignment ra in loadResultList.Cast()) + { + ra.AssignedUser = u; + result.Add(ra); + } + + return result; + } + + public static void SetLoadQuery(IDbCommand cmd, params object?[] args) + { + cmd.CommandText = "SELECT id, user_id, role_id FROM user_role_map WHERE user_id = @UID"; + if (args.Length != 1 || !(args[0] is User)) + throw new ArgumentException("loader needs single user as argument"); + IDataParameter uid = cmd.CreateParameter(); + uid.ParameterName = "UID"; + if (args[0] is User u) + uid.Value = u.Id; + cmd.Parameters.Add(uid); + } + + public static List LoadElems(IDataReader reader) + { + List result = new List(); + while (reader.Read()) + { + RoleAssignment ra = new(); + ra.Id = (uint)reader.GetInt32(0); + if (!reader.IsDBNull(1)) ra.UserId = reader.GetInt32(1); + if (!reader.IsDBNull(2)) ra.RoleId = reader.GetInt32(2); + result.Add(ra); + } + return result; + } + + #endregion + + #region overrides + + public override void SetUpdate(IDbCommand cmd) + { + throw new NotImplementedException(); + } + + public override void SetCreate(IDbCommand cmd) + { + cmd.CommandText = "INSERT INTO user_role_map (user_id, role_id) VALUES (@USERID, @ROLEID)"; + + IDbDataParameter userid = cmd.CreateParameter(); + userid.ParameterName = "USERID"; + userid.Value = this.UserId; + cmd.Parameters.Add(userid); + + IDbDataParameter roleid = cmd.CreateParameter(); + roleid.ParameterName = "ROLEID"; + roleid.Value = this.RoleId; + cmd.Parameters.Add(roleid); + } + + public override void SetDelete(IDbCommand cmd) + { + cmd.CommandText = "DELETE FROM user_role_map WHERE id = @ID"; + + IDataParameter idParam = cmd.CreateParameter(); + idParam.ParameterName = "ID"; + idParam.Value = this.Id; + cmd.Parameters.Add(idParam); + } + + public override string ToString() + { + if (this.AssignedRole == null) + { + return $"{Id}: "; + } + else + { + return AssignedRole.Name ?? AssignedRole.Id.ToString(); + } + } + + #endregion + + } +} diff --git a/src/brecal.model/Securable.cs b/src/brecal.model/Securable.cs new file mode 100644 index 0000000..0235ea0 --- /dev/null +++ b/src/brecal.model/Securable.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace brecal.model +{ + public class Securable : DbEntity + { + + #region Properties + + public string? Name { get; set; } + + #endregion + + #region public static methods + + public static async Task> LoadAll(IDBManager manager) + { + List loadResultList = await manager.Load(SetLoadQuery, LoadElems); + List result = new(); + foreach (Securable s in loadResultList.Cast()) + result.Add(s); + return result; + } + + public static void SetLoadQuery(IDbCommand cmd, params object?[] list) + { + cmd.CommandText = "SELECT id, name, created, modified FROM securable"; + } + + public static List LoadElems(IDataReader reader) + { + List result = new List(); + while (reader.Read()) + { + Securable s = new(); + s.Id = (uint)reader.GetInt32(0); + if (!reader.IsDBNull(1)) s.Name = reader.GetString(1); + if (!reader.IsDBNull(2)) s.Created = reader.GetDateTime(2); + if (!reader.IsDBNull(3)) s.Modified = reader.GetDateTime(3); + result.Add(s); + } + return result; + } + + #endregion + + #region overrides + + public override void SetUpdate(IDbCommand cmd) + { + cmd.CommandText = "UPDATE securable set name = @NAME WHERE id = @ID"; + this.SetParameters(cmd); + } + + public override void SetCreate(IDbCommand cmd) + { + cmd.CommandText = "INSERT INTO securable (name) VALUES ( @NAME)"; + this.SetParameters(cmd); + } + + public override void SetDelete(IDbCommand cmd) + { + cmd.CommandText = "DELETE FROM securable WHERE id = @ID"; + this.SetParameters(cmd); + } + + public override string ToString() + { + return this.Name ?? $"{base.Id} - {this.GetType().Name}"; + } + + #endregion + + #region private methods + + private void SetParameters(IDbCommand cmd) + { + IDbDataParameter name = cmd.CreateParameter(); + name.ParameterName = "NAME"; + name.Value = this.Name; + cmd.Parameters.Add(name); + + IDataParameter idParam = cmd.CreateParameter(); + idParam.ParameterName = "ID"; + idParam.Value = this.Id; + cmd.Parameters.Add(idParam); + } + + #endregion + + } +} diff --git a/src/brecal.model/SecurableAssignment.cs b/src/brecal.model/SecurableAssignment.cs new file mode 100644 index 0000000..ed1abf1 --- /dev/null +++ b/src/brecal.model/SecurableAssignment.cs @@ -0,0 +1,109 @@ +using System.Data; + +namespace brecal.model +{ + public class SecurableAssignment : DbEntity + { + #region Properties + + public Role? AssignedRole { get; set; } + + public Securable? AssignedSecurable { get; set; } + + public int? RoleId { get; set; } + + public int? SecurableId { get; set; } + + #endregion + + #region public static methods + + public static async Task> LoadForRole(Role? r, IDBManager manager) + { + List loadResultList = await manager.Load(SetLoadQuery, LoadElems, args: r); + List result = new(); + foreach (SecurableAssignment sa in loadResultList.Cast()) + { + sa.AssignedRole = r; + result.Add(sa); + } + + return result; + } + + public static void SetLoadQuery(IDbCommand cmd, params object?[] args) + { + cmd.CommandText = "SELECT id, role_id, securable_id FROM role_securable_map WHERE role_id = @RID"; + if (args.Length != 1 || !(args[0] is Role)) + throw new ArgumentException("loader needs single role as argument"); + IDataParameter rid = cmd.CreateParameter(); + rid.ParameterName = "RID"; + if (args[0] is Role r) + rid.Value = r.Id; + cmd.Parameters.Add(rid); + } + + public static List LoadElems(IDataReader reader) + { + List result = new List(); + while (reader.Read()) + { + SecurableAssignment sa = new(); + sa.Id = (uint)reader.GetInt32(0); + if (!reader.IsDBNull(1)) sa.RoleId = reader.GetInt32(1); + if (!reader.IsDBNull(2)) sa.SecurableId = reader.GetInt32(2); + result.Add(sa); + } + return result; + } + + #endregion + + #region public overrides + + public override void SetUpdate(IDbCommand cmd) + { + throw new NotImplementedException(); + } + + public override void SetCreate(IDbCommand cmd) + { + cmd.CommandText = "INSERT INTO role_securable_map (securable_id, role_id) VALUES (@SECURABLEID, @ROLEID)"; + + IDbDataParameter userid = cmd.CreateParameter(); + userid.ParameterName = "SECURABLEID"; + userid.Value = this.SecurableId; + cmd.Parameters.Add(userid); + + IDbDataParameter roleid = cmd.CreateParameter(); + roleid.ParameterName = "ROLEID"; + roleid.Value = this.RoleId; + cmd.Parameters.Add(roleid); + } + + public override void SetDelete(IDbCommand cmd) + { + cmd.CommandText = "DELETE FROM role_securable_map WHERE id = @ID"; + + IDataParameter idParam = cmd.CreateParameter(); + idParam.ParameterName = "ID"; + idParam.Value = this.Id; + cmd.Parameters.Add(idParam); + } + + public override string ToString() + { + if (this.AssignedSecurable == null) + { + return $"{Id}: "; + } + else + { + return AssignedSecurable.Name ?? AssignedSecurable.Id.ToString(); + } + } + + #endregion + + } +} diff --git a/src/brecal.model/User.cs b/src/brecal.model/User.cs new file mode 100644 index 0000000..2c4cf65 --- /dev/null +++ b/src/brecal.model/User.cs @@ -0,0 +1,143 @@ +using System.Data; + +namespace brecal.model +{ + public class User : DbEntity + { + #region Properties + + public string? Firstname { get; set; } + + public string? Lastname { get; set; } + + public string Username { get; set; } = ""; + + public string? PasswordHash { get; set; } + + public string? APIKey { get; set; } + + public uint? Participant_Id { get; set; } + + #endregion + + #region public static methods + + public static async Task> LoadForParticipant(Participant? p, IDBManager manager) + { + List loadResultList = await manager.Load(SetLoadQuery, LoadElems, args: p); + List result = new(); + foreach (User u in loadResultList.Cast()) + result.Add(u); + return result; + } + + public static void SetLoadQuery(IDbCommand cmd, params object?[] args) + { + cmd.CommandText = "SELECT id, first_name, last_name, user_name, api_key, created, modified FROM user WHERE participant_id = @PID"; + if (args.Length != 1 || !(args[0] is Participant)) + throw new ArgumentException("loader needs single partipant as argument"); + IDataParameter pid = cmd.CreateParameter(); + pid.ParameterName = "PID"; + if (args[0] is Participant p) + pid.Value = p.Id; + cmd.Parameters.Add(pid); + } + + public static List LoadElems(IDataReader reader) + { + List result = new List(); + while (reader.Read()) + { + User u = new(); + u.Id = (uint)reader.GetInt32(0); + if (!reader.IsDBNull(1)) u.Firstname = reader.GetString(1); + if (!reader.IsDBNull(2)) u.Lastname = reader.GetString(2); + if (!reader.IsDBNull(3)) u.Username = reader.GetString(3); + if (!reader.IsDBNull(4)) u.APIKey = reader.GetString(4); + if (!reader.IsDBNull(5)) u.Created = reader.GetDateTime(5); + if (!reader.IsDBNull(6)) u.Modified = reader.GetDateTime(6); + result.Add(u); + } + return result; + } + + #endregion + + #region overrides + + public override void SetUpdate(IDbCommand cmd) + { + if(!string.IsNullOrEmpty(this.PasswordHash)) + cmd.CommandText = "UPDATE user SET first_name = @FIRSTNAME, last_name = @LASTNAME, user_name = @USERNAME, password_hash = @PWHASH, api_key = @APIKEY WHERE id = @ID"; + else + cmd.CommandText = "UPDATE user SET first_name = @FIRSTNAME, last_name = @LASTNAME, user_name = @USERNAME, api_key = @APIKEY WHERE id = @ID"; + this.SetParameters(cmd); + } + + public override void SetCreate(IDbCommand cmd) + { + cmd.CommandText = "INSERT INTO user (participant_id, first_name, last_name, user_name, password_hash, api_key) VALUES ( @PID, @FIRSTNAME, @LASTNAME, @USERNAME, @PWHASH, @APIKEY)"; + this.SetParameters(cmd); + } + + public override void SetDelete(IDbCommand cmd) + { + cmd.CommandText = "DELETE FROM user WHERE id = @ID"; + + IDataParameter idParam = cmd.CreateParameter(); + idParam.ParameterName = "ID"; + idParam.Value = this.Id; + cmd.Parameters.Add(idParam); + } + + public override string ToString() + { + return this.Username ?? $"{base.Id} - {this.GetType().Name}"; + } + + #endregion + + #region private methods + + private void SetParameters(IDbCommand cmd) + { + IDbDataParameter id = cmd.CreateParameter(); + id.ParameterName = "ID"; + id.Value = this.Id; + cmd.Parameters.Add(id); + + IDbDataParameter pid = cmd.CreateParameter(); + pid.ParameterName = "PID"; + pid.Value = this.Participant_Id; + cmd.Parameters.Add(pid); + + IDbDataParameter firstname = cmd.CreateParameter(); + firstname.ParameterName = "FIRSTNAME"; + firstname.Value = this.Firstname; + cmd.Parameters.Add(firstname); + + IDbDataParameter lastname = cmd.CreateParameter(); + lastname.ParameterName = "LASTNAME"; + lastname.Value = this.Lastname; + cmd.Parameters.Add(lastname); + + IDbDataParameter username = cmd.CreateParameter(); + username.ParameterName = "USERNAME"; + username.Value = this.Username; + cmd.Parameters.Add(username); + + IDbDataParameter pwhash = cmd.CreateParameter(); + pwhash.ParameterName = "PWHASH"; + pwhash.Value = this.PasswordHash; + cmd.Parameters.Add(pwhash); + + IDbDataParameter apikey = cmd.CreateParameter(); + apikey.ParameterName = "APIKEY"; + apikey.Value = this.APIKey; + cmd.Parameters.Add(apikey); + } + + #endregion + + } +} \ No newline at end of file diff --git a/src/brecal.model/brecal.model.csproj b/src/brecal.model/brecal.model.csproj new file mode 100644 index 0000000..132c02c --- /dev/null +++ b/src/brecal.model/brecal.model.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/src/brecal.mysql/DBManager.cs b/src/brecal.mysql/DBManager.cs new file mode 100644 index 0000000..054caa9 --- /dev/null +++ b/src/brecal.mysql/DBManager.cs @@ -0,0 +1,54 @@ +using brecal.model; +using MySqlConnector; +using System.Data; +using System.Runtime.CompilerServices; +using static brecal.model.IDBManager; + +namespace brecal.mysql +{ + public class DBManager : IDBManager + { + // TODO: remove this and use external configuration + private static readonly string _connectionString = "Server=localhost;User ID=ds;Password=HalloWach23;Database=bremen_calling"; + + public async Task> Load(QueryFunc prepareAction, LoadFunc loadAction, params object?[] args) + { + await using MySqlConnection connection = new MySqlConnection(_connectionString); + await connection.OpenAsync(); + + using MySqlCommand cmd = new(); + cmd.Connection = connection; + prepareAction(cmd, args); + MySqlDataReader reader = await cmd.ExecuteReaderAsync(); + List result = loadAction(reader); + reader.Close(); + return result; + } + + public async Task ExecuteScalar(Action prepareAction) + { + await using MySqlConnection connection = new MySqlConnection(_connectionString); + await connection.OpenAsync(); + + using MySqlCommand cmd = new(); + cmd.Connection = connection; + prepareAction(cmd); + object? result = await cmd.ExecuteScalarAsync(); + return result; + } + + public async Task ExecuteNonQuery(Action prepareAction) + { + await using MySqlConnection connection = new(_connectionString); + await connection.OpenAsync(); + + using MySqlCommand cmd = new(); + cmd.Connection = connection; + prepareAction(cmd); + await cmd.ExecuteNonQueryAsync(); + int result = (int)cmd.LastInsertedId; + return result; + } + + } +} \ No newline at end of file diff --git a/src/brecal.mysql/brecal.mysql.csproj b/src/brecal.mysql/brecal.mysql.csproj new file mode 100644 index 0000000..786b11e --- /dev/null +++ b/src/brecal.mysql/brecal.mysql.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/src/server/BreCal/local_db.py b/src/server/BreCal/local_db.py index c2c2cf9..ed115a8 100644 --- a/src/server/BreCal/local_db.py +++ b/src/server/BreCal/local_db.py @@ -8,7 +8,7 @@ def initPool(): try: global connection_pool connection_pool = mysql.connector.connect( - host="lager", + host="lager", # TODO: move these settings outside the code! port=3306, user="ds", password="HalloWach23",