1
2
Pluggable security realm that connects external user database to Hudson.

这里介绍一下 jenkins的登录认证 相关的

分析 active-directory-plugin/, ldap-plugin/, script-realm-plugin/ 这几个插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103




UserPrincipalName:指定要由客户端进行身份验证的服务的用户主体名称 (UPN)。一个用户帐户名(有时称为“用户登录名”)和一个域名(标识用户帐户所在的域),
这是登录到Windows域的标准用法。格式是: xiaowen@azureyun.com (类电子邮件地址)。

SamAccountName:在AD属性AMAccountName中,存储帐户登录名或用户对象,实际上是命名符号“Domain\LogonName”中使用的旧NetBIOS表单,
该属性是域用户对象的必需属性;而SAMAccountName应始终与UPN主体名称保持一致,即SAMAccountName必须等于属性“UserPrincipalName” 的前缀部分。







public UserDetails call() throws AuthenticationException, NamingException {
DirContext context;
boolean anonymousBind = false; // did we bind anonymously?

// LDAP treats empty password as anonymous bind, so we need to reject it
if (StringUtils.isEmpty(password)) {
throw new BadCredentialsException("Empty password");
}

String userPrincipalName = getPrincipalName(username, domain.getName()); // mage@company.com
String samAccountName = userPrincipalName.substring(0, userPrincipalName.indexOf('@')); // mage


// 匿名绑定ad域控
try {
context = descriptor.bind(userPrincipalName, password, ldapServers, props, domain.getTlsConfiguration());
// userPrincipalName=mage@company.com
// password=mypassword.
// ldapServers = arraylist, 10.0.0.60:389
// props=hashtable, com.sun.jndi.ldap.connect.timeout -> 30000, com.sun.jndi.ldap.read.timeout -> 60000
// tlsConfiguration


} catch (BadCredentialsException e) {
if (anonymousBind)
// in my observation, if we attempt an anonymous bind and AD doesn't allow it, it still passes the bind method
// and only fail later when we actually do a query. So perhaps this is a dead path, but I'm leaving it here
// anyway as a precaution.
throw new UserMayOrMayNotExistException("Unable to retrieve the user information without bind DN/password configured");
throw e;
}


try {
// locate this user's record
final String domainDN = toDC(domain.getName()); //DC=company,DC=com

Attributes user = new LDAPSearchBuilder(context, domainDN).subTreeScope().searchOne("(& (userPrincipalName={0})(objectCategory=user))", userPrincipalName);



Object dnObject = user.get(DN_FORMATTED).get(); // CN=xxx,OU=xxxx,OU=,OU=,OU=,DC=company,DC=com



String dn = dnObject.toString(); // CN=xxx,OU=xxxx,OU=,OU=,OU=,DC=company,DC=com
LdapName ldapName = new LdapName(dn); //CN=xxx,OU=xxxx,OU=,OU=,OU=,DC=company,DC=com
String dnFormatted = ldapName.toString();//CN=xxx,OU=xxxx,OU=,OU=,OU=,DC=company,DC=com



Set<GrantedAuthority> groups = resolveGroups(domainDN, dnFormatted, context);// 就是把 域账号所属于的组都搞过来.
groups.add(SecurityRealm.AUTHENTICATED_AUTHORITY);

cacheMiss[0] = new ActiveDirectoryUserDetail(username, password, true, true, true, true, groups.toArray(new GrantedAuthority[0]),
getStringAttribute(user, "displayName"),
getStringAttribute(user, "mail"),
getStringAttribute(user, "telephoneNumber")
);
return cacheMiss[0];
} catch (NamingException e) {
if (activeDirectoryInternalUser != null) {
throw e;
}
if (anonymousBind && e.getMessage().contains("successful bind must be completed") && e.getMessage().contains("000004DC")) {
// sometimes (or always?) anonymous bind itself will succeed but the actual query will fail.
// see JENKINS-12619. On my AD the error code is DSID-0C0906DC
throw new UserMayOrMayNotExistException("Unable to retrieve the user information without bind DN/password configured");
}
if (anonymousBind && e.getMessage().contains("Operation unavailable without authentication") && e.getMessage().contains("00002020")) {
// sometimes (or always?) anonymous bind itself will succeed but the actual query will fail.
// see JENKINS-47133
String msg = String.format("Server doesn't allow to retrieve the information for user `%s` without bind DN/password configured", username);
LOGGER.log(Level.WARNING, msg);
throw new UserMayOrMayNotExistException(msg);
}

LOGGER.log(Level.WARNING, String.format("Failed to retrieve user information for %s", username), e);
throw new BadCredentialsException("Failed to retrieve user information for " + username, e);
} finally {
closeQuietly(context);
}
}