minijackson / paddle Goto Github PK
View Code? Open in Web Editor NEWA library simplifying LDAP usage in Elixir projects
License: MIT License
A library simplifying LDAP usage in Elixir projects
License: MIT License
I seem to be getting a lot of errors such as
phonedb-57c69655fc-mfdj8 phonedb 02:40:08.704 [info] Stopped LDAP
phonedb-57c69655fc-mfdj8 phonedb 02:40:08.705 [error] GenServer Paddle terminating
phonedb-57c69655fc-mfdj8 phonedb ** (FunctionClauseError) no function clause matching in Paddle.Parsing.clean_eldap_search_results/2
phonedb-57c69655fc-mfdj8 phonedb (paddle 0.1.4) lib/paddle/parsing.ex:148: Paddle.Parsing.clean_eldap_search_results({:ok, {:eldap_search
_result, [], [], :asn1_NOVALUE}}, 'dc=pri')
phonedb-57c69655fc-mfdj8 phonedb (paddle 0.1.4) lib/paddle.ex:204: Paddle.handle_call/3
phonedb-57c69655fc-mfdj8 phonedb (stdlib 3.17.1) gen_server.erl:721: :gen_server.try_handle_call/4
phonedb-57c69655fc-mfdj8 phonedb (stdlib 3.17.1) gen_server.erl:750: :gen_server.handle_msg/6
phonedb-57c69655fc-mfdj8 phonedb (stdlib 3.17.1) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
phonedb-57c69655fc-mfdj8 phonedb Last message (from #PID<0.9786.10>): {:get, {:and, [equalityMatch: {:AttributeValueAssertion, 'telephoneNumb
er', '0438539906'}, equalityMatch: {:AttributeValueAssertion, 'objectClass', 'person'}]}, "ou=people", :base}
phonedb-57c69655fc-mfdj8 phonedb 02:40:08.708 [info] Connecting to ldap://['ldap.pri']:389
phonedb-57c69655fc-mfdj8 phonedb 02:40:08.709 [error] #PID<0.9786.10> running PhoneDbWeb.Endpoint (connection #PID<0.9787.10>, stream id 1) t
erminated
phonedb-57c69655fc-mfdj8 phonedb Server: phonedb.linuxpenguins.xyz:80 (http)
phonedb-57c69655fc-mfdj8 phonedb Request: POST /api/incoming_call/
phonedb-57c69655fc-mfdj8 phonedb ** (exit) exited in: GenServer.call(Paddle, {:get, {:and, [equalityMatch: {:AttributeValueAssertion, 'telephoneNumber', '0438539906'}, equalityMatch: {:AttributeValueAssertion, 'objectClass', 'person'}]}, "ou=people", :base}, 5000)
phonedb-57c69655fc-mfdj8 phonedb ** (EXIT) an exception was raised:
phonedb-57c69655fc-mfdj8 phonedb ** (FunctionClauseError) no function clause matching in Paddle.Parsing.clean_eldap_search_results/2
phonedb-57c69655fc-mfdj8 phonedb (paddle 0.1.4) lib/paddle/parsing.ex:148: Paddle.Parsing.clean_eldap_search_results({:ok, {:eldap_search_result, [], [], :asn1_NOVALUE}}, 'dc=pri')
phonedb-57c69655fc-mfdj8 phonedb (paddle 0.1.4) lib/paddle.ex:204: Paddle.handle_call/3
phonedb-57c69655fc-mfdj8 phonedb (stdlib 3.17.1) gen_server.erl:721: :gen_server.try_handle_call/4
phonedb-57c69655fc-mfdj8 phonedb (stdlib 3.17.1) gen_server.erl:750: :gen_server.handle_msg/6
phonedb-57c69655fc-mfdj8 phonedb (stdlib 3.17.1) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
I think this might be on a get request:
case Paddle.get(%PhoneDb.Contacts.Ldap.Person{telephoneNumber: contact.phone_number}, nil) do
Where I expect it to return {:error, :noSuchObject}
in this case.
Unfortunately this is not an error I can reproduce on demand. Just searching for a non-existent object produces the excepted results.
How to declare LDAPTLS_REQCERT=never in paddle configuration?
This will probably require to add an optional "pid" argument to every user function
It would be highly useful to include an LDAP filter along with the authenticate, even though the authenticate constructs a full DN. The handiness of this is in being able to do an authenticate+ authorize all in one step, ala:
Paddle.authenticate([cn: username], password, filter:[memberOf: "cn=SpecialGroup,ou=..."])
In testing this my connection is failing, but I can connect with ldapsearch to my ldapserver. However, ldapsearch does report that it doesn't like the certificate because the root CA is untrusted. My speculation is that this might be happening under the hood, and it is just bubbling up to the top as a connection failure?
Hello!
Here's my scenario:
I have built an umbrella project with 2 applications: LdapInterface, which does some LDAP user management, and a Slack bot which passes user commands to the other app.
Sometimes our LDAP will be unavailable due to external factors, say, VPN hiccups, and connecting to LDAP would fail preventing the app from starting. When this happens, my Slack bot doesn't connect as well.
Instead, I wanted to give feedback on Slack when the LDAP connection is not available.
This may not be the best approach and I may be missing something, but here's what I did:
Forked the repo and let the app start without holding a connection to LDAP, and when a message arrives it returns {:error, :not_connected} so I can gracefully handle it, send a message on Slack and try a reconnection.
Those are the changes: shamanime@7efb4a0
This solves my issue. Is there a better approach to achieve this result? Do you think this would be valuable for anyone else? Would you be interested in a PR?
Thank you for your work on this project!
Encountered here
I have a need to allow my users to change their password. Is this possible with Paddle? I don't see any mention of it in the docs.
In our ldap, we have a whole tree of different organizations - for example, my dn looks something like this:
cn=andymc,ou=myOrg,ou=New York,ou=US,o=myCo
and other users might be in other countries or states, etc...
It would be great if authenticate could accept a form of username that was just the whole DN, and didn't mess with it at all. If I have time I'll implement it, but not sure how you'd like it done.
La signature de eldap:open
est open([Host], [Option]) -> {ok, Handle} | {error, Reason}
, et pourtant dans la configuration de Paddle, il ne semble être prévu qu'un seul hôte soit supporté, ainsi que dans paddle.ex où l'hôte (unique, donc) est mis dans une liste.
C'est relativement dommage si on a une réplique du LDAP et que le primaire tombe.
(PS: j'espère que tu t'amuses au Canada ❤️)
phoenix_1 | 22:34:50.538 level=error GenServer Paddle terminating
phoenix_1 | ** (CaseClauseError) no case clause matching: :other
phoenix_1 | (paddle) lib/paddle.ex:612: Paddle.clean_eldap_search_results/1
phoenix_1 | (paddle) lib/paddle.ex:206: Paddle.handle_call/3
phoenix_1 | (stdlib) gen_server.erl:636: :gen_server.try_handle_call/4
phoenix_1 | (stdlib) gen_server.erl:665: :gen_server.handle_msg/6
phoenix_1 | (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
I don't know if it is in your plans, but have you considered setting up a supervisor process so when it does die like this, we can recover?
Thanks,
What the title says
I could not deduce the use of the library from the README, nor find any link to the documentation or a tutorial.
Moreover, the Wiki is empty :(
I created an Active Directory user with the following value.
sn
張givenName
阿翔cn
張阿翔Result
cn: [<<195, 165, 194, 188, 194, 181, 195, 169, 194, 152, 194, 191, 195, 167,
194, 191, 194, 148>>]
givenName: [<<195, 169, 194, 152, 194, 191, 195, 167, 194, 191, 194, 148>>],
sn: ["å¼µ"]
Detail Log
iex(4)> Paddle.get(%Gaia.AdUser{sAMAccountName: "PaddleTest"})
[debug] Getting entries with dn: CN=Users,DC=jourdeness,DC=com,DC=tw and filter: {:and,
[equalityMatch: {:AttributeValueAssertion, 'sAMAccountName', 'PaddleTest'},
equalityMatch: {:AttributeValueAssertion, 'objectClass', 'person'},
equalityMatch: {:AttributeValueAssertion, 'objectClass',
'organizationalPerson'},
equalityMatch: {:AttributeValueAssertion, 'objectClass', 'user'}]}
[debug] search request = {'SearchRequest',"CN=Users,DC=jourdeness,DC=com,DC=tw",
wholeSubtree,derefAlways,0,0,false,
{'and',
[{equalityMatch,
{'AttributeValueAssertion',"sAMAccountName",
"PaddleTest"}},
{equalityMatch,
{'AttributeValueAssertion',"objectClass",
"person"}},
{equalityMatch,
{'AttributeValueAssertion',"objectClass",
"organizationalPerson"}},
{equalityMatch,
{'AttributeValueAssertion',"objectClass",
"user"}}]},
[]}
[debug] search reply = {ok,{'LDAPMessage',3,
{searchResEntry,
{'SearchResultEntry',
[67,78,61,229,188,181,233,152,191,231,191,148,
44,67,78,61,85,115,101,114,115,44,68,67,61,
106,111,117,114,100,101,110,101,115,115,44,68,
67,61,99,111,109,44,68,67,61,116,119],
[{'PartialAttribute',"objectClass",
["top","person","organizationalPerson",
"user"]},
{'PartialAttribute',"cn",
[[229,188,181,233,152,191,231,191,148]]},
{'PartialAttribute',"sn",["å¼µ"]},
{'PartialAttribute',"description",
[[80,97,100,100,108,101,32,230,184,172,
232,169,166,229,184,179,232,153,159]]},
{'PartialAttribute',"givenName",
[[233,152,191,231,191,148]]},
{'PartialAttribute',"distinguishedName",
[[67,78,61,229,188,181,233,152,191,231,
191,148,44,67,78,61,85,115,101,114,115,
44,68,67,61,106,111,117,114,100,101,110,
101,115,115,44,68,67,61,99,111,109,44,
68,67,61,116,119]]},
{'PartialAttribute',"instanceType",["4"]},
{'PartialAttribute',"whenCreated",
["20171222032945.0Z"]},
{'PartialAttribute',"whenChanged",
["20171222033009.0Z"]},
{'PartialAttribute',"displayName",
[[229,188,181,233,152,191,231,191,148]]},
{'PartialAttribute',"uSNCreated",["13734606"]},
{'PartialAttribute',"uSNChanged",["13734624"]},
{'PartialAttribute',"name",
[[229,188,181,233,152,191,231,191,148]]},
{'PartialAttribute',"objectGUID",
[[6,229,41,227,165,153,107,75,170,160,77,
74,103,237,250,7]]},
{'PartialAttribute',"userAccountControl",
["512"]},
{'PartialAttribute',"codePage",["0"]},
{'PartialAttribute',"countryCode",["0"]},
{'PartialAttribute',"pwdLastSet",["0"]},
{'PartialAttribute',"primaryGroupID",["513"]},
{'PartialAttribute',"objectSid",
[[1,5,0,0,0,0,0,5,21,0,0,0,14,34,35,12,84,
61,232,41,62,12,155,179,58,53,0,0]]},
{'PartialAttribute',"accountExpires",
["9223372036854775807"]},
{'PartialAttribute',"sAMAccountName",
["PaddleTest"]},
{'PartialAttribute',"sAMAccountType",
["805306368"]},
{'PartialAttribute',"userPrincipalName",
["[email protected]"]},
{'PartialAttribute',"objectCategory",
["CN=Person,CN=Schema,CN=Configuration,DC=jourdeness,DC=com,DC=tw"]},
{'PartialAttribute',"dSCorePropagationData",
["16010101000000.0Z"]}]}},
asn1_NOVALUE}}
[debug] search reply = {ok,{'LDAPMessage',3,
{searchResDone,
{'LDAPResult',success,[],[],asn1_NOVALUE}},
asn1_NOVALUE}}
[debug] search reply = searchResDone
{:ok,
[%{__struct__: Gaia.AdUser, accountExpires: ["9223372036854775807"],
cn: [<<195, 165, 194, 188, 194, 181, 195, 169, 194, 152, 194, 191, 195, 167,
194, 191, 194, 148>>], codePage: ["0"], countryCode: ["0"],
dSCorePropagationData: ["16010101000000.0Z"],
description: [<<80, 97, 100, 100, 108, 101, 32, 195, 166, 194, 184, 194,
172, 195, 168, 194, 169, 194, 166, 195, 165, 194, 184, 194, 179, 195,
168, 194, 153, 194, 159>>],
displayName: [<<195, 165, 194, 188, 194, 181, 195, 169, 194, 152, 194, 191,
195, 167, 194, 191, 194, 148>>],
distinguishedName: [<<67, 78, 61, 195, 165, 194, 188, 194, 181, 195, 169,
194, 152, 194, 191, 195, 167, 194, 191, 194, 148, 44, 67, 78, 61, 85,
115, 101, 114, 115, 44, 68, 67, 61, 106, 111, 117, 114, 100, 101, ...>>],
givenName: [<<195, 169, 194, 152, 194, 191, 195, 167, 194, 191, 194, 148>>],
instanceType: ["4"], mail: nil, mobile: nil,
name: [<<195, 165, 194, 188, 194, 181, 195, 169, 194, 152, 194, 191, 195,
167, 194, 191, 194, 148>>],
objectCategory: ["CN=Person,CN=Schema,CN=Configuration,DC=jourdeness,DC=com,DC=tw"],
objectGUID: [<<6, 195, 165, 41, 195, 163, 194, 165, 194, 153, 107, 75, 194,
170, 194, 160, 77, 74, 103, 195, 173, 195, 186, 7>>],
objectSid: [<<1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 14, 34, 35, 12, 84, 61,
195, 168, 41, 62, 12, 194, 155, 194, 179, 58, 53, 0, 0>>],
primaryGroupID: ["513"], pwdLastSet: ["0"], sAMAccountName: ["PaddleTest"],
sAMAccountType: ["805306368"], sn: ["å¼µ"], telephoneNumber: nil,
uSNChanged: ["13734624"], uSNCreated: ["13734606"],
userAccountControl: ["512"],
userPrincipalName: ["[email protected]"],
whenChanged: ["20171222033009.0Z"], whenCreated: ["20171222032945.0Z"]}]}
Yo, y'a une option pour avoir l'IPv6 dans eldap
un peu planquée dans les notes de version.
Apparemment ça fonctionne avec :eldap.open(['cimmeria.rezel.enst.fr'], [tcpopts: [:inet6], port: 389])
, du coup si y'a moyen d'avoir un booléen v6?
, ça serait chouette :)
We are getting errors about patterns not matching and our GenServer then dies. Would it be a good idea to include a fourth default case:
_ -> {?}
At this location:
https://github.com/minijackson/paddle/blob/master/lib/paddle.ex#L179
Dear all,
First of, thanks for the amazing work with this library.
I am currently trying to generate a class for inetorgperson.schema
, but with no success so far, and I would be really grateful if you could provide some hints on how to solve my issue. Please have my apologies, as I am still pretty new to Elixir (and LDAP, actually).
I have my schema installed in /etc/openldap/schema/*.schema
, and thus have configured
config :paddle, Paddle,
host: "myserver.com"
base: "dc=myserver,dc=com",
ssl: false,
account_subdn: "ou=users",
port: 389,
schema_files: Path.wildcard("/etc/openldap/schema/*.schema")
as suggested by the documentation.
I then have created, in a file api/ldap.ex
, my helper module to authenticate my users, etc., and, after the module
defmodule Api.Ldap do
....
end
require Paddle.Class.Helper
Paddle.Class.Helper.gen_class_from_schema(Api.Ldap.InetOrgPerson, ["inetOrgPerson"], "ou=users")
If I get this right, the first parameter tells the macro to generate a module named Api.Ldap.InetOrgPerson
, based on the LDAP schema inetOrgPerson
, and that such entries should be stored under, in my case, ou=users,dc=myserver,dc=com
. If I am not mistaken, Paddle is able to guess in which .schema
file inetOrgPerson
is defined, but this might really be where I get this wrong. However, I don't understand how it should then be specified which .schema
defines inetOrgPerson
.
Indeed, I get the following error from mix
:
Erlang/OTP 23 [erts-11.0.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]
Compiling 20 files (.ex)
== Compilation error in file lib/ldap.ex ==
** (RuntimeError) Missing object classe(s) definition(s): inetOrgPerson
lib/paddle/schema_parser.ex:114: Paddle.SchemaParser.filter_definitions/3
lib/paddle/schema_parser.ex:48: Paddle.SchemaParser.attributes/1
lib/paddle/class.ex:186: Paddle.Class.Helper."MACRO-gen_class_from_schema"/6
expanding macro: Paddle.Class.Helper.gen_class_from_schema/3
lib/ldap.ex:38: (file)
Which would mean, I think, that Paddle is not able to guess where to find the inetOrgPerson
definition.
Could you indicate me
Best regards
Maybe I am making something wrong but when I try to add this entry
[
objectClass: ["top", "person", "inetOrgPerson"],
cn: "rodrigo+1@email",
sn: "Rodrigo Rebouças",
displayName: "Rodrigo Rebouças",
uid: "na",
mail: "rodrigo+1@email",
userPassword: 123
]
I receive:
14:47:07.844 [debug] add request = {'AddRequest',
"cn=rodrigo\\+1@email,ou=users,ou=opengalaxy,dc=opengalaxy",
[{'AddRequest_attributes',"objectClass",
["top","person","inetOrgPerson"]},
{'AddRequest_attributes',"cn",["rodrigo+1@email"]},
{'AddRequest_attributes',"sn",["Rodrigo Rebouças"]},
{'AddRequest_attributes',"displayName",
["Rodrigo Rebouças"]},
{'AddRequest_attributes',"uid",["na"]},
{'AddRequest_attributes',"mail",["rodrigo+1@email"]},
{'AddRequest_attributes',"userPassword",["123"]}]}
14:47:07.845 [debug] add reply = {ok,{'LDAPMessage',2,
{addResponse,
{'LDAPResult',invalidAttributeSyntax,[],
"sn: value #0 invalid per syntax",asn1_NOVALUE}},
asn1_NOVALUE}}
Sounds like encoding to me or if I done something wrong please tell me, thanks!!
An issue to test taiga webhook
Hi, I see that in the LDAP v3 there is pagination to deal with the limit exceed results exception but that is not implemented neither in eldap nor paddle.
Do you have plans to add it or have an idea what to do?
Thanks!
I don't know whom to contact, but I'm feeling like I've hit a dead-end. I can run an ldap query using ldapsearch
and it returns valid results. But the seemingly same query from paddle returns no results.
A redacted ldapsearch (which runs successfully) looks like:
$ ldapsearch -W -H ldaps://ldap.domain\
-D cn=username@loc,ou=users,dc=domain\
-b ou=users,dc=domain\
'(&(cn=username@loc)(memberOf=cn=Developers,cn=roles,dc=domain))'
... data with valid search result
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
The code I have in elixir looks like:
result = Paddle.get(filter: [cn: username,
memberOf: "cn=Developers,cn=roles,dc=domain"])
Logger.error("GROUP result=#{inspect result}")
And the output from this makes it /appear/ that the search query ran in the same manner as the ldapsearch command above, but it has no results.
20:51:40.691 level=debug Getting entries with dn: ou=users,dc=domain and filter: {:and,
[equalityMatch: {:AttributeValueAssertion, 'cn', 'username@loc'},
equalityMatch: {:AttributeValueAssertion, 'memberOf', 'cn=Developers,cn=roles,dc=domain'}]}
20:51:40.692 level=debug search request = {'SearchRequest',"ou=users,dc=domain",
wholeSubtree,derefAlways,0,0,false,
{'and',
[{equalityMatch,
{'AttributeValueAssertion',"cn",
"username@loc"}},
{equalityMatch,
{'AttributeValueAssertion',"memberOf",
"cn=Developers,cn=roles,dc=domain"}}]},
[]}
20:51:40.729 level=debug search reply = {ok,{'LDAPMessage',4,
{searchResDone,
{'LDAPResult',success,[],[],asn1_NOVALUE}},
asn1_NOVALUE}}
20:51:40.729 level=debug search reply = searchResDone
20:51:40.729 level=error GROUP result={:error, :noSuchObject}
I am not certain what the debug line2 wholeSubtree...
is, and how it relates to the search. My guess is something with the context of the search is different enough that it is not matching.
Any help would be greatly appreciated. Thx.
The problem may be on our end, but Paddle's LDAP connection appears to be timing out. The application works for a while, but the connection seems to go bad after a while. What is the best way to reestablish the connection when this happens?
14:38:45.708 request_id=10arplb3dffjiu9pcleek143qaptqve1 [info] POST /authenticate
14:38:47.748 [error] #PID<0.1971.0> running MyApp.Endpoint terminated
Server: myproject-api:8080 (http)
Request: POST /authenticate
** (exit) exited in: GenServer.call(Paddle, {:authenticate, 'uid=user,ou=People,dc=domain,dc=com', 'user'}, 5000)
** (EXIT) time out
14:38:48.962 [error] #PID<0.1972.0> running MyApp.Endpoint terminated
Server: myproject-api:8080 (http)
Request: POST /authenticate
** (exit) exited in: GenServer.call(Paddle, {:authenticate, 'uid=user,ou=People,dc=domain,dc=com', 'user'}, 5000)
** (EXIT) time out
14:38:50.712 [error] #PID<0.1973.0> running MyApp.Endpoint terminated
Server: myproject-api:8080 (http)
Request: POST /authenticate
** (exit) exited in: GenServer.call(Paddle, {:authenticate, 'uid=user,ou=People,dc=domain,dc=com', 'user'}, 5000)
** (EXIT) time out
14:38:55.092 request_id=8l0msrlt26jl7t3fo1tulf4k8f1nl4at [info] POST /authenticate
14:39:00.099 [error] #PID<0.1974.0> running MyApp.Endpoint terminated
Server: myproject-api:8080 (http)
Request: POST /authenticate
** (exit) exited in: GenServer.call(Paddle, {:authenticate, 'uid=user,ou=People,dc=domain,dc=com', 'user'}, 5000)
** (EXIT) time out
14:39:01.802 request_id=nq8cd2l5jsch6nsu37bqmmjiop7s3qcg [info] POST /authenticate
14:39:06.807 [error] #PID<0.1975.0> running MyApp.Endpoint terminated
Server: myproject-api:8080 (http)
Request: POST /authenticate
** (exit) exited in: GenServer.call(Paddle, {:authenticate, 'uid=user,ou=People,dc=domain,dc=com', 'user'}, 5000)
** (EXIT) time out
14:39:08.415 request_id=vfo18ec9f30tqi2aaa8dj6pucobu9h0c [info] POST /authenticate
14:39:10.238 request_id=33hoql67t1qvak09t2iap446keutnog0 [info] POST /authenticate
14:39:13.423 [error] #PID<0.1976.0> running MyApp.Endpoint terminated
Server: myproject-api:8080 (http)
Request: POST /authenticate
** (exit) exited in: GenServer.call(Paddle, {:authenticate, 'uid=user,ou=People,dc=domain,dc=com', 'user'}, 5000)
** (EXIT) time out
14:39:15.245 [error] #PID<0.1977.0> running MyApp.Endpoint terminated
Server: myproject-api:8080 (http)
Request: POST /authenticate
** (exit) exited in: GenServer.call(Paddle, {:authenticate, 'uid=user,ou=People,dc=domain,dc=com', 'user'}, 5000)
** (EXIT) time out
I need to modify user's attribute, say mobile
in Active Directory. But the dn
or distinguishedName
in return value contains base dn like DC=example,DC=com
.
{:ok, result} = Paddle.get(filter: [sAMAccountName: "wendy"], base: [ou: "user", ou: "mail"])
wendy = List.first(result)
# Not worked here
Paddle.modify(wendy["dn"], replace: {"mobile", "0912345678"})
workaround for now
remove base dn from object["dn"]
.
String.replace(wendy["dn"], ",DC=example,DC=com", "")
# remove some data for brevity
%{"sAMAccountType" => ["805306368"],
"lastLogonTimestamp" => ["131580319435909134"],
"whenChanged" => ["20171218005223.0Z"], "sAMAccountName" => ["wendyyao"],
"cn" => [<<195, 165, 194, 167, 194, 154, 195, 164, 194, 189, 194, 169, 195,
165, 194, 189, 194, 164>>], "lastLogon" => ["131581226138521747"],
<<67, 78, 61, 72, 81, 45, 195, 168, 194, 179, 194, 135, 195, 168, 194, 168,
194, 138, 195, 169, 194, 131, 194, 168, 44, 79, 85, 61, 195, 168, 194, 179,
...>>, "CN=Remote Desktop Users,CN=Builtin,DC=jourdeness,DC=com,DC=tw",
"CN=Account Operators,CN=Builtin,DC=jourdeness,DC=com,DC=tw"],
"givenName" => ["佩彤"], "sn" => [<<195, 165, 194, 167, 194, 154>>],
"logonCount" => ["209"], "primaryGroupID" => ["513"],
"uSNChanged" => ["13653562"], "accountExpires" => ["9223372036854775807"],
"dn" => <<67, 78, 61, 195, 165, 194, 167, 194, 154, 195, 164, 194, 189, 194,
169, 195, 165, 194, 189, 194, 164, 44, 79, 85, 61, 195, 168, 194, 179, 194,
135, 195, ...>>, "postalCode" => ["007429"],
"name" => [<<195, 165, 194, 167, 194, 154, 195, 164, 194, 189, 194, 169, 195,
165, 194, 189, 194, 164>>],
"objectSid" => [<<1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 14, 34, 35, 12, 84, 61,
195, 168, 41, 62, 12, 194, 155, 194, 179, 121, 36, ...>>],
"objectClass" => ["top", "person", "organizationalPerson", "user"],
"distinguishedName" => [<<67, 78, 61, 195, 165, 194, 167, 194, 154, 195, 164,
194, 189, 194, 169, 195, 165, 194, 189, 194, 164, 44, 79, 85, ...>>],
"displayName" => [<<195, 165, 194, 167, 194, 154, 195, 164, 194, 189, 194,
169, 195, 165, 194, 189, 194, 164>>], "countryCode" => ["0"]}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.