Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
jenkins
提交
6720a412
J
jenkins
项目概览
xxadev
/
jenkins
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
J
jenkins
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
6720a412
编写于
3月 09, 2016
作者:
D
Daniel Beck
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #2042 from kzantow/2.0
Make Jenkins secure by default
上级
90476095
1561bcaf
变更
41
展开全部
隐藏空白更改
内联
并排
Showing
41 changed file
with
1944 addition
and
991 deletion
+1944
-991
core/src/main/java/hudson/PluginManager.java
core/src/main/java/hudson/PluginManager.java
+1
-1
core/src/main/java/hudson/model/UpdateCenter.java
core/src/main/java/hudson/model/UpdateCenter.java
+61
-50
core/src/main/java/hudson/security/FullControlOnceLoggedInAuthorizationStrategy.java
...ecurity/FullControlOnceLoggedInAuthorizationStrategy.java
+29
-6
core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
...main/java/hudson/security/HudsonPrivateSecurityRealm.java
+34
-10
core/src/main/java/jenkins/install/InstallState.java
core/src/main/java/jenkins/install/InstallState.java
+40
-9
core/src/main/java/jenkins/install/InstallUtil.java
core/src/main/java/jenkins/install/InstallUtil.java
+16
-2
core/src/main/java/jenkins/install/SetupWizard.java
core/src/main/java/jenkins/install/SetupWizard.java
+178
-0
core/src/main/java/jenkins/model/Jenkins.java
core/src/main/java/jenkins/model/Jenkins.java
+43
-16
core/src/main/resources/hudson/security/FullControlOnceLoggedInAuthorizationStrategy/config.jelly
...FullControlOnceLoggedInAuthorizationStrategy/config.jelly
+7
-0
core/src/main/resources/hudson/security/FullControlOnceLoggedInAuthorizationStrategy/help-allowAnonymousRead.html
...oggedInAuthorizationStrategy/help-allowAnonymousRead.html
+3
-0
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm.jelly
...dson/security/HudsonPrivateSecurityRealm/_entryForm.jelly
+38
-59
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryFormPage.jelly
.../security/HudsonPrivateSecurityRealm/_entryFormPage.jelly
+50
-0
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/addUser.jelly
.../hudson/security/HudsonPrivateSecurityRealm/addUser.jelly
+1
-1
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/firstUser.jelly
...udson/security/HudsonPrivateSecurityRealm/firstUser.jelly
+1
-1
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/setupWizardFirstUser.jelly
...ity/HudsonPrivateSecurityRealm/setupWizardFirstUser.jelly
+53
-0
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly
...s/hudson/security/HudsonPrivateSecurityRealm/signup.jelly
+1
-1
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signupWithFederatedIdentity.jelly
...sonPrivateSecurityRealm/signupWithFederatedIdentity.jelly
+1
-1
core/src/main/resources/jenkins/install/SetupWizard/authenticate-security-token.jelly
...ins/install/SetupWizard/authenticate-security-token.jelly
+44
-0
core/src/main/resources/jenkins/install/SetupWizard/index.jelly
...rc/main/resources/jenkins/install/SetupWizard/index.jelly
+8
-0
core/src/main/resources/jenkins/install/SetupWizard/proxy-configuration.jelly
...ces/jenkins/install/SetupWizard/proxy-configuration.jelly
+13
-0
core/src/main/resources/jenkins/install/pluginSetupWizard.properties
...in/resources/jenkins/install/pluginSetupWizard.properties
+13
-2
core/src/main/resources/jenkins/model/Jenkins/login.jelly
core/src/main/resources/jenkins/model/Jenkins/login.jelly
+5
-0
core/src/main/resources/jenkins/model/Jenkins/loginError.jelly
...src/main/resources/jenkins/model/Jenkins/loginError.jelly
+4
-0
core/src/main/resources/lib/layout/html.jelly
core/src/main/resources/lib/layout/html.jelly
+177
-0
core/src/main/resources/lib/layout/layout.jelly
core/src/main/resources/lib/layout/layout.jelly
+0
-4
test/src/test/java/hudson/model/UpdateCenterPluginInstallTest.java
...test/java/hudson/model/UpdateCenterPluginInstallTest.java
+2
-1
war/src/main/js/api/pluginManager.js
war/src/main/js/api/pluginManager.js
+1
-1
war/src/main/js/api/securityConfig.js
war/src/main/js/api/securityConfig.js
+33
-0
war/src/main/js/pluginSetupWizard.js
war/src/main/js/pluginSetupWizard.js
+6
-1
war/src/main/js/pluginSetupWizardGui.js
war/src/main/js/pluginSetupWizardGui.js
+156
-73
war/src/main/js/templates/firstUserPanel.hbs
war/src/main/js/templates/firstUserPanel.hbs
+16
-0
war/src/main/js/templates/incompleteInstallationPanel.hbs
war/src/main/js/templates/incompleteInstallationPanel.hbs
+0
-1
war/src/main/js/templates/offlinePanel.hbs
war/src/main/js/templates/offlinePanel.hbs
+9
-6
war/src/main/js/templates/pluginSelectionPanel.hbs
war/src/main/js/templates/pluginSelectionPanel.hbs
+0
-1
war/src/main/js/templates/proxyConfigPanel.hbs
war/src/main/js/templates/proxyConfigPanel.hbs
+16
-0
war/src/main/js/templates/setupCompletePanel.hbs
war/src/main/js/templates/setupCompletePanel.hbs
+22
-0
war/src/main/js/templates/successPanel.hbs
war/src/main/js/templates/successPanel.hbs
+2
-3
war/src/main/js/templates/welcomePanel.hbs
war/src/main/js/templates/welcomePanel.hbs
+0
-1
war/src/main/js/util/jenkins.js
war/src/main/js/util/jenkins.js
+112
-24
war/src/main/less/pluginSetupWizard.less
war/src/main/less/pluginSetupWizard.less
+625
-606
war/src/test/js/pluginSetupWizard-spec.js
war/src/test/js/pluginSetupWizard-spec.js
+123
-110
未找到文件。
core/src/main/java/hudson/PluginManager.java
浏览文件 @
6720a412
...
...
@@ -1189,7 +1189,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
break
;
}
updateCenter
.
persistInstallStatus
();
jenkins
.
setInstallState
(
InstallState
.
INITIAL_PLUGINS_INSTALL
ED
);
jenkins
.
setInstallState
(
InstallState
.
INITIAL_PLUGINS_INSTALL
ING
.
getNextState
()
);
InstallUtil
.
saveLastExecVersion
();
}
}.
start
();
...
...
core/src/main/java/hudson/model/UpdateCenter.java
浏览文件 @
6720a412
...
...
@@ -57,7 +57,8 @@ import jenkins.install.InstallState;
import
jenkins.install.InstallUtil
;
import
jenkins.model.Jenkins
;
import
jenkins.util.io.OnMaster
;
import
net.sf.json.JSONArray
;
import
net.sf.json.JSONObject
;
import
org.acegisecurity.Authentication
;
import
org.acegisecurity.context.SecurityContext
;
import
org.apache.commons.codec.binary.Base64
;
...
...
@@ -73,7 +74,6 @@ import javax.annotation.Nonnull;
import
javax.net.ssl.SSLHandshakeException
;
import
javax.servlet.ServletException
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.io.OutputStream
;
...
...
@@ -81,12 +81,12 @@ import java.net.MalformedURLException;
import
java.net.URL
;
import
java.net.URLConnection
;
import
java.net.UnknownHostException
;
import
java.security.DigestInputStream
;
import
java.security.DigestOutputStream
;
import
java.security.MessageDigest
;
import
java.security.NoSuchAlgorithmException
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
...
...
@@ -131,7 +131,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST;
*/
@ExportedBean
public
class
UpdateCenter
extends
AbstractModelObject
implements
Saveable
,
OnMaster
{
private
static
final
String
UPDATE_CENTER_URL
=
System
.
getProperty
(
UpdateCenter
.
class
.
getName
()+
".updateCenterUrl"
,
"http://updates.jenkins-ci.org/"
);
/**
...
...
@@ -142,7 +142,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
@Restricted
(
NoExternalUse
.
class
)
public
static
final
String
ID_UPLOAD
=
"_upload"
;
/**
* {@link ExecutorService} that performs installation.
* @since 1.501
...
...
@@ -155,7 +155,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
*/
protected
final
ExecutorService
updateService
=
Executors
.
newCachedThreadPool
(
new
NamingThreadFactory
(
new
DaemonThreadFactory
(),
"Update site data downloader"
));
/**
* List of created {@link UpdateCenterJob}s. Access needs to be synchronized.
*/
...
...
@@ -302,6 +302,27 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
}
}
if
(
checkJob
!=
null
)
{
boolean
isOffline
=
false
;
for
(
ConnectionStatus
status
:
checkJob
.
connectionStates
.
values
())
{
if
(
ConnectionStatus
.
FAILED
.
equals
(
status
))
{
isOffline
=
true
;
break
;
}
}
if
(
isOffline
)
{
// retry connection states if determined to be offline
checkJob
.
run
();
isOffline
=
false
;
for
(
ConnectionStatus
status
:
checkJob
.
connectionStates
.
values
())
{
if
(
ConnectionStatus
.
FAILED
.
equals
(
status
))
{
isOffline
=
true
;
break
;
}
}
if
(!
isOffline
)
{
// also need to download the metadata
updateAllSites
();
}
}
return
HttpResponses
.
okJSON
(
checkJob
.
connectionStates
);
}
else
{
return
HttpResponses
.
errorJSON
(
String
.
format
(
"Unknown site '%s'."
,
siteId
));
...
...
@@ -311,29 +332,16 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
}
}
/**
* Called to bypass install wizard
*/
@Restricted
(
DoNotUse
.
class
)
// WebOnly
public
HttpResponse
doCompleteInstall
()
{
if
(
isRestartRequiredForCompletion
())
{
Jenkins
.
getActiveInstance
().
setInstallState
(
InstallState
.
RESTART
);
}
InstallUtil
.
saveLastExecVersion
();
Jenkins
.
getActiveInstance
().
setInstallState
(
InstallState
.
INITIAL_PLUGINS_INSTALLED
);
return
HttpResponses
.
okJSON
();
}
/**
* Called to determine if there was an incomplete installation, what the statuses of the plugins are
*/
@Restricted
(
DoNotUse
.
class
)
// WebOnly
public
HttpResponse
doIncompleteInstallStatus
()
{
try
{
Map
<
String
,
String
>
jobs
=
InstallUtil
.
getPersistedInstallStatus
();
if
(
jobs
==
null
)
{
jobs
=
Collections
.
emptyMap
();
}
Map
<
String
,
String
>
jobs
=
InstallUtil
.
getPersistedInstallStatus
();
if
(
jobs
==
null
)
{
jobs
=
Collections
.
emptyMap
();
}
return
HttpResponses
.
okJSON
(
jobs
);
}
catch
(
Exception
e
)
{
return
HttpResponses
.
errorJSON
(
String
.
format
(
"ERROR: %s"
,
e
.
getMessage
()));
...
...
@@ -353,16 +361,16 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
if
(
job
instanceof
InstallationJob
)
{
InstallationJob
installationJob
=
(
InstallationJob
)
job
;
if
(!
installationJob
.
status
.
isSuccess
())
{
activeInstalls
=
true
;
activeInstalls
=
true
;
}
}
}
if
(
activeInstalls
)
{
InstallUtil
.
persistInstallStatus
(
jobs
);
// save this info
InstallUtil
.
persistInstallStatus
(
jobs
);
// save this info
}
else
{
InstallUtil
.
clearInstallStatus
();
// clear this info
InstallUtil
.
clearInstallStatus
();
// clear this info
}
}
...
...
@@ -379,7 +387,10 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
public
HttpResponse
doInstallStatus
(
StaplerRequest
request
)
{
try
{
String
correlationId
=
request
.
getParameter
(
"correlationId"
);
Map
<
String
,
Object
>
response
=
new
HashMap
<>();
response
.
put
(
"state"
,
Jenkins
.
getInstance
().
getInstallState
().
name
());
List
<
Map
<
String
,
String
>>
installStates
=
new
ArrayList
<>();
response
.
put
(
"jobs"
,
installStates
);
List
<
UpdateCenterJob
>
jobCopy
=
getJobs
();
for
(
UpdateCenterJob
job
:
jobCopy
)
{
...
...
@@ -400,7 +411,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
}
}
}
return
HttpResponses
.
okJSON
(
JSON
Array
.
fromObject
(
installStates
));
return
HttpResponses
.
okJSON
(
JSON
Object
.
fromObject
(
response
));
}
catch
(
Exception
e
)
{
return
HttpResponses
.
errorJSON
(
String
.
format
(
"ERROR: %s"
,
e
.
getMessage
()));
}
...
...
@@ -558,7 +569,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
}
response
.
sendRedirect2
(
"."
);
}
/**
* Cancel all scheduled jenkins restarts
*/
...
...
@@ -845,16 +856,16 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
return
new
ArrayList
<
Plugin
>(
pluginMap
.
values
());
}
/**
* Ensure that all UpdateSites are up to date, without requiring a user to
* browse to the instance.
*
*
* @return a list of {@link FormValidation} for each updated Update Site
* @throws ExecutionException
* @throws InterruptedException
* @throws ExecutionException
* @throws InterruptedException
* @since 1.501
*
*
*/
public
List
<
FormValidation
>
updateAllSites
()
throws
InterruptedException
,
ExecutionException
{
List
<
Future
<
FormValidation
>>
futures
=
new
ArrayList
<
Future
<
FormValidation
>>();
...
...
@@ -864,8 +875,8 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
futures
.
add
(
future
);
}
}
List
<
FormValidation
>
results
=
new
ArrayList
<
FormValidation
>();
List
<
FormValidation
>
results
=
new
ArrayList
<
FormValidation
>();
for
(
Future
<
FormValidation
>
f
:
futures
)
{
results
.
add
(
f
.
get
());
}
...
...
@@ -1211,7 +1222,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
public
String
getErrorMessage
()
{
return
error
!=
null
?
error
.
getMessage
()
:
null
;
}
public
Throwable
getError
()
{
return
error
;
}
...
...
@@ -1226,10 +1237,10 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
*/
@Exported
(
inline
=
true
)
public
volatile
RestartJenkinsJobStatus
status
=
new
Pending
();
/**
* Cancel job
*/
*/
public
synchronized
boolean
cancel
()
{
if
(
status
instanceof
Pending
)
{
status
=
new
Canceled
();
...
...
@@ -1237,7 +1248,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
}
return
false
;
}
public
RestartJenkinsJob
(
UpdateSite
site
)
{
super
(
site
);
}
...
...
@@ -1260,26 +1271,26 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
public
abstract
class
RestartJenkinsJobStatus
{
@Exported
public
final
int
id
=
iota
.
incrementAndGet
();
}
public
class
Pending
extends
RestartJenkinsJobStatus
{
@Exported
public
String
getType
()
{
return
getClass
().
getSimpleName
();
}
}
public
class
Running
extends
RestartJenkinsJobStatus
{
}
public
class
Failure
extends
RestartJenkinsJobStatus
{
}
public
class
Canceled
extends
RestartJenkinsJobStatus
{
}
}
...
...
@@ -1603,7 +1614,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
File
baseDir
=
pm
.
rootDir
;
return
new
File
(
baseDir
,
plugin
.
name
+
".jpi"
);
}
private
File
getLegacyDestination
()
{
File
baseDir
=
pm
.
rootDir
;
return
new
File
(
baseDir
,
plugin
.
name
+
".hpi"
);
...
...
@@ -1649,7 +1660,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
public
String
toString
()
{
return
super
.
toString
()+
"[plugin="
+
plugin
.
title
+
"]"
;
}
/**
* Called when the download is completed to overwrite
* the old file with the new file.
...
...
@@ -1704,7 +1715,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
File
baseDir
=
pm
.
rootDir
;
final
File
legacy
=
new
File
(
baseDir
,
plugin
.
name
+
".hpi"
);
if
(
legacy
.
exists
()){
return
legacy
;
return
legacy
;
}
return
new
File
(
baseDir
,
plugin
.
name
+
".jpi"
);
}
...
...
core/src/main/java/hudson/security/FullControlOnceLoggedInAuthorizationStrategy.java
浏览文件 @
6720a412
...
...
@@ -29,6 +29,7 @@ import jenkins.model.Jenkins;
import
org.kohsuke.accmod.Restricted
;
import
org.kohsuke.accmod.restrictions.NoExternalUse
;
import
org.kohsuke.stapler.DataBoundConstructor
;
import
org.kohsuke.stapler.DataBoundSetter
;
import
javax.inject.Inject
;
import
java.util.Collections
;
...
...
@@ -36,30 +37,52 @@ import java.util.List;
/**
* {@link AuthorizationStrategy} that grants full-control to authenticated user
*
(other than anonymous users.)
*
and optionally read access to anonymous users
*
* @author Kohsuke Kawaguchi
*/
public
class
FullControlOnceLoggedInAuthorizationStrategy
extends
AuthorizationStrategy
{
/**
* Whether to allow anonymous read access, for backward compatibility
* default is to allow it
*/
private
boolean
denyAnonymousReadAccess
=
false
;
@DataBoundConstructor
public
FullControlOnceLoggedInAuthorizationStrategy
()
{
}
@Override
public
ACL
getRootACL
()
{
return
THE_ACL
;
return
denyAnonymousReadAccess
?
AUTHENTICATED_READ
:
ANONYMOUS_READ
;
}
public
List
<
String
>
getGroups
()
{
return
Collections
.
emptyList
();
}
/**
* If true, anonymous read access will be allowed
*/
public
boolean
isAllowAnonymousRead
()
{
return
!
denyAnonymousReadAccess
;
}
@DataBoundSetter
public
void
setAllowAnonymousRead
(
boolean
allowAnonymousRead
)
{
this
.
denyAnonymousReadAccess
=
!
allowAnonymousRead
;
}
private
static
final
SparseACL
THE_ACL
=
new
SparseACL
(
null
);
private
static
final
SparseACL
AUTHENTICATED_READ
=
new
SparseACL
(
null
);
private
static
final
SparseACL
ANONYMOUS_READ
=
new
SparseACL
(
null
);
static
{
THE_ACL
.
add
(
ACL
.
EVERYONE
,
Jenkins
.
ADMINISTER
,
true
);
THE_ACL
.
add
(
ACL
.
ANONYMOUS
,
Jenkins
.
ADMINISTER
,
false
);
THE_ACL
.
add
(
ACL
.
ANONYMOUS
,
Permission
.
READ
,
true
);
ANONYMOUS_READ
.
add
(
ACL
.
EVERYONE
,
Jenkins
.
ADMINISTER
,
true
);
ANONYMOUS_READ
.
add
(
ACL
.
ANONYMOUS
,
Jenkins
.
ADMINISTER
,
false
);
ANONYMOUS_READ
.
add
(
ACL
.
ANONYMOUS
,
Permission
.
READ
,
true
);
AUTHENTICATED_READ
.
add
(
ACL
.
EVERYONE
,
Jenkins
.
ADMINISTER
,
true
);
AUTHENTICATED_READ
.
add
(
ACL
.
ANONYMOUS
,
Jenkins
.
ADMINISTER
,
false
);
}
/**
...
...
core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
浏览文件 @
6720a412
/*
* The MIT License
*
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, David Calavera, Seiji Sogabe
*
*
* 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
...
...
@@ -29,6 +29,8 @@ import hudson.ExtensionList;
import
hudson.Util
;
import
hudson.diagnosis.OldDataMonitor
;
import
hudson.model.Descriptor
;
import
jenkins.install.InstallState
;
import
jenkins.install.SetupWizard
;
import
jenkins.model.Jenkins
;
import
hudson.model.ManagementLink
;
import
hudson.model.ModelObject
;
...
...
@@ -97,7 +99,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
/**
* If true, sign up is not allowed.
* <p>
* This is a negative switch so that the default value 'false' remains compatible with older installations.
* This is a negative switch so that the default value 'false' remains compatible with older installations.
*/
private
final
boolean
disableSignup
;
...
...
@@ -279,14 +281,36 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
* This can be run by anyone, but only to create the very first user account.
*/
public
void
doCreateFirstAccount
(
StaplerRequest
req
,
StaplerResponse
rsp
)
throws
IOException
,
ServletException
{
if
(
hasSomeUser
())
{
boolean
inSetup
=
!
Jenkins
.
getInstance
().
getInstallState
().
isSetupComplete
();
if
(!
inSetup
&&
hasSomeUser
())
{
rsp
.
sendError
(
SC_UNAUTHORIZED
,
"First user was already created"
);
return
;
}
User
u
=
createAccount
(
req
,
rsp
,
false
,
"firstUser.jelly"
);
if
(
u
!=
null
)
{
tryToMakeAdmin
(
u
);
loginAndTakeBack
(
req
,
rsp
,
u
);
User
admin
=
null
;
try
{
String
view
=
"firstUser.jelly"
;
if
(
inSetup
)
{
admin
=
getUser
(
SetupWizard
.
initialSetupAdminUserName
);
if
(
admin
!=
null
)
{
admin
.
delete
();
// assume the new user may well be 'admin'
}
view
=
"setupWizardFirstUser.jelly"
;
}
User
u
=
createAccount
(
req
,
rsp
,
false
,
view
);
if
(
u
!=
null
)
{
tryToMakeAdmin
(
u
);
if
(
admin
!=
null
)
{
admin
=
null
;
}
Jenkins
.
getInstance
().
setInstallState
(
InstallState
.
CREATE_ADMIN_USER
.
getNextState
());
loginAndTakeBack
(
req
,
rsp
,
u
);
}
}
finally
{
if
(
admin
!=
null
)
{
admin
.
save
();
// recreate this initial user if something failed
}
}
}
...
...
core/src/main/java/jenkins/install/InstallState.java
浏览文件 @
6720a412
...
...
@@ -34,32 +34,63 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
@Restricted
(
NoExternalUse
.
class
)
public
enum
InstallState
{
/**
* New Jenkins install.
* The initial set up has been completed
*/
INITIAL_SETUP_COMPLETED
(
true
,
null
),
/**
* Creating an admin user for an initial Jenkins install.
*/
NEW
,
CREATE_ADMIN_USER
(
false
,
INITIAL_SETUP_COMPLETED
)
,
/**
* New Jenkins install. The user has kicked off the process of installing an
* initial set of plugins (via the install wizard).
*/
INITIAL_PLUGINS_INSTALLING
,
INITIAL_PLUGINS_INSTALLING
(
false
,
CREATE_ADMIN_USER
)
,
/**
* New Jenkins install.
The initial set of plugins are now installed.
* New Jenkins install.
*/
INITIAL_PLUGINS_INSTALLED
,
NEW
(
false
,
INITIAL_PLUGINS_INSTALLING
)
,
/**
* Restart of an existing Jenkins install.
*/
RESTART
,
RESTART
(
true
,
INITIAL_SETUP_COMPLETED
)
,
/**
* Upgrade of an existing Jenkins install.
*/
UPGRADE
,
UPGRADE
(
true
,
INITIAL_SETUP_COMPLETED
)
,
/**
* Downgrade of an existing Jenkins install.
*/
DOWNGRADE
,
DOWNGRADE
(
true
,
INITIAL_SETUP_COMPLETED
)
,
/**
* Jenkins started in test mode (JenkinsRule).
*/
TEST
TEST
(
true
,
INITIAL_SETUP_COMPLETED
),
/**
* Jenkins started in development mode: Bolean.getBoolean("hudson.Main.development").
* Can be run normally with the -Djenkins.install.runSetupWizard=true
*/
DEVELOPMENT
(
true
,
INITIAL_SETUP_COMPLETED
);
private
final
boolean
isSetupComplete
;
private
final
InstallState
nextState
;
private
InstallState
(
boolean
isSetupComplete
,
InstallState
nextState
)
{
this
.
isSetupComplete
=
isSetupComplete
;
this
.
nextState
=
nextState
;
}
/**
* Indicates the initial setup is complete
*/
public
boolean
isSetupComplete
()
{
return
isSetupComplete
;
}
/**
* Gets the next state
*/
public
InstallState
getNextState
()
{
return
nextState
;
}
}
core/src/main/java/jenkins/install/InstallUtil.java
浏览文件 @
6720a412
...
...
@@ -61,6 +61,7 @@ public class InstallUtil {
private
static
final
Logger
LOGGER
=
Logger
.
getLogger
(
InstallUtil
.
class
.
getName
());
// tests need this to be 1.0
private
static
final
VersionNumber
NEW_INSTALL_VERSION
=
new
VersionNumber
(
"1.0"
);
/**
...
...
@@ -68,8 +69,15 @@ public class InstallUtil {
* @return The type of "startup" currently under way in Jenkins.
*/
public
static
InstallState
getInstallState
()
{
if
(
Functions
.
getIsUnitTest
())
{
return
InstallState
.
TEST
;
// install wizard will always run if environment specified
if
(!
Boolean
.
getBoolean
(
"jenkins.install.runSetupWizard"
))
{
if
(
Functions
.
getIsUnitTest
())
{
return
InstallState
.
TEST
;
}
if
(
Boolean
.
getBoolean
(
"hudson.Main.development"
))
{
return
InstallState
.
DEVELOPMENT
;
}
}
VersionNumber
lastRunVersion
=
new
VersionNumber
(
getLastExecVersion
());
...
...
@@ -77,6 +85,12 @@ public class InstallUtil {
// Neither the top level config or the lastExecVersionFile have a version
// stored in them, which means it's a new install.
if
(
lastRunVersion
.
compareTo
(
NEW_INSTALL_VERSION
)
==
0
)
{
// Edge case: used Jenkins 1 but did not save the system config page,
// the version is not persisted and returns 1.0, so try to check if
// they actually did anything
if
(!
Jenkins
.
getInstance
().
getItemMap
().
isEmpty
())
{
return
InstallState
.
UPGRADE
;
}
return
InstallState
.
NEW
;
}
...
...
core/src/main/java/jenkins/install/SetupWizard.java
0 → 100644
浏览文件 @
6720a412
package
jenkins.install
;
import
java.io.IOException
;
import
java.util.Locale
;
import
java.util.UUID
;
import
java.util.logging.Logger
;
import
javax.servlet.Filter
;
import
javax.servlet.FilterChain
;
import
javax.servlet.FilterConfig
;
import
javax.servlet.ServletException
;
import
javax.servlet.ServletRequest
;
import
javax.servlet.ServletResponse
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.servlet.http.HttpServletRequestWrapper
;
import
org.kohsuke.stapler.HttpResponse
;
import
org.kohsuke.stapler.StaplerRequest
;
import
org.kohsuke.stapler.StaplerResponse
;
import
hudson.BulkChange
;
import
hudson.ExtensionList
;
import
hudson.model.User
;
import
hudson.model.UserProperty
;
import
hudson.security.FullControlOnceLoggedInAuthorizationStrategy
;
import
hudson.security.HudsonPrivateSecurityRealm
;
import
hudson.security.PermissionAdder
;
import
hudson.security.SecurityRealm
;
import
hudson.security.csrf.DefaultCrumbIssuer
;
import
hudson.util.HttpResponses
;
import
hudson.util.PluginServletFilter
;
import
jenkins.model.Jenkins
;
import
jenkins.security.s2m.AdminWhitelistRule
;
/**
* A Jenkins instance used during first-run to provide a limited set of services while
* initial installation is in progress
*/
public
class
SetupWizard
{
/**
* The security token parameter name
*/
public
static
String
initialSetupAdminUserName
=
"admin"
;
private
final
Logger
LOGGER
=
Logger
.
getLogger
(
SetupWizard
.
class
.
getName
());
public
SetupWizard
(
Jenkins
j
)
throws
IOException
{
User
admin
;
// Create an admin user by default with a
// difficult password
if
(
j
.
getSecurityRealm
()
==
null
||
j
.
getSecurityRealm
()
==
SecurityRealm
.
NO_AUTHENTICATION
)
{
// this seems very fragile
BulkChange
bc
=
new
BulkChange
(
j
);
HudsonPrivateSecurityRealm
securityRealm
=
new
HudsonPrivateSecurityRealm
(
false
,
false
,
null
);
j
.
setSecurityRealm
(
securityRealm
);
String
randomUUID
=
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
).
toLowerCase
(
Locale
.
ENGLISH
);
admin
=
securityRealm
.
createAccount
(
SetupWizard
.
initialSetupAdminUserName
,
randomUUID
);
admin
.
addProperty
(
new
SetupWizard
.
AuthenticationKey
(
randomUUID
));
// Lock Jenkins down:
FullControlOnceLoggedInAuthorizationStrategy
authStrategy
=
new
FullControlOnceLoggedInAuthorizationStrategy
();
authStrategy
.
setAllowAnonymousRead
(
false
);
j
.
setAuthorizationStrategy
(
authStrategy
);
// Shut down all the ports we can by default:
j
.
setSlaveAgentPort
(-
1
);
// -1 to disable
// require a crumb issuer
j
.
setCrumbIssuer
(
new
DefaultCrumbIssuer
(
false
));
// set master -> slave security:
j
.
getInjector
().
getInstance
(
AdminWhitelistRule
.
class
)
.
setMasterKillSwitch
(
false
);
try
{
j
.
save
();
// !!
}
finally
{
bc
.
commit
();
}
}
else
{
admin
=
j
.
getUser
(
SetupWizard
.
initialSetupAdminUserName
);
}
String
setupKey
=
null
;
if
(
admin
!=
null
&&
admin
.
getProperty
(
SetupWizard
.
AuthenticationKey
.
class
)
!=
null
)
{
setupKey
=
admin
.
getProperty
(
SetupWizard
.
AuthenticationKey
.
class
).
getKey
();
}
if
(
setupKey
!=
null
)
{
LOGGER
.
info
(
"\n\n*************************************************************\n"
+
"*************************************************************\n"
+
"*************************************************************\n"
+
"\n"
+
"Jenkins initial setup is required. A security token is required to proceed. \n"
+
"Please use the following security token to proceed to installation: \n"
+
"\n"
+
""
+
setupKey
+
"\n"
+
"\n"
+
"*************************************************************\n"
+
"*************************************************************\n"
+
"*************************************************************\n"
);
}
try
{
PluginServletFilter
.
addFilter
(
FORCE_SETUP_WIZARD_FILTER
);
}
catch
(
ServletException
e
)
{
throw
new
AssertionError
(
e
);
}
}
/**
* Remove the setupWizard filter, ensure all updates are written to disk, etc
*/
public
HttpResponse
doCompleteInstall
(
StaplerRequest
req
,
StaplerResponse
rsp
)
throws
IOException
,
ServletException
{
Jenkins
j
=
Jenkins
.
getActiveInstance
();
j
.
setInstallState
(
InstallState
.
INITIAL_SETUP_COMPLETED
);
InstallUtil
.
saveLastExecVersion
();
PluginServletFilter
.
removeFilter
(
FORCE_SETUP_WIZARD_FILTER
);
// Also, clean up the setup wizard if it's completed
j
.
setSetupWizard
(
null
);
return
HttpResponses
.
okJSON
();
}
// Stores a user property for the authentication key, which is really the auto-generated user's password
public
static
class
AuthenticationKey
extends
UserProperty
{
String
key
;
public
AuthenticationKey
()
{
}
public
AuthenticationKey
(
String
key
)
{
this
.
key
=
key
;
}
public
String
getKey
()
{
return
key
;
}
public
void
setKey
(
String
key
)
{
this
.
key
=
key
;
}
}
/**
* This filter will validate that the security token is provided
*/
private
final
Filter
FORCE_SETUP_WIZARD_FILTER
=
new
Filter
()
{
@Override
public
void
init
(
FilterConfig
cfg
)
throws
ServletException
{
}
@Override
public
void
doFilter
(
ServletRequest
request
,
ServletResponse
response
,
FilterChain
chain
)
throws
IOException
,
ServletException
{
// As an extra measure of security, the install wizard generates a security token, and
// requires the user to enter it before proceeding through the installation. Once set
// we'll set a cookie so the subsequent operations succeed
if
(
request
instanceof
HttpServletRequest
)
{
HttpServletRequest
req
=
(
HttpServletRequest
)
request
;
//if (!Pattern.compile(".*[.](css|ttf|gif|woff|eot|png|js)").matcher(req.getRequestURI()).matches()) {
// Allow js & css requests through
if
((
req
.
getContextPath
()
+
"/"
).
equals
(
req
.
getRequestURI
()))
{
chain
.
doFilter
(
new
HttpServletRequestWrapper
(
req
)
{
public
String
getRequestURI
()
{
return
getContextPath
()
+
"/setupWizard/"
;
}
},
response
);
return
;
}
// fall through to handling the request normally
}
chain
.
doFilter
(
request
,
response
);
}
@Override
public
void
destroy
()
{
}
};
}
core/src/main/java/jenkins/model/Jenkins.java
浏览文件 @
6720a412
...
...
@@ -194,6 +194,7 @@ import jenkins.ExtensionRefreshException;
import
jenkins.InitReactorRunner
;
import
jenkins.install.InstallState
;
import
jenkins.install.InstallUtil
;
import
jenkins.install.SetupWizard
;
import
jenkins.model.ProjectNamingStrategy.DefaultProjectNamingStrategy
;
import
jenkins.security.ConfidentialKey
;
import
jenkins.security.ConfidentialStore
;
...
...
@@ -333,7 +334,13 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
/**
* The Jenkins instance startup type i.e. NEW, UPGRADE etc
*/
private
InstallState
installState
;
private
transient
InstallState
installState
=
InstallState
.
NEW
;
/**
* If we're in the process of an initial setup,
* this will be set
*/
private
transient
SetupWizard
setupWizard
;
/**
* Number of executors of the master node.
...
...
@@ -759,7 +766,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
protected
Jenkins
(
File
root
,
ServletContext
context
,
PluginManager
pluginManager
)
throws
IOException
,
InterruptedException
,
ReactorException
{
long
start
=
System
.
currentTimeMillis
();
// As Jenkins is starting, grant this process full control
// As Jenkins is starting, grant this process full control
ACL
.
impersonate
(
ACL
.
SYSTEM
);
try
{
this
.
root
=
root
;
...
...
@@ -773,7 +780,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
if
(
installState
==
InstallState
.
RESTART
||
installState
==
InstallState
.
DOWNGRADE
)
{
InstallUtil
.
saveLastExecVersion
();
}
if
(!
new
File
(
root
,
"jobs"
).
exists
())
{
// if this is a fresh install, use more modern default layout that's consistent with agents
workspaceDir
=
"${JENKINS_HOME}/workspace/${ITEM_FULLNAME}"
;
...
...
@@ -834,6 +841,11 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
if
(
KILL_AFTER_LOAD
)
System
.
exit
(
0
);
if
(!
installState
.
isSetupComplete
())
{
// Start immediately with the setup wizard for new installs
setupWizard
=
new
SetupWizard
(
this
);
}
launchTcpSlaveAgentListener
();
if
(
UDPBroadcastThread
.
PORT
!=
-
1
)
{
...
...
@@ -913,6 +925,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
* Get the Jenkins {@link jenkins.install.InstallState install state}.
* @return The Jenkins {@link jenkins.install.InstallState install state}.
*/
@Nonnull
@Restricted
(
NoExternalUse
.
class
)
public
InstallState
getInstallState
()
{
return
installState
;
...
...
@@ -1437,10 +1450,10 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
*/
@Exported
(
name
=
"jobs"
)
public
List
<
TopLevelItem
>
getItems
()
{
if
(
authorizationStrategy
instanceof
AuthorizationStrategy
.
Unsecured
||
authorizationStrategy
instanceof
FullControlOnceLoggedInAuthorizationStrategy
)
{
return
new
ArrayList
(
items
.
values
());
}
if
(
authorizationStrategy
instanceof
AuthorizationStrategy
.
Unsecured
||
authorizationStrategy
instanceof
FullControlOnceLoggedInAuthorizationStrategy
)
{
return
new
ArrayList
(
items
.
values
());
}
List
<
TopLevelItem
>
viewableItems
=
new
ArrayList
<
TopLevelItem
>();
for
(
TopLevelItem
item
:
items
.
values
())
{
...
...
@@ -1815,11 +1828,11 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
}
public
DescribableList
<
NodeProperty
<?>,
NodePropertyDescriptor
>
getNodeProperties
()
{
return
nodeProperties
;
return
nodeProperties
;
}
public
DescribableList
<
NodeProperty
<?>,
NodePropertyDescriptor
>
getGlobalNodeProperties
()
{
return
globalNodeProperties
;
return
globalNodeProperties
;
}
/**
...
...
@@ -2428,7 +2441,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
*/
@Override
public
TopLevelItem
getItem
(
String
name
)
throws
AccessDeniedException
{
if
(
name
==
null
)
return
null
;
TopLevelItem
item
=
items
.
get
(
name
);
TopLevelItem
item
=
items
.
get
(
name
);
if
(
item
==
null
)
return
null
;
if
(!
item
.
hasPermission
(
Item
.
READ
))
{
...
...
@@ -3079,10 +3092,10 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
}
public
HttpResponse
doToggleCollapse
()
throws
ServletException
,
IOException
{
final
StaplerRequest
request
=
Stapler
.
getCurrentRequest
();
final
String
paneId
=
request
.
getParameter
(
"paneId"
);
final
StaplerRequest
request
=
Stapler
.
getCurrentRequest
();
final
String
paneId
=
request
.
getParameter
(
"paneId"
);
PaneStatusProperties
.
forCurrentUser
().
toggleCollapsed
(
paneId
);
PaneStatusProperties
.
forCurrentUser
().
toggleCollapsed
(
paneId
);
return
HttpResponses
.
forwardToPreviousPage
();
}
...
...
@@ -3113,9 +3126,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
LOGGER
.
info
(
"Failed to get thread dump for node "
+
c
.
getName
()
+
": "
+
e
.
getMessage
());
}
}
if
(
toComputer
()
==
null
)
{
future
.
put
(
"master"
,
RemotingDiagnostics
.
getThreadDumpAsync
(
FilePath
.
localChannel
));
}
if
(
toComputer
()
==
null
)
{
future
.
put
(
"master"
,
RemotingDiagnostics
.
getThreadDumpAsync
(
FilePath
.
localChannel
));
}
// if the result isn't available in 5 sec, ignore that.
// this is a precaution against hang nodes
...
...
@@ -3891,6 +3904,20 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
public
List
<
ManagementLink
>
getManagementLinks
()
{
return
ManagementLink
.
all
();
}
/**
* If set, a currently active setup wizard - e.g. installation
*/
public
SetupWizard
getSetupWizard
()
{
return
setupWizard
;
}
/**
* Sets the setup wizard
*/
public
void
setSetupWizard
(
SetupWizard
setupWizard
)
{
this
.
setupWizard
=
setupWizard
;
}
/**
* Exposes the current user to <tt>/me</tt> URL.
...
...
core/src/main/resources/hudson/security/FullControlOnceLoggedInAuthorizationStrategy/config.jelly
0 → 100644
浏览文件 @
6720a412
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson"
xmlns:f="/lib/form">
<f:entry field="allowAnonymousRead">
<f:checkbox default="true" title="${%Allow anonymous read access}"/>
</f:entry>
</j:jelly>
core/src/main/resources/hudson/security/FullControlOnceLoggedInAuthorizationStrategy/help-allowAnonymousRead.html
0 → 100644
浏览文件 @
6720a412
<div>
If checked, this will allow users who are not authenticated to access Jenkins in a read-only mode.
</div>
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryForm.jelly
浏览文件 @
6720a412
...
...
@@ -25,64 +25,43 @@ THE SOFTWARE.
<!-- tag file sed by both signup.jelly and addUser.jelly -->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:layout norefresh="true" title="${%Sign up}">
<l:header>
<style>
<!-- match width with captcha image -->
INPUT {
width:200px;
}
</style>
</l:header>
<l:hasPermission permission="${app.READ}" it="${host}">
<st:include page="sidepanel.jelly" it="${host}" />
</l:hasPermission>
<l:main-panel>
<h1>${title}</h1>
<div style="margin: 2em;">
<j:if test="${data.errorMessage!=null}">
<div class="error" style="margin-bottom:1em">
${data.errorMessage}
</div>
</j:if>
<form action="${rootURL}/securityRealm/${action}" method="post" style="text-size:smaller">
<table>
<tr>
<td>${%Username}:</td>
<td><input type="text" name="username" id="username" value="${data.username}" /></td>
</tr>
<tr>
<td>${%Password}:</td>
<td><input type="password" name="password1" value="${data.password1}" /></td>
</tr>
<tr>
<td>${%Confirm password}:</td>
<td><input type="password" name="password2" value="${data.password2}" /></td>
</tr>
<tr>
<td>${%Full name}:</td>
<td><input type="text" name="fullname" value="${data.fullname}" /></td>
</tr>
<tr>
<td>${%E-mail address}:</td>
<td><input type="text" name="email" value="${data.email}" /></td>
</tr>
<j:if test="${captcha}">
<tr>
<td>${%Enter text as shown}:</td>
<td>
<input type="text" name="captcha" autocomplete="off" /><br />
<img src="${rootURL}/securityRealm/captcha" alt="[captcha]"/>
</td>
</tr>
</j:if>
</table>
<f:submit value="${title}" />
<script>
$('username').focus();
</script>
</form>
<h1>${title}</h1>
<div style="margin: 2em;">
<j:if test="${data.errorMessage!=null}">
<div class="error" style="margin-bottom:1em">
${data.errorMessage}
</div>
</l:main-panel>
</l:layout>
</j:if>
<table>
<tr>
<td>${%Username}:</td>
<td><input type="text" name="username" id="username" value="${data.username}" /></td>
</tr>
<tr>
<td>${%Password}:</td>
<td><input type="password" name="password1" value="${data.password1}" /></td>
</tr>
<tr>
<td>${%Confirm password}:</td>
<td><input type="password" name="password2" value="${data.password2}" /></td>
</tr>
<tr>
<td>${%Full name}:</td>
<td><input type="text" name="fullname" value="${data.fullname}" /></td>
</tr>
<tr>
<td>${%E-mail address}:</td>
<td><input type="text" name="email" value="${data.email}" /></td>
</tr>
<j:if test="${captcha}">
<tr>
<td>${%Enter text as shown}:</td>
<td>
<input type="text" name="captcha" autocomplete="off" /><br />
<img src="${rootURL}/securityRealm/captcha" alt="[captcha]"/>
</td>
</tr>
</j:if>
</table>
</div>
</j:jelly>
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/_entryFormPage.jelly
0 → 100644
浏览文件 @
6720a412
<!--
The MIT License
Copyright (c) 2004-2016, Sun Microsystems, Inc., CloudBees, Inc., Kohsuke Kawaguchi, Seiji Sogabe
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.
-->
<!-- tag file used by both signup.jelly and addUser.jelly -->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:layout norefresh="true" title="${title}">
<l:header>
<style>
<!-- match width with captcha image -->
INPUT {
width:200px;
}
</style>
</l:header>
<l:hasPermission permission="${app.READ}" it="${host}">
<st:include page="sidepanel.jelly" it="${host}" />
</l:hasPermission>
<l:main-panel>
<form action="${rootURL}/securityRealm/${action}" method="post" style="text-size:smaller">
<local:_entryForm title="${title}" action="${action}" captcha="${captcha}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<f:submit value="${title}" />
<script>
$('username').focus();
</script>
</form>
</l:main-panel>
</l:layout>
</j:jelly>
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/addUser.jelly
浏览文件 @
6720a412
...
...
@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:_entryForm host="${it}" title="${%Create User}" action="createAccountByAdmin" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<local:_entryForm
Page
host="${it}" title="${%Create User}" action="createAccountByAdmin" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>
\ No newline at end of file
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/firstUser.jelly
浏览文件 @
6720a412
...
...
@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:_entryForm host="${it}" title="${%Create First Admin User}" action="createFirstAccount" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<local:_entryForm
Page
host="${it}" title="${%Create First Admin User}" action="createFirstAccount" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>
\ No newline at end of file
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/setupWizardFirstUser.jelly
0 → 100644
浏览文件 @
6720a412
<!--
This is used to create the first user.
-->
<?jelly escape-by-default='true'?>
<l:html norefresh="true" title="${it.displayName}" xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:main-panel>
<style type="text/css">
@import url(https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css);
@import url(https://fonts.googleapis.com/css?family=Roboto:400,300,500,900,700);
#main-panel {
margin: 0;
padding: 0;
}
tr td {
padding-bottom: 2px;
}
body {
padding: 20px 20px 20px 100px;
font-family: 'roboto';
}
form > div {
margin: 0 !important;
}
h1 {
font-family: 'roboto', sans-serif;
font-size: 48px;
margin-top: 30px;
font-weight: 500;
}
h1 img {
position: absolute;
right: -80px;
top: 38px;
}
tr td, input {
line-height: 25px;
margin-bottom: 6px;
}
input[type=text], input[type=password] {
border: 1px solid #ddd;
border-radius: 2px;
padding: 1px 8px;
}
</style>
<form action="${rootURL}/securityRealm/${action}" method="post">
<local:_entryForm host="${app.securityRealm}" title="${%Create First Admin User}" action="createFirstAccount" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<script>
$('username').focus();
</script>
</form>
</l:main-panel>
</l:html>
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly
浏览文件 @
6720a412
...
...
@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:_entryForm host="${app}" title="${%Sign up}" action="createAccount" captcha="${it.isEnableCaptcha()}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<local:_entryForm
Page
host="${app}" title="${%Sign up}" action="createAccount" captcha="${it.isEnableCaptcha()}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>
core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signupWithFederatedIdentity.jelly
浏览文件 @
6720a412
...
...
@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:_entryForm host="${app}" title="${%Sign up}" action="createAccountWithFederatedIdentity" captcha="${true}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<local:_entryForm
Page
host="${app}" title="${%Sign up}" action="createAccountWithFederatedIdentity" captcha="${true}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>
\ No newline at end of file
core/src/main/resources/jenkins/install/SetupWizard/authenticate-security-token.jelly
0 → 100644
浏览文件 @
6720a412
<?jelly escape-by-default='true'?>
<l:html norefresh="true" title="${app.instance.displayName}" xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:main-panel>
<form action="${app.instance.securityRealm.authenticationGatewayUrl}" method="POST">
<div class="plugin-setup-wizard bootstrap-3">
<div class="modal fade in" style="display: block;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">${%Security Token}</h4>
</div>
<div class="modal-body">
<i class="water-mark icon-service"></i>
<div class="jumbotron welcome-panel offline">
<h1>${%Security Token}</h1>
<p>${%As an extra measure of security, please enter the setup security token to proceed.}</p>
<p>${%It can be found in the logs for this Jenkins instance.}</p>
<j:if test="${error}">
<div class="alert alert-danger">
<strong>${%ERROR:} </strong>
${%There is a problem with your security token, please check the server logs for the correct token}
</div>
</j:if>
<div class="form-group ${error ? 'has-error' : ''}">
<label class="control-label" for="security-token">${%Security token}</label>
<input name="j_username" value="${j.setupWizard.initialSetupAdminUserName}" type="hidden"/>
<input id="security-token" class="form-control" name="j_password"/>
<link rel="stylesheet" href="${j.installWizardPath}.css" type="text/css" />
</div>
</div>
</div>
<div class="modal-footer">
<input type="submit" class="btn btn-primary set-security-key" value="${%Continue}"/>
</div>
</div>
</div>
</div>
</div>
</form>
</l:main-panel>
</l:html>
core/src/main/resources/jenkins/install/SetupWizard/index.jelly
0 → 100644
浏览文件 @
6720a412
<?jelly escape-by-default='true'?>
<l:html norefresh="true" title="${it.displayName}" xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:main-panel>
<div class="plugin-setup-wizard-container"></div>
<script src="${resURL}/${j.installWizardPath}.js" type="text/javascript"/>
<link rel="stylesheet" href="${resURL}/${j.installWizardPath}.css" type="text/css" />
</l:main-panel>
</l:html>
core/src/main/resources/jenkins/install/SetupWizard/proxy-configuration.jelly
0 → 100644
浏览文件 @
6720a412
<?jelly escape-by-default='true'?>
<l:html norefresh="true" title="${it.displayName}" xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:main-panel>
<h1>${%HTTP Proxy Configuration}</h1>
<f:form method="post" action="/pluginManager/proxyConfigure" name="proxyConfigure">
<j:scope>
<j:set var="instance" value="${app.proxy}"/>
<j:set var="descriptor" value="${app.pluginManager.proxyDescriptor}"/>
<st:include from="${descriptor}" page="${descriptor.configPage}" />
</j:scope>
</f:form>
</l:main-panel>
</l:html>
core/src/main/resources/jenkins/install/pluginSetupWizard.properties
浏览文件 @
6720a412
...
...
@@ -10,7 +10,7 @@ installWizard_offline_message=This Jenkins instance appears to be offline. \
<p style="font-size:18px; margin-top: 6%">
\
For information about installing Jenkins without an internet connection, see the
\
<a href="https://wiki.jenkins-ci.org/display/JENKINS/Offline+Jenkins+Installation" target="_blank">Offline Jenkins Installation Documentation</a>. <br/><br/>
\
If you need to configure a proxy, you may close this wizard and use the Plugin Manager
.
\
You may choose to continue by configuring a proxy or skipping plugin installation
.
\
</p>
installWizard_error_header
=
An error occurred
installWizard_error_message
=
An error occurred during installation:
...
...
@@ -28,7 +28,8 @@ installWizard_installing_title=Installing...
installWizard_installing_detailsLink
=
Details...
installWizard_installComplete_title
=
Installed
installWizard_installComplete_banner
=
Jenkins is ready!
installWizard_installComplete_message
=
Your plugin installations are complete.
installWizard_pluginsInstalled_message
=
Your plugin installations are complete.
installWizard_installComplete_message
=
Your Jenkins installation is complete.
installWizard_installComplete_finishButtonLabel
=
Get Started
installWizard_installComplete_restartRequiredMessage
=
Some plugins require Jenkins to be restarted.
installWizard_installComplete_restartLabel
=
Restart
...
...
@@ -36,4 +37,14 @@ installWizard_installIncomplete_title=Resume Installation
installWizard_installIncomplete_banner
=
Resume Installation
installWizard_installIncomplete_message
=
Jenknins was restarted during installation and some plugins didn't seem to get installed.
installWizard_installIncomplete_resumeInstallationButtonLabel
=
Resume
installWizard_saveFirstUser
=
Save and Finish
installWizard_skipFirstUser
=
Skip
installWizard_firstUserSkippedMessage
=
<div class="alert alert-warning fade in">
\
You've skipped creating an admin user. To log in, use the username: 'admin' and
\
the security token you used to access the setup wizard.
\
</div>
installWizard_addFirstUser_title
=
Create an Admin User
installWizard_configureProxy_label
=
Configure Proxy
installWizard_configureProxy_save
=
Save and Continue
installWizard_skipPluginInstallations
=
Skip Plugin Installations
installWizard_installIncomplete_dependenciesLabel
=
Dependencies
core/src/main/resources/jenkins/model/Jenkins/login.jelly
浏览文件 @
6720a412
...
...
@@ -24,6 +24,10 @@ THE SOFTWARE.
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<j:if test="${it.setupWizard != null}">
<st:include it="${it.setupWizard}" page="authenticate-security-token"/>
</j:if>
<j:if test="${it.setupWizard == null}">
<l:layout norefresh="true">
<l:hasPermission permission="${app.READ}">
<st:include page="sidepanel.jelly" />
...
...
@@ -70,4 +74,5 @@ THE SOFTWARE.
</div>
</l:main-panel>
</l:layout>
</j:if>
</j:jelly>
core/src/main/resources/jenkins/model/Jenkins/loginError.jelly
浏览文件 @
6720a412
...
...
@@ -27,6 +27,10 @@ THE SOFTWARE.
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<j:choose>
<j:new var="h" className="hudson.Functions" />
<j:when test="${app.setupWizard != null}">
<j:set var="error" value="true"/>
<st:include it="${app.setupWizard}" page="authenticate-security-token"/>
</j:when>
<j:when test="${app.isUseSecurity() and h.isAnonymous()}">
<!--
The only time the error message makes sense is when Jenkins is protected and the user failed to authenticate.
...
...
core/src/main/resources/lib/layout/html.jelly
0 → 100644
浏览文件 @
6720a412
<!--
The MIT License
Copyright (c) 2004-2016, Sun Microsystems, Inc., Kohsuke Kawaguchi,
Daniel Dyer, Seiji Sogabe, Tom Huybrechts, Manufacture Francaise des Pneumatiques
Michelin, Romain Seguy
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.
-->
<?jelly escape-by-default='true'?>
<j:jelly
xmlns:j=
"jelly:core"
xmlns:st=
"jelly:stapler"
xmlns:d=
"jelly:define"
xmlns:l=
"/lib/layout"
xmlns:i=
"jelly:fmt"
xmlns:x=
"jelly:xml"
>
<st:documentation>
Outer-most tag for a normal (non-AJAX) HTML rendering.
This is used with nested
<
header>,
<
side-panel>, and
<
main-panel>
to form Jenkins's basic HTML layout.
<st:attribute
name=
"title"
use=
"required"
>
Title of the HTML page. Rendered into
<
title> tag.
</st:attribute>
<st:attribute
name=
"norefresh"
>
If non-null and not "false", auto refresh is disabled on this page.
This is necessary for pages that include forms.
</st:attribute>
<st:attribute
name=
"css"
deprecated=
"true"
>
specify path that starts from "/" for loading additional CSS stylesheet.
path is interprted as relative to the context root. e.g.,
{noformat}
<
l:layout css="/plugin/mysuperplugin/css/myneatstyle.css">{noformat}
This was originally added to allow plugins to load their stylesheets, but
*the use of thie attribute is discouraged now.*
plugins should now do so by inserting
<
style> elements and/or
<
script> elements
in
<
l:header/> tag.
</st:attribute>
<st:attribute
name=
"permission"
>
If given, this page is only made available to users that has the specified permission.
(The permission will be checked against the "it" object.)
</st:attribute>
</st:documentation>
<st:setHeader
name=
"Expires"
value=
"0"
/>
<st:setHeader
name=
"Cache-Control"
value=
"no-cache,no-store,must-revalidate"
/>
<st:setHeader
name=
"X-Hudson-Theme"
value=
"default"
/>
<st:contentType
value=
"text/html;charset=UTF-8"
/>
<j:new
var=
"h"
className=
"hudson.Functions"
/>
<!-- instead of JSP functions -->
${h.initPageVariables(context)}
<!--
depending on what tags are used, we can later end up discovering that we needed a session,
but it's too late because the headers are already committed. so ensure we always have a session.
this also allows us to configure HttpSessionContextIntegrationFilter not to create sessions,
which I suspect can end up creating sessions for wrong resource types (such as static resources.)
-->
<j:set
var=
"isMSIE"
value=
"${userAgent.contains('MSIE')}"
/>
<j:set
var=
"_"
value=
"${request.getSession()}"
/>
<j:set
var=
"_"
value=
"${h.configureAutoRefresh(request, response, attrs.norefresh!=null and !attrs.norefresh.equals(false))}"
/>
<j:set
var=
"extensionsAvailable"
value=
"${h.extensionsAvailable}"
/>
<j:if
test=
"${request.servletPath=='/' || request.servletPath==''}"
>
${h.advertiseHeaders(response)}
<j:if
test=
"${extensionsAvailable}"
>
<j:forEach
var=
"pd"
items=
"${h.pageDecorators}"
>
<st:include
it=
"${pd}"
page=
"httpHeaders.jelly"
optional=
"true"
/>
</j:forEach>
</j:if>
</j:if>
<x:doctype
name=
"html"
/>
<html>
<head
data-rooturl=
"${rootURL}"
data-resurl=
"${resURL}"
resURL=
"${resURL}"
>
${h.checkPermission(it,permission)}
<j:if
test=
"${isMSIE}"
>
<meta
http-equiv=
"X-UA-Compatible"
content=
"IE=Edge"
/>
</j:if>
<title>
${h.appendIfNotNull(title, ' [Jenkins]', 'Jenkins')}
</title>
<link
rel=
"stylesheet"
href=
"${resURL}/css/style.css"
type=
"text/css"
/>
<link
rel=
"stylesheet"
href=
"${resURL}/css/color.css"
type=
"text/css"
/>
<link
rel=
"stylesheet"
href=
"${resURL}/css/responsive-grid.css"
type=
"text/css"
/>
<j:if
test=
"${attrs.css!=null}"
>
<link
rel=
"stylesheet"
href=
"${resURL}${attrs.css}"
type=
"text/css"
/>
</j:if>
<link
rel=
"shortcut icon"
href=
"${resURL}/favicon.ico"
type=
"image/vnd.microsoft.icon"
/>
<link
rel=
"mask-icon"
href=
"${rootURL}/images/mask-icon.svg"
color=
"black"
/>
<!-- are we running as an unit test? -->
<script>
var
isRunAsTest
=
$
{
h
.
isUnitTest
};
var
rootURL
=
"
${rootURL}
"
;
var
resURL
=
"
${resURL}
"
;
</script>
<script
src=
"${resURL}/scripts/prototype.js"
type=
"text/javascript"
/>
<
script
src
=
"
${resURL}/scripts/behavior.js
"
type
=
"
text/javascript
"
/>
<!--
we
include
our
own
prototype
.
js
,
so
don
'
t let stapler pull in another. -->
<st:adjunct assumes="org.kohsuke.stapler.framework.prototype.prototype"
includes="org.kohsuke.stapler.bind"/>
<!-- To use the debug version of YUI, set the system property
'
debug
.
YUI
'
to true -->
<j:set var="yuiSuffix" value="${h.yuiSuffix}" />
<l:yui module="yahoo" />
<l:yui module="dom" />
<l:yui module="event" />
<j:if test="${h.yuiSuffix==
'
debug
'
}">
<l:yui module="logger" />
</j:if>
<l:yui module="animation" />
<l:yui module="dragdrop" />
<l:yui module="container" />
<l:yui module="connection" />
<l:yui module="datasource" />
<l:yui module="autocomplete" />
<l:yui module="menu" />
<l:yui module="element" />
<l:yui module="button" />
<l:yui module="storage" />
<!--l:yui module="editor" suffix="-beta" /-->
<script src="${resURL}/scripts/hudson-behavior.js" type="text/javascript">
</script>
<script
src=
"${resURL}/scripts/sortable.js"
type=
"text/javascript"
/>
<
j
:
if
test
=
"
${extensionsAvailable}
"
>
<
script
>
crumb
.
init
(
"
${h.getCrumbRequestField()}
"
,
"
${h.getCrumb(request)}
"
);
</script>
</j:if>
<link
rel=
"stylesheet"
href=
"${resURL}/scripts/yui/container/assets/container.css"
type=
"text/css"
/>
<link
rel=
"stylesheet"
href=
"${resURL}/scripts/yui/assets/skins/sam/skin.css"
type=
"text/css"
/>
<link
rel=
"stylesheet"
href=
"${resURL}/scripts/yui/container/assets/skins/sam/container.css"
type=
"text/css"
/>
<link
rel=
"stylesheet"
href=
"${resURL}/scripts/yui/button/assets/skins/sam/button.css"
type=
"text/css"
/>
<link
rel=
"stylesheet"
href=
"${resURL}/scripts/yui/menu/assets/skins/sam/menu.css"
type=
"text/css"
/>
<!--link rel="stylesheet" href="${resURL}/scripts/yui/editor/assets/skins/sam/editor.css" type="text/css" /-->
<l:hasPermission
permission=
"${app.READ}"
>
<link
rel=
"search"
type=
"application/opensearchdescription+xml"
href=
"${rootURL}/opensearch.xml"
title=
"Jenkins"
/>
</l:hasPermission>
<meta
name=
"ROBOTS"
content=
"INDEX,NOFOLLOW"
/>
<j:set
var=
"mode"
value=
"header"
/>
<d:invokeBody
/>
<j:if
test=
"${extensionsAvailable}"
>
<j:forEach
var=
"pd"
items=
"${h.pageDecorators}"
>
<st:include
it=
"${pd}"
page=
"header.jelly"
optional=
"true"
/>
</j:forEach>
</j:if>
<j:invokeStatic
var=
"j"
className=
"jenkins.model.Jenkins"
method=
"getActiveInstance"
/>
<j:set
var=
"installState"
value=
"${j.installState.name()}"
/>
<j:if
test=
"${installState == 'NEW' || installState == 'INITIAL_PLUGINS_INSTALLING'}"
>
<script
src=
"${resURL}/${j.installWizardPath}.js"
type=
"text/javascript"
/>
<
link
rel
=
"
stylesheet
"
href
=
"
${resURL}/${j.installWizardPath}.css
"
type
=
"
text/css
"
/>
<
/j:if
>
<
j
:
if
test
=
"
${isMSIE}
"
>
<
script
src
=
"
${resURL}/scripts/msie.js
"
type
=
"
text/javascript
"
/>
<
/j:if
>
<
/head
>
<
body
id
=
"
jenkins
"
class
=
"
yui-skin-sam jenkins-${h.version}
"
data
-
version
=
"
jenkins-${h.version}
"
data
-
model
-
type
=
"
${it.class.name}
"
>
<
div
id
=
"
main-panel
"
style
=
"
margin-left: 0;
"
>
<
j
:
set
var
=
"
mode
"
value
=
"
main-panel
"
/>
<
d
:
invokeBody
/>
<
/div
>
<
/body
>
<
/html
>
<
/j:jelly
>
core/src/main/resources/lib/layout/layout.jelly
浏览文件 @
6720a412
...
...
@@ -157,10 +157,6 @@ ${h.initPageVariables(context)}
<j:invokeStatic
var=
"j"
className=
"jenkins.model.Jenkins"
method=
"getActiveInstance"
/>
<j:set
var=
"installState"
value=
"${j.installState.name()}"
/>
<j:if
test=
"${installState == 'NEW' || installState == 'INITIAL_PLUGINS_INSTALLING'}"
>
<script
src=
"${resURL}/${j.installWizardPath}.js"
type=
"text/javascript"
/>
<
link
rel
=
"
stylesheet
"
href
=
"
${resURL}/${j.installWizardPath}.css
"
type
=
"
text/css
"
/>
<
/j:if
>
<j:if
test=
"${isMSIE}"
>
<script
src=
"${resURL}/scripts/msie.js"
type=
"text/javascript"
/>
...
...
test/src/test/java/hudson/model/UpdateCenterPluginInstallTest.java
浏览文件 @
6720a412
...
...
@@ -75,7 +75,8 @@ public class UpdateCenterPluginInstallTest {
String
correlationId
=
data
.
getString
(
"correlationId"
);
JSONObject
installStatus
=
jenkinsRule
.
getJSON
(
"updateCenter/installStatus?correlationId="
+
correlationId
).
getJSONObject
();
Assert
.
assertEquals
(
"ok"
,
json
.
get
(
"status"
));
JSONArray
states
=
installStatus
.
getJSONArray
(
"data"
);
JSONObject
status
=
installStatus
.
getJSONObject
(
"data"
);
JSONArray
states
=
status
.
getJSONArray
(
"jobs"
);
Assert
.
assertEquals
(
2
,
states
.
size
());
JSONObject
pluginInstallState
=
states
.
getJSONObject
(
0
);
...
...
war/src/main/js/api/pluginManager.js
浏览文件 @
6720a412
...
...
@@ -149,7 +149,7 @@ exports.incompleteInstallStatus = function(handler, correlationId) {
* Call this to complete the installation without installing anything
*/
exports
.
completeInstall
=
function
(
handler
)
{
jenkins
.
get
(
'
/
updateCenter
/completeInstall
'
,
function
()
{
jenkins
.
get
(
'
/
setupWizard
/completeInstall
'
,
function
()
{
handler
.
call
({
isError
:
false
});
},
{
timeout
:
pluginManagerErrorTimeoutMillis
,
...
...
war/src/main/js/api/securityConfig.js
0 → 100644
浏览文件 @
6720a412
/**
* Provides a wrapper to interact with the security configuration
*/
var
jenkins
=
require
(
'
../util/jenkins
'
);
var
jquery
=
require
(
'
jquery-detached
'
);
var
wh
=
require
(
'
window-handle
'
);
/**
* Calls a stapler post method to save the first user settings
*/
exports
.
saveFirstUser
=
function
(
$form
,
success
,
error
)
{
jenkins
.
staplerPost
(
'
/securityRealm/createFirstAccount
'
,
$form
,
success
,
{
dataType
:
'
html
'
,
error
:
error
});
};
/**
* Calls a stapler post method to save the first user settings
*/
exports
.
saveProxy
=
function
(
$form
,
success
,
error
)
{
jenkins
.
staplerPost
(
'
/pluginManager/proxyConfigure
'
,
$form
,
success
,
{
dataType
:
'
html
'
,
error
:
error
});
};
war/src/main/js/pluginSetupWizard.js
浏览文件 @
6720a412
...
...
@@ -6,5 +6,10 @@ var pluginSetupWizard = require('./pluginSetupWizardGui');
// This entry point for the bundle only bootstraps the main module in a browser
$
(
function
()
{
pluginSetupWizard
.
init
();
$
(
'
.plugin-setup-wizard-container
'
).
each
(
function
()
{
var
$container
=
$
(
this
);
if
(
$container
.
children
().
length
===
0
)
{
// this may get double-initialized
pluginSetupWizard
.
init
(
$container
);
}
});
});
war/src/main/js/pluginSetupWizardGui.js
浏览文件 @
6720a412
...
...
@@ -7,9 +7,13 @@ var jquery = require('jquery-detached');
var
bootstrap
=
require
(
'
bootstrap-detached
'
);
var
jenkins
=
require
(
'
./util/jenkins
'
);
var
pluginManager
=
require
(
'
./api/pluginManager
'
);
var
securityConfig
=
require
(
'
./api/securityConfig
'
);
var
wh
=
require
(
'
window-handle
'
);
window
.
zq
=
jquery
.
getJQuery
();
// Setup the dialog, exported
var
createPluginSetupWizard
=
function
()
{
var
createPluginSetupWizard
=
function
(
appendTarget
)
{
// call getJQuery / getBootstrap within the main function so it will work with tests -- if getJQuery etc is called in the main
var
$
=
jquery
.
getJQuery
();
var
$bs
=
bootstrap
.
getBootstrap
();
...
...
@@ -109,6 +113,9 @@ var createPluginSetupWizard = function() {
var
progressPanel
=
require
(
'
./templates/progressPanel.hbs
'
);
var
pluginSelectionPanel
=
require
(
'
./templates/pluginSelectionPanel.hbs
'
);
var
successPanel
=
require
(
'
./templates/successPanel.hbs
'
);
var
setupCompletePanel
=
require
(
'
./templates/setupCompletePanel.hbs
'
);
var
proxyConfigPanel
=
require
(
'
./templates/proxyConfigPanel.hbs
'
);
var
firstUserPanel
=
require
(
'
./templates/firstUserPanel.hbs
'
);
var
offlinePanel
=
require
(
'
./templates/offlinePanel.hbs
'
);
var
pluginSetupWizard
=
require
(
'
./templates/pluginSetupWizard.hbs
'
);
var
incompleteInstallationPanel
=
require
(
'
./templates/incompleteInstallationPanel.hbs
'
);
...
...
@@ -133,16 +140,16 @@ var createPluginSetupWizard = function() {
// state variables for plugin data, selected plugins, etc.:
var
pluginList
=
pluginManager
.
plugins
();
var
allPluginNames
=
pluginManager
.
pluginNames
();
var
selectedPluginNames
=
pluginManager
.
recommendedPluginNames
();
var
allPluginNames
=
pluginManager
.
pluginNames
();
var
selectedPluginNames
=
pluginManager
.
recommendedPluginNames
();
var
visibleDependencies
=
{};
var
categories
=
[];
var
availablePlugins
=
{};
var
categorizedPlugins
=
{};
var
categories
=
[];
var
availablePlugins
=
{};
var
categorizedPlugins
=
{};
// Instantiate the wizard panel
var
$wizard
=
$
(
pluginSetupWizard
());
$wizard
.
appendTo
(
'
body
'
);
$wizard
.
appendTo
(
appendTarget
);
var
$container
=
$wizard
.
find
(
'
.modal-content
'
);
var
currentPanel
;
...
...
@@ -165,10 +172,10 @@ var createPluginSetupWizard = function() {
var
translations
=
{};
var
decorations
=
[
function
()
{
function
()
{
// any decorations after DOM replacement go here
}
];
}
];
// call this to set the panel in the app, this performs some additional things & adds common transitions
var
setPanel
=
function
(
panel
,
data
,
oncomplete
)
{
...
...
@@ -177,7 +184,7 @@ var createPluginSetupWizard = function() {
decorations
[
i
](
$base
);
}
};
var
html
=
panel
(
$
.
extend
({
translations
:
translations
},
data
));
var
html
=
panel
(
$
.
extend
({
translations
:
translations
,
baseUrl
:
jenkins
.
baseUrl
},
data
));
if
(
panel
===
currentPanel
)
{
// just replace id-marked elements
var
$upd
=
$
(
html
);
$upd
.
find
(
'
*[id]
'
).
each
(
function
()
{
...
...
@@ -200,6 +207,12 @@ var createPluginSetupWizard = function() {
currentPanel
=
panel
;
$container
.
append
(
html
);
decorate
(
$container
);
var
$modalHeader
=
$container
.
find
(
'
.modal-header
'
);
if
(
$modalHeader
.
length
>
0
)
{
$modalHeader
.
prepend
(
'
<button type="button" class="close" aria-label="Close"><span aria-hidden="true">×</span></button>
'
);
}
if
(
oncomplete
)
{
oncomplete
();
...
...
@@ -255,25 +268,25 @@ var createPluginSetupWizard = function() {
// Initializes the set of installing plugins with pending statuses
var
initInstallingPluginList
=
function
()
{
installingPlugins
=
[];
installingPlugins
.
names
=
[];
installingPlugins
.
names
=
[];
for
(
var
i
=
0
;
i
<
selectedPluginNames
.
length
;
i
++
)
{
var
pluginName
=
selectedPluginNames
[
i
];
var
p
=
availablePlugins
[
pluginName
];
var
pluginName
=
selectedPluginNames
[
i
];
var
p
=
availablePlugins
[
pluginName
];
if
(
p
)
{
var
plug
=
$
.
extend
({
installStatus
:
'
pending
'
},
p
);
installingPlugins
.
push
(
plug
);
installingPlugins
[
plug
.
name
]
=
plug
;
installingPlugins
.
names
.
push
(
pluginName
);
installingPlugins
.
names
.
push
(
pluginName
);
}
}
};
// call this to go install the selected set of plugins
var
installPlugins
=
function
(
plugins
)
{
var
installPlugins
=
function
(
plugins
)
{
pluginManager
.
installPlugins
(
plugins
,
handleGenericError
(
function
()
{
showInstallProgress
();
showInstallProgress
();
}));
setPanel
(
progressPanel
,
{
installingPlugins
:
installingPlugins
});
...
...
@@ -306,13 +319,22 @@ var createPluginSetupWizard = function() {
};
// Define actions
var
showInstallProgress
=
function
()
{
initInstallingPluginList
();
var
showInstallProgress
=
function
(
state
)
{
if
(
state
)
{
if
(
/CREATE_ADMIN_USER/
.
test
(
state
))
{
setupFirstUser
();
return
;
}
}
initInstallingPluginList
();
setPanel
(
progressPanel
,
{
installingPlugins
:
installingPlugins
});
// call to the installStatus, update progress bar & plugin details; transition on complete
var
updateStatus
=
function
()
{
pluginManager
.
installStatus
(
handleGenericError
(
function
(
jobs
)
{
pluginManager
.
installStatus
(
handleGenericError
(
function
(
data
)
{
var
jobs
=
data
.
jobs
;
var
i
,
j
;
var
complete
=
0
;
var
total
=
0
;
...
...
@@ -402,10 +424,7 @@ var createPluginSetupWizard = function() {
else
{
// mark complete
$
(
'
.progress-bar
'
).
css
({
width
:
'
100%
'
});
setPanel
(
successPanel
,
{
installingPlugins
:
installingPlugins
,
restartRequired
:
restartRequired
});
setupFirstUser
();
}
}));
};
...
...
@@ -416,7 +435,7 @@ var createPluginSetupWizard = function() {
// Called to complete the installation
var
finishInstallation
=
function
()
{
jenkins
.
goTo
(
'
/
'
);
closeInstaller
(
);
};
// load the plugin data, callback
...
...
@@ -514,28 +533,28 @@ var createPluginSetupWizard = function() {
});
// walk the elements and search for the text
var
walk
=
function
(
elements
,
element
,
text
,
xform
)
{
var
i
,
child
,
n
=
element
.
childNodes
.
length
;
for
(
i
=
0
;
i
<
n
;
i
++
)
{
child
=
element
.
childNodes
[
i
];
if
(
child
.
nodeType
===
3
&&
xform
(
child
.
data
).
indexOf
(
text
)
!==-
1
)
{
elements
.
push
(
element
);
break
;
}
}
for
(
i
=
0
;
i
<
n
;
i
++
)
{
child
=
element
.
childNodes
[
i
];
if
(
child
.
nodeType
===
1
)
{
walk
(
elements
,
child
,
text
,
xform
);
}
}
};
// find elements matching the given text, optionally transforming the text before match (e.g. you can .toLowerCase() it)
var
walk
=
function
(
elements
,
element
,
text
,
xform
)
{
var
i
,
child
,
n
=
element
.
childNodes
.
length
;
for
(
i
=
0
;
i
<
n
;
i
++
)
{
child
=
element
.
childNodes
[
i
];
if
(
child
.
nodeType
===
3
&&
xform
(
child
.
data
).
indexOf
(
text
)
!==-
1
)
{
elements
.
push
(
element
);
break
;
}
}
for
(
i
=
0
;
i
<
n
;
i
++
)
{
child
=
element
.
childNodes
[
i
];
if
(
child
.
nodeType
===
1
)
{
walk
(
elements
,
child
,
text
,
xform
);
}
}
};
// find elements matching the given text, optionally transforming the text before match (e.g. you can .toLowerCase() it)
var
findElementsWithText
=
function
(
ancestor
,
text
,
xform
)
{
var
elements
=
[];
walk
(
elements
,
ancestor
,
text
,
xform
?
xform
:
function
(
d
){
return
d
;
});
return
elements
;
var
elements
=
[];
walk
(
elements
,
ancestor
,
text
,
xform
?
xform
:
function
(
d
){
return
d
;
});
return
elements
;
};
// search UI vars
...
...
@@ -646,7 +665,64 @@ var createPluginSetupWizard = function() {
$c
.
slideDown
();
}
};
var
enableButtonsAfterFrameLoad
=
function
()
{
$
(
'
iframe[src]
'
).
load
(
function
()
{
var
location
=
$
(
this
).
contents
().
get
(
0
).
location
.
href
;
$
(
'
button
'
).
prop
({
disabled
:
false
});
});
};
var
setupFirstUser
=
function
()
{
setPanel
(
firstUserPanel
,
{},
enableButtonsAfterFrameLoad
);
};
// call to submit the firstuser
var
saveFirstUser
=
function
()
{
$
(
'
button
'
).
prop
({
disabled
:
true
});
var
handleSubmit
=
function
(
data
)
{
if
(
data
.
status
&&
data
.
status
>
200
)
{
// Nothing we can really do here
setPanel
(
errorPanel
,
{
errorMessage
:
data
.
statusText
});
return
;
}
// we get 200 OK
var
$page
=
$
(
data
);
var
$errors
=
$page
.
find
(
'
.error
'
);
if
(
$errors
.
length
>
0
)
{
var
$main
=
$page
.
find
(
'
#main-panel
'
).
detach
();
if
(
$main
.
length
>
0
)
{
data
=
data
.
replace
(
/body
([^
>
]
*
)[
>
](
.|
[\r\n])
+
[
<
][/]
body/
,
'
body$1>
'
+
$main
.
html
()
+
'
</body
'
);
}
var
doc
=
$
(
'
iframe[src]
'
).
contents
()[
0
];
doc
.
open
();
doc
.
write
(
data
);
doc
.
close
();
}
else
{
setPanel
(
setupCompletePanel
);
}
};
securityConfig
.
saveFirstUser
(
$
(
'
iframe[src]
'
).
contents
().
find
(
'
form:not(.no-json)
'
),
handleSubmit
,
handleSubmit
);
};
var
skipFirstUser
=
function
()
{
$
(
'
button
'
).
prop
({
disabled
:
true
});
setPanel
(
setupCompletePanel
,
{
message
:
translations
.
installWizard_firstUserSkippedMessage
});
};
// call to setup the proxy
var
setupProxy
=
function
()
{
setPanel
(
proxyConfigPanel
,
{},
enableButtonsAfterFrameLoad
);
};
// Save the proxy config
var
saveProxyConfig
=
function
()
{
securityConfig
.
saveProxy
(
$
(
'
iframe[src]
'
).
contents
().
find
(
'
form:not(.no-json)
'
),
function
()
{
jenkins
.
goTo
(
'
/
'
);
// this will re-run connectivity test
});
};
// Call this to resume an installation after restart
var
resumeInstallation
=
function
()
{
// don't re-initialize installing plugins
...
...
@@ -716,7 +792,12 @@ var createPluginSetupWizard = function() {
'
.select-category
'
:
selectCategory
,
'
.close
'
:
closeInstaller
,
'
.resume-installation
'
:
resumeInstallation
,
'
.install-done-restart
'
:
restartJenkins
'
.install-done-restart
'
:
restartJenkins
,
'
.save-first-user:not([disabled])
'
:
saveFirstUser
,
'
.skip-first-user
'
:
skipFirstUser
,
'
.show-proxy-config
'
:
setupProxy
,
'
.save-proxy-config
'
:
saveProxyConfig
,
'
.skip-plugin-installs
'
:
function
()
{
installPlugins
([]);
}
};
for
(
var
cls
in
actions
)
{
bindClickHandler
(
cls
,
actions
[
cls
]);
...
...
@@ -737,26 +818,28 @@ var createPluginSetupWizard = function() {
}
// check for updates when first loaded...
pluginManager
.
installStatus
(
handleGenericError
(
function
(
jobs
)
{
pluginManager
.
installStatus
(
handleGenericError
(
function
(
data
)
{
var
jobs
=
data
.
jobs
;
if
(
jobs
.
length
>
0
)
{
if
(
installingPlugins
.
length
===
0
)
{
// This can happen on a page reload if we are in the middle of
// an install. So, lets get a list of plugins being installed at the
// moment and use that as the "selectedPlugins" list.
selectedPluginNames
=
[];
loadPluginData
(
handleGenericError
(
function
()
{
for
(
var
i
=
0
;
i
<
jobs
.
length
;
i
++
)
{
// If the job does not have a 'correlationId', then it was not selected
// by the user for install i.e. it's probably a dependency plugin.
if
(
jobs
[
i
].
correlationId
)
{
selectedPluginNames
.
push
(
jobs
[
i
].
name
);
}
}
showInstallProgress
();
}));
}
else
{
showInstallProgress
(
);
}
if
(
installingPlugins
.
length
===
0
)
{
// This can happen on a page reload if we are in the middle of
// an install. So, lets get a list of plugins being installed at the
// moment and use that as the "selectedPlugins" list.
selectedPluginNames
=
[];
loadPluginData
(
handleGenericError
(
function
()
{
for
(
var
i
=
0
;
i
<
jobs
.
length
;
i
++
)
{
// If the job does not have a 'correlationId', then it was not selected
// by the user for install i.e. it's probably a dependency plugin.
if
(
jobs
[
i
].
correlationId
)
{
selectedPluginNames
.
push
(
jobs
[
i
].
name
);
}
}
showInstallProgress
(
data
.
state
);
}));
}
else
{
showInstallProgress
(
data
.
state
);
}
return
;
}
...
...
@@ -774,12 +857,12 @@ var createPluginSetupWizard = function() {
for
(
var
plugName
in
incompleteStatus
)
{
var
j
=
installingPlugins
[
plugName
];
if
(
!
j
)
{
console
.
warn
(
'
Plugin "
'
+
plugName
+
'
" not found in the list of installing plugins.
'
);
console
.
warn
(
'
\t
Installing plugins:
'
+
installingPlugins
.
names
);
continue
;
}
if
(
!
j
)
{
console
.
warn
(
'
Plugin "
'
+
plugName
+
'
" not found in the list of installing plugins.
'
);
console
.
warn
(
'
\t
Installing plugins:
'
+
installingPlugins
.
names
);
continue
;
}
var
txt
=
false
;
var
state
=
false
;
...
...
war/src/main/js/templates/firstUserPanel.hbs
0 → 100644
浏览文件 @
6720a412
<div
class=
"modal-header"
>
<h4
class=
"modal-title"
>
{{
translations
.
installWizard_addFirstUser_title
}}
</h4>
</div>
<div
class=
"modal-body"
>
<div
class=
"jumbotron welcome-panel security-panel"
>
<iframe
src=
"
{{
baseUrl
}}
/securityRealm/setupWizardFirstUser"
></iframe>
</div>
</div>
<div
class=
"modal-footer"
>
<button
type=
"button"
class=
"btn btn-link skip-first-user"
disabled
>
{{
translations
.
installWizard_skipFirstUser
}}
</button>
<button
type=
"button"
class=
"btn btn-primary save-first-user"
disabled
>
{{
translations
.
installWizard_saveFirstUser
}}
</button>
</div>
war/src/main/js/templates/incompleteInstallationPanel.hbs
浏览文件 @
6720a412
<div
class=
"modal-header"
>
<button
type=
"button"
class=
"close"
aria-label=
"Close"
><span
aria-hidden=
"true"
>
×
</span></button>
<h4
class=
"modal-title"
>
{{
translations
.
installWizard_installIncomplete_title
}}
</h4>
</div>
<div
class=
"modal-body"
>
...
...
war/src/main/js/templates/offlinePanel.hbs
浏览文件 @
6720a412
<div
class=
"modal-header"
>
<button
type=
"button"
class=
"close"
aria-label=
"Close"
><span
aria-hidden=
"true"
>
×
</span></button>
<h4
class=
"modal-title"
>
{{
translations
.
installWizard_offline_title
}}
</h4>
<h4
class=
"modal-title"
>
{{
translations
.
installWizard_offline_title
}}
</h4>
</div>
<div
class=
"modal-body"
>
<div
class=
"jumbotron welcome-panel offline"
>
<h1>
{{
translations
.
installWizard_offline_title
}}
</h1>
<p>
{{{
translations
.
installWizard_offline_message
}}}
</p>
</div>
<div
class=
"jumbotron welcome-panel offline"
>
<h1>
{{
translations
.
installWizard_offline_title
}}
</h1>
<p>
{{{
translations
.
installWizard_offline_message
}}}
</p>
<p>
<button
type=
"button"
class=
"btn btn-primary show-proxy-config"
>
{{
translations
.
installWizard_configureProxy_label
}}
</button>
<button
type=
"button"
class=
"btn btn-primary skip-plugin-installs"
>
{{
translations
.
installWizard_skipPluginInstallations
}}
</button>
</p>
</div>
</div>
war/src/main/js/templates/pluginSelectionPanel.hbs
浏览文件 @
6720a412
<div
class=
"modal-header"
>
<button
type=
"button"
class=
"close"
aria-label=
"Close"
><span
aria-hidden=
"true"
>
×
</span></button>
<h4
class=
"modal-title"
>
{{
translations
.
installWizard_installCustom_title
}}
</h4>
</div>
<div
class=
"modal-body plugin-selector"
>
...
...
war/src/main/js/templates/proxyConfigPanel.hbs
0 → 100644
浏览文件 @
6720a412
<div
class=
"modal-header"
>
<h4
class=
"modal-title"
>
{{
translations
.
installWizard_configureProxy_label
}}
</h4>
</div>
<div
class=
"modal-body"
>
<div
class=
"jumbotron welcome-panel security-panel"
>
<iframe
src=
"
{{
baseUrl
}}
/setupWizard/proxy-configuration"
></iframe>
</div>
</div>
<div
class=
"modal-footer"
>
<button
type=
"button"
class=
"btn btn-link install-home"
>
{{
translations
.
installWizard_goBack
}}
</button>
<button
type=
"button"
class=
"btn btn-primary save-proxy-config"
disabled
>
{{
translations
.
installWizard_configureProxy_save
}}
</button>
</div>
war/src/main/js/templates/setupCompletePanel.hbs
0 → 100644
浏览文件 @
6720a412
<div
class=
"modal-header"
>
<h4
class=
"modal-title"
>
{{
translations
.
installWizard_installComplete_title
}}
</h4>
</div>
<div
class=
"modal-body"
>
<div
class=
"jumbotron welcome-panel success-panel"
>
<h1>
{{
translations
.
installWizard_installComplete_banner
}}
</h1>
{{{
message
}}}
{{#if
restartRequired
}}
<p>
{{
translations
.
installWizard_installComplete_message
}}
{{
translations
.
installWizard_installComplete_restartRequiredMessage
}}
</p>
<button
type=
"button"
class=
"btn btn-primary install-done-restart"
>
{{
translations
.
installWizard_installComplete_restartLabel
}}
</button>
{{else}}
<p>
{{
translations
.
installWizard_installComplete_message
}}
</p>
<button
type=
"button"
class=
"btn btn-primary install-done"
>
{{
translations
.
installWizard_installComplete_finishButtonLabel
}}
</button>
{{/if}}
</div>
</div>
war/src/main/js/templates/successPanel.hbs
浏览文件 @
6720a412
<div
class=
"modal-header"
>
<button
type=
"button"
class=
"close"
aria-label=
"Close"
><span
aria-hidden=
"true"
>
×
</span></button>
<h4
class=
"modal-title"
>
{{
translations
.
installWizard_installComplete_title
}}
</h4>
<h4
class=
"modal-title"
>
{{
translations
.
installWizard_pluginsInstalled_title
}}
</h4>
</div>
<div
class=
"modal-body"
>
<div
class=
"jumbotron welcome-panel success-panel"
>
<h1>
{{
translations
.
installWizard_
installComplete
_banner
}}
</h1>
<h1>
{{
translations
.
installWizard_
pluginsInstalled
_banner
}}
</h1>
{{#if
restartRequired
}}
<p>
{{
translations
.
installWizard_installComplete_message
}}
{{
translations
.
installWizard_installComplete_restartRequiredMessage
}}
</p>
...
...
war/src/main/js/templates/welcomePanel.hbs
浏览文件 @
6720a412
<div
class=
"modal-header"
>
<button
type=
"button"
class=
"close"
aria-label=
"Close"
><span
aria-hidden=
"true"
>
×
</span></button>
<h4
class=
"modal-title"
>
{{
translations
.
installWizard_welcomePanel_title
}}
</h4>
</div>
<div
class=
"modal-body"
>
...
...
war/src/main/js/util/jenkins.js
浏览文件 @
6720a412
...
...
@@ -28,15 +28,15 @@ exports.stringify = function(o) {
h
:
Hash
.
prototype
.
toJSON
,
s
:
String
.
prototype
.
toJSON
};
try
{
delete
Array
.
prototype
.
toJSON
;
try
{
delete
Array
.
prototype
.
toJSON
;
delete
Object
.
prototype
.
toJSON
;
delete
Hash
.
prototype
.
toJSON
;
delete
String
.
prototype
.
toJSON
;
delete
Hash
.
prototype
.
toJSON
;
delete
String
.
prototype
.
toJSON
;
return
JSON
.
stringify
(
o
);
}
finally
{
}
finally
{
if
(
protoJSON
.
a
)
{
Array
.
prototype
.
toJSON
=
protoJSON
.
a
;
}
...
...
@@ -49,7 +49,7 @@ exports.stringify = function(o) {
if
(
protoJSON
.
s
)
{
String
.
prototype
.
toJSON
=
protoJSON
.
s
;
}
}
}
}
else
{
return
JSON
.
stringify
(
o
);
...
...
@@ -67,7 +67,7 @@ exports.idIfy = function(str) {
* redirect
*/
exports
.
goTo
=
function
(
url
)
{
wh
.
getWindow
().
location
.
replace
(
exports
.
baseUrl
()
+
url
);
wh
.
getWindow
().
location
.
replace
(
exports
.
baseUrl
()
+
url
);
};
/**
...
...
@@ -76,13 +76,13 @@ exports.goTo = function(url) {
*/
exports
.
get
=
function
(
url
,
success
,
options
)
{
if
(
debug
)
{
console
.
log
(
'
get:
'
+
url
);
}
console
.
log
(
'
get:
'
+
url
);
}
var
$
=
jquery
.
getJQuery
();
var
args
=
{
url
:
exports
.
baseUrl
()
+
url
,
type
:
'
GET
'
,
cache
:
false
,
cache
:
false
,
dataType
:
'
json
'
,
success
:
success
};
...
...
@@ -98,22 +98,49 @@ exports.get = function(url, success, options) {
*/
exports
.
post
=
function
(
url
,
data
,
success
,
options
)
{
if
(
debug
)
{
console
.
log
(
'
post:
'
+
url
);
}
console
.
log
(
'
post:
'
+
url
);
}
var
$
=
jquery
.
getJQuery
();
// handle crumbs
var
headers
=
{};
var
wnd
=
wh
.
getWindow
();
var
crumb
;
if
(
'
crumb
'
in
options
)
{
crumb
=
options
.
crumb
;
}
else
if
(
'
crumb
'
in
wnd
)
{
crumb
=
wnd
.
crumb
;
}
if
(
crumb
)
{
headers
[
crumb
.
fieldName
]
=
crumb
.
value
;
}
var
formBody
=
data
;
if
(
formBody
instanceof
Object
)
{
if
(
crumb
)
{
formBody
=
$
.
extend
({},
formBody
);
formBody
[
crumb
.
fieldName
]
=
crumb
.
value
;
}
formBody
=
exports
.
stringify
(
formBody
);
}
var
args
=
{
url
:
exports
.
baseUrl
()
+
url
,
type
:
'
POST
'
,
cache
:
false
,
cache
:
false
,
dataType
:
'
json
'
,
data
:
exports
.
stringify
(
data
),
contentType
:
"
application/json
"
,
success
:
success
data
:
formBody
,
contentType
:
"
application/json
"
,
success
:
success
,
headers
:
headers
};
if
(
options
instanceof
Object
)
{
$
.
extend
(
args
,
options
);
}
$
.
ajax
(
args
);
$
.
ajax
(
args
);
};
/**
...
...
@@ -124,20 +151,20 @@ exports.initHandlebars = function() {
Handlebars
.
registerHelper
(
'
ifeq
'
,
function
(
o1
,
o2
,
options
)
{
if
(
o1
===
o2
)
{
return
options
.
fn
();
}
return
options
.
fn
();
}
});
Handlebars
.
registerHelper
(
'
ifneq
'
,
function
(
o1
,
o2
,
options
)
{
if
(
o1
!==
o2
)
{
return
options
.
fn
();
}
return
options
.
fn
();
}
});
Handlebars
.
registerHelper
(
'
in-array
'
,
function
(
arr
,
val
,
options
)
{
if
(
arr
.
indexOf
(
val
)
>=
0
)
{
return
options
.
fn
();
}
return
options
.
fn
();
}
});
Handlebars
.
registerHelper
(
'
id
'
,
exports
.
idIfy
);
...
...
@@ -186,3 +213,64 @@ exports.testConnectivity = function(handler) {
};
testConnectivity
();
};
/**
* gets the window containing a form, taking in to account top-level iframes
*/
exports
.
getWindow
=
function
(
$form
)
{
var
$
=
jquery
.
getJQuery
();
$form
=
$
(
$form
);
var
wnd
=
wh
.
getWindow
();
$
(
top
.
document
).
find
(
'
iframe
'
).
each
(
function
()
{
var
windowFrame
=
this
.
contentWindow
;
var
$f
=
$
(
this
).
contents
().
find
(
'
form
'
);
if
(
$f
.
length
>
0
&&
$form
[
0
]
===
$f
[
0
])
{
wnd
=
windowFrame
;
}
});
return
wnd
;
};
/**
* Builds a stapler form post
*/
exports
.
buildFormPost
=
function
(
$form
)
{
var
$
=
jquery
.
getJQuery
();
$form
=
$
(
$form
);
var
wnd
=
exports
.
getWindow
(
$form
);
var
form
=
$form
[
0
];
if
(
wnd
.
buildFormTree
(
form
))
{
return
$form
.
serialize
()
+
'
&core:apply=&Submit=Save&json=
'
+
$form
.
find
(
'
input[name=json]
'
).
val
();
}
return
''
;
};
/**
* Gets the crumb, if crumbs are enabled
*/
exports
.
getFormCrumb
=
function
(
$form
)
{
var
$
=
jquery
.
getJQuery
();
$form
=
$
(
$form
);
var
wnd
=
exports
.
getWindow
(
$form
);
return
wnd
.
crumb
;
};
/**
* Jenkins Stapler JSON POST callback
* If last parameter is an object, will be extended to jQuery options (e.g. pass { error: function() ... } to handle errors)
*/
exports
.
staplerPost
=
function
(
url
,
$form
,
success
,
options
)
{
var
$
=
jquery
.
getJQuery
();
$form
=
$
(
$form
);
var
postBody
=
exports
.
buildFormPost
(
$form
);
var
crumb
=
exports
.
getFormCrumb
(
$form
);
exports
.
post
(
url
,
postBody
,
success
,
$
.
extend
({
processData
:
false
,
contentType
:
'
application/x-www-form-urlencoded
'
,
crumb
:
crumb
},
options
));
};
war/src/main/less/pluginSetupWizard.less
浏览文件 @
6720a412
此差异已折叠。
点击以展开。
war/src/test/js/pluginSetupWizard-spec.js
浏览文件 @
6720a412
...
...
@@ -15,20 +15,20 @@ pluginList.recommendedPlugins = ['subversion'];
// Iterates through all responses until the end and returns the last response repeatedly
var
LastResponse
=
function
(
responses
)
{
var
counter
=
0
;
this
.
next
=
function
()
{
if
(
counter
<
responses
.
length
)
{
try
{
return
responses
[
counter
];
}
finally
{
counter
++
;
}
}
if
(
responses
.
length
>
0
)
{
return
responses
[
counter
-
1
];
}
return
{
status
:
'
fail
'
};
};
var
counter
=
0
;
this
.
next
=
function
()
{
if
(
counter
<
responses
.
length
)
{
try
{
return
responses
[
counter
];
}
finally
{
counter
++
;
}
}
if
(
responses
.
length
>
0
)
{
return
responses
[
counter
-
1
];
}
return
{
status
:
'
fail
'
};
};
};
// common mocks for jQuery $.ajax
...
...
@@ -48,17 +48,23 @@ var ajaxMocks = function(responseMappings) {
},
'
/jenkins/updateCenter/installStatus
'
:
new
LastResponse
([{
status
:
'
ok
'
,
data
:
[]
// first, return nothing by default, no ongoing install
data
:
{
// first, return nothing by default, no ongoing install
state
:
'
NEW
'
,
jobs
:
[]
}
},
{
status
:
'
ok
'
,
data
:
[
{
name
:
'
subversion
'
,
type
:
'
InstallJob
'
,
installStatus
:
'
Success
'
}
]
data
:
{
state
:
'
INSTALLING_PLUGINS
'
,
jobs
:
[
{
name
:
'
subversion
'
,
type
:
'
InstallJob
'
,
installStatus
:
'
Success
'
}
]
}
}]),
'
/jenkins/pluginManager/plugins
'
:
{
status
:
'
ok
'
,
...
...
@@ -92,8 +98,8 @@ var ajaxMocks = function(responseMappings) {
}
},
'
/jenkins/pluginManager/installPlugins
'
:
{
status
:
'
ok
'
,
data
:
'
RANDOM_UUID_1234
'
status
:
'
ok
'
,
data
:
'
RANDOM_UUID_1234
'
}
};
...
...
@@ -114,7 +120,7 @@ var ajaxMocks = function(responseMappings) {
throw
'
No data mapping provided for AJAX call:
'
+
call
.
url
;
}
if
(
response
instanceof
LastResponse
)
{
response
=
response
.
next
();
response
=
response
.
next
();
}
call
.
success
(
response
);
};
...
...
@@ -122,7 +128,7 @@ var ajaxMocks = function(responseMappings) {
// call this for each test, it will provide a new wizard, jquery to the caller
var
test
=
function
(
test
,
ajaxMappings
)
{
jsTest
.
onPage
(
function
()
{
jsTest
.
onPage
(
function
()
{
// deps
var
$
=
getJQuery
();
...
...
@@ -133,41 +139,41 @@ var test = function(test, ajaxMappings) {
var
pluginSetupWizard
=
jsTest
.
requireSrcModule
(
'
pluginSetupWizardGui
'
);
// exported init
pluginSetupWizard
.
init
();
pluginSetupWizard
.
init
(
'
body
'
);
test
(
$
,
pluginSetupWizard
);
});
});
};
// helper to validate the appropriate plugins were installed
var
validatePlugins
=
function
(
plugins
,
complete
)
{
var
jenkins
=
jsTest
.
requireSrcModule
(
'
util/jenkins
'
);
if
(
!
jenkins
.
originalPost
)
{
jenkins
.
originalPost
=
jenkins
.
post
;
jenkins
.
originalPost
=
jenkins
.
post
;
}
jenkins
.
post
=
function
(
url
,
data
,
cb
)
{
expect
(
url
).
toBe
(
'
/pluginManager/installPlugins
'
);
var
allMatch
=
true
;
for
(
var
i
=
0
;
i
<
plugins
.
length
;
i
++
)
{
if
(
data
.
plugins
.
indexOf
(
plugins
[
i
])
<
0
)
{
allMatch
=
false
;
break
;
}
if
(
data
.
plugins
.
indexOf
(
plugins
[
i
])
<
0
)
{
allMatch
=
false
;
break
;
}
}
if
(
!
allMatch
)
{
expect
(
JSON
.
stringify
(
plugins
)).
toBe
(
'
In:
'
+
JSON
.
stringify
(
data
.
plugins
));
}
// return status
cb
({
status
:
'
ok
'
,
data
:{
correlationId
:
1
}});
if
(
complete
)
{
complete
();
}
if
(
complete
)
{
complete
();
}
};
};
describe
(
"
pluginSetupWizard.js
"
,
function
()
{
it
(
"
wizard shows
"
,
function
(
done
)
{
test
(
function
(
$
)
{
it
(
"
wizard shows
"
,
function
(
done
)
{
test
(
function
(
$
)
{
// Make sure the dialog was shown
var
$wizard
=
$
(
'
.plugin-setup-wizard
'
);
expect
(
$wizard
.
size
()).
toBe
(
1
);
...
...
@@ -176,49 +182,49 @@ describe("pluginSetupWizard.js", function () {
});
});
it
(
"
offline shows
"
,
function
(
done
)
{
jsTest
.
onPage
(
function
()
{
// deps
var
jenkins
=
jsTest
.
requireSrcModule
(
'
./util/jenkins
'
);
it
(
"
offline shows
"
,
function
(
done
)
{
jsTest
.
onPage
(
function
()
{
// deps
var
jenkins
=
jsTest
.
requireSrcModule
(
'
./util/jenkins
'
);
var
$
=
getJQuery
();
$
.
ajax
=
ajaxMocks
();
var
$
=
getJQuery
();
$
.
ajax
=
ajaxMocks
();
var
get
=
jenkins
.
get
;
try
{
// Respond with failure
jenkins
.
get
=
function
(
url
,
cb
)
{
if
(
debug
)
{
var
get
=
jenkins
.
get
;
try
{
// Respond with failure
jenkins
.
get
=
function
(
url
,
cb
)
{
if
(
debug
)
{
console
.
log
(
'
Jenkins.GET:
'
+
url
);
}
if
(
url
===
'
/updateCenter/connectionStatus?siteId=default
'
)
{
cb
({
status
:
'
ok
'
,
data
:
{
updatesite
:
'
ERROR
'
,
internet
:
'
ERROR
'
}
});
}
else
{
get
(
url
,
cb
);
}
};
// load the module
var
pluginSetupWizard
=
jsTest
.
requireSrcModule
(
'
pluginSetupWizardGui
'
);
// exported init
pluginSetupWizard
.
init
(
);
expect
(
$
(
'
.welcome-panel h1
'
).
text
()).
toBe
(
'
Offline
'
);
done
();
}
finally
{
jenkins
.
get
=
get
;
}
});
if
(
url
===
'
/updateCenter/connectionStatus?siteId=default
'
)
{
cb
({
status
:
'
ok
'
,
data
:
{
updatesite
:
'
ERROR
'
,
internet
:
'
ERROR
'
}
});
}
else
{
get
(
url
,
cb
);
}
};
// load the module
var
pluginSetupWizard
=
jsTest
.
requireSrcModule
(
'
pluginSetupWizardGui
'
);
// exported init
pluginSetupWizard
.
init
(
'
body
'
);
expect
(
$
(
'
.welcome-panel h1
'
).
text
()).
toBe
(
'
Offline
'
);
done
();
}
finally
{
jenkins
.
get
=
get
;
}
});
});
it
(
"
install defaults
"
,
function
(
done
)
{
...
...
@@ -240,16 +246,16 @@ describe("pluginSetupWizard.js", function () {
});
var
doit
=
function
(
$
,
sel
,
trigger
)
{
var
$el
=
$
(
sel
);
if
(
$el
.
length
!==
1
)
{
console
.
log
(
'
Not found!
'
+
sel
);
var
$el
=
$
(
sel
);
if
(
$el
.
length
!==
1
)
{
console
.
log
(
'
Not found!
'
+
sel
);
console
.
log
(
new
Error
().
stack
);
}
if
(
trigger
===
'
check
'
)
{
$el
.
prop
(
'
checked
'
,
true
);
trigger
=
'
change
'
;
}
$el
.
trigger
(
trigger
);
}
if
(
trigger
===
'
check
'
)
{
$el
.
prop
(
'
checked
'
,
true
);
trigger
=
'
change
'
;
}
$el
.
trigger
(
trigger
);
};
it
(
"
install custom
"
,
function
(
done
)
{
...
...
@@ -258,8 +264,8 @@ describe("pluginSetupWizard.js", function () {
// validate a call to installPlugins with our defaults
validatePlugins
([
'
junit
'
,
'
mailer
'
],
function
()
{
// install a specific, other 'set' of plugins
$
(
'
input[name=searchbox]
'
).
val
(
'
junit
'
);
// install a specific, other 'set' of plugins
$
(
'
input[name=searchbox]
'
).
val
(
'
junit
'
);
done
();
});
...
...
@@ -277,7 +283,7 @@ describe("pluginSetupWizard.js", function () {
it
(
"
resume install
"
,
function
(
done
)
{
var
ajaxMappings
=
{
'
/jenkins/updateCenter/incompleteInstallStatus
'
:
{
'
/jenkins/updateCenter/incompleteInstallStatus
'
:
{
status
:
'
ok
'
,
data
:
{
'
junit
'
:
'
Success
'
,
...
...
@@ -288,27 +294,27 @@ describe("pluginSetupWizard.js", function () {
}
};
test
(
function
(
$
)
{
expect
(
$
(
'
.modal-title
'
).
text
()).
toBe
(
'
Resume Installation
'
);
expect
(
$
(
'
*[data-name="junit"]
'
).
is
(
'
.success
'
)).
toBe
(
true
);
done
();
expect
(
$
(
'
.modal-title
'
).
text
()).
toBe
(
'
Resume Installation
'
);
expect
(
$
(
'
*[data-name="junit"]
'
).
is
(
'
.success
'
)).
toBe
(
true
);
done
();
},
ajaxMappings
);
});
it
(
"
error conditions
"
,
function
(
done
)
{
var
ajaxMappings
=
{
'
/jenkins/updateCenter/incompleteInstallStatus
'
:
{
'
/jenkins/updateCenter/incompleteInstallStatus
'
:
{
status
:
'
error
'
,
data
:
{
'
junit
'
:
'
Success
'
,
'
subversion
'
:
'
Pending
'
,
'
other
'
:
'
Failed
'
,
'
mailer
'
:
'
Success
'
'
junit
'
:
'
Success
'
,
'
subversion
'
:
'
Pending
'
,
'
other
'
:
'
Failed
'
,
'
mailer
'
:
'
Success
'
}
}
};
test
(
function
(
$
)
{
expect
(
$
(
'
.error-container h1
'
).
html
()).
toBe
(
'
Error
'
);
done
();
expect
(
$
(
'
.error-container h1
'
).
html
()).
toBe
(
'
Error
'
);
done
();
},
ajaxMappings
);
});
...
...
@@ -321,18 +327,24 @@ describe("pluginSetupWizard.js", function () {
var
ajaxMappings
=
{
'
/jenkins/updateCenter/installStatus
'
:
new
LastResponse
([{
status
:
'
ok
'
,
data
:
[]
// first, return nothing by default, no ongoing install
data
:
{
// first, return nothing by default, no ongoing install
state
:
'
NEW
'
,
jobs
:
[]
}
},
{
status
:
'
ok
'
,
data
:
[
{
name
:
'
subversion
'
,
type
:
'
InstallJob
'
,
installStatus
:
'
Success
'
,
requiresRestart
:
'
true
'
// a string...
}
]
data
:
{
state
:
'
INSTALLING_PLUGINS
'
,
jobs
:
[
{
name
:
'
subversion
'
,
type
:
'
InstallJob
'
,
installStatus
:
'
Success
'
,
requiresRestart
:
'
true
'
// a string...
}
]
}
}])
};
test
(
function
(
$
)
{
...
...
@@ -340,12 +352,13 @@ describe("pluginSetupWizard.js", function () {
expect
(
goButton
.
size
()).
toBe
(
1
);
// validate a call to installPlugins with our defaults
setTimeout
(
function
()
{
setTimeout
(
function
()
{
expect
(
$
(
'
.install-done
'
).
is
(
'
:visible
'
)).
toBe
(
false
);
expect
(
$
(
'
.install-done-restart
'
).
is
(
'
:visible
'
)).
toBe
(
true
);
expect
(
$
(
'
.save-first-user
'
).
is
(
'
:visible
'
)).
toBe
(
true
);
done
();
},
500
);
},
500
);
goButton
.
click
();
},
ajaxMappings
);
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录