Пример #1
0
        private void SaveUsersAndGroupsToXML()
        {
            XDoc doc = new XDoc(UsersAndGroupsXMLRootName);

            foreach (string confluenceUserName in _convertedUsers.Keys)
            {
                ACConverterUserInfo userInfo = _convertedUsers[confluenceUserName];
                XDoc userDoc = new XDoc(UserXMLTagName);
                userDoc.Attr(ConfluenceUserNameXMLAttributeName, confluenceUserName);
                userDoc.Attr(DekiUserNameXMLAttributeName, userInfo.DekiUserName);
                doc.Add(userDoc);
            }
            foreach (string confluenceGroupName in _convertedGroups.Keys)
            {
                ACConverterGroupInfo groupInfo = _convertedGroups[confluenceGroupName];
                XDoc groupDoc = new XDoc(GroupXMLTagName);
                groupDoc.Attr(ConfluenceGroupNameXMLAttributeName, confluenceGroupName);
                groupDoc.Attr(DekiGroupNameXMLAttributeName, groupInfo.DekiGroupName);
                doc.Add(groupDoc);
            }
            doc.Save(ConvertedUsersAndGroupsFileName);
        }
Пример #2
0
        private void LoadUsersAndGroupsFromXML()
        {
            WriteLineToConsole("Reading groups and users from " + ConvertedUsersAndGroupsFileName);
            Dictionary<string, string> readedDekiUsers = new Dictionary<string, string>();
            XDoc doc = XDocFactory.LoadFrom(ConvertedUsersAndGroupsFileName, MimeType.XML);
            foreach (XDoc user in doc["//" + UserXMLTagName])
            {
                string confluenceUserName = user["@" + ConfluenceUserNameXMLAttributeName].AsText;
                string dekiName = user["@" + DekiUserNameXMLAttributeName].AsText;

                if ((confluenceUserName == null) && (dekiName == null))
                {
                    WriteLineToConsole("Invalid XML attributes in " + ConvertedUsersAndGroupsFileName);
                    WriteLineToConsole(user.ToString());
                    continue;
                }

                if (confluenceUserName == null)
                {
                    WriteLineToConsole(ConfluenceUserNameXMLAttributeName + " not specified for " +
                        dekiName + " in " + ConvertedUsersAndGroupsFileName + ". Record skiped.");
                    continue;
                }

                if (dekiName == null)
                {
                    WriteLineToConsole(DekiUserNameXMLAttributeName + " not specified for " +
                        confluenceUserName + " in " + ConvertedUsersAndGroupsFileName + ". Record skiped.");
                    continue;
                }

                if (readedDekiUsers.ContainsKey(dekiName.ToLower()))
                {
                    WriteLineToConsole("Repeating entry of Deki user \"" + dekiName + "\". Record skiped.");
                    continue;
                }

                DreamMessage dekiUserMessage = _dekiPlug.At("users", "=" + Utils.DoubleUrlEncode(dekiName)).GetAsync().Wait();
                if (dekiUserMessage.Status == DreamStatus.NotFound)
                {
                    WriteLineToConsole("Deki user \"" + dekiName + "\" is specified in " + ConvertedUsersAndGroupsFileName +
                        " but not exists in Deki. New user created.");
                    continue;
                }
                XDoc dekiUserDoc = dekiUserMessage.AsDocument();
                int dekiUserId = dekiUserDoc["@id"].AsInt.Value;

                string newPassword = Guid.NewGuid().ToString();

                DreamMessage pass = DreamMessage.Ok(MimeType.TEXT, newPassword);
                DreamMessage res = _dekiPlug.At("users", dekiUserId.ToString(), "password").PutAsync(pass).Wait();
                if (res.Status != DreamStatus.Ok)
                {
                    WriteLineToConsole("Error converting user \"" + confluenceUserName + "\"");
                    WriteLineToLog("Confluence user name: " + confluenceUserName);
                    WriteLineToLog("Deki user name: " + dekiName);
                    WriteErrorResponse(res);
                    continue;
                }

                string[] userGroupNames = new string[0];
                bool userExistsInConfluence = _confluenceService.HasUser(confluenceUserName);
                if (userExistsInConfluence)
                {
                    userGroupNames = _confluenceService.GetUserGroups(confluenceUserName);
                }
                else
                {
                    WriteLineToConsole("Confluence user name \"" + confluenceUserName +
                        "\" specified in " + ConvertedUsersAndGroupsFileName + " but not exists in Confluence.");
                }
                ACConverterUserInfo userInfo = new ACConverterUserInfo(dekiName, newPassword, dekiUserId,
                    userGroupNames);
                if (_convertedUsers.ContainsKey(confluenceUserName.ToLower()))
                {
                    WriteLineToConsole("Repeating entry of user \"" + confluenceUserName + "\" into " + ConvertedUsersAndGroupsFileName +
                        ". Last record used.");
                }
                _convertedUsers[confluenceUserName.ToLower()] = userInfo;
                readedDekiUsers[dekiName.ToLower()] = confluenceUserName;
            }
            Dictionary<string, string> readedDekiGroups = new Dictionary<string, string>();
            foreach (XDoc group in doc["//" + GroupXMLTagName])
            {
                string confluenceGroupName = group["@" + ConfluenceGroupNameXMLAttributeName].AsText;
                string dekiGroupName = group["@" + DekiGroupNameXMLAttributeName].AsText;

                if ((confluenceGroupName == null) && (dekiGroupName == null))
                {
                    WriteLineToConsole("Invalid XML attributes in " + ConvertedUsersAndGroupsFileName);
                    WriteLineToConsole(group.AsText);
                    continue;
                }

                if (confluenceGroupName == null)
                {
                    WriteLineToConsole(ConfluenceGroupNameXMLAttributeName + " not specified for \"" +
                        dekiGroupName + "\" in " + ConvertedUsersAndGroupsFileName + ". Record skiped.");
                    continue;
                }

                if (dekiGroupName == null)
                {
                    WriteLineToConsole(DekiGroupNameXMLAttributeName + " not specified for \"" +
                        confluenceGroupName + "\" in " + ConvertedUsersAndGroupsFileName + ". Record skiped.");
                    continue;
                }

                if (readedDekiGroups.ContainsKey(dekiGroupName.ToLower()))
                {
                    WriteLineToConsole("Repeating entry of Deki group \"" + dekiGroupName + "\". Record skiped.");
                    continue;
                }

                dekiGroupName = dekiGroupName.Replace(" ", "_");
                DreamMessage dekiGroupMessage = _dekiPlug.At("groups", "=" + dekiGroupName).GetAsync().Wait();
                if (dekiGroupMessage.Status == DreamStatus.NotFound)
                {
                    WriteLineToConsole("Deki group \"" + dekiGroupName + "\" is specified in " + ConvertedUsersAndGroupsFileName +
                        " but not exists in Deki. New group created.");
                    continue;
                }
                XDoc dekiGroupDoc = dekiGroupMessage.AsDocument();
                int dekiGroupId = dekiGroupDoc["@id"].AsInt.Value;

                ACConverterGroupInfo groupInfo = new ACConverterGroupInfo(confluenceGroupName,
                    dekiGroupName, dekiGroupId);
                if (_convertedUsers.ContainsKey(confluenceGroupName.ToLower()))
                {
                    WriteLineToConsole("Repeating entry of group \"" + confluenceGroupName + "\" into " + ConvertedUsersAndGroupsFileName +
                        ". Last record used.");
                }
                _convertedGroups[confluenceGroupName.ToLower()] = groupInfo;
            }
            WriteLineToConsole("Users and groups readed!");
        }
