Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #11 from ikedam/feature/JENKINS-22470_SupportsApiT…
…oken

[JENKINS-22470] Supports apitoken for authentication.
  • Loading branch information
ikedam committed Aug 4, 2015
2 parents 34ab307 + 99d8c7e commit a753afb
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 4 deletions.
Expand Up @@ -32,6 +32,7 @@
import javax.servlet.ServletException;

import jenkins.model.Jenkins;
import jenkins.security.ApiTokenProperty;
import hudson.Extension;
import hudson.model.Queue;
import hudson.model.User;
Expand All @@ -40,6 +41,7 @@
import hudson.model.Descriptor.FormException;
import hudson.model.Job;
import hudson.security.ACL;
import hudson.security.AbstractPasswordBasedSecurityRealm;
import hudson.util.FormValidation;
import net.sf.json.JSONObject;

Expand Down Expand Up @@ -289,6 +291,28 @@ protected boolean authenticate(
return true;
}

/**
* Authenticate the specified user with Apitoken.
*
* @param strategy
* @param apitoken
* @return true if the authentication is succeeded.
*/
protected boolean authenticateWithApitoken(
SpecificUsersAuthorizationStrategy strategy,
String apitoken
) {
User u = User.get(strategy.getUserid(), false, Collections.emptyMap());
if (u == null) {
return false;
}
ApiTokenProperty p = u.getProperty(ApiTokenProperty.class);
if (p == null) {
return false;
}
return p.matchesPassword(apitoken);
}

/**
* Authenticate the specified user.
*
Expand All @@ -304,7 +328,11 @@ protected boolean authenticate(
StaplerRequest req,
JSONObject formData
) {
return authenticate(strategy, formData.getString("password"));
boolean useApitoken = formData.optBoolean("useApitoken");

return useApitoken
?authenticateWithApitoken(strategy, formData.getString("apitoken"))
:authenticate(strategy, formData.getString("password"));
}

/**
Expand Down Expand Up @@ -386,6 +414,8 @@ public FormValidation doCheckPassword(
StaplerRequest req,
@QueryParameter String userid,
@QueryParameter String password,
@QueryParameter String apitoken,
@QueryParameter boolean useApitoken,
@QueryParameter boolean noNeedReauthentication
) {
SpecificUsersAuthorizationStrategy newStrategy = new SpecificUsersAuthorizationStrategy(userid, noNeedReauthentication);
Expand All @@ -397,7 +427,10 @@ public FormValidation doCheckPassword(
return FormValidation.ok();
}

if (StringUtils.isBlank(password)) {
if (
(!useApitoken && StringUtils.isBlank(password))
|| (useApitoken && StringUtils.isBlank(apitoken))
) {
return FormValidation.error(Messages.SpecificUsersAuthorizationStrategy_password_required());
}

Expand All @@ -406,7 +439,10 @@ public FormValidation doCheckPassword(
* is used for brute force attack.
* Authentication is done only in saving the configuration
* (that is, in DescriptorImpl#newInstance)
if (!authenticate(newStrategy, password)) {
if (
(!useApitoken && !authenticate(newStrategy, password))
(useApitoken && authenticateWithApitoken(newStrategy, apitoken))
) {
return FormValidation.error(Messages.SpecificUsersAuthorizationStrategy_password_invalid());
}
*/
Expand All @@ -425,6 +461,10 @@ public FormValidation doCheckNoNeedReauthentication(@QueryParameter boolean noNe
return FormValidation.ok();
}

public boolean isUseApitoken() {
return !(Jenkins.getInstance().getSecurityRealm() instanceof AbstractPasswordBasedSecurityRealm);
}

/**
* {@link SpecificUsersAuthorizationStrategy} should be disabled by default for JENKINS-28298
* @return false
Expand Down
Expand Up @@ -32,13 +32,20 @@ THE SOFTWARE.
<f:textbox checkPasswordRequestedUrl="${descriptor.calcCheckPasswordRequestedUrl()}" />
</f:entry>
<f:entry title="${%Password}" field="password">
<f:password />
<div class="specific-user-authorization-password">
<f:password />
</div>
<div class="specific-user-authorization-apitoken">
<f:textbox field="apitoken" />
</div>
<f:checkbox field="useApitoken" checked="${descriptor.useApitoken}" title="${%Use API Token for authentication}" />
</f:entry>
<f:entry title="${%No need for re-authentication}" field="noNeedReauthentication">
<f:checkbox />
</f:entry>
</table></f:block>
<st:once>
<st:adjunct includes="org.jenkinsci.plugins.authorizeproject.strategy.SpecificUsersAuthorizationStrategy.checkPasswordRequested" />
<st:adjunct includes="org.jenkinsci.plugins.authorizeproject.strategy.SpecificUsersAuthorizationStrategy.passwordApitokenSwitch" />
</st:once>
</j:jelly>
Expand Up @@ -24,5 +24,7 @@
User\ ID=\u30e6\u30fc\u30b6\u30fcID
#Password=パスワード
Password=\u30d1\u30b9\u30ef\u30fc\u30c9
#Use\ API\ Token\ for\ authentication=認証に API トークンを使用する
Use\ API\ Token\ for\ authentication=\u8a8d\u8a3c\u306b API \u30c8\u30fc\u30af\u30f3\u3092\u4f7f\u7528\u3059\u308b
#No\ need\ for\ re-authentication=再認証を必要としない
No\ need\ for\ re-authentication=\u518d\u8a8d\u8a3c\u3092\u5fc5\u8981\u3068\u3057\u306a\u3044
@@ -1,4 +1,7 @@
<div>
Password of the user specified in User ID.
This field is shown only when authentication is required for this user.
<p>
You can use API token for authentication instead by checking "Use API Token for authentication".
You can get your API token from your user configuration page.
</div>
@@ -1,4 +1,7 @@
<div>
ユーザーID で指定したユーザーのパスワード。
このフィールドは認証が必要な場合にだけ表示されます。
<p>
「認証に API トークンを使用する」にチェックをいれるとパスワードの代わりに API トークンで認証できます。
API トークンはユーザーの設定画面から確認できます。
</div>
@@ -0,0 +1,60 @@
/*
* The MIT License
*
* Copyright (c) 2015 IKEDA Yasuyuki
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

/**
* Swtich password and ApiToken
*/
Behaviour.specify(".specific-user-authorization", "passwordApiTokenSwitch", 0, function (e) {
var findChild = function(startNode) {
return function(src,filter) {
return find(src,filter,function (e) {
if (e.firstChild != null) {
return e.firstChild;
}
while (e != null && e != startNode && e.nextSibling == null) {
e = e.parentNode
}
if (e == null || e == startNode) {
return null;
}
return e.nextSibling;
});
};
};

var onchange = function() {
var e = this.up(".specific-user-authorization");
if (this.checked) {
e.down('.specific-user-authorization-password').hide();
e.down('.specific-user-authorization-apitoken').show();
} else {
e.down('.specific-user-authorization-password').show();
e.down('.specific-user-authorization-apitoken').hide();
}
};

var useApitokenField = findFormItem(e, "useApitoken", findChild(e));
useApitokenField.observe("click", onchange);
onchange.call(useApitokenField);
});

0 comments on commit a753afb

Please sign in to comment.