From 33ea1505798315b19da4364f66847658dcba1a91 Mon Sep 17 00:00:00 2001 From: abarz722 Date: Fri, 3 May 2024 14:16:39 +0200 Subject: [PATCH 1/6] fixes app data import + Ldap cleanup --- .../FWO.Middleware.Server/AppDataImport.cs | 52 +- .../files/FWO.Middleware.Server/Ldap.cs | 949 ++++++++---------- 2 files changed, 478 insertions(+), 523 deletions(-) diff --git a/roles/middleware/files/FWO.Middleware.Server/AppDataImport.cs b/roles/middleware/files/FWO.Middleware.Server/AppDataImport.cs index 38bd17291..681fbc55d 100644 --- a/roles/middleware/files/FWO.Middleware.Server/AppDataImport.cs +++ b/roles/middleware/files/FWO.Middleware.Server/AppDataImport.cs @@ -263,29 +263,31 @@ private async Task AddAllGroupMembersToUiUser(string userGroupDn) { foreach (string memberDn in ldap.GetGroupMembers(userGroupDn)) { - await UiUserHandler.UpsertUiUser(apiConnection, await ConvertLdapToUiUser(apiConnection, memberDn), false); + UiUser? uiUser = await ConvertLdapToUiUser(apiConnection, memberDn); + if(uiUser != null) + { + await UiUserHandler.UpsertUiUser(apiConnection, uiUser, false); + } } - } + } } - private async Task ConvertLdapToUiUser(ApiConnection apiConnection, string userDn) + private async Task ConvertLdapToUiUser(ApiConnection apiConnection, string userDn) { // add the modelling user to local uiuser table for later ref to email address - UiUser uiUser = new(); - // find the user in all connected ldaps foreach (Ldap ldap in connectedLdaps) { - if (!ldap.UserSearchPath.IsNullOrEmpty() && userDn.ToLower().Contains(ldap.UserSearchPath.ToLower())) + if (!ldap.UserSearchPath.IsNullOrEmpty() && userDn.ToLower().Contains(ldap.UserSearchPath!.ToLower())) { - LdapEntry ldapUser = ldap.GetUserDetailsFromLdap(userDn); + LdapEntry? ldapUser = ldap.GetUserDetailsFromLdap(userDn); if (ldapUser != null) { // add data from ldap entry to uiUser - uiUser = new() + return new() { - LdapConnection = new UiLdapConnection(), + LdapConnection = new UiLdapConnection(){ Id = ldap.Id }, Dn = ldapUser.Dn, Name = ldap.GetName(ldapUser), Firstname = ldap.GetFirstName(ldapUser), @@ -293,19 +295,15 @@ private async Task ConvertLdapToUiUser(ApiConnection apiConnection, stri Email = ldap.GetEmail(ldapUser), Tenant = await DeriveTenantFromLdap(ldap, ldapUser) }; - uiUser.LdapConnection.Id = ldap.Id; - return uiUser; } } } - return uiUser; - + return null; } private async Task DeriveTenantFromLdap(Ldap ldap, LdapEntry ldapUser) { // try to derive the the user's tenant from the ldap settings - Tenant tenant = new() { Id = GlobalConst.kTenant0Id // default: tenant0 (id=1) @@ -325,7 +323,7 @@ private async Task DeriveTenantFromLdap(Ldap ldap, LdapEntry ldapUser) { if (!ldap.GlobalTenantName.IsNullOrEmpty()) { - tenantName = ldap.GlobalTenantName; + tenantName = ldap.GlobalTenantName ?? ""; } } @@ -336,9 +334,7 @@ private async Task DeriveTenantFromLdap(Ldap ldap, LdapEntry ldapUser) tenant.Id = tenants[0].Id; } } - return tenant; - } private string CreateUserGroup(ModellingImportAppData incomingApp) @@ -403,9 +399,31 @@ private string UpdateUserGroup(ModellingImportAppData incomingApp, string groupD internalLdap.RemoveUserFromEntry(member, groupDn); } } + UpdateRoles(groupDn); return groupDn; } + private void UpdateRoles(string groupDn) + { + List roles = internalLdap.GetRoles([groupDn]); + if(!roles.Contains(Roles.Modeller)) + { + internalLdap.AddUserToEntry(groupDn, modellerRoleDn); + } + if(!roles.Contains(Roles.Requester)) + { + internalLdap.AddUserToEntry(groupDn, requesterRoleDn); + } + if(!roles.Contains(Roles.Implementer)) + { + internalLdap.AddUserToEntry(groupDn, implementerRoleDn); + } + if(!roles.Contains(Roles.Reviewer)) + { + internalLdap.AddUserToEntry(groupDn, reviewerRoleDn); + } + } + private async Task ImportAppServers(ModellingImportAppData incomingApp, int applId) { int successCounter = 0; diff --git a/roles/middleware/files/FWO.Middleware.Server/Ldap.cs b/roles/middleware/files/FWO.Middleware.Server/Ldap.cs index c5779e5f9..376e1ab15 100644 --- a/roles/middleware/files/FWO.Middleware.Server/Ldap.cs +++ b/roles/middleware/files/FWO.Middleware.Server/Ldap.cs @@ -38,9 +38,9 @@ private LdapConnection Connect() { try { - LdapConnectionOptions ldapOptions = new LdapConnectionOptions(); + LdapConnectionOptions ldapOptions = new (); if (Tls) ldapOptions.ConfigureRemoteCertificateValidationCallback((object sen, X509Certificate? cer, X509Chain? cha, SslPolicyErrors err) => true); // todo: allow real cert validation - LdapConnection connection = new LdapConnection(ldapOptions) { SecureSocketLayer = Tls, ConnectionTimeout = timeOutInMs }; + LdapConnection connection = new (ldapOptions) { SecureSocketLayer = Tls, ConnectionTimeout = timeOutInMs }; connection.Connect(Address, Port); return connection; @@ -79,18 +79,16 @@ private static bool TryBind(LdapConnection connection, string user, string passw /// public void TestConnection() { - using (LdapConnection connection = Connect()) - { - if (!string.IsNullOrEmpty(SearchUser)) - { - if (!TryBind(connection, SearchUser, SearchUserPwd)) throw new Exception("Binding failed for search user"); - } - if (!string.IsNullOrEmpty(WriteUser)) - { - if (!TryBind(connection, WriteUser, WriteUserPwd)) throw new Exception("Binding failed for write user"); - } - } - } + using LdapConnection connection = Connect(); + if (!string.IsNullOrEmpty(SearchUser)) + { + if (!TryBind(connection, SearchUser, SearchUserPwd)) throw new Exception("Binding failed for search user"); + } + if (!string.IsNullOrEmpty(WriteUser)) + { + if (!TryBind(connection, WriteUser, WriteUserPwd)) throw new Exception("Binding failed for write user"); + } + } private string GetUserSearchFilter(string searchPattern) { @@ -111,7 +109,7 @@ private string GetUserSearchFilter(string searchPattern) userFilter = "(&(|(objectclass=user)(objectclass=person)(objectclass=inetOrgPerson)(objectclass=organizationalPerson))(!(objectclass=computer)))"; searchFilter = $"(|(cn={searchPattern})(uid={searchPattern})(userPrincipalName={searchPattern})(mail={searchPattern}))"; } - return ((searchPattern == null || searchPattern == "") ? userFilter : $"(&{userFilter}{searchFilter})"); + return (searchPattern == null || searchPattern == "") ? userFilter : $"(&{userFilter}{searchFilter})"; } private string GetGroupSearchFilter(string searchPattern) @@ -145,63 +143,60 @@ private string GetGroupSearchFilter(string searchPattern) Log.WriteDebug("User Validation", $"Validating User: \"{user.Name}\" ..."); try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - TryBind(connection, SearchUser, SearchUserPwd); - - LdapSearchConstraints cons = connection.SearchConstraints; - cons.ReferralFollowing = true; - connection.Constraints = cons; - - List possibleUserEntries = new List(); - - // If dn was already provided - if (!user.Dn.IsNullOrEmpty()) - { - // Try to read user entry directly - LdapEntry? userEntry = connection.Read(user.Dn); - if (userEntry != null) - { - possibleUserEntries.Add(userEntry); - } - } - else // Dn was not provided, search for user name - { - string[] attrList = new string[] { "*", "memberof" }; - string userSearchFilter = GetUserSearchFilter(user.Name); - - // Search for users in ldap with same name as user to validate - possibleUserEntries = ((LdapSearchResults)connection.Search( - UserSearchPath, // top-level path under which to search for user - LdapConnection.ScopeSub, // search all levels beneath - userSearchFilter, - attrList, - typesOnly: false - )).ToList(); - } - - // If credentials are not checked return user that was found first - // It could happen that multiple users with the same name were found (impossible if dn was provided) - if (!validateCredentials && possibleUserEntries.Count > 0) - { - return possibleUserEntries.First(); - } - // If credentials should be checked - else if (validateCredentials) - { - // Multiple users with the same name could have been found (impossible if dn was provided) - foreach (LdapEntry possibleUserEntry in possibleUserEntries) - { - // Check credentials - if multiple users were found and the credentials are valid this is most definitely the correct user - if (CredentialsValid(connection, possibleUserEntry.Dn, user.Password)) - { - return possibleUserEntry; - } - } - } - } - } + using LdapConnection connection = Connect(); + TryBind(connection, SearchUser, SearchUserPwd); + + LdapSearchConstraints cons = connection.SearchConstraints; + cons.ReferralFollowing = true; + connection.Constraints = cons; + + List possibleUserEntries = []; + + // If dn was already provided + if (!user.Dn.IsNullOrEmpty()) + { + // Try to read user entry directly + LdapEntry? userEntry = connection.Read(user.Dn); + if (userEntry != null) + { + possibleUserEntries.Add(userEntry); + } + } + else // Dn was not provided, search for user name + { + string[] attrList = ["*", "memberof"]; + string userSearchFilter = GetUserSearchFilter(user.Name); + + // Search for users in ldap with same name as user to validate + possibleUserEntries = ((LdapSearchResults)connection.Search( + UserSearchPath, // top-level path under which to search for user + LdapConnection.ScopeSub, // search all levels beneath + userSearchFilter, + attrList, + typesOnly: false + )).ToList(); + } + + // If credentials are not checked return user that was found first + // It could happen that multiple users with the same name were found (impossible if dn was provided) + if (!validateCredentials && possibleUserEntries.Count > 0) + { + return possibleUserEntries.First(); + } + // If credentials should be checked + else if (validateCredentials) + { + // Multiple users with the same name could have been found (impossible if dn was provided) + foreach (LdapEntry possibleUserEntry in possibleUserEntries) + { + // Check credentials - if multiple users were found and the credentials are valid this is most definitely the correct user + if (CredentialsValid(connection, possibleUserEntry.Dn, user.Password)) + { + return possibleUserEntry; + } + } + } + } catch (LdapException ldapException) { Log.WriteInfo("Ldap entry exception", $"Ldap entry search at \"{Address}:{Port}\" lead to exception: {ldapException.Message}"); @@ -215,34 +210,24 @@ private string GetGroupSearchFilter(string searchPattern) return null; } + /// + /// Get the LdapEntry for the given user by Dn + /// + /// LdapEntry for the given user if found public LdapEntry? GetUserDetailsFromLdap(string distinguishedName) { try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - TryBind(connection, SearchUser, SearchUserPwd); + using LdapConnection connection = Connect(); + TryBind(connection, SearchUser, SearchUserPwd); - LdapSearchConstraints cons = connection.SearchConstraints; - cons.ReferralFollowing = true; - connection.Constraints = cons; + LdapSearchConstraints cons = connection.SearchConstraints; + cons.ReferralFollowing = true; + connection.Constraints = cons; - List possibleUserEntries = []; - - // Try to read user entry directly - LdapEntry? userEntry = connection.Read(distinguishedName); - if (userEntry != null) - { - possibleUserEntries.Add(userEntry); - } - - if (possibleUserEntries.Count > 0) - { - return possibleUserEntries.First(); - } - } - } + // Try to read user entry directly + return connection.Read(distinguishedName); + } catch (LdapException ldapException) { Log.WriteInfo("Ldap entry exception", $"Ldap entry search at \"{Address}:{Port}\" lead to exception: {ldapException.Message}"); @@ -351,7 +336,7 @@ public List GetGroups(LdapEntry user) // - Probably this doesn't work for nested groups. // - Some systems may only save the "primaryGroupID", then we would have to resolve the name. // - Some others may force us to look into all groups to find the membership. - List groups = new List(); + List groups = []; foreach (var attribute in user.GetAttributeSet()) { if (attribute.Name.ToLower() == "memberof") @@ -376,25 +361,22 @@ public string ChangePassword(string userDn, string oldPassword, string newPasswo { try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Try to authenticate as user with old password - if (TryBind(connection, userDn, oldPassword)) - { - // authentication was successful (user is bound): set new password - LdapAttribute attribute = new("userPassword", newPassword); - LdapModification[] mods = { new LdapModification(LdapModification.Replace, attribute) }; - - connection.Modify(userDn, mods); - Log.WriteDebug("Change password", $"Password for user {userDn} changed in {Address}:{Port}"); - } - else - { - return "wrong old password"; - } - } - } + using LdapConnection connection = Connect(); + // Try to authenticate as user with old password + if (TryBind(connection, userDn, oldPassword)) + { + // authentication was successful (user is bound): set new password + LdapAttribute attribute = new("userPassword", newPassword); + LdapModification[] mods = [new LdapModification(LdapModification.Replace, attribute)]; + + connection.Modify(userDn, mods); + Log.WriteDebug("Change password", $"Password for user {userDn} changed in {Address}:{Port}"); + } + else + { + return "wrong old password"; + } + } catch (Exception exception) { return exception.Message; @@ -410,24 +392,21 @@ public string SetPassword(string userDn, string newPassword) { try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - if (TryBind(connection, WriteUser, WriteUserPwd)) - { - // authentication was successful: set new password - LdapAttribute attribute = new LdapAttribute("userPassword", newPassword); - LdapModification[] mods = { new LdapModification(LdapModification.Replace, attribute) }; - - connection.Modify(userDn, mods); - Log.WriteDebug("Change password", $"Password for user {userDn} changed in {Address}:{Port}"); - } - else - { - return "error in write user authentication"; - } - } - } + using LdapConnection connection = Connect(); + if (TryBind(connection, WriteUser, WriteUserPwd)) + { + // authentication was successful: set new password + LdapAttribute attribute = new ("userPassword", newPassword); + LdapModification[] mods = [new LdapModification(LdapModification.Replace, attribute)]; + + connection.Modify(userDn, mods); + Log.WriteDebug("Change password", $"Password for user {userDn} changed in {Address}:{Port}"); + } + else + { + return "error in write user authentication"; + } + } catch (Exception exception) { return exception.Message; @@ -462,46 +441,43 @@ private List GetMemberships(List dnList, string? searchPath) { try { - // Connect to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as search user - TryBind(connection, SearchUser, AesEnc.Decrypt(SearchUserPwd, AesEnc.GetMainKey())); - - // Search for Ldap roles / groups in given directory - int searchScope = LdapConnection.ScopeSub; // TODO: Correct search scope? - string searchFilter = $"(&(objectClass=groupOfUniqueNames)(cn=*))"; - LdapSearchResults searchResults = (LdapSearchResults)connection.Search(searchPath, searchScope, searchFilter, null, false); - - // convert dnList to lower case to avoid case problems - dnList = dnList.ConvertAll(dn => dn.ToLower()); - - // Foreach found role / group - foreach (LdapEntry entry in searchResults) - { - Log.WriteDebug("Ldap Roles/Groups", $"Try to get roles / groups from ldap entry {entry.GetAttribute("cn").StringValue}"); - - // Get dn of users having current role / group - LdapAttribute members = entry.GetAttribute("uniqueMember"); - string[] memberDn = members.StringValueArray; - - // Foreach user - foreach (string currentDn in memberDn) - { - Log.WriteDebug("Ldap Roles/Groups", $"Checking if current Dn: \"{currentDn}\" is user Dn. Then user has current role / group."); - - // Check if current user dn is matching with given user dn => Given user has current role / group - if (dnList.Contains(currentDn.ToLower())) - { - // Get name and add it to list of roles / groups of given user - string name = entry.GetAttribute("cn").StringValue; - userMemberships.Add(name); - break; - } - } - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as search user + TryBind(connection, SearchUser, AesEnc.Decrypt(SearchUserPwd, AesEnc.GetMainKey())); + + // Search for Ldap roles / groups in given directory + int searchScope = LdapConnection.ScopeSub; // TODO: Correct search scope? + string searchFilter = $"(&(objectClass=groupOfUniqueNames)(cn=*))"; + LdapSearchResults searchResults = (LdapSearchResults)connection.Search(searchPath, searchScope, searchFilter, null, false); + + // convert dnList to lower case to avoid case problems + dnList = dnList.ConvertAll(dn => dn.ToLower()); + + // Foreach found role / group + foreach (LdapEntry entry in searchResults) + { + Log.WriteDebug("Ldap Roles/Groups", $"Try to get roles / groups from ldap entry {entry.GetAttribute("cn").StringValue}"); + + // Get dn of users having current role / group + LdapAttribute members = entry.GetAttribute("uniqueMember"); + string[] memberDn = members.StringValueArray; + + // Foreach user + foreach (string currentDn in memberDn) + { + Log.WriteDebug("Ldap Roles/Groups", $"Checking if current Dn: \"{currentDn}\" is user Dn. Then user has current role / group."); + + // Check if current user dn is matching with given user dn => Given user has current role / group + if (dnList.Contains(currentDn.ToLower())) + { + // Get name and add it to list of roles / groups of given user + string name = entry.GetAttribute("cn").StringValue; + userMemberships.Add(name); + break; + } + } + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to get memberships", exception); @@ -525,36 +501,33 @@ public List GetAllRoles() { try { - // Connect to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as search user - TryBind(connection, SearchUser, SearchUserPwd); - - // Search for Ldap roles in given directory - int searchScope = LdapConnection.ScopeSub; // TODO: Correct search scope? - string searchFilter = $"(&(objectClass=groupOfUniqueNames)(cn=*))"; - LdapSearchResults searchResults = (LdapSearchResults)connection.Search(RoleSearchPath, searchScope, searchFilter, null, false); - - // Foreach found role - foreach (LdapEntry entry in searchResults) - { - List attributes = []; - string roleDesc = entry.GetAttribute("description").StringValue; - attributes.Add(new RoleAttribute() { Key = "description", Value = roleDesc }); - - string[] roleMemberDn = entry.GetAttribute("uniqueMember").StringValueArray; - foreach (string currentDn in roleMemberDn) - { - if (currentDn != "") - { - attributes.Add(new RoleAttribute() { Key = "user", Value = currentDn }); - } - } - roleUsers.Add(new RoleGetReturnParameters() { Role = entry.Dn, Attributes = attributes }); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as search user + TryBind(connection, SearchUser, SearchUserPwd); + + // Search for Ldap roles in given directory + int searchScope = LdapConnection.ScopeSub; // TODO: Correct search scope? + string searchFilter = $"(&(objectClass=groupOfUniqueNames)(cn=*))"; + LdapSearchResults searchResults = (LdapSearchResults)connection.Search(RoleSearchPath, searchScope, searchFilter, null, false); + + // Foreach found role + foreach (LdapEntry entry in searchResults) + { + List attributes = []; + string roleDesc = entry.GetAttribute("description").StringValue; + attributes.Add(new () { Key = "description", Value = roleDesc }); + + string[] roleMemberDn = entry.GetAttribute("uniqueMember").StringValueArray; + foreach (string currentDn in roleMemberDn) + { + if (currentDn != "") + { + attributes.Add(new () { Key = "user", Value = currentDn }); + } + } + roleUsers.Add(new RoleGetReturnParameters() { Role = entry.Dn, Attributes = attributes }); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to get all roles", exception); @@ -572,22 +545,19 @@ public List GetAllGroups(string searchPattern) List allGroups = []; try { - // Connect to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as search user - TryBind(connection, SearchUser, SearchUserPwd); - - // Search for Ldap groups in given directory - int searchScope = LdapConnection.ScopeSub; - LdapSearchResults searchResults = (LdapSearchResults)connection.Search(GroupSearchPath, searchScope, GetGroupSearchFilter(searchPattern), null, false); - - foreach (LdapEntry entry in searchResults) - { - allGroups.Add(entry.Dn); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as search user + TryBind(connection, SearchUser, SearchUserPwd); + + // Search for Ldap groups in given directory + int searchScope = LdapConnection.ScopeSub; + LdapSearchResults searchResults = (LdapSearchResults)connection.Search(GroupSearchPath, searchScope, GetGroupSearchFilter(searchPattern), null, false); + + foreach (LdapEntry entry in searchResults) + { + allGroups.Add(entry.Dn); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to get all groups", exception); @@ -605,36 +575,33 @@ public List GetAllInternalGroups() try { - // Connect to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as search user - TryBind(connection, SearchUser, SearchUserPwd); - - // Search for Ldap groups in given directory - int searchScope = LdapConnection.ScopeSub; - LdapSearchResults searchResults = (LdapSearchResults)connection.Search(GroupSearchPath, searchScope, GetGroupSearchFilter(""), null, false); - - foreach (LdapEntry entry in searchResults) - { - List members = []; - string[] groupMemberDn = entry.GetAttribute("uniqueMember").StringValueArray; - foreach (string currentDn in groupMemberDn) - { - if (currentDn != "") - { - members.Add(currentDn); - } - } - allGroups.Add(new GroupGetReturnParameters() - { - GroupDn = entry.Dn, - Members = members, - OwnerGroup = entry.GetAttributeSet().ContainsKey("businessCategory") && entry.GetAttribute("businessCategory").StringValue.Equals("ownergroup", StringComparison.CurrentCultureIgnoreCase) - }); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as search user + TryBind(connection, SearchUser, SearchUserPwd); + + // Search for Ldap groups in given directory + int searchScope = LdapConnection.ScopeSub; + LdapSearchResults searchResults = (LdapSearchResults)connection.Search(GroupSearchPath, searchScope, GetGroupSearchFilter(""), null, false); + + foreach (LdapEntry entry in searchResults) + { + List members = []; + string[] groupMemberDn = entry.GetAttribute("uniqueMember").StringValueArray; + foreach (string currentDn in groupMemberDn) + { + if (currentDn != "") + { + members.Add(currentDn); + } + } + allGroups.Add(new GroupGetReturnParameters() + { + GroupDn = entry.Dn, + Members = members, + OwnerGroup = entry.GetAttributeSet().ContainsKey("businessCategory") && entry.GetAttribute("businessCategory").StringValue.Equals("ownergroup", StringComparison.CurrentCultureIgnoreCase) + }); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to get all internal groups", exception); @@ -654,26 +621,23 @@ public List GetGroupMembers(string groupDn) { try { - // Connect to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as search user - TryBind(connection, SearchUser, SearchUserPwd); - LdapEntry entry = connection.Read(groupDn); - - if (entry != null) - { - string[] groupMemberDn = entry.GetAttribute("uniqueMember").StringValueArray; - foreach (string currentDn in groupMemberDn) - { - if (currentDn != "") - { - allMembers.Add(currentDn); - } - } - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as search user + TryBind(connection, SearchUser, SearchUserPwd); + LdapEntry entry = connection.Read(groupDn); + + if (entry != null) + { + string[] groupMemberDn = entry.GetAttribute("uniqueMember").StringValueArray; + foreach (string currentDn in groupMemberDn) + { + if (currentDn != "") + { + allMembers.Add(currentDn); + } + } + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", $"Unexpected error while trying to get all group members of group {groupDn}", exception); @@ -693,32 +657,29 @@ public List GetAllUsers(string searchPattern) try { - // Connect to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as search user - TryBind(connection, SearchUser, SearchUserPwd); - - // Search for Ldap users in given directory - int searchScope = LdapConnection.ScopeSub; - - LdapSearchConstraints cons = connection.SearchConstraints; - cons.ReferralFollowing = true; - connection.Constraints = cons; - - LdapSearchResults searchResults = (LdapSearchResults)connection.Search(UserSearchPath, searchScope, GetUserSearchFilter(searchPattern), null, false); - - foreach (LdapEntry entry in searchResults) - { - allUsers.Add(new LdapUserGetReturnParameters() - { - UserDn = entry.Dn, - Email = entry.GetAttributeSet().ContainsKey("mail") ? entry.GetAttribute("mail").StringValue : null - // add first and last name of user - }); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as search user + TryBind(connection, SearchUser, SearchUserPwd); + + // Search for Ldap users in given directory + int searchScope = LdapConnection.ScopeSub; + + LdapSearchConstraints cons = connection.SearchConstraints; + cons.ReferralFollowing = true; + connection.Constraints = cons; + + LdapSearchResults searchResults = (LdapSearchResults)connection.Search(UserSearchPath, searchScope, GetUserSearchFilter(searchPattern), null, false); + + foreach (LdapEntry entry in searchResults) + { + allUsers.Add(new LdapUserGetReturnParameters() + { + UserDn = entry.Dn, + Email = entry.GetAttributeSet().ContainsKey("mail") ? entry.GetAttribute("mail").StringValue : null + // add first and last name of user + }); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to get all users", exception); @@ -736,38 +697,35 @@ public bool AddUser(string userDn, string password, string email) bool userAdded = false; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - string userName = new FWO.Api.Data.DistName(userDn).UserName; - LdapAttributeSet attributeSet = new LdapAttributeSet - { - new LdapAttribute("objectclass", "inetOrgPerson"), - new LdapAttribute("sn", userName), - new LdapAttribute("cn", userName), - new LdapAttribute("uid", userName), - new LdapAttribute("userPassword", password), - new LdapAttribute("mail", email) - }; + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); - LdapEntry newEntry = new LdapEntry(userDn, attributeSet); - - try - { - //Add the entry to the directory - connection.Add(newEntry); - userAdded = true; - Log.WriteDebug("Add user", $"User {userName} added in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Add User", $"couldn't add user to LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + string userName = new DistName(userDn).UserName; + LdapAttributeSet attributeSet = new () + { + new LdapAttribute("objectclass", "inetOrgPerson"), + new LdapAttribute("sn", userName), + new LdapAttribute("cn", userName), + new LdapAttribute("uid", userName), + new LdapAttribute("userPassword", password), + new LdapAttribute("mail", email) + }; + + LdapEntry newEntry = new (userDn, attributeSet); + + try + { + //Add the entry to the directory + connection.Add(newEntry); + userAdded = true; + Log.WriteDebug("Add user", $"User {userName} added in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Add User", $"couldn't add user to LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to add user", exception); @@ -785,27 +743,24 @@ public bool UpdateUser(string userDn, string email) bool userUpdated = false; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - LdapAttribute attribute = new LdapAttribute("mail", email); - LdapModification[] mods = { new LdapModification(LdapModification.Replace, attribute) }; - - try - { - //Add the entry to the directory - connection.Modify(userDn, mods); - userUpdated = true; - Log.WriteDebug("Update user", $"User {userDn} updated in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Update User", $"couldn't update user in LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + LdapAttribute attribute = new ("mail", email); + LdapModification[] mods = [new (LdapModification.Replace, attribute)]; + + try + { + //Add the entry to the directory + connection.Modify(userDn, mods); + userUpdated = true; + Log.WriteDebug("Update user", $"User {userDn} updated in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Update User", $"couldn't update user in LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to update user", exception); @@ -823,25 +778,22 @@ public bool DeleteUser(string userDn) bool userDeleted = false; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - try - { - //Delete the entry in the directory - connection.Delete(userDn); - userDeleted = true; - Log.WriteDebug("Delete user", $"User {userDn} deleted in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Delete User", $"couldn't delete user in LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + + try + { + //Delete the entry in the directory + connection.Delete(userDn); + userDeleted = true; + Log.WriteDebug("Delete user", $"User {userDn} deleted in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Delete User", $"couldn't delete user in LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to delete user", exception); @@ -860,41 +812,38 @@ public string AddGroup(string groupName, bool ownerGroup) string groupDn = ""; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + + groupDn = $"cn={groupName},{GroupSearchPath}"; + LdapAttributeSet attributeSet = new (); + attributeSet.Add(new LdapAttribute("objectclass", "groupofuniquenames")); + attributeSet.Add(new LdapAttribute("uniqueMember", "")); + if (ownerGroup) { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - groupDn = $"cn={groupName},{GroupSearchPath}"; - LdapAttributeSet attributeSet = new LdapAttributeSet(); - attributeSet.Add(new LdapAttribute("objectclass", "groupofuniquenames")); - attributeSet.Add(new LdapAttribute("uniqueMember", "")); - if (ownerGroup) - { - attributeSet.Add(new LdapAttribute("businessCategory", "ownergroup")); - } - - LdapEntry newEntry = new LdapEntry(groupDn, attributeSet); - - try - { - //Add the entry to the directory - connection.Add(newEntry); - groupAdded = true; - Log.WriteDebug("Add group", $"Group {groupName} added in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Add Group", $"couldn't add group to LDAP {Address}:{Port}: {exception.ToString()}"); - } + attributeSet.Add(new LdapAttribute("businessCategory", "ownergroup")); } - } + + LdapEntry newEntry = new (groupDn, attributeSet); + + try + { + //Add the entry to the directory + connection.Add(newEntry); + groupAdded = true; + Log.WriteDebug("Add group", $"Group {groupName} added in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Add Group", $"couldn't add group to LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to add group", exception); } - return (groupAdded ? groupDn : ""); + return groupAdded ? groupDn : ""; } /// @@ -910,30 +859,27 @@ public string UpdateGroup(string oldName, string newName) try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - try - { - //Add the entry to the directory - connection.Rename(oldGroupDn, newGroupRdn, true); - groupUpdated = true; - Log.WriteDebug("Update group", $"Group {oldName} renamed to {newName} in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Update Group", $"couldn't update group in LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + + try + { + //Add the entry to the directory + connection.Rename(oldGroupDn, newGroupRdn, true); + groupUpdated = true; + Log.WriteDebug("Update group", $"Group {oldName} renamed to {newName} in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Update Group", $"couldn't update group in LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to update group", exception); } - return (groupUpdated ? $"{newGroupRdn},{GroupSearchPath}" : ""); + return groupUpdated ? $"{newGroupRdn},{GroupSearchPath}" : ""; } /// @@ -946,26 +892,23 @@ public bool DeleteGroup(string groupName) bool groupDeleted = false; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - try - { - //Delete the entry in the directory - string groupDn = $"cn={groupName},{GroupSearchPath}"; - connection.Delete(groupDn); - groupDeleted = true; - Log.WriteDebug("Delete group", $"Group {groupName} deleted in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Delete Group", $"couldn't delete group in LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + + try + { + //Delete the entry in the directory + string groupDn = $"cn={groupName},{GroupSearchPath}"; + connection.Delete(groupDn); + groupDeleted = true; + Log.WriteDebug("Delete group", $"Group {groupName} deleted in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Delete Group", $"couldn't delete group in LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to delete group", exception); @@ -999,8 +942,7 @@ public bool RemoveUserFromEntry(string userDn, string entry) /// true if user removed from all entries public bool RemoveUserFromAllEntries(string userDn) { - List dnList = new List(); - dnList.Add(userDn); // group memberships do not need to be regarded here + List dnList = [userDn]; // group memberships do not need to be regarded here List roles = GetRoles(dnList); bool allRemoved = true; foreach (var role in roles) @@ -1015,34 +957,31 @@ public bool RemoveUserFromAllEntries(string userDn) return allRemoved; } - private bool ModifyUserInEntry(string userDn, string entry, int LdapModification) + private bool ModifyUserInEntry(string userDn, string entry, int ldapModification) { bool userModified = false; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - // Add a new value to the description attribute - LdapAttribute attribute = new LdapAttribute("uniquemember", userDn); - LdapModification[] mods = { new LdapModification(LdapModification, attribute) }; - - try - { - //Modify the entry in the directory - connection.Modify(entry, mods); - userModified = true; - Log.WriteDebug("Modify Entry", $"Entry {entry} modified in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Modify Entry", $"maybe entry doesn't exist in this LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + + // Add a new value to the description attribute + LdapAttribute attribute = new("uniquemember", userDn); + LdapModification[] mods = [new(ldapModification, attribute)]; + + try + { + //Modify the entry in the directory + connection.Modify(entry, mods); + userModified = true; + Log.WriteDebug("Modify Entry", $"Entry {entry} modified in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Modify Entry", $"maybe entry doesn't exist in this LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to modify user", exception); @@ -1058,34 +997,31 @@ public bool AddTenant(string tenantName) { Log.WriteInfo("Add Tenant", $"Trying to add Tenant: \"{tenantName}\""); bool tenantAdded = false; - string tenantDn = ""; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - tenantDn = $"ou={tenantName},{UserSearchPath}"; - LdapAttributeSet attributeSet = new LdapAttributeSet(); - attributeSet.Add(new LdapAttribute("objectclass", "organizationalUnit")); + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); - LdapEntry newEntry = new LdapEntry(tenantDn, attributeSet); - - try - { - //Add the entry to the directory - connection.Add(newEntry); - tenantAdded = true; - Log.WriteDebug("Add tenant", $"Tenant {tenantName} added in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Add Tenant", $"couldn't add tenant to LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + LdapAttributeSet attributeSet = new () + { + new LdapAttribute("objectclass", "organizationalUnit") + }; + + LdapEntry newEntry = new (TenantNameToDn(tenantName), attributeSet); + + try + { + //Add the entry to the directory + connection.Add(newEntry); + tenantAdded = true; + Log.WriteDebug("Add tenant", $"Tenant {tenantName} added in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Add Tenant", $"couldn't add tenant to LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to add tenant", exception); @@ -1103,32 +1039,33 @@ public bool DeleteTenant(string tenantName) bool tenantDeleted = false; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - try - { - string tenantDn = "ou=" + tenantName + "," + UserSearchPath; - - //Delete the entry in the directory - connection.Delete(tenantDn); - tenantDeleted = true; - Log.WriteDebug("Delete Tenant", $"tenant {tenantDn} deleted in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Delete Tenant", $"couldn't delete tenant in LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + + try + { + string tenantDn = TenantNameToDn(tenantName); + //Delete the entry in the directory + connection.Delete(tenantDn); + tenantDeleted = true; + Log.WriteDebug("Delete Tenant", $"tenant {tenantDn} deleted in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Delete Tenant", $"couldn't delete tenant in LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to delete tenant", exception); } return tenantDeleted; } + + private string TenantNameToDn(string tenantName) + { + return $"ou={tenantName},{UserSearchPath}"; + } } } From 379fe708a7b5cc62ad021739ceeeee2431b9f5e1 Mon Sep 17 00:00:00 2001 From: abarz722 Date: Fri, 3 May 2024 14:18:18 +0200 Subject: [PATCH 2/6] some docu + cleanup --- .../files/sql/idempotent/fworch-texts.sql | 34 +++++++++++++++++-- .../files/FWO.Api.Client/Data/StateMatrix.cs | 17 +++++----- .../Pages/Help/HelpModellingSidebar.cshtml | 4 ++- .../Pages/Help/HelpModellingWorkflow.cshtml | 17 ++++++++++ 4 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 roles/ui/files/FWO.UI/Pages/Help/HelpModellingWorkflow.cshtml diff --git a/roles/database/files/sql/idempotent/fworch-texts.sql b/roles/database/files/sql/idempotent/fworch-texts.sql index 47eaf9131..5ba9eb08c 100644 --- a/roles/database/files/sql/idempotent/fworch-texts.sql +++ b/roles/database/files/sql/idempotent/fworch-texts.sql @@ -5747,11 +5747,11 @@ INSERT INTO txt VALUES ('H9011', 'English', 'An application is - from the perspe INSERT INTO txt VALUES ('H9021', 'German', 'Verbindungen sind die Hauptbestandteile des Kommunikationsprofils. Es wird zwischen verschiedenen Arten von Verbindungen unterschieden:'); INSERT INTO txt VALUES ('H9021', 'English', 'Connections are the main components of the communication profile. There are different types of connections:'); INSERT INTO txt VALUES ('H9022', 'German', 'Schnittstellen: Sie dienen in erster Linie der Modellierung von (aus Sicht der Applikation) externen Verbindungen oder der Bündelung interner Objekte. - Es müssen in der Applikation neben dem Dienst entweder Quelle oder Ziel definiert werden. Die Schnittstellen werden in den anderen Applikationen - zur Auswahl angeboten und können dort in der Definition von eigenen Verbindungen verwendet werden. + Es müssen in der Applikation neben dem Dienst entweder Quelle oder Ziel definiert werden. Die Schnittstellen können durch Setzen des entsprechendenn Häkchens veröffentlicht und dadurch in den anderen Applikationen + zur Auswahl angeboten werden. Sie können dann dort in der Definition von eigenen Verbindungen verwendet werden. '); INSERT INTO txt VALUES ('H9022', 'English', 'Interfaces: They serve primarily the modelling of (relative to the application) external connections or the bundling of internal objects. - Besides the service either source or destination have to be defined in the application. The interfaces are offered to other applications to use + Besides the service either source or destination have to be defined in the application. The interfaces can be published by setting the respective flag and are then offered to other applications to use them in the definition of own connections. '); INSERT INTO txt VALUES ('H9023', 'German', 'Standard: Zentrale Objekte zur Modellierung der Kommunikationsverbindungen. Dabei müssen Quelle, Dienst und Ziel aus den in der Bibliothek @@ -5810,3 +5810,31 @@ INSERT INTO txt VALUES ('H9043', 'German', 'Dienstgruppen: In Dienstgruppen k&o INSERT INTO txt VALUES ('H9043', 'English', 'Service Groups: Simple services can be bundled in Service Groups. A name has to be given to them, comments can be added. Again definition can be done by the modeller, but also Service Groups predefined by the administrator can be used. '); +INSERT INTO txt VALUES ('H9051', 'German', 'Beantragung neuer Schnittstellen: Wenn externe Schnittstellen von anderen Applikationen benötigt werden, können diese über die entsprechende Schaltfläche in der Bibliothek beantragt werden. +
    +
  • Es erscheint ein Dialog, in dem die externe Applikation ausgewählt und eine Begründung eingetragen werden müssen, sowie das Häkchen, ob die Schnittstelle als Quelle oder Ziel genutzt werden soll.
  • +
  • Beim Abschicken der Anforderung wird +
      +
    • bei der externen Applikation automatisch eine Dummy-Schnittstelle angelegt, die dann in der eigenen Schnittstellen-Auswahl erscheint und direkt zur Erstellung eigener Verbindungen genutzt werden kann. + Sie wird in der Liste der eigenen Verbindungen mit Einträgen "Schnittstelle angefordert" in Quelle/Ziel und Dienst als solche gekennzeichnet.
    • +
    • der oder die für die externe Applikation Verantwortlichen per Email über den Antrag informiert.
    • +
    • im Workflow-Modul ein Ticket mit dem Antrag erstellt. Je nach Konfiguration des Workflows kann hier der Auftrag abgelehnt, an andere Applikationen weitergeleitet, einzelnen Bearbeitern zugewiesen oder mit Kommentaren versehen werden.
    • +
    +
  • +
  • Wird die Schnittstelle auf der Gegenseite modelliert und veröffentlicht, wandelt sich auch die eigene nutzende Verbindung automatisch in eine "normale" Verbindung um, eine weiteres Eingreifen des Antragstellers ist nicht mehr notwendig.
  • +
+'); +INSERT INTO txt VALUES ('H9051', 'English', 'Request new interface: If external interfaces from other applications are needed, they can be requested via a button in the library. +
    +
  • A dialogue is displayed to select the external Application. A reason field has to be filled as well as the checkbox, if the interface should be used as source or destination.
  • +
  • If the request is submitted +
      +
    • a dummy interface is created automatically at the target application, which then appears in the own interface selection in the library and can be used for the definition of the own connection. + It is marked as such by the text "Interface requested" in Source/Destination and Service in the list of own the connections.
    • +
    • the responsible(s) of the external Application is informed about the request by email.
    • +
    • a ticket in the Workflow module is created. Depending on the configuration of the workflow, the request can be rejected, forwarded to other applications, assigned to aperson in charge or commented.
    • +
    +
  • +
  • When the requested interface is modelled and published on the other side, the own using connection is changed to a "regular" connection automatically, further action is not necessary.
  • +
+'); diff --git a/roles/lib/files/FWO.Api.Client/Data/StateMatrix.cs b/roles/lib/files/FWO.Api.Client/Data/StateMatrix.cs index 097279f46..6a6f5e518 100644 --- a/roles/lib/files/FWO.Api.Client/Data/StateMatrix.cs +++ b/roles/lib/files/FWO.Api.Client/Data/StateMatrix.cs @@ -1,5 +1,4 @@ using FWO.Api.Client.Queries; -using FWO.GlobalConstants; using FWO.Api.Data; using System.Text.Json.Serialization; using Newtonsoft.Json; @@ -20,10 +19,10 @@ public enum WorkflowPhases public class StateMatrix { [JsonProperty("matrix"), JsonPropertyName("matrix")] - public Dictionary> Matrix { get; set; } = new (); + public Dictionary> Matrix { get; set; } = []; [JsonProperty("derived_states"), JsonPropertyName("derived_states")] - public Dictionary DerivedStates { get; set; } = new (); + public Dictionary DerivedStates { get; set; } = []; [JsonProperty("lowest_input_state"), JsonPropertyName("lowest_input_state")] public int LowestInputState { get; set; } @@ -37,7 +36,7 @@ public class StateMatrix [JsonProperty("active"), JsonPropertyName("active")] public bool Active { get; set; } - public Dictionary PhaseActive = new Dictionary(); + public Dictionary PhaseActive = []; public bool IsLastActivePhase = true; public int MinImplTasksNeeded; @@ -77,7 +76,7 @@ public bool getNextActivePhase(ref WorkflowPhases phase) public List getAllowedTransitions(int stateIn) { - return Matrix.ContainsKey(stateIn) ? Matrix[stateIn] : new (); + return Matrix.ContainsKey(stateIn) ? Matrix[stateIn] : []; } public int getDerivedStateFromSubStates(List statesIn) @@ -86,7 +85,7 @@ public int getDerivedStateFromSubStates(List statesIn) { return 0; } - int stateOut = 0; + int stateOut; int backAssignedState = LowestInputState; int initState = 0; int inWorkState = LowestEndState; @@ -160,7 +159,7 @@ public int getDerivedStateFromSubStates(List statesIn) public class GlobalStateMatrix { [JsonProperty("config_value"), JsonPropertyName("config_value")] - public Dictionary GlobalMatrix { get; set; } = new (); + public Dictionary GlobalMatrix { get; set; } = []; public async Task Init(ApiConnection apiConnection, TaskType taskType = TaskType.master, bool reset = false) @@ -198,11 +197,11 @@ public class GlobalStateMatrixHelper public class StateMatrixDict { - public Dictionary Matrices { get; set; } = new Dictionary(); + public Dictionary Matrices { get; set; } = []; public async Task Init(WorkflowPhases phase, ApiConnection apiConnection) { - Matrices = new Dictionary(); + Matrices = []; foreach(TaskType taskType in Enum.GetValues(typeof(TaskType))) { Matrices.Add(taskType.ToString(), new StateMatrix()); diff --git a/roles/ui/files/FWO.UI/Pages/Help/HelpModellingSidebar.cshtml b/roles/ui/files/FWO.UI/Pages/Help/HelpModellingSidebar.cshtml index 679a4c9b6..c710dd21b 100644 --- a/roles/ui/files/FWO.UI/Pages/Help/HelpModellingSidebar.cshtml +++ b/roles/ui/files/FWO.UI/Pages/Help/HelpModellingSidebar.cshtml @@ -21,7 +21,9 @@ @(userConfig.GetText("services")) - + + @(userConfig.GetText("workflow")) + diff --git a/roles/ui/files/FWO.UI/Pages/Help/HelpModellingWorkflow.cshtml b/roles/ui/files/FWO.UI/Pages/Help/HelpModellingWorkflow.cshtml new file mode 100644 index 000000000..ae2b73ec1 --- /dev/null +++ b/roles/ui/files/FWO.UI/Pages/Help/HelpModellingWorkflow.cshtml @@ -0,0 +1,17 @@ +@page "/help/modelling/workflow" +@model FWO.Ui.Pages.Help.MainModel +@{ + Layout = "HelpLayout"; +} +@section sidebar{ + @{ + await Html.RenderPartialAsync("HelpModellingSidebar.cshtml"); + } +} +@using FWO.Config.Api +@inject UserConfig userConfig + +
+

@userConfig.GetText("workflow")

+ @(Html.Raw(userConfig.GetText("H9051"))) +
From 498472bb020620055e076a6e24f1df1b526c5dba Mon Sep 17 00:00:00 2001 From: abarz722 Date: Fri, 3 May 2024 14:20:06 +0200 Subject: [PATCH 3/6] refine actions --- .../modelling/updateConnectionOwner.graphql | 12 +++++ .../modelling/updateConnectionPublish.graphql | 14 ++++++ .../Queries/ModellingQueries.cs | 4 ++ .../ui/files/FWO.UI/Services/ActionHandler.cs | 50 ++++++++++++++----- .../Services/ModellingConnectionHandler.cs | 2 +- 5 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionOwner.graphql create mode 100644 roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionPublish.graphql diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionOwner.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionOwner.graphql new file mode 100644 index 000000000..fdf9b53b6 --- /dev/null +++ b/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionOwner.graphql @@ -0,0 +1,12 @@ +mutation updateConnectionOwner( + $id: Int! + $appId: Int + ) { + update_modelling_connection_by_pk( + pk_columns: { id: $id } + _set: { + app_id: $appId + }) { + UpdatedId: id + } +} diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionPublish.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionPublish.graphql new file mode 100644 index 000000000..9f5589d8d --- /dev/null +++ b/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionPublish.graphql @@ -0,0 +1,14 @@ +mutation updateConnectionPublish( + $id: Int! + $isPublished: Boolean + $isRequested: Boolean + ) { + update_modelling_connection_by_pk( + pk_columns: { id: $id } + _set: { + is_requested: $isRequested + is_published: $isPublished + }) { + UpdatedId: id + } +} diff --git a/roles/lib/files/FWO.Api.Client/Queries/ModellingQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/ModellingQueries.cs index 1b6458b46..1cced1946 100644 --- a/roles/lib/files/FWO.Api.Client/Queries/ModellingQueries.cs +++ b/roles/lib/files/FWO.Api.Client/Queries/ModellingQueries.cs @@ -34,6 +34,8 @@ public class ModellingQueries : Queries public static readonly string getCommonServices; public static readonly string newConnection; public static readonly string updateConnection; + public static readonly string updateConnectionOwner; + public static readonly string updateConnectionPublish; public static readonly string deleteConnection; public static readonly string addAppServerToConnection; public static readonly string removeAppServerFromConnection; @@ -122,6 +124,8 @@ static ModellingQueries() getCommonServices = connectionDetailsFragment + File.ReadAllText(QueryPath + "modelling/getCommonServices.graphql"); newConnection = File.ReadAllText(QueryPath + "modelling/newConnection.graphql"); updateConnection = File.ReadAllText(QueryPath + "modelling/updateConnection.graphql"); + updateConnectionOwner = File.ReadAllText(QueryPath + "modelling/updateConnectionOwner.graphql"); + updateConnectionPublish = File.ReadAllText(QueryPath + "modelling/updateConnectionPublish.graphql"); deleteConnection = File.ReadAllText(QueryPath + "modelling/deleteConnection.graphql"); addAppServerToConnection = File.ReadAllText(QueryPath + "modelling/addAppServerToConnection.graphql"); removeAppServerFromConnection = File.ReadAllText(QueryPath + "modelling/removeAppServerFromConnection.graphql"); diff --git a/roles/ui/files/FWO.UI/Services/ActionHandler.cs b/roles/ui/files/FWO.UI/Services/ActionHandler.cs index 7ad73174b..8389a7276 100644 --- a/roles/ui/files/FWO.UI/Services/ActionHandler.cs +++ b/roles/ui/files/FWO.UI/Services/ActionHandler.cs @@ -119,7 +119,7 @@ public async Task PerformAction(RequestStateAction action, RequestStatefulObject await UpdateConnectionOwner(owner, ticketId); break; case nameof(StateActionTypes.UpdateConnectionRelease): - await UpdateConnectionRelease(action, owner); + await UpdateConnectionPublish(owner, ticketId); break; case nameof(StateActionTypes.DisplayConnection): await DisplayConnection(statefulObject, scope); @@ -189,16 +189,9 @@ public async Task UpdateConnectionOwner(FwoOwner? owner, long? ticketId) var Variables = new { id = conn.Id, - name = conn.Name, - appId = owner.Id, - reason = conn.Reason, - isInterface = conn.IsInterface, - usedInterfaceId = conn.UsedInterfaceId, - isRequested = conn.IsRequested, - isPublished = conn.IsPublished, - commonSvc = conn.IsCommonService + appId = owner.Id }; - await apiConnection.SendQueryAsync(ModellingQueries.updateConnection, Variables); + await apiConnection.SendQueryAsync(ModellingQueries.updateConnectionOwner, Variables); await ModellingHandlerBase.LogChange(ModellingTypes.ChangeType.Update, ModellingTypes.ModObjectType.Connection, conn.Id, $"Updated {(conn.IsInterface? "Interface" : "Connection")}: {conn.Name}", apiConnection, requestHandler.userConfig, owner.Id, DefaultInit.DoNothing); } @@ -212,9 +205,42 @@ await ModellingHandlerBase.LogChange(ModellingTypes.ChangeType.Update, Modelling } } - public async Task UpdateConnectionRelease(RequestStateAction action, FwoOwner? owner) + public async Task UpdateConnectionPublish(FwoOwner? owner, long? ticketId) { - Log.WriteDebug("UpdateConnectionRelease", "Perform Action"); + Log.WriteDebug("UpdateConnectionPublish", "Perform Action"); + try + { + if(owner != null && ticketId != null) // todo: role check + { + apiConnection.SetRole(Roles.Modeller); + List Connections = await apiConnection.SendQueryAsync>(ModellingQueries.getConnectionsByTicketId, new { ticketId }); + foreach(var conn in Connections) + { + if(conn.IsRequested && !conn.IsPublished) + { + ConnHandler = new (apiConnection, requestHandler.userConfig, owner, new(), conn, true, false, DefaultInit.DoNothing, DefaultInit.DoNothing, false); + await ConnHandler.PartialInit(); + if(ConnHandler.CheckConn()) + { + var Variables = new + { + id = conn.Id, + isRequested = false, + isPublished = true + }; + await apiConnection.SendQueryAsync(ModellingQueries.updateConnectionPublish, Variables); + await ModellingHandlerBase.LogChange(ModellingTypes.ChangeType.Update, ModellingTypes.ModObjectType.Connection, conn.Id, + $"Updated {(conn.IsInterface? "Interface" : "Connection")}: {conn.Name}", apiConnection, requestHandler.userConfig, owner.Id, DefaultInit.DoNothing); + } + } + } + apiConnection.SwitchBack(); + } + } + catch(Exception exc) + { + Log.WriteError("Update Connection Publish", $"Could not publish connection: ", exc); + } } public async Task DisplayConnection(RequestStatefulObject statefulObject, RequestObjectScopes scope) diff --git a/roles/ui/files/FWO.UI/Services/ModellingConnectionHandler.cs b/roles/ui/files/FWO.UI/Services/ModellingConnectionHandler.cs index ad6244be2..a08702eca 100644 --- a/roles/ui/files/FWO.UI/Services/ModellingConnectionHandler.cs +++ b/roles/ui/files/FWO.UI/Services/ModellingConnectionHandler.cs @@ -923,7 +923,7 @@ public async Task Save() return false; } - private bool CheckConn() + public bool CheckConn() { if(ActConn.Name == null || ActConn.Name == "" || ActConn.Reason == null || ActConn.Reason == "") { From 1c14104095cf0b2c1afc81ae007ff25d9ada9e35 Mon Sep 17 00:00:00 2001 From: abarz722 Date: Fri, 3 May 2024 14:20:52 +0200 Subject: [PATCH 4/6] workflow fixes --- .../Request/DisplayImplementationTask.razor | 8 +++----- .../Pages/Request/DisplayRequestTask.razor | 18 +++++++++--------- .../FWO.UI/Pages/Request/DisplayTicket.razor | 8 +++----- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/roles/ui/files/FWO.UI/Pages/Request/DisplayImplementationTask.razor b/roles/ui/files/FWO.UI/Pages/Request/DisplayImplementationTask.razor index 69c0631c5..abd72225c 100644 --- a/roles/ui/files/FWO.UI/Pages/Request/DisplayImplementationTask.razor +++ b/roles/ui/files/FWO.UI/Pages/Request/DisplayImplementationTask.razor @@ -202,14 +202,12 @@
-
+
@foreach(var action in offeredActions) { @if(ReqHandler.ImplementImplTaskMode || ReqHandler.ReviewImplTaskMode || RequestStateAction.IsReadonlyType(action.ActionType)) { -
- -
+ } }
@@ -381,7 +379,7 @@ private async Task PerformAction(RequestStateAction action) { - await ReqHandler.ActionHandler.PerformAction(action, ReqHandler.ActImplTask, RequestObjectScopes.ImplementationTask); + await ReqHandler.ActionHandler.PerformAction(action, ReqHandler.ActImplTask, RequestObjectScopes.ImplementationTask, actOwner, ReqHandler.ActReqTask.TicketId); } private void RequestAssignOwner() diff --git a/roles/ui/files/FWO.UI/Pages/Request/DisplayRequestTask.razor b/roles/ui/files/FWO.UI/Pages/Request/DisplayRequestTask.razor index be27d0eb0..fc1eb18d5 100644 --- a/roles/ui/files/FWO.UI/Pages/Request/DisplayRequestTask.razor +++ b/roles/ui/files/FWO.UI/Pages/Request/DisplayRequestTask.razor @@ -255,16 +255,16 @@
} -
- @foreach(var action in offeredActions) - { - @if(ReqHandler.EditReqTaskMode || ReqHandler.PlanReqTaskMode || ReqHandler.ApproveReqTaskMode || RequestStateAction.IsReadonlyType(action.ActionType)) +
+
+ @foreach(var action in offeredActions) { -
- -
+ @if(ReqHandler.EditReqTaskMode || ReqHandler.PlanReqTaskMode || ReqHandler.ApproveReqTaskMode || RequestStateAction.IsReadonlyType(action.ActionType)) + { + + } } - } +
@if(Phase == WorkflowPhases.planning || ReqHandler.ActReqTask.ImplementationTasks.Count > 0) @@ -492,7 +492,7 @@ private async Task PerformAction(RequestStateAction action) { - await ReqHandler.ActionHandler.PerformAction(action, ReqHandler.ActReqTask, RequestObjectScopes.RequestTask); + await ReqHandler.ActionHandler.PerformAction(action, ReqHandler.ActReqTask, RequestObjectScopes.RequestTask, actOwner, ReqHandler.ActReqTask.TicketId); await Reset(); } diff --git a/roles/ui/files/FWO.UI/Pages/Request/DisplayTicket.razor b/roles/ui/files/FWO.UI/Pages/Request/DisplayTicket.razor index 74da0f632..34e6364e3 100644 --- a/roles/ui/files/FWO.UI/Pages/Request/DisplayTicket.razor +++ b/roles/ui/files/FWO.UI/Pages/Request/DisplayTicket.razor @@ -90,14 +90,12 @@
-
+
@foreach(var action in offeredActions) { @if(!ReqHandler.ReadOnlyMode || RequestStateAction.IsReadonlyType(action.ActionType)) { -
- -
+ } }
@@ -262,7 +260,7 @@ private async Task PerformAction(RequestStateAction action) { - await ReqHandler.ActionHandler.PerformAction(action, ReqHandler.ActTicket, RequestObjectScopes.Ticket); + await ReqHandler.ActionHandler.PerformAction(action, ReqHandler.ActTicket, RequestObjectScopes.Ticket, null, ReqHandler.ActTicket.Id); await ResetParent(); } From 629b5e062b1c5f126805076a65818aa6776a10c8 Mon Sep 17 00:00:00 2001 From: abarz722 Date: Fri, 3 May 2024 15:43:08 +0200 Subject: [PATCH 5/6] fix refresh error --- .../ui/files/FWO.UI/Services/ModellingServiceGroupHandler.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/roles/ui/files/FWO.UI/Services/ModellingServiceGroupHandler.cs b/roles/ui/files/FWO.UI/Services/ModellingServiceGroupHandler.cs index 19c3a67d5..4f3053184 100644 --- a/roles/ui/files/FWO.UI/Services/ModellingServiceGroupHandler.cs +++ b/roles/ui/files/FWO.UI/Services/ModellingServiceGroupHandler.cs @@ -49,7 +49,10 @@ public async Task RefreshActServiceGroup() { try { - ActServiceGroup = await apiConnection.SendQueryAsync(ModellingQueries.getServiceGroupById, new { id = ActServiceGroup.Id }); + if(ActServiceGroup.Id > 0) + { + ActServiceGroup = await apiConnection.SendQueryAsync(ModellingQueries.getServiceGroupById, new { id = ActServiceGroup.Id }); + } await RefreshParent(); } catch (Exception exception) From 2dac48d4b1c1f3b1c49b71e6cc06c6b17865efe9 Mon Sep 17 00:00:00 2001 From: abarz722 Date: Mon, 6 May 2024 14:24:27 +0200 Subject: [PATCH 6/6] no translation of obj type --- roles/ui/files/FWO.UI/Shared/ObjectGroup.razor | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/ui/files/FWO.UI/Shared/ObjectGroup.razor b/roles/ui/files/FWO.UI/Shared/ObjectGroup.razor index cfeb0a370..906607f9e 100644 --- a/roles/ui/files/FWO.UI/Shared/ObjectGroup.razor +++ b/roles/ui/files/FWO.UI/Shared/ObjectGroup.razor @@ -52,7 +52,7 @@ - + @@ -120,7 +120,7 @@ - + @if (context.Type.Name != ObjectType.Group) { @@ -196,7 +196,7 @@ - + @if (context.Type != null && context.Type.Name == ObjectType.Group && context.UserGroups != null && context.UserGroupFlats != null)