Пример #3
0
        private void MoveGroups()
        {
            DreamMessage msg = _dekiPlug.At("groups").With("limit", int.MaxValue).GetAsync().Wait();

            if (msg.Status != DreamStatus.Ok)
            {
                WriteLineToConsole("Error while receiving groups from Deki. Groups not converted.");
                WriteErrorResponse(msg);
                return;
            }

            Dictionary <string, string> dekiGroups = new Dictionary <string, string>();

            XDoc groupsDoc = msg.AsDocument();

            foreach (XDoc groupDoc in groupsDoc["//group"])
            {
                string dekiGroupName = groupDoc["groupname"].AsText;
                dekiGroups[dekiGroupName.ToLower()] = null;
            }

            string[] confluenceGroupNames = _confluenceService.GetGroups();

            foreach (string confluenceGroupName in confluenceGroupNames)
            {
                string dekiGroupName;
                if (!_convertedGroups.ContainsKey(confluenceGroupName.ToLower()))
                {
                    int groupNum = 0;
                    dekiGroupName = confluenceGroupName;
                    while (dekiGroups.ContainsKey(dekiGroupName.ToLower()))
                    {
                        groupNum++;
                        dekiGroupName = confluenceGroupName + groupNum.ToString();
                    }
                    if (dekiGroupName != confluenceGroupName)
                    {
                        WriteLineToConsole("Confluence group \"" + confluenceGroupName + "\" converted as \"" + dekiGroupName + "\" becouse of existing same group in Deki");
                    }

                    XDoc newGroupDoc = new XDoc("group");
                    newGroupDoc.Elem("name", dekiGroupName)
                    .Start("users");

                    foreach (ACConverterUserInfo convertedUser in _convertedUsers.Values)
                    {
                        if (Array.IndexOf(convertedUser.ConfluenceUserGroupNames, confluenceGroupName) >= 0)
                        {
                            newGroupDoc.Start("user").Attr("id", convertedUser.DekiUserId).End();
                        }
                    }

                    newGroupDoc.End();

                    Log.DebugFormat("Creating group: {0}", dekiGroupName);

                    DreamMessage res = _dekiPlug.At("groups").PostAsync(newGroupDoc).Wait();
                    if (res.Status != DreamStatus.Ok)
                    {
                        WriteLineToLog("Error converting group \"" + confluenceGroupName + "\"");
                        WriteErrorResponse(res);
                        WriteErrorRequest(newGroupDoc);
                        continue;
                    }

                    XDoc resGroupsDoc   = res.AsDocument();
                    int  newDekiGroupId = resGroupsDoc["@id"].AsInt.Value;

                    ACConverterGroupInfo convertedGroup =
                        new ACConverterGroupInfo(confluenceGroupName, dekiGroupName, newDekiGroupId);
                    _convertedGroups[confluenceGroupName.ToLower()] = convertedGroup;
                }
                else
                {
                    //This group already converted during previous ACConverter start
                    dekiGroupName = _convertedGroups[confluenceGroupName.ToLower()].DekiGroupName;

                    XDoc usersDoc = new XDoc("users");
                    foreach (ACConverterUserInfo convertedUser in _convertedUsers.Values)
                    {
                        if (Array.IndexOf(convertedUser.ConfluenceUserGroupNames, confluenceGroupName) >= 0)
                        {
                            usersDoc.Start("user").Attr("id", convertedUser.DekiUserId).End();
                        }
                    }
                    DreamMessage res = _dekiPlug.At("groups", _convertedGroups[confluenceGroupName.ToLower()].DekiGroupId.ToString(),
                                                    "users").PutAsync(usersDoc).Wait();
                    if (res.Status != DreamStatus.Ok)
                    {
                        WriteLineToLog("Error converting group's users");
                        WriteErrorResponse(res);
                        WriteErrorRequest(usersDoc);
                    }
                }
            }
        }
        private void MovePermissions(ACConverterPageInfo pageInfo)
        {
            RemotePermission[] pagePermissions = _confluenceService.GetPagePermissions(pageInfo.ConfluencePage.id);

            //Change permissions list according to parent page permissoins
            List <RemotePermission> newPermissions = new List <RemotePermission>();

            foreach (RemotePermission permission in pageInfo.ConfluenceUsersWithViewPermissions.Values)
            {
                newPermissions.Add(permission);
            }
            foreach (RemotePermission permission in pagePermissions)
            {
                if (permission.lockType == ConfluenceEditPermissionName)
                {
                    if ((pageInfo.ConfluenceUsersWithViewPermissions.Count == 0) || (pageInfo.ConfluenceUsersWithViewPermissions.ContainsKey(permission.lockedBy.ToLower())))
                    {
                        newPermissions.Add(permission);
                    }
                }
            }

            pagePermissions = newPermissions.ToArray();

            if (pagePermissions.Length == 0)
            {
                return;
            }

            string dekiRestriction;

            //Possible two entry of one user or group in pagePermissions.
            //As View permission and as Edit permission for this group/user.
            //To prevent repeated addition to Deki in this dictionary stored true
            //if permission to this user/group added to Deki.
            Dictionary <string, bool> permissionAddedToDeki = new Dictionary <string, bool>();

            Dictionary <string, bool> userHaveWritePermission = new Dictionary <string, bool>();

            if (_compatibleConvertUserPermissions)
            {
                bool onlyWriteRestrictions = true;
                foreach (RemotePermission permission in pagePermissions)
                {
                    if (permission.lockType == ConfluenceViewPermissionName)
                    {
                        onlyWriteRestrictions = false;
                        break;
                    }
                }
                if (onlyWriteRestrictions)
                {
                    //If there no view restrictions on this Confluence page set Semi-Public restrictions to Deki users
                    dekiRestriction = "Semi-Public";
                }
                else
                {
                    //If there is view restrictions on this Confluence page to allow users/groups with View and Edit
                    //restrictions view this page set Private restriction in Deki.
                    //Users without Edit permission but with View permission in Confluence can edit this page in Deki.
                    dekiRestriction = "Private";
                }
            }
            else
            {
                dekiRestriction = "Private";

                foreach (RemotePermission permission in pagePermissions)
                {
                    if (permission.lockType == ConfluenceEditPermissionName)
                    {
                        userHaveWritePermission[permission.lockedBy.ToLower()] = true;
                    }
                    else
                    {
                        if (!userHaveWritePermission.ContainsKey(permission.lockedBy.ToLower()))
                        {
                            userHaveWritePermission[permission.lockedBy.ToLower()] = false;
                        }
                    }
                }
            }

            XDoc securityDoc = new XDoc("security")
                               .Start("permissions.page")
                               .Elem("restriction", dekiRestriction)
                               .End()
                               .Start("grants");

            foreach (RemotePermission permission in pagePermissions)
            {
                if (permissionAddedToDeki.ContainsKey(permission.lockedBy.ToLower()))
                {
                    continue;
                }

                securityDoc
                .Start("grant")
                .Start("permissions");
                if (_compatibleConvertUserPermissions)
                {
                    securityDoc.Elem("role", "Contributor");
                }
                else
                {
                    bool haveWritePermission = false;
                    userHaveWritePermission.TryGetValue(permission.lockedBy.ToLower(), out haveWritePermission);
                    if (haveWritePermission)
                    {
                        securityDoc.Elem("role", "Contributor");
                    }
                    else
                    {
                        securityDoc.Elem("role", "Viewer");
                    }
                }
                securityDoc.End();

                //Detect if this is group or user permission
                ACConverterUserInfo dekiUser;
                if (_convertedUsers.TryGetValue(permission.lockedBy.ToLower(), out dekiUser))
                {
                    securityDoc.Start("user").Attr("id", dekiUser.DekiUserId).End();
                }
                else
                {
                    ACConverterGroupInfo dekiGroup = null;
                    if (_convertedGroups.TryGetValue(permission.lockedBy.ToLower(), out dekiGroup))
                    {
                        securityDoc.Start("group").Attr("id", dekiGroup.DekiGroupId).End();
                    }
                    else
                    {
                        WriteLineToConsole("Page " + pageInfo.ConfluencePage.title + " locked by " + permission.lockedBy +
                                           " that is not a user and not a group. Restriction ignored.");
                    }
                }
                securityDoc.End();

                permissionAddedToDeki[permission.lockedBy.ToLower()] = true;
            }

            securityDoc.End();

            DreamMessage res = _dekiPlug.At("pages", pageInfo.DekiPageId.ToString(), "security").PutAsync(securityDoc).Wait();

            if (res.Status != DreamStatus.Ok)
            {
                WriteLineToLog("Error converting permissions");
                WriteErrorResponse(res);
                WriteErrorRequest(securityDoc);
            }
        }
