Credential Provider v2 API - Feststellen, dass Other User Tile ausgewählt



  • Hallo Leute,

    ich bin dabei einen Credential Provider zu schreiben. Das unter Nutzung der Microsoft Beispiele und der MSDN.
    Für Windows 8 gibt es ja das ICredentialProviderCredential2 Interface. Dieses bietet die Funktion: GetUserSID .
    Jetzt sagt die Dokumentation, wenn mein CredentialProvider in der Kachel für "anderer Benutzer" auftauchen soll, muss ich dort eine Null-SID und S_FALSE zurück geben.
    Ich habe das ausprobiert und das funktioniert auch. In meinem Test habe ich das dann aber generell zurück gegeben.
    Die Dokumentation hat auch ein Codesnippet als Beispiel, das leider im Beispiel des CredentialProviders nicht auftaucht und das so aussieht:

    // Gets the SID of the user corresponding to the credential.
    HRESULT CSampleCredential::GetUserSid(__deref_out PWSTR *ppszSid)
    {
        *ppszSid = nullptr;
        HRESULT hr = E_UNEXPECTED;
    
        // _pszUserSid is a private member of CSampleCredential
        if (_pszUserSid != nullptr)
        {
            // ppszSid will be freed by Logon UI
            hr = SHStrDupW(_pszUserSid, ppszSid);
        }
        // Return S_FALSE with a null SID in ppszSid for the
        // credential to be associated with an anonymous user tile.
        else if (_fIsOtherUserTile)
        {
            hr = S_FALSE;
        }
    
        return hr;
    }
    

    Hier gibt es nun das Flag "_fIsOtherUserTile". Es ist nett von den Leuten von Microsoft, dass sie dieses Flag im Beispiel anwenden, leider haben sie vergessen mir zu sagen, wo ich es denn setzen kann 🙄 .
    Könnt ihr mir sagen, welches Interface, oder welchen Parameter ich prüfen muss, um festzustellen, dass die "anderer Benutzer" Kachel ausgewählt wurde?

    Vielen Dank

    euer Khali

    PS: Die Dokumentationsquelle die ich hier zitiere ist folgende:
    go.microsoft.com/fwlink/p/?linkid=253508


  • Mod

    Müssen sie doch nicht. Es ist eben ein Beispiel...
    Es geht um die Frage wie sich Dein Provider verhalten soll. Also ist es eher ein "Konstante", die Du definierst.

    So verstehe ich das zumindest.



  • Ja, Sie müssen es im Beispiel nicht erklären, das ist richtig.
    Dennoch stehe ich leider vor dem Problem. Das Szenario sieht unter Windows 8 ja normalerweise so aus:
    Der Benutzer sendet den SAS (Strg + Alt + Entf) und hat dann eine Reihe von Benutzerkacheln zur Auswahl.
    Im Fall einer Domänenanmeldung in der Regel die des zuletzt angemeldeten Benutzers und die "anderer Benutzer" Kachel.
    Er wählt eine der beiden aus und kann dann auf "Anmeldeoptionen" den CredentialProvider auswählen, den er benutzen möchte.

    Nun taucht der Beispielcredentialprovider nur bei den normalen Benutzerkacheln auf, nicht aber in der "anderer Benutzer" Kachel. Mit den Anmerkungen aus dem Änderungsdokument, taucht er in der "anderer Benutzer" Kachel auf, aber eben in den normalen Benutzerkacheln nicht, es sei denn, ich würde das Flag "_fIsOtherUserTile" nicht statisch setzen, sondern zur Laufzeit, wenn diese Kachel wirklich ausgewählt wird.
    Wobei das gerade der Struktur der Auswahl geschuldet ist. Selbst wenn ich "_fIsOtherUserTile" auf true setze, erreiche ich in 90% der Fälle das else if nicht, da für gewöhnlich zuerst der zuletzt angemeldete User angezeigt wird. Das ist jedoch eine Einstellungssache.
    Dennoch ist es so, wenn ich dann zurück gehe und mich als anderer User anmelden möchte, taucht mein CredentialProvider in dieser Kachel nicht auf, was ich mir aber wünschen würde.
    Deswegen habe ich im Moment das Probelm, das mein CredentialProvider entweder bei den normalen Benutzern angezeigt wird, oder bei der "anderer Benutzer" Kachel, nie jedoch in beiden.
    Trotzdem hast du mich schon einen kleinen Schritt weiter gebracht, aber noch nicht zum Ziel.
    Sprich wenn noch jemand eine Idee hat, wäre ich sehr dankbar.

    Bis dahin


  • Mod

    Nach meinem Verständnis des neuen Interfaces ist ein Provider eben auch nur das eine oder das andere. Soweit ich das Interface verstehe wird Dein Provider aufgerufen und eben anhand des Rückgabewertes in die eine oder andere Klasse gepackt.

    Aber wie ich nach einer Recherche im Netz festgestellt habe, gibt es dazu keine "publike" Antwort.

    Wenn Du da wirklich in die Tiefe abtauchen willst solltest Du evtl. einen Support Case bei Microsoft aufmachen. Wen DU ein MSDN Abo hast, dann hast Du auch IMHO 2 Anfragen frei.



  • Das Beispiel im Änderungsdokument suggeriert ja, dass es geht. Danke für den Tip mit dem Microsoft Ticket, mal sehen ob ich das nutzen kann.
    Ich danke dir aber für deine Mühe Martin.



  • Mal ganz blöd gefragt: kann man nicht einfach zwei Provider registrieren, einen "normalen" und einen "other user"?



  • Das ist zu einfach! 😃

    Das geht mit Sicherheit, ist aber die wirklich unelegante Lösung. Wenn ich aber nichts anderes finde, dann mach ich das so. Da hätte man auch selbst drauf kommen können.

    Also danke für zumindest diesen Rettungsanker.


  • Mod

    Khalidjian schrieb:

    Das Beispiel im Änderungsdokument suggeriert ja, dass es geht. Danke für den Tip mit dem Microsoft Ticket, mal sehen ob ich das nutzen kann.
    Ich danke dir aber für deine Mühe Martin.

    Nö. Für mich nicht.
    Es sugerriert nur, dass Sie kein #ifdef genommen haben, was das ganze klar gemacht hätte... 😉 oder eine const Definition.

    Die Frage ist, wie oft wird die Funktion aufgerufen. Vermutlich eben nur einmal. Wie also soll man also "alternativ" etwas zurück geben.
    Insofern hat hustbaer vermutlich recht mit seinem Vorschlag..



  • Also mit der Funktion GetAccountOptions von ICredentialProviderUserArray lässt sich feststellen, ob die "Anderer Benutzer" Kachel angezeigt wird.
    Das müsste sich benutzen lassen um es dynamisch zu machen. Ich muss nur noch herausbekommen wo 😃

    Siehe dazu MSDN:
    https://msdn.microsoft.com/en-us/library/windows/desktop/hh706924%28v=vs.85%29.aspx

    Edit: ich bekomme von GetAccountOptions keinen Fehler zurück, aber ein undocumentiertes Value in der Umgebung der Domäne hier, nämlich "3" Während es tatsächlich NONE(0) ist, wenn ich es auf einem nicht Domänenrechner ausprobiere.



  • Mir ist übrigens jetzt bei meinen Lokalen Tests aufgefallen, dass auch hier nur die 1. Kachel meinen Credentialprovider hat, der zweite Lokale Nutzer hat ihn auch nicht.
    Das überzeugt mich davon, dass es nicht gewollt ist, einen zweiten CredentialProvider zu registrieren, denn Microsoft kann nicht erwarten, dass man für jeden lokalen Nutzer einen CredentialProvider registriert. Zumindest wenn sie einen funken Vernunft haben und in der Datei ist ja auch von user(s) die Rede, jetzt muss ich nur herausbekommen, wie ich bei "GetUserSID" mehr als eine SID zurück gebe, bzw. erst einmal dort hin bekomme, dass ich mehr als einem User meinen CredentialProviderCredential nutzen lassen will.



  • Lange hat es gedauert, aber ich habe eine Lösung gefunden. Der Grundstock ist hier beschrieben: http://stackoverflow.com/questions/31241665/how-to-show-icredentialprovidercredentialv2-on-more-than-one-user-tile-on-the-ot .

    Wenn man sich am Beispiel orientiert, muss man in EnumerateCredentials ein Array con ICredentialProviderCredential erstellen, oder dem was man benutzen möchte.
    Dieses Array muss man um eins vergrößern wenn ICredentialProviderUserArray::GetAccountOptions != CPAO_NONE ist und dafür ein weiteres Credential erstellen, indem dann bei "GetUserSID" hr =S_FALSE zurück gegeben wird.

    Vielen Dank euch!



  • Könntest Du den Code posten?
    Danke



  • Das kann ich leider nicht mehr. Das Projekt ist nicht mehr unter meinen fittichen, weshalb es nicht mehr mein Code ist. Den ich Posten könnte. Außerdem muss ich gestehen, da man an vielen stellen Kleinigkeiten machen muss, weiß ich auch nicht genau, wie ich es ordentlich posten kann, ohne zu große Verwirrung zu stiften.

    Hast du es denn schon geschafft, dass dein Credentialprovider bei mehr als einem User angezeigt wird?



  • Khalidjian schrieb:

    Hast du es denn schon geschafft, dass dein Credentialprovider bei mehr als einem User angezeigt wird?

    Leider nein. Die beiden Funktionen die du angespreochen hast schauen in meinem Sample so aus. Wenn ich in GetUserSid gleich S_FALSE zurückgebe (Zeile 5), dann kommt der Provider bei "Anderer Benutzer", nicht aber bei namentlich bekannten Benutzern. Workaround wären halt zwei Provider ... ist aber nicht schön.

    // Gets the SID of the user corresponding to the credential.
    HRESULT CSampleCredential::GetUserSid(_Outptr_result_nullonfailure_ PWSTR *ppszSid)
    {
        *ppszSid = nullptr;
        //return S_FALSE
    
        HRESULT hr = E_UNEXPECTED;
        if (_pszUserSid != nullptr)
        {
    		hr = SHStrDupW(_pszUserSid, ppszSid);
        }
        // Return S_FALSE with a null SID in ppszSid for the
        // credential to be associated with an empty user tile.
    	else
    	{
    		*ppszSid = nullptr;
    		hr = S_FALSE;
    	}
    
        return hr;
    }
    
    HRESULT CSampleProvider::_EnumerateCredentials()
    {
    	HRESULT hr = E_UNEXPECTED;
        if (_pCredProviderUserArray != nullptr)
        {		
    		DWORD dwUserCount;
            _pCredProviderUserArray->GetCount(&dwUserCount);
            if (dwUserCount > 0)
            {
                ICredentialProviderUser *pCredUser;
                hr = _pCredProviderUserArray->GetAt(0, &pCredUser);
                if (SUCCEEDED(hr))
                {
                    _pCredential = new(std::nothrow) CSampleCredential();
                    if (_pCredential != nullptr)
                    {
                        hr = _pCredential->Initialize(_cpus, s_rgCredProvFieldDescriptors, s_rgFieldStatePairs, pCredUser);
                        if (FAILED(hr))
                        {
                            _pCredential->Release();
                            _pCredential = nullptr;
                        }
                    }
                    else
                    {
                        hr = E_OUTOFMEMORY;
                    }
                    pCredUser->Release();
                }
            }
        }
        return hr;
    }
    


  • Also der Knackpunkt ist in deinem zweiten Codebeispiel in Zeile 14:

    _pCredential = new(std::nothrow) CSampleCredential();
    

    Du musst im Header aus:

    CSampleCredential* _pCredential;
    

    ein Array/einen Vektor von Credentials machen. Du hast also nur einen Provider, der aber mehrere ICredentialProviderCredential Objekte zur Verfügung stellt.
    Dann musst du für jeden Nutzer in dwUserCount einmal ein Credential erzeugen:

    for(DWORD i = 0; i < dwUserCount; ++i)
            {
                ICredentialProviderUser *pCredUser;
                hr = _pCredProviderUserArray->GetAt(i, &pCredUser);
                if (SUCCEEDED(hr))
                {
                    _pCredential->push_back(new(std::nothrow) CSampleCredential());
                    if (_pCredential->at(i) != nullptr)
                    {
                        hr = _pCredential->at(i)->Initialize(_cpus, s_rgCredProvFieldDescriptors, s_rgFieldStatePairs, pCredUser);
                        if (FAILED(hr))
                        {
                            _pCredential->Release();
                            _pCredential = nullptr;
                        }
                    }
                    else
                    {
                        hr = E_OUTOFMEMORY;
                    }
                    pCredUser->Release();
                }
            }
    

    Das oben ist nur wie es vom Prinzip her am Ende ungefähr aussehen müsste. Um das sauber zu machen, musst du dir natürlich überlegen ob du ein Array von ICredentialProviderCredentials möchtest oder einen Vektor oder eine andere Art von Container.
    Ich hatte es mit einem std::vektor gelöst. Der Zugriff im Interface am Rechner hatte aber noch einmal ein paar Tücken. Da musst du schauen, wo du überall auf das Credential zugreifst und wie du die Ordnung beibehälst, ebsonders, wenn sich die Anzahl während des Logins ändern könnte. Das war bei uns dann auch der Fall.



  • danke, das schau ich mir mal genau an


Anmelden zum Antworten