還原已刪除的物件
Windows Server 2003 包含「還原已刪除的物件」功能。
若要啟用已刪除的物件還原,網域中的至少一個域控制器必須在 Windows Server 2003 或更新版本的 Windows 上執行。 根據預設,只有網域系統管理員可以還原已刪除的對象,不過這可以委派給其他人。
下列限制適用於還原已刪除的物件:
- 物件的墓碑存留期已過期時,無法還原對象,因為當墓碑存留期過期時,物件就會永久刪除。
- 無法還原存在於命名內容根目錄的物件,例如網域或應用程式分割區。
- 無法還原架構物件。 不應該刪除架構物件。
- 您可以還原已刪除的容器,但刪除之前容器中已刪除的物件還原很困難,因為必須手動重新建構容器下的樹狀結構。
還原已刪除物件所需的許可權
刪除物件時,會保留物件安全性描述元。 雖然擁有者可從安全性描述元識別,但基於安全性考慮,只允許網域系統管理員還原已刪除的物件。 網域管理員可以授與許可權,以將刪除物件還原給其他使用者和群組,方法是授與使用者或群組 “Reanimate Tombstone” 控制訪問許可權。 命名內容根目錄會授與 「Reanimate Tombstone」 控制權限。 只有具有物件讀取訪問許可權的使用者及其屬性,才能在刪除對象之後讀取物件和可存取的屬性。
注意
授與使用者此許可權可能是安全性風險,因為它可能允許使用者還原可存取使用者通常無法存取之資源的帳戶物件。 藉由還原帳戶,使用者基本上會取得此帳戶的控制權,因為用戶必須在還原帳戶時,在帳戶上設定初始密碼。
若要完全還原已刪除的物件,用戶必須:
擁有或成為具有 「Reanimate Tombstone」 的群組成員控制訪問許可權。
對於需要更新的每個必要屬性,都有寫入許可權。
具有相對辨別名稱 (RDN) 的寫入許可權。
擁有需要更新之每個選擇性屬性的寫入許可權。
針對已還原對象的物件類別,在目的地容器上擁有子系建立許可權。
注意
還原 作業期間不會驗證 isDeleted 屬性。 兩者都不會驗證 「Deleted Objects」 容器上的 delete-child 許可權。
還原已刪除的物件
若要還原已刪除的物件,對象必須先位於 Deleted Objects 容器中。 如需擷取已刪除對象的詳細資訊,請參閱 擷取已刪除的物件。
當物件找到時,必須在單一LDAP作業中完成下列作業。 這需要搭配 LDAP_SERVER_SHOW_DELETED_OID 控件使用 ldap_modify_ext_s 函式。
- 拿掉 isDeleted 屬性值。 必須移除 isDeleted 屬性值,而不是設定為 FALSE。
- 取代物件的辨別名稱,使其移至已刪除的物件容器以外的容器。 這可以是一般可以包含 物件的任何容器。 您可以在已刪除物件的 lastKnownParent 屬性中找到物件上一個容器的辨別名稱。 只有在 Windows Server 2003 域控制器上刪除物件時,才會設定 lastKnownParent 屬性。 因此,lastKnownParent 屬性的內容可能不正確。
- 還原刪除期間清除之對象的強制屬性。
注意
物件還原時也可以設定 objectCategory 屬性,但並非必要。 如果未指定 objectCategory 值,則會使用物件 objectClass 的預設 objectCategory。
還原對象之後,就可以在刪除物件之前存取它。 此時,應該還原任何重要的選擇性屬性。 也必須還原目錄中其他物件之物件的任何參考。
作為安全性措施,用戶物件會在還原時停用。 還原選擇性屬性之後,必須啟用用戶物件,才能使用用戶物件。
如需詳細資訊和示範如何還原已刪除物件的程式代碼範例,請參閱下方的 RestoreDeletedObject 函式。
RestoreDeletedObject
下列 C++ 程式代碼範例示範如何還原已刪除的物件。
//***************************************************************************
//
// RestoreDeletedObject()
//
// Restores a deleted object.
//
// pwszDeletedDN - Contains the fully qualified distinguished name of the
// deleted object.
//
// pwszDestContainerDN - Contains the fully qualified distinguished name of
// the folder that the deleted object should be moved to.
//
// Returns S_OK if successful or an HRESULT or LDAP error code otherwise.
//
//***************************************************************************
HRESULT RestoreDeletedObject(LPCWSTR pwszDeletedDN, LPCWSTR pwszDestContainerDN)
{
if((NULL == pwszDeletedDN) || (NULL == pwszDestContainerDN))
{
return E_POINTER;
}
HRESULT hr = E_FAIL;
// Build the new distinguished name.
LPWSTR pwszNewDN = new WCHAR[lstrlenW(pwszDeletedDN) + lstrlenW(pwszDestContainerDN) + 1];
if(pwszNewDN)
{
wcscpy_s(pwszNewDN, pwszDeletedDN);
// Search for the first 0x0A character. This is the delimiter in the deleted object name.
LPWSTR pwszChar;
for(pwszChar = pwszNewDN; *pwszChar; pwszChar = CharNextW(pwszChar))
{
if(('\\' == *pwszChar) && ('0' == *(pwszChar + 1)) && ('A' == *(pwszChar + 2)))
{
break;
}
}
if(0 != *pwszChar)
{
// Truncate the name string at the delimiter.
*pwszChar = 0;
// Add the last known parent DN to complete the DN.
wcscat_s(pwszNewDN, L",");
wcscat_s(pwszNewDN, pwszDestContainerDN);
PLDAP ld;
// Initialize LDAP.
ld = ldap_init(NULL, LDAP_PORT);
if(NULL != ld)
{
ULONG ulRC;
ULONG version = LDAP_VERSION3;
// Set the LDAP version.
ulRC = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, (void*)&version);
if(LDAP_SUCCESS == ulRC)
{
// Establish a connection with the server.
ulRC = ldap_connect(ld, NULL);
if(LDAP_SUCCESS == ulRC)
{
// Bind to the LDAP server.
ulRC = ldap_bind_s(ld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
if(LDAP_SUCCESS == ulRC)
{
// Setup the new values.
LPWSTR rgNewVals[] = {pwszNewDN, NULL};
/*
Remove the isDeleted attribute. This cannot be set
to FALSE or the restore operation will not work.
*/
LDAPModW modIsDeleted = { LDAP_MOD_DELETE, L"isDeleted", NULL };
/*
Set the new DN, in effect, moving the deleted
object to where it resided before the deletion.
*/
LDAPModW modDN = { LDAP_MOD_REPLACE, L"distinguishedName", rgNewVals };
// Initialize the LDAPMod structure.
PLDAPModW ldapMods[] =
{
&modIsDeleted,
&modDN,
NULL
};
/*
Use the LDAP_SERVER_SHOW_DELETED_OID control to
modify deleted objects.
*/
LDAPControlW showDeletedControl;
showDeletedControl.ldctl_oid = LDAP_SERVER_SHOW_DELETED_OID_W;
showDeletedControl.ldctl_value.bv_len = 0;
showDeletedControl.ldctl_value.bv_val = NULL;
showDeletedControl.ldctl_iscritical = TRUE;
// Initialzie the LDAPControl structure
PLDAPControlW ldapControls[] = { &showDeletedControl, NULL };
/*
Modify the specified attributes. This must performed
in one step, which is why the LDAP APIs must be used
to restore a deleted object.
*/
ulRC = ldap_modify_ext_sW(ld, (PWCHAR)pwszDeletedDN, ldapMods, ldapControls, NULL);
if(LDAP_SUCCESS == ulRC)
{
hr = S_OK;
}
else if(LDAP_ALREADY_EXISTS == ulRC)
{
/*
An object already exists with the specified name
in the specified target container. At this point,
a new name must be selected.
*/
}
}
}
}
if(LDAP_SUCCESS != ulRC)
{
hr = ulRC;
OutputDebugString(ldap_err2string(ulRC));
}
// Release the LDAP session.
ldap_unbind(ld);
}
}
else
{
/*
If the end of the string is reached before the delimiter is found, just
end and fail.
*/
hr = E_INVALIDARG;
}
delete pwszNewDN;
}
else
{
hr = E_OUTOFMEMORY;
}
return hr;
}