Пример #5
0
        private void LoadUsersAndGroupsFromXML()
        {
            WriteLineToConsole("Reading groups and users from " + ConvertedUsersAndGroupsFileName);
            Dictionary <string, string> readedDekiUsers = new Dictionary <string, string>();
            XDoc doc = XDocFactory.LoadFrom(ConvertedUsersAndGroupsFileName, MimeType.XML);

            foreach (XDoc user in doc["//" + UserXMLTagName])
            {
                string confluenceUserName = user["@" + ConfluenceUserNameXMLAttributeName].AsText;
                string dekiName           = user["@" + DekiUserNameXMLAttributeName].AsText;

                if ((confluenceUserName == null) && (dekiName == null))
                {
                    WriteLineToConsole("Invalid XML attributes in " + ConvertedUsersAndGroupsFileName);
                    WriteLineToConsole(user.ToString());
                    continue;
                }

                if (confluenceUserName == null)
                {
                    WriteLineToConsole(ConfluenceUserNameXMLAttributeName + " not specified for " +
                                       dekiName + " in " + ConvertedUsersAndGroupsFileName + ". Record skiped.");
                    continue;
                }

                if (dekiName == null)
                {
                    WriteLineToConsole(DekiUserNameXMLAttributeName + " not specified for " +
                                       confluenceUserName + " in " + ConvertedUsersAndGroupsFileName + ". Record skiped.");
                    continue;
                }

                if (readedDekiUsers.ContainsKey(dekiName.ToLower()))
                {
                    WriteLineToConsole("Repeating entry of Deki user \"" + dekiName + "\". Record skiped.");
                    continue;
                }

                DreamMessage dekiUserMessage = _dekiPlug.At("users", "=" + Utils.DoubleUrlEncode(dekiName)).GetAsync().Wait();
                if (dekiUserMessage.Status == DreamStatus.NotFound)
                {
                    WriteLineToConsole("Deki user \"" + dekiName + "\" is specified in " + ConvertedUsersAndGroupsFileName +
                                       " but not exists in Deki. New user created.");
                    continue;
                }
                XDoc dekiUserDoc = dekiUserMessage.AsDocument();
                int  dekiUserId  = dekiUserDoc["@id"].AsInt.Value;

                string newPassword = Guid.NewGuid().ToString();

                DreamMessage pass = DreamMessage.Ok(MimeType.TEXT, newPassword);
                DreamMessage res  = _dekiPlug.At("users", dekiUserId.ToString(), "password").PutAsync(pass).Wait();
                if (res.Status != DreamStatus.Ok)
                {
                    WriteLineToConsole("Error converting user \"" + confluenceUserName + "\"");
                    WriteLineToLog("Confluence user name: " + confluenceUserName);
                    WriteLineToLog("Deki user name: " + dekiName);
                    WriteErrorResponse(res);
                    continue;
                }

                string[] userGroupNames         = new string[0];
                bool     userExistsInConfluence = _confluenceService.HasUser(confluenceUserName);
                if (userExistsInConfluence)
                {
                    userGroupNames = _confluenceService.GetUserGroups(confluenceUserName);
                }
                else
                {
                    WriteLineToConsole("Confluence user name \"" + confluenceUserName +
                                       "\" specified in " + ConvertedUsersAndGroupsFileName + " but not exists in Confluence.");
                }
                ACConverterUserInfo userInfo = new ACConverterUserInfo(dekiName, newPassword, dekiUserId,
                                                                       userGroupNames);
                if (_convertedUsers.ContainsKey(confluenceUserName.ToLower()))
                {
                    WriteLineToConsole("Repeating entry of user \"" + confluenceUserName + "\" into " + ConvertedUsersAndGroupsFileName +
                                       ". Last record used.");
                }
                _convertedUsers[confluenceUserName.ToLower()] = userInfo;
                readedDekiUsers[dekiName.ToLower()]           = confluenceUserName;
            }
            Dictionary <string, string> readedDekiGroups = new Dictionary <string, string>();

            foreach (XDoc group in doc["//" + GroupXMLTagName])
            {
                string confluenceGroupName = group["@" + ConfluenceGroupNameXMLAttributeName].AsText;
                string dekiGroupName       = group["@" + DekiGroupNameXMLAttributeName].AsText;

                if ((confluenceGroupName == null) && (dekiGroupName == null))
                {
                    WriteLineToConsole("Invalid XML attributes in " + ConvertedUsersAndGroupsFileName);
                    WriteLineToConsole(group.AsText);
                    continue;
                }

                if (confluenceGroupName == null)
                {
                    WriteLineToConsole(ConfluenceGroupNameXMLAttributeName + " not specified for \"" +
                                       dekiGroupName + "\" in " + ConvertedUsersAndGroupsFileName + ". Record skiped.");
                    continue;
                }

                if (dekiGroupName == null)
                {
                    WriteLineToConsole(DekiGroupNameXMLAttributeName + " not specified for \"" +
                                       confluenceGroupName + "\" in " + ConvertedUsersAndGroupsFileName + ". Record skiped.");
                    continue;
                }

                if (readedDekiGroups.ContainsKey(dekiGroupName.ToLower()))
                {
                    WriteLineToConsole("Repeating entry of Deki group \"" + dekiGroupName + "\". Record skiped.");
                    continue;
                }

                dekiGroupName = dekiGroupName.Replace(" ", "_");
                DreamMessage dekiGroupMessage = _dekiPlug.At("groups", "=" + dekiGroupName).GetAsync().Wait();
                if (dekiGroupMessage.Status == DreamStatus.NotFound)
                {
                    WriteLineToConsole("Deki group \"" + dekiGroupName + "\" is specified in " + ConvertedUsersAndGroupsFileName +
                                       " but not exists in Deki. New group created.");
                    continue;
                }
                XDoc dekiGroupDoc = dekiGroupMessage.AsDocument();
                int  dekiGroupId  = dekiGroupDoc["@id"].AsInt.Value;

                ACConverterGroupInfo groupInfo = new ACConverterGroupInfo(confluenceGroupName,
                                                                          dekiGroupName, dekiGroupId);
                if (_convertedUsers.ContainsKey(confluenceGroupName.ToLower()))
                {
                    WriteLineToConsole("Repeating entry of group \"" + confluenceGroupName + "\" into " + ConvertedUsersAndGroupsFileName +
                                       ". Last record used.");
                }
                _convertedGroups[confluenceGroupName.ToLower()] = groupInfo;
            }
            WriteLineToConsole("Users and groups readed!");
        }
