LibreOffice Module extensions (master) 1
WinUserInfoBe.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10#include "WinUserInfoBe.hxx"
11
12#include <com/sun/star/beans/Optional.hpp>
13#include <comphelper/base64.hxx>
16#include <map>
19#include <officecfg/UserProfile.hxx>
20
21#include <Iads.h>
22#include <Adshlp.h>
23#include <Lmcons.h>
24#define SECURITY_WIN32
25#include <Security.h>
26
27#include <systools/win32/comtools.hxx>
28#include <systools/win32/oleauto.hxx>
29
30namespace extensions
31{
32namespace config
33{
34namespace WinUserInfo
35{
37{
38public:
40 virtual OUString GetGivenName() = 0;
41 virtual OUString GetSn() { return ""; }
42 virtual OUString GetFathersname() { return ""; }
43 virtual OUString GetInitials() { return ""; }
44 virtual OUString GetStreet() { return ""; }
45 virtual OUString GetCity() { return ""; }
46 virtual OUString GetState() { return ""; }
47 virtual OUString GetApartment() { return ""; }
48 virtual OUString GetPostalCode() { return ""; }
49 virtual OUString GetCountry() { return ""; }
50 virtual OUString GetOrganization() { return ""; }
51 virtual OUString GetPosition() { return ""; }
52 virtual OUString GetTitle() { return ""; }
53 virtual OUString GetHomePhone() { return ""; }
54 virtual OUString GetTelephoneNumber() { return ""; }
55 virtual OUString GetFaxNumber() { return ""; }
56 virtual OUString GetMail() { return ""; }
57};
58}
59}
60}
61
62namespace
63{
64constexpr OUStringLiteral givenname(u"givenname");
65constexpr OUStringLiteral sn(u"sn");
66constexpr char fathersname[]("fathersname");
67constexpr OUStringLiteral initials(u"initials");
68constexpr OUStringLiteral street(u"street");
69constexpr OUStringLiteral l(u"l");
70constexpr OUStringLiteral st(u"st");
71constexpr char apartment[]("apartment");
72constexpr OUStringLiteral postalcode(u"postalcode");
73constexpr OUStringLiteral c(u"c");
74constexpr OUStringLiteral o(u"o");
75constexpr char position[]("position");
76constexpr OUStringLiteral title(u"title");
77constexpr OUStringLiteral homephone(u"homephone");
78constexpr OUStringLiteral telephonenumber(u"telephonenumber");
79constexpr OUStringLiteral facsimiletelephonenumber(u"facsimiletelephonenumber");
80constexpr OUStringLiteral mail(u"mail");
81
82// Backend class implementing access to Active Directory user data. It caches its encoded data
83// in a configuration entry, to allow reusing it when user later doesn't have access to AD DC
84// (otherwise the user would get different data when connected vs not connected).
86{
87public:
88 ADsUserAccess()
89 {
90 try
91 {
92 sal::systools::CoInitializeGuard aCoInitializeGuard(COINIT_APARTMENTTHREADED);
93
94 sal::systools::COMReference<IADsADSystemInfo> pADsys(CLSID_ADSystemInfo, nullptr,
95 CLSCTX_INPROC_SERVER);
96
97 sal::systools::BStr sUserDN;
98 sal::systools::ThrowIfFailed(pADsys->get_UserName(&sUserDN), "get_UserName failed");
99 // If this user is an AD user, then without an active connection to the domain, all the
100 // above will succeed, and m_sUserDN will be correctly initialized, but the following
101 // call to ADsGetObject will fail, and we will attempt reading cached values.
102 m_sUserDN = sUserDN;
103 OUString sLdapUserDN = "LDAP://" + m_sUserDN;
104 sal::systools::COMReference<IADsUser> pUser;
105 sal::systools::ThrowIfFailed(ADsGetObject(o3tl::toW(sLdapUserDN.getStr()), IID_IADsUser,
106 reinterpret_cast<void**>(&pUser)),
107 "ADsGetObject failed");
108 // Fetch all the required information right now, when we know to have access to AD
109 // (later the connection may already be lost)
110 m_aMap[givenname] = Str(pUser, &IADsUser::get_FirstName);
111 m_aMap[sn] = Str(pUser, &IADsUser::get_LastName);
112 m_aMap[initials] = Str(pUser, L"initials");
113 m_aMap[street] = Str(pUser, L"streetAddress");
114 m_aMap[l] = Str(pUser, L"l");
115 m_aMap[st] = Str(pUser, L"st");
116 m_aMap[postalcode] = Str(pUser, L"postalCode");
117 m_aMap[c] = Str(pUser, L"co");
118 m_aMap[o] = Str(pUser, L"company");
119 m_aMap[title] = Str(pUser, &IADsUser::get_Title);
120 m_aMap[homephone] = Str(pUser, L"homePhone");
121 m_aMap[telephonenumber] = Str(pUser, L"TelephoneNumber");
122 m_aMap[facsimiletelephonenumber] = Str(pUser, L"facsimileTelephoneNumber");
123 m_aMap[mail] = Str(pUser, &IADsUser::get_EmailAddress);
124
125 CacheData();
126 }
127 catch (sal::systools::ComError&)
128 {
129 // Maybe we temporarily lost connection to AD; try to get cached data
130 GetCachedData();
131 }
132 }
133
134 virtual OUString GetGivenName() override { return m_aMap[givenname]; }
135 virtual OUString GetSn() override { return m_aMap[sn]; }
136 virtual OUString GetInitials() override { return m_aMap[initials]; }
137 virtual OUString GetStreet() override { return m_aMap[street]; }
138 virtual OUString GetCity() override { return m_aMap[l]; }
139 virtual OUString GetState() override { return m_aMap[st]; }
140 virtual OUString GetPostalCode() override { return m_aMap[postalcode]; }
141 virtual OUString GetCountry() override { return m_aMap[c]; }
142 virtual OUString GetOrganization() override { return m_aMap[o]; }
143 virtual OUString GetTitle() override { return m_aMap[title]; }
144 virtual OUString GetHomePhone() override { return m_aMap[homephone]; }
145 virtual OUString GetTelephoneNumber() override { return m_aMap[telephonenumber]; }
146 virtual OUString GetFaxNumber() override { return m_aMap[facsimiletelephonenumber]; }
147 virtual OUString GetMail() override { return m_aMap[mail]; }
148
149private:
150 typedef HRESULT (__stdcall IADsUser::*getstrfunc)(BSTR*);
151 static OUString Str(IADsUser* pUser, getstrfunc func)
152 {
153 sal::systools::BStr sBstr;
154 if (FAILED((pUser->*func)(&sBstr)))
155 return "";
156 return OUString(sBstr);
157 }
158 static OUString Str(IADsUser* pUser, const wchar_t* property)
159 {
160 sal::systools::BStr sBstrProp{ o3tl::toU(property) };
161 struct AutoVariant : public VARIANT
162 {
163 AutoVariant() { VariantInit(this); }
164 ~AutoVariant() { VariantClear(this); }
165 } varArr;
166 if (FAILED(pUser->GetEx(sBstrProp, &varArr)))
167 return "";
168 SAFEARRAY* sa = V_ARRAY(&varArr);
169 LONG nStart, nEnd;
170 if (FAILED(SafeArrayGetLBound(sa, 1, &nStart)) || FAILED(SafeArrayGetUBound(sa, 1, &nEnd)))
171 return "";
172 AutoVariant varItem;
173 for (LONG i = nStart; i <= nEnd; i++)
174 {
175 if (FAILED(SafeArrayGetElement(sa, &i, &varItem)))
176 continue;
177 if (varItem.vt == VT_BSTR)
178 return OUString(o3tl::toU(V_BSTR(&varItem)));
179 VariantClear(&varItem);
180 }
181 return "";
182 }
183
184 void CacheData()
185 {
186 try
187 {
188 OUString sCachedData = "user=" + m_sUserDN // user DN
189 + "\0" + givenname + "=" + GetGivenName() // 1st name
190 + "\0" + sn + "=" + GetSn() // sn
191 + "\0" + initials + "=" + GetInitials() // initials
192 + "\0" + street + "=" + GetStreet() // street
193 + "\0" + l + "=" + GetCity() // l
194 + "\0" + st + "=" + GetState() // st
195 + "\0" + postalcode + "=" + GetPostalCode() // p.code
196 + "\0" + c + "=" + GetCountry() // c
197 + "\0" + o + "=" + GetOrganization() // o
198 + "\0" + title + "=" + GetTitle() // title
199 + "\0" + homephone + "=" + GetHomePhone() // h.phone
200 + "\0" + telephonenumber + "=" + GetTelephoneNumber() // tel
201 + "\0" + facsimiletelephonenumber + "=" + GetFaxNumber() // fax
202 + "\0" + mail + "=" + GetMail(); // mail
203 const css::uno::Sequence<sal_Int8> seqCachedData(
204 reinterpret_cast<const sal_Int8*>(sCachedData.getStr()),
205 sCachedData.getLength() * sizeof(sal_Unicode));
206 OUStringBuffer sOutBuf;
207 comphelper::Base64::encode(sOutBuf, seqCachedData);
208
209 std::shared_ptr<comphelper::ConfigurationChanges> batch(
211 officecfg::UserProfile::WinUserInfo::Cache::set(sOutBuf.makeStringAndClear(), batch);
212 batch->commit();
213 }
214 catch (const css::uno::Exception&)
215 {
216 TOOLS_WARN_EXCEPTION("extensions.config",
217 "ADsUserAccess: access to configuration data failed:");
218 }
219 }
220
221 void GetCachedData()
222 {
223 if (m_sUserDN.isEmpty())
224 throw css::uno::RuntimeException();
225
226 OUString sCache = officecfg::UserProfile::WinUserInfo::Cache::get();
227
228 if (sCache.isEmpty())
229 throw css::uno::RuntimeException();
230
231 {
232 css::uno::Sequence<sal_Int8> seqCachedData;
233 comphelper::Base64::decode(seqCachedData, sCache);
234 sCache = OUString(reinterpret_cast<const sal_Unicode*>(seqCachedData.getConstArray()),
235 seqCachedData.getLength() / sizeof(sal_Unicode));
236 }
237
238 OUString sUserDN;
239 std::map<const OUString, OUString> aMap;
240 sal_Int32 nIndex = 0;
241 do
242 {
243 const OUString sEntry = sCache.getToken(0, '\0', nIndex);
244 sal_Int32 nEqIndex = 0;
245 const OUString sEntryName = sEntry.getToken(0, '=', nEqIndex);
246 OUString sEntryVal;
247 if (nEqIndex >= 0)
248 sEntryVal = sEntry.copy(nEqIndex);
249 if (sEntryName == "user")
250 sUserDN = sEntryVal;
251 else
252 aMap[sEntryName] = sEntryVal;
253 } while (nIndex >= 0);
254
255 if (sUserDN != m_sUserDN)
256 throw css::uno::RuntimeException();
257 m_aMap = std::move(aMap);
258 }
259
260 OUString m_sUserDN; // used to check if the cached data is for current user
261 std::map<const OUString, OUString> m_aMap;
262};
263
264class SysInfoUserAccess : public extensions::config::WinUserInfo::WinUserInfoBe_Impl
265{
266public:
267 SysInfoUserAccess()
268 {
269 try
270 {
271 ULONG nSize = 0;
272 GetUserNameExW(NameDisplay, nullptr, &nSize);
273 if (GetLastError() != ERROR_MORE_DATA)
274 throw css::uno::RuntimeException();
275 auto pNameBuf(std::make_unique<wchar_t[]>(nSize));
276 if (!GetUserNameExW(NameDisplay, pNameBuf.get(), &nSize))
277 throw css::uno::RuntimeException();
278 m_sName = o3tl::toU(pNameBuf.get());
279 }
280 catch (css::uno::RuntimeException&)
281 {
282 // GetUserNameEx may fail in some cases (e.g., for built-in AD domain
283 // administrator account on non-DC systems), where GetUserName will
284 // still give a name.
285 DWORD nSize = UNLEN + 1;
286 auto pNameBuf(std::make_unique<wchar_t[]>(nSize));
287 if (!GetUserNameW(pNameBuf.get(), &nSize))
288 throw css::uno::RuntimeException();
289 m_sName = o3tl::toU(pNameBuf.get());
290 }
291 }
292
293 virtual OUString GetGivenName() override { return m_sName; }
294
295private:
296 OUString m_sName;
297};
298}
299
300namespace extensions
301{
302namespace config
303{
304namespace WinUserInfo
305{
308 , BackendBase(mMutex)
309{
310 try
311 {
312 m_pImpl.reset(new ADsUserAccess());
313 }
314 catch (css::uno::RuntimeException&)
315 {
316 m_pImpl.reset(new SysInfoUserAccess);
317 }
318}
319
321
322void WinUserInfoBe::setPropertyValue(OUString const&, css::uno::Any const&)
323{
324 throw css::lang::IllegalArgumentException("setPropertyValue not supported",
325 static_cast<cppu::OWeakObject*>(this), -1);
326}
327
328css::uno::Any WinUserInfoBe::getPropertyValue(OUString const& PropertyName)
329{
330 OUString sValue;
331 // Only process the first argument of possibly multiple space- or comma-separated arguments
332 OUString sToken = PropertyName.getToken(0, ' ').getToken(0, ',');
333 if (sToken == givenname)
334 {
335 sValue = m_pImpl->GetGivenName();
336 }
337 else if (sToken == sn)
338 {
339 sValue = m_pImpl->GetSn();
340 }
341 else if (sToken == fathersname)
342 {
343 sValue = m_pImpl->GetFathersname();
344 }
345 else if (sToken == initials)
346 {
347 sValue = m_pImpl->GetInitials();
348 }
349 else if (sToken == street)
350 {
351 sValue = m_pImpl->GetStreet();
352 }
353 else if (sToken == l)
354 {
355 sValue = m_pImpl->GetCity();
356 }
357 else if (sToken == st)
358 {
359 sValue = m_pImpl->GetState();
360 }
361 else if (sToken == apartment)
362 {
363 sValue = m_pImpl->GetApartment();
364 }
365 else if (sToken == postalcode)
366 {
367 sValue = m_pImpl->GetPostalCode();
368 }
369 else if (sToken == c)
370 {
371 sValue = m_pImpl->GetCountry();
372 }
373 else if (sToken == o)
374 {
375 sValue = m_pImpl->GetOrganization();
376 }
377 else if (sToken == position)
378 {
379 sValue = m_pImpl->GetPosition();
380 }
381 else if (sToken == title)
382 {
383 sValue = m_pImpl->GetTitle();
384 }
385 else if (sToken == homephone)
386 {
387 sValue = m_pImpl->GetHomePhone();
388 }
389 else if (sToken == telephonenumber)
390 {
391 sValue = m_pImpl->GetTelephoneNumber();
392 }
393 else if (sToken == facsimiletelephonenumber)
394 {
395 sValue = m_pImpl->GetFaxNumber();
396 }
397 else if (sToken == mail)
398 {
399 sValue = m_pImpl->GetMail();
400 }
401 else
402 throw css::beans::UnknownPropertyException(sToken, static_cast<cppu::OWeakObject*>(this));
403
404 return css::uno::Any(css::beans::Optional<css::uno::Any>(
405 !sValue.isEmpty(), sValue.isEmpty() ? css::uno::Any() : css::uno::Any(sValue)));
406}
407
409{
410 return "com.sun.star.comp.configuration.backend.WinUserInfoBe";
411}
412
413sal_Bool SAL_CALL WinUserInfoBe::supportsService(const OUString& aServiceName)
414{
415 return cppu::supportsService(this, aServiceName);
416}
417
418css::uno::Sequence<OUString> SAL_CALL WinUserInfoBe::getSupportedServiceNames()
419{
420 return { "com.sun.star.configuration.backend.WinUserInfoBe" };
421}
422}
423}
424}
425
426extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
428 css::uno::Sequence<css::uno::Any> const&)
429{
430 return cppu::acquire(new extensions::config::WinUserInfo::WinUserInfoBe());
431}
432
433/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * extensions_WinUserInfoBe_get_implementation(css::uno::XComponentContext *, css::uno::Sequence< css::uno::Any > const &)
static void encode(OUStringBuffer &aStrBuffer, const css::uno::Sequence< sal_Int8 > &aPass)
static void decode(css::uno::Sequence< sal_Int8 > &aPass, std::u16string_view sBuffer)
static std::shared_ptr< ConfigurationChanges > create()
Implements the PlatformBackend service, a specialization of the XPropertySet service for retrieving A...
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
virtual void SAL_CALL setPropertyValue(OUString const &, css::uno::Any const &) override
virtual css::uno::Any SAL_CALL getPropertyValue(OUString const &PropertyName) override
virtual OUString SAL_CALL getImplementationName() override
virtual sal_Bool SAL_CALL supportsService(const OUString &aServiceName) override
std::unique_ptr< WinUserInfoBe_Impl > m_pImpl
#define TOOLS_WARN_EXCEPTION(area, stream)
sal_Int32 nIndex
OUString m_sName
Definition: logger.cxx:63
def position(n=-1)
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
cppu::WeakComponentImplHelper< css::beans::XPropertySet, css::lang::XServiceInfo > BackendBase
config
int i
HashMap_OWString_Interface aMap
LONG
unsigned char sal_Bool
sal_uInt16 sal_Unicode
signed char sal_Int8