Пример #6
0
        private void MoveGroups()
        {
            DreamMessage msg = _dekiPlug.At("groups").With("limit", int.MaxValue).GetAsync().Wait();
            if (msg.Status != DreamStatus.Ok)
            {
                WriteLineToConsole("Error while receiving groups from Deki. Groups not converted.");
                WriteErrorResponse(msg);
                return;
            }

            Dictionary<string, string> dekiGroups = new Dictionary<string, string>();

            XDoc groupsDoc = msg.AsDocument();
            foreach (XDoc groupDoc in groupsDoc["//group"])
            {
                string dekiGroupName = groupDoc["groupname"].AsText;
                dekiGroups[dekiGroupName.ToLower()] = null;
            }

            string[] confluenceGroupNames = _confluenceService.GetGroups();

            foreach (string confluenceGroupName in confluenceGroupNames)
            {
                string dekiGroupName;
                if (!_convertedGroups.ContainsKey(confluenceGroupName.ToLower()))
                {
                    int groupNum = 0;
                    dekiGroupName = confluenceGroupName;
                    while (dekiGroups.ContainsKey(dekiGroupName.ToLower()))
                    {
                        groupNum++;
                        dekiGroupName = confluenceGroupName + groupNum.ToString();
                    }
                    if (dekiGroupName != confluenceGroupName)
                    {
                        WriteLineToConsole("Confluence group \"" + confluenceGroupName + "\" converted as \"" + dekiGroupName + "\" becouse of existing same group in Deki");
                    }

                    XDoc newGroupDoc = new XDoc("group");
                    newGroupDoc.Elem("name", dekiGroupName)
                        .Start("users");

                    foreach (ACConverterUserInfo convertedUser in _convertedUsers.Values)
                    {
                        if (Array.IndexOf(convertedUser.ConfluenceUserGroupNames, confluenceGroupName) >= 0)
                        {
                            newGroupDoc.Start("user").Attr("id", convertedUser.DekiUserId).End();
                        }
                    }

                    newGroupDoc.End();

                    Log.DebugFormat("Creating group: {0}", dekiGroupName);

                    DreamMessage res = _dekiPlug.At("groups").PostAsync(newGroupDoc).Wait();
                    if (res.Status != DreamStatus.Ok)
                    {
                        WriteLineToLog("Error converting group \"" + confluenceGroupName + "\"");
                        WriteErrorResponse(res);
                        WriteErrorRequest(newGroupDoc);
                        continue;
                    }

                    XDoc resGroupsDoc = res.AsDocument();
                    int newDekiGroupId = resGroupsDoc["@id"].AsInt.Value;

                    ACConverterGroupInfo convertedGroup =
                        new ACConverterGroupInfo(confluenceGroupName, dekiGroupName, newDekiGroupId);
                    _convertedGroups[confluenceGroupName.ToLower()] = convertedGroup;
                }
                else
                {
                    //This group already converted during previous ACConverter start
                    dekiGroupName = _convertedGroups[confluenceGroupName.ToLower()].DekiGroupName;

                    XDoc usersDoc = new XDoc("users");
                    foreach (ACConverterUserInfo convertedUser in _convertedUsers.Values)
                    {
                        if (Array.IndexOf(convertedUser.ConfluenceUserGroupNames, confluenceGroupName) >= 0)
                        {
                            usersDoc.Start("user").Attr("id", convertedUser.DekiUserId).End();
                        }
                    }
                    DreamMessage res = _dekiPlug.At("groups", _convertedGroups[confluenceGroupName.ToLower()].DekiGroupId.ToString(),
                        "users").PutAsync(usersDoc).Wait();
                    if (res.Status != DreamStatus.Ok)
                    {
                        WriteLineToLog("Error converting group's users");
                        WriteErrorResponse(res);
                        WriteErrorRequest(usersDoc);
                    }
                }
            }
        }