Merge branch 'stable-2.11'
* stable-2.11: SshDaemon: Don't use deprecated IoAcceptor.dispose() PatchListLoader: Don't use deprecated TemporaryBuffer.LocalFile constructor Fix unused exception throws in EncryptedContactStore Remove unused imports Don't use deprecated PGPPublicKeyRingCollection constructor Print proper name for mergeability check tasks in show-queue command Print proper name for ListenableFutureTask in show-queue command Work around asciidoctor handling of nested ` and * Revert "Revert "Allow configuration of SSH rekey values"" Revert "Revert "SSHD: Allow ECDSA based public key authentication"" Revert "Revert "SSHD: Prevent double authentication for the same public key"" Revert "Downgrade SSHD to 0.9.0-4-g5967cfd" Recommend --data-binary flag when testing submit rules from curl Change edit shortcut from 'm' to 'e' Fix NPE in MergeabilityCache.getIfPresent Fix F5 shortcut being hijacked by edit topic in Firefox Update 2.11 release notes Update version to 2.10.2 Release notes for Gerrit 2.10.2 Do not return 403 when clicking on Gitweb breadcrumb Add log messages to troubleshoot OAuth/OpenID linking Remove unused imports Convert to new AutoCloseable instances coming in JGit 4.0 Remove unused OAuthToken in authorisation URL OnlineReindexer: log the success/failure numbers on exit Work around MyersDiff infinite loop in PatchListLoader Bind SecureStore and SecureStoreClassName in WebAppInitializer Update replication plugin Rework intra line diff to interrupt threads instead of killing them Update JGit to 3.7.0.201502260915-r.58-g65c379e Update replication plugin Workaround a RecursiveMerger bug [1], avoid online reindexing failure OAuth: Allow to link claimed identity to existing accounts OAuth: Allow to change username Allow PatchListLoader to use recursive merger Allow plugins to use self-provided licenses for used Maven Jars Change-Id: Ie7f96abd97bd2c7e9205a708eed11f9052546187
This commit is contained in:
commit
efdb2461d3
@ -210,8 +210,8 @@ Reference-level access control is also possible.
|
||||
|
||||
Permissions can be set on a single reference name to match one
|
||||
branch (e.g. `refs/heads/master`), or on a reference namespace
|
||||
(e.g. `refs/heads/*`) to match any branch starting with that
|
||||
prefix. So a permission with `refs/heads/*` will match
|
||||
(e.g. `+refs/heads/*+`) to match any branch starting with that
|
||||
prefix. So a permission with `+refs/heads/*+` will match
|
||||
`refs/heads/master` and `refs/heads/experimental`, etc.
|
||||
|
||||
Reference names can also be described with a regular expression
|
||||
@ -227,7 +227,7 @@ particular regular expression flavor.
|
||||
References can have the current user name automatically included,
|
||||
creating dynamic access controls that change to match the currently
|
||||
logged in user. For example to provide a personal sandbox space
|
||||
to all developers, `refs/heads/sandbox/${username}/*` allowing
|
||||
to all developers, `+refs/heads/sandbox/${username}/*+` allowing
|
||||
the user 'joe' to use 'refs/heads/sandbox/joe/foo'.
|
||||
|
||||
When evaluating a reference-level access right, Gerrit will use
|
||||
@ -405,19 +405,19 @@ link:user-upload.html#push_create[Upload changes] page.
|
||||
|
||||
==== refs/publish/*
|
||||
|
||||
`refs/publish/*` is an alternative name to `refs/for/*` when pushing new changes
|
||||
`+refs/publish/*+` is an alternative name to `+refs/for/*+` when pushing new changes
|
||||
and patch sets.
|
||||
|
||||
|
||||
==== refs/drafts/*
|
||||
|
||||
Push to `refs/drafts/*` creates a change like push to `refs/for/*`, except the
|
||||
Push to `+refs/drafts/*+` creates a change like push to `+refs/for/*+`, except the
|
||||
resulting change remains hidden from public review. You then have the option
|
||||
of adding individual reviewers before making the change public to all. The
|
||||
change page will have a 'Publish' button which allows you to convert individual
|
||||
draft patch sets of a change into public patch sets for review.
|
||||
|
||||
To block push permission to `refs/drafts/*` the following permission rule can
|
||||
To block push permission to `+refs/drafts/*+` the following permission rule can
|
||||
be configured:
|
||||
|
||||
====
|
||||
@ -464,18 +464,18 @@ branch permissions, allowing the holder of both to create new branches
|
||||
as well as bypass review for new commits on that branch.
|
||||
|
||||
To push lightweight (non-annotated) tags, grant
|
||||
`Create Reference` for reference name `refs/tags/*`, as lightweight
|
||||
`Create Reference` for reference name `+refs/tags/*+`, as lightweight
|
||||
tags are implemented just like branches in Git.
|
||||
|
||||
For example, to grant the possibility to create new branches under the
|
||||
namespace `foo`, you have to grant this permission on
|
||||
`refs/heads/foo/*` for the group that should have it.
|
||||
`+refs/heads/foo/*+` for the group that should have it.
|
||||
Finally, if you plan to grant each user a personal namespace in
|
||||
where they are free to create as many branches as they wish, you
|
||||
should grant the create reference permission so it's possible
|
||||
to create new branches. This is done by using the special
|
||||
`${username}` keyword in the reference pattern, e.g.
|
||||
`refs/heads/sandbox/${username}/*`. If you do, it's also recommended
|
||||
`+refs/heads/sandbox/${username}/*+`. If you do, it's also recommended
|
||||
you grant the users the push force permission to be able to clean up
|
||||
stale branches.
|
||||
|
||||
@ -547,7 +547,7 @@ they are a member of, just like for any other user.
|
||||
Ownership over a particular branch subspace may be delegated by
|
||||
entering a branch pattern. To delegate control over all branches
|
||||
that begin with `qa/` to the QA group, add `Owner` category
|
||||
for reference `refs/heads/qa/*`. Members of the QA group can
|
||||
for reference `+refs/heads/qa/*+`. Members of the QA group can
|
||||
further refine access, but only for references that begin with
|
||||
`refs/heads/qa/`. See <<project_owners,project owners>> to find
|
||||
out more about this role.
|
||||
@ -600,15 +600,15 @@ a new commit on their local system, so in practice they must also
|
||||
have the `Read` access granted to upload a change.
|
||||
|
||||
For an open source, public Gerrit installation, it is common to
|
||||
grant `Read` and `Push` for `refs/for/refs/heads/*`
|
||||
grant `Read` and `Push` for `+refs/for/refs/heads/*+`
|
||||
to `Registered Users` in the `All-Projects` ACL. For more
|
||||
private installations, its common to simply grant `Read` and
|
||||
`Push` for `refs/for/refs/heads/*` to all users of a project.
|
||||
`Push` for `+refs/for/refs/heads/*+` to all users of a project.
|
||||
|
||||
* Force option
|
||||
+
|
||||
The force option has no function when granted to a branch in the
|
||||
`refs/for/refs/heads/*` namespace.
|
||||
`+refs/for/refs/heads/*+` namespace.
|
||||
|
||||
|
||||
[[category_push_merge]]
|
||||
@ -661,11 +661,11 @@ must be also granted in addition to `Push Annotated Tag`.
|
||||
|
||||
To push lightweight (non annotated) tags, grant
|
||||
<<category_create,`Create Reference`>> for reference name
|
||||
`refs/tags/*`, as lightweight tags are implemented just like
|
||||
`+refs/tags/*+`, as lightweight tags are implemented just like
|
||||
branches in Git.
|
||||
|
||||
To delete or overwrite an existing tag, grant `Push` with the force
|
||||
option enabled for reference name `refs/tags/*`, as deleting a tag
|
||||
option enabled for reference name `+refs/tags/*+`, as deleting a tag
|
||||
requires the same permission as deleting a branch.
|
||||
|
||||
|
||||
@ -1026,7 +1026,7 @@ Suggested access rights to grant:
|
||||
|
||||
== Enforcing site wide access policies
|
||||
|
||||
By granting the <<category_owner,`Owner`>> access right on the `refs/*` to a
|
||||
By granting the <<category_owner,`Owner`>> access right on the `+refs/*+` to a
|
||||
group, Gerrit administrators can delegate the responsibility of maintaining
|
||||
access rights for that project to that group.
|
||||
|
||||
|
@ -2621,8 +2621,8 @@ are specified in the link:#container[container section]:
|
||||
If set to true, files with the MIME type `<name>` will be sent as
|
||||
direct downloads to the user's browser, rather than being wrapped up
|
||||
inside of zipped archives. The type name may be a complete type
|
||||
name, e.g. `image/gif`, a generic media type, e.g. `image/*`,
|
||||
or the wildcard `*/*` to match all types.
|
||||
name, e.g. `image/gif`, a generic media type, e.g. `+image/*+`,
|
||||
or the wildcard `+*/*+` to match all types.
|
||||
+
|
||||
By default, false for all MIME types.
|
||||
|
||||
@ -3064,6 +3064,14 @@ namespace. To alias `replication start` to `gerrit replicate`:
|
||||
[[sshd]]
|
||||
=== Section sshd
|
||||
|
||||
[[sshd.backend]]sshd.backend::
|
||||
+
|
||||
Starting from version 0.9.0 Apache SSHD project added support for NIO2
|
||||
IoSession. To use the new NIO2 session the `backend` option must be set
|
||||
to `NIO2`.
|
||||
+
|
||||
By default, `MINA`.
|
||||
|
||||
[[sshd.listenAddress]]sshd.listenAddress::
|
||||
+
|
||||
Specifies the local addresses the internal SSHD should listen
|
||||
@ -3265,6 +3273,24 @@ programmatic configuration.
|
||||
+
|
||||
By default, true.
|
||||
|
||||
[[sshd.rekeyBytesLimit]]sshd.rekeyBytesLimit::
|
||||
+
|
||||
The SSH daemon will issue a rekeying after a certain amount of data.
|
||||
This configuration option allows you to tweak that setting.
|
||||
+
|
||||
By default, 1073741824 (bytes, 1GB).
|
||||
+
|
||||
The rekeyBytesLimit cannot be set to lower than 32.
|
||||
|
||||
[[sshd.rekeyTimeLimit]]sshd.rekeyTimeLimit::
|
||||
+
|
||||
The SSH daemon will issue a rekeying after a certain amount of time.
|
||||
This configuration option allows you to tweak that setting.
|
||||
+
|
||||
By default, 1h.
|
||||
+
|
||||
Set to 0 to disable this check.
|
||||
|
||||
[[suggest]]
|
||||
=== Section suggest
|
||||
|
||||
|
@ -215,7 +215,7 @@ gitweb CGI.
|
||||
|
||||
The CGI's `$projectroot` should be the same directory as
|
||||
gerrit.basePath, or a fairly current replica. If a replica is
|
||||
being used, ensure it uses a full mirror, so the `refs/changes/*`
|
||||
being used, ensure it uses a full mirror, so the `+refs/changes/*+`
|
||||
namespace is available.
|
||||
|
||||
----
|
||||
|
@ -64,7 +64,7 @@ As you can see, there are several sections.
|
||||
The link:#project-section[+project+ section] appears once per project.
|
||||
|
||||
The link:#access-section[+access+ section] appears once per reference pattern,
|
||||
such as `refs/*` or `refs/heads/*`. Only one access section per pattern is
|
||||
such as `+refs/*+` or `+refs/heads/*+`. Only one access section per pattern is
|
||||
allowed. You will find examples of keys and values in each category section
|
||||
<<access_category,below>>.
|
||||
|
||||
|
@ -1692,7 +1692,7 @@ class HelloServlet extends HttpServlet {
|
||||
----
|
||||
|
||||
The auto registration only works for standard servlet mappings like
|
||||
`/foo` or `/foo/*`. Regex style bindings must use a Guice ServletModule
|
||||
`/foo` or `+/foo/*+`. Regex style bindings must use a Guice ServletModule
|
||||
to register the HTTP servlets and declare it explicitly in the manifest
|
||||
with the `Gerrit-HttpModule` attribute:
|
||||
|
||||
@ -1820,7 +1820,7 @@ BranchWebLinks will appear in the branch list in the last column.
|
||||
== Documentation
|
||||
|
||||
If a plugin does not register a filter or servlet to handle URLs
|
||||
`/Documentation/*` or `/static/*`, the core Gerrit server will
|
||||
`+/Documentation/*+` or `+/static/*+`, the core Gerrit server will
|
||||
automatically export these resources over HTTP from the plugin JAR.
|
||||
|
||||
Static resources under the `static/` directory in the JAR will be
|
||||
|
@ -9,32 +9,32 @@ In particular this error occurs:
|
||||
1. if you push a commit for code review to a branch for which you
|
||||
don't have upload permissions (access right
|
||||
link:access-control.html#category_push_review['Push'] on
|
||||
`refs/for/refs/heads/*`)
|
||||
`+refs/for/refs/heads/*+`)
|
||||
2. if you bypass code review without
|
||||
link:access-control.html#category_push_direct['Push'] access right
|
||||
on `refs/heads/*`
|
||||
on `+refs/heads/*+`
|
||||
3. if you bypass code review pushing to a non-existing branch without
|
||||
link:access-control.html#category_create['Create Reference'] access
|
||||
right on `refs/heads/*`
|
||||
right on `+refs/heads/*+`
|
||||
4. if you push an annotated tag without
|
||||
link:access-control.html#category_push_annotated['Push Annotated Tag']
|
||||
access right on 'refs/tags/*'
|
||||
access right on `+refs/tags/*+`
|
||||
5. if you push a signed tag without
|
||||
link:access-control.html#category_push_signed['Push Signed Tag']
|
||||
access right on 'refs/tags/*'
|
||||
access right on `+refs/tags/*+`
|
||||
6. if you push a lightweight tag without the access right link:access-control.html#category_create['Create
|
||||
Reference'] for the reference name 'refs/tags/*'
|
||||
Reference'] for the reference name `+refs/tags/*+`
|
||||
7. if you push a tag with somebody else as tagger and you don't have the
|
||||
link:access-control.html#category_forge_committer['Forge Committer']
|
||||
access right for the reference name 'refs/tags/*'
|
||||
access right for the reference name `+refs/tags/*+`
|
||||
8. if you push to a project that is in state 'Read Only'
|
||||
|
||||
For new users it often happens that they accidentally try to bypass
|
||||
code review. The push then fails with the error message 'prohibited
|
||||
by Gerrit' because the project didn't allow to bypass code review.
|
||||
Bypassing the code review is done by pushing directly to refs/heads/*
|
||||
(e.g. refs/heads/master) instead of pushing to refs/for/* (e.g.
|
||||
refs/for/master). Details about how to push commits for code review
|
||||
Bypassing the code review is done by pushing directly to `+refs/heads/*+`
|
||||
(e.g. `refs/heads/master`) instead of pushing to `+refs/for/*+` (e.g.
|
||||
`refs/for/master`). Details about how to push commits for code review
|
||||
are explained link:user-upload.html#push_create[here].
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@ workflows for a project.
|
||||
Being project owner means that you own a project in Gerrit.
|
||||
Technically this is expressed by having the
|
||||
link:access-control.html#category_owner[Owner] access right on
|
||||
`refs/*` on that project. As project owner you have the permission to
|
||||
`+refs/*+` on that project. As project owner you have the permission to
|
||||
edit the access control list and the project settings of the project.
|
||||
It also means that you should get familiar with these settings so that
|
||||
you can adapt them to the needs of your project.
|
||||
@ -127,12 +127,12 @@ Access rights can be assigned on a concrete ref, e.g.
|
||||
`refs/heads/master` but also on ref patterns and regular expressions
|
||||
for ref names.
|
||||
|
||||
A ref pattern ends with `/*` and describes a complete ref name
|
||||
namespace, e.g. access rights assigned on `refs/heads/*` apply to all
|
||||
A ref pattern ends with `+/*+` and describes a complete ref name
|
||||
namespace, e.g. access rights assigned on `+refs/heads/*+` apply to all
|
||||
branches.
|
||||
|
||||
Regular expressions must start with `^`, e.g. access rights assigned
|
||||
on `^refs/heads/rel-.*` would apply to all `rel-*` branches.
|
||||
on `+^refs/heads/rel-.*+` would apply to all `+rel-*+` branches.
|
||||
|
||||
[[groups]]
|
||||
=== Groups
|
||||
|
@ -987,11 +987,11 @@ submit_type(cherry_pick).
|
||||
----
|
||||
|
||||
[[SubmitTypePerBranch]]
|
||||
=== Example 2: `Fast Forward Only` for all `refs/heads/stable*` branches
|
||||
For all `refs/heads/stable*` branches we would like to enforce the `Fast
|
||||
=== Example 2: `Fast Forward Only` for all `+refs/heads/stable*+` branches
|
||||
For all `+refs/heads/stable*+` branches we would like to enforce the `Fast
|
||||
Forward Only` submit type. A reason for this decision may be a need to never
|
||||
break the build in the stable branches. For all other branches, those not
|
||||
matching the `refs/heads/stable*` pattern, we would like to use the project's
|
||||
matching the `+refs/heads/stable*+` pattern, we would like to use the project's
|
||||
default submit type as defined on the project settings page.
|
||||
|
||||
`rules.pl`
|
||||
@ -1004,13 +1004,13 @@ submit_type(T) :- gerrit:project_default_submit_type(T)
|
||||
----
|
||||
|
||||
The first `submit_type` predicate defines the `Fast Forward Only` submit type
|
||||
for `refs/heads/stable.*` branches. The second `submit_type` predicate returns
|
||||
for `+refs/heads/stable.*+` branches. The second `submit_type` predicate returns
|
||||
the project's default submit type.
|
||||
|
||||
=== Example 3: Don't require `Fast Forward Only` if only documentation was changed
|
||||
Like in the previous example we want the `Fast Forward Only` submit type for the
|
||||
`refs/heads/stable*` branches. However, if only documentation was changed
|
||||
(only `*.txt` files), then we allow project's default submit type for such
|
||||
`+refs/heads/stable*+` branches. However, if only documentation was changed
|
||||
(only `+*.txt+` files), then we allow project's default submit type for such
|
||||
changes.
|
||||
|
||||
`rules.pl`
|
||||
|
@ -70,8 +70,8 @@ For more details, see link:cmd-hook-commit-msg.html[commit-msg].
|
||||
Change Upload
|
||||
--------------
|
||||
|
||||
During upload by pushing to `refs/for/*`, `refs/drafts/*` or
|
||||
`refs/heads/*`, Gerrit will try to find an existing review the
|
||||
During upload by pushing to `+refs/for/*+`, `+refs/drafts/*+` or
|
||||
`+refs/heads/*+`, Gerrit will try to find an existing review the
|
||||
uploaded commit relates to. For an existing review to match, the
|
||||
following properties have to match:
|
||||
|
||||
|
@ -58,7 +58,7 @@ to changes for the current user:
|
||||
== Project Dashboards
|
||||
|
||||
It is possible to share custom dashboards at a project level. To do
|
||||
this define the dashboards in a `refs/meta/dashboards/*` branch of the
|
||||
this define the dashboards in a `+refs/meta/dashboards/*+` branch of the
|
||||
project. For each dashboard create a config file. The file path/name
|
||||
will be used as name (equivalent to a title in a custom dashboard) for
|
||||
the dashboard.
|
||||
|
@ -319,9 +319,9 @@ be rewritten.
|
||||
|
||||
Gerrit restricts direct pushes that bypass review to:
|
||||
|
||||
* `refs/heads/*`: any branch can be updated, created, deleted,
|
||||
* `+refs/heads/*+`: any branch can be updated, created, deleted,
|
||||
or rewritten by the pusher.
|
||||
* `refs/tags/*`: annotated tag objects pointing to any other type
|
||||
* `+refs/tags/*+`: annotated tag objects pointing to any other type
|
||||
of Git object can be created.
|
||||
|
||||
To push branches, the proper access rights must be configured first.
|
||||
@ -445,8 +445,8 @@ As Gerrit implements the entire SSH and Git server stack within its
|
||||
own process space, Gerrit maintains complete control over how the
|
||||
repository is updated, and what responses are sent to the `git push`
|
||||
client invoked by the end-user, or by `repo upload`. This allows
|
||||
Gerrit to provide magical refs, such as `refs/for/*` for new
|
||||
change submission and `refs/changes/*` for change replacement.
|
||||
Gerrit to provide magical refs, such as `+refs/for/*+` for new
|
||||
change submission and `+refs/changes/*+` for change replacement.
|
||||
When a push request is received to create a ref in one of these
|
||||
namespaces Gerrit performs its own logic to update the database,
|
||||
and then lies to the client about the result of the operation.
|
||||
|
31
ReleaseNotes/ReleaseNotes-2.10.2.txt
Normal file
31
ReleaseNotes/ReleaseNotes-2.10.2.txt
Normal file
@ -0,0 +1,31 @@
|
||||
Release notes for Gerrit 2.10.2
|
||||
===============================
|
||||
|
||||
There are no schema changes from link:ReleaseNotes-2.10.1.html[2.10.1].
|
||||
|
||||
Download:
|
||||
link:https://gerrit-releases.storage.googleapis.com/gerrit-2.10.2.war[
|
||||
https://gerrit-releases.storage.googleapis.com/gerrit-2.10.2.war]
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
* Work around MyersDiff infinite loop in PatchListLoader. If the MyersDiff diff
|
||||
doesn't finish within 5 seconds, interrupt it and fall back to a different diff
|
||||
algorithm. From the user perspective, the only difference when the infinite
|
||||
loop is detected is that the files in the commit will not be compared in-depth,
|
||||
which will result in bigger edit regions.
|
||||
|
||||
Secondary Index
|
||||
---------------
|
||||
|
||||
* Online reindexing: log the number of done/failed changes in the error_log.
|
||||
Administrators can use the logged information to decide whether to activate the
|
||||
new index version or not.
|
||||
|
||||
Gitweb
|
||||
------
|
||||
|
||||
* Do not return `Forbidden` when clicking on Gitweb breadcrumb. Now when the
|
||||
user clicks on the parent folder, redirect to Gerrit projects list screen with
|
||||
the parent folder path as the filter.
|
@ -8,7 +8,8 @@ link:https://gerrit-releases.storage.googleapis.com/gerrit-2.11.war[
|
||||
https://gerrit-releases.storage.googleapis.com/gerrit-2.11.war]
|
||||
|
||||
Gerrit 2.11 includes the bug fixes done with
|
||||
link:ReleaseNotes-2.10.1.html[Gerrit 2.10.1].
|
||||
link:ReleaseNotes-2.10.1.html[Gerrit 2.10.1] and
|
||||
link:ReleaseNotes-2.10.2.html[Gerrit 2.10.2].
|
||||
These bug fixes are *not* listed in these release notes.
|
||||
|
||||
|
||||
|
@ -9,6 +9,7 @@ Version 2.11.x
|
||||
[[2_10]]
|
||||
Version 2.10.x
|
||||
--------------
|
||||
* link:ReleaseNotes-2.10.2.html[2.10.2]
|
||||
* link:ReleaseNotes-2.10.1.html[2.10.1]
|
||||
* link:ReleaseNotes-2.10.html[2.10]
|
||||
|
||||
|
@ -22,30 +22,21 @@ import java.io.IOException;
|
||||
@ExtensionPoint
|
||||
public interface OAuthServiceProvider {
|
||||
|
||||
/**
|
||||
* Retrieve the request token.
|
||||
*
|
||||
* @return request token
|
||||
*/
|
||||
OAuthToken getRequestToken();
|
||||
|
||||
/**
|
||||
* Returns the URL where you should redirect your users to authenticate
|
||||
* your application.
|
||||
*
|
||||
* @param requestToken the request token you need to authorize
|
||||
* @return the URL where you should redirect your users
|
||||
* @return the OAuth service URL to redirect your users for authentication
|
||||
*/
|
||||
String getAuthorizationUrl(OAuthToken requestToken);
|
||||
String getAuthorizationUrl();
|
||||
|
||||
/**
|
||||
* Retrieve the access token
|
||||
*
|
||||
* @param requestToken request token (obtained previously)
|
||||
* @param verifier verifier code
|
||||
* @return access token
|
||||
*/
|
||||
OAuthToken getAccessToken(OAuthToken requestToken, OAuthVerifier verifier);
|
||||
OAuthToken getAccessToken(OAuthVerifier verifier);
|
||||
|
||||
/**
|
||||
* After establishing of secure communication channel, this method supossed to
|
||||
|
@ -20,15 +20,18 @@ public class OAuthUserInfo {
|
||||
private final String userName;
|
||||
private final String emailAddress;
|
||||
private final String displayName;
|
||||
private final String claimedIdentity;
|
||||
|
||||
public OAuthUserInfo(String externalId,
|
||||
String userName,
|
||||
String emailAddress,
|
||||
String displayName) {
|
||||
String displayName,
|
||||
String claimedIdentity) {
|
||||
this.externalId = externalId;
|
||||
this.userName = userName;
|
||||
this.emailAddress = emailAddress;
|
||||
this.displayName = displayName;
|
||||
this.claimedIdentity = claimedIdentity;
|
||||
}
|
||||
|
||||
public String getExternalId() {
|
||||
@ -46,4 +49,8 @@ public class OAuthUserInfo {
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public String getClaimedIdentity() {
|
||||
return claimedIdentity;
|
||||
}
|
||||
}
|
||||
|
@ -583,6 +583,12 @@ public class ChangeScreen extends Screen {
|
||||
@Override
|
||||
public void onKeyPress(KeyPressEvent event) {
|
||||
if (Gerrit.isSignedIn()) {
|
||||
// In Firefox this event is mistakenly called when F5 is pressed so
|
||||
// differentiate F5 from 't' by checking the charCode(F5=0, t=116).
|
||||
if (event.getNativeEvent().getCharCode() == 0) {
|
||||
Window.Location.reload();
|
||||
return;
|
||||
}
|
||||
if (topic.canEdit()) {
|
||||
topic.onEdit();
|
||||
}
|
||||
|
@ -79,7 +79,9 @@ public class OnlineReindexer {
|
||||
SiteIndexer.Result result =
|
||||
batchIndexer.indexAll(index, projectCache.all());
|
||||
if (!result.success()) {
|
||||
log.error("Online reindex of schema version {} failed", version(index));
|
||||
log.error("Online reindex of schema version {} failed. Successfully"
|
||||
+ " indexed {} changes, failed to index {} changes",
|
||||
version(index), result.doneCount(), result.failedCount());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -11,9 +11,11 @@ java_library(
|
||||
'//gerrit-common:annotations',
|
||||
'//gerrit-extension-api:api',
|
||||
'//gerrit-httpd:httpd',
|
||||
'//gerrit-reviewdb:server',
|
||||
'//gerrit-server:server',
|
||||
'//lib:gson',
|
||||
'//lib:guava',
|
||||
'//lib:gwtorm',
|
||||
'//lib/commons:codec',
|
||||
'//lib/guice:guice',
|
||||
'//lib/guice:guice-servlet',
|
||||
|
@ -23,9 +23,11 @@ import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
|
||||
import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
|
||||
import com.google.gerrit.extensions.registration.DynamicItem;
|
||||
import com.google.gerrit.httpd.WebSession;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.account.AccountException;
|
||||
import com.google.gerrit.server.account.AccountManager;
|
||||
import com.google.gerrit.server.account.AuthResult;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.servlet.SessionScoped;
|
||||
|
||||
@ -87,8 +89,7 @@ class OAuthSession {
|
||||
}
|
||||
|
||||
log.debug("Login-Retrieve-User " + this);
|
||||
token = oauth.getAccessToken(null,
|
||||
new OAuthVerifier(request.getParameter("code")));
|
||||
token = oauth.getAccessToken(new OAuthVerifier(request.getParameter("code")));
|
||||
|
||||
user = oauth.getUserInfo(token);
|
||||
|
||||
@ -103,7 +104,7 @@ class OAuthSession {
|
||||
} else {
|
||||
log.debug("Login-PHASE1 " + this);
|
||||
redirectUrl = request.getRequestURI();
|
||||
response.sendRedirect(oauth.getAuthorizationUrl(null) +
|
||||
response.sendRedirect(oauth.getAuthorizationUrl() +
|
||||
"&state=" + state);
|
||||
return false;
|
||||
}
|
||||
@ -113,11 +114,48 @@ class OAuthSession {
|
||||
throws IOException {
|
||||
com.google.gerrit.server.account.AuthRequest areq =
|
||||
new com.google.gerrit.server.account.AuthRequest(user.getExternalId());
|
||||
areq.setUserName(user.getUserName());
|
||||
areq.setEmailAddress(user.getEmailAddress());
|
||||
areq.setDisplayName(user.getDisplayName());
|
||||
AuthResult arsp;
|
||||
try {
|
||||
String claimedIdentifier = user.getClaimedIdentity();
|
||||
Account.Id actualId = accountManager.lookup(user.getExternalId());
|
||||
if (!Strings.isNullOrEmpty(claimedIdentifier)) {
|
||||
Account.Id claimedId = accountManager.lookup(claimedIdentifier);
|
||||
if (claimedId != null && actualId != null) {
|
||||
if (claimedId.equals(actualId)) {
|
||||
// Both link to the same account, that's what we expected.
|
||||
log.debug("OAuth2: claimed identity equals current id");
|
||||
} else {
|
||||
// This is (for now) a fatal error. There are two records
|
||||
// for what might be the same user.
|
||||
//
|
||||
log.error("OAuth accounts disagree over user identity:\n"
|
||||
+ " Claimed ID: " + claimedId + " is " + claimedIdentifier
|
||||
+ "\n" + " Delgate ID: " + actualId + " is "
|
||||
+ user.getExternalId());
|
||||
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
} else if (claimedId != null && actualId == null) {
|
||||
// Claimed account already exists: link to it.
|
||||
//
|
||||
log.info("OAuth2: linking claimed identity to {}",
|
||||
claimedId.toString());
|
||||
try {
|
||||
accountManager.link(claimedId, areq);
|
||||
} catch (OrmException e) {
|
||||
log.error("Cannot link: " + user.getExternalId()
|
||||
+ " to user identity:\n"
|
||||
+ " Claimed ID: " + claimedId + " is " + claimedIdentifier);
|
||||
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.debug("OAuth2: claimed identity is empty");
|
||||
}
|
||||
areq.setUserName(user.getUserName());
|
||||
areq.setEmailAddress(user.getEmailAddress());
|
||||
areq.setDisplayName(user.getDisplayName());
|
||||
arsp = accountManager.authenticate(areq);
|
||||
} catch (AccountException e) {
|
||||
log.error("Unable to authenticate user \"" + user + "\"", e);
|
||||
|
@ -144,6 +144,7 @@ class OpenIdServiceImpl {
|
||||
final AuthRequest aReq;
|
||||
try {
|
||||
aReq = manager.authenticate(state.discovered, state.retTo.toString());
|
||||
log.debug("OpenID: openid-realm={}", state.contextUrl);
|
||||
aReq.setRealm(state.contextUrl);
|
||||
|
||||
if (requestRegistration(aReq)) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.gerrit.pgm.util;
|
||||
|
||||
import static com.google.gerrit.server.config.GerritServerConfigModule.getSecureStoreClassName;
|
||||
import static com.google.inject.Scopes.SINGLETON;
|
||||
import static com.google.inject.Stage.PRODUCTION;
|
||||
|
||||
@ -24,7 +25,6 @@ import com.google.gerrit.lifecycle.LifecycleModule;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.config.GerritServerConfigModule;
|
||||
import com.google.gerrit.server.config.SitePath;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
|
||||
import com.google.gerrit.server.schema.DataSourceModule;
|
||||
import com.google.gerrit.server.schema.DataSourceProvider;
|
||||
@ -41,20 +41,15 @@ import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.ProvisionException;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Named;
|
||||
import com.google.inject.name.Names;
|
||||
import com.google.inject.spi.Message;
|
||||
import com.google.inject.util.Providers;
|
||||
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.storage.file.FileBasedConfig;
|
||||
import org.eclipse.jgit.util.FS;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -190,27 +185,7 @@ public abstract class SiteProgram extends AbstractProgram {
|
||||
}
|
||||
|
||||
protected final String getConfiguredSecureStoreClass() {
|
||||
Module m = new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(Path.class).annotatedWith(SitePath.class).toInstance(getSitePath());
|
||||
bind(SitePaths.class);
|
||||
}
|
||||
};
|
||||
Injector i = Guice.createInjector(m);
|
||||
SitePaths site = i.getInstance(SitePaths.class);
|
||||
FileBasedConfig cfg =
|
||||
new FileBasedConfig(site.gerrit_config.toFile(), FS.DETECTED);
|
||||
if (!cfg.getFile().exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
cfg.load();
|
||||
return cfg.getString("gerrit", null, "secureStoreClass");
|
||||
} catch (IOException | ConfigInvalidException e) {
|
||||
throw new ProvisionException(e.getMessage(), e);
|
||||
}
|
||||
return getSecureStoreClassName(sitePath);
|
||||
}
|
||||
|
||||
private String getDbType(Provider<DataSource> dsProvider) {
|
||||
|
@ -15,16 +15,16 @@
|
||||
|
||||
# Version should match lib/bouncycastle/BUCK
|
||||
[library "bouncyCastleProvider"]
|
||||
name = Bouncy Castle Crypto Provider v149
|
||||
url = http://www.bouncycastle.org/download/bcprov-jdk15on-149.jar
|
||||
sha1 = f5155f04330459104b79923274db5060c1057b99
|
||||
name = Bouncy Castle Crypto Provider v151
|
||||
url = http://www.bouncycastle.org/download/bcprov-jdk15on-151.jar
|
||||
sha1 = 9ab8afcc2842d5ef06eb775a0a2b12783b99aa80
|
||||
remove = bcprov-.*[.]jar
|
||||
|
||||
# Version should match lib/bouncycastle/BUCK
|
||||
[library "bouncyCastleSSL"]
|
||||
name = Bouncy Castle Crypto SSL v149
|
||||
url = http://www.bouncycastle.org/download/bcpkix-jdk15on-149.jar
|
||||
sha1 = 924cc7ad2f589630c97b918f044296ebf1bb6855
|
||||
name = Bouncy Castle Crypto SSL v151
|
||||
url = http://www.bouncycastle.org/download/bcpkix-jdk15on-151.jar
|
||||
sha1 = 6c8c1f61bf27a09f9b1a8abc201523669bba9597
|
||||
needs = bouncyCastleProvider
|
||||
remove = bcpkix-.*[.]jar
|
||||
|
||||
|
@ -40,8 +40,7 @@ public class DefaultRealm extends AbstractRealm {
|
||||
|
||||
@Override
|
||||
public boolean allowsEdit(final Account.FieldName field) {
|
||||
if (authConfig.getAuthType() == AuthType.HTTP
|
||||
|| authConfig.getAuthType() == AuthType.OAUTH) {
|
||||
if (authConfig.getAuthType() == AuthType.HTTP) {
|
||||
switch (field) {
|
||||
case USER_NAME:
|
||||
return false;
|
||||
|
@ -16,14 +16,61 @@ package com.google.gerrit.server.config;
|
||||
|
||||
import static com.google.inject.Scopes.SINGLETON;
|
||||
|
||||
import com.google.gerrit.server.securestore.DefaultSecureStore;
|
||||
import com.google.gerrit.server.securestore.SecureStore;
|
||||
import com.google.gerrit.server.securestore.SecureStoreProvider;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.ProvisionException;
|
||||
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.storage.file.FileBasedConfig;
|
||||
import org.eclipse.jgit.util.FS;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/** Creates {@link GerritServerConfig}. */
|
||||
public class GerritServerConfigModule extends AbstractModule {
|
||||
public static String getSecureStoreClassName(final Path sitePath) {
|
||||
if (sitePath != null) {
|
||||
return getSecureStoreFromGerritConfig(sitePath);
|
||||
}
|
||||
|
||||
String secureStoreProperty = System.getProperty("gerrit.secure_store_class");
|
||||
return nullToDefault(secureStoreProperty);
|
||||
}
|
||||
|
||||
private static String getSecureStoreFromGerritConfig(final Path sitePath) {
|
||||
AbstractModule m = new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
|
||||
bind(SitePaths.class);
|
||||
}
|
||||
};
|
||||
Injector injector = Guice.createInjector(m);
|
||||
SitePaths site = injector.getInstance(SitePaths.class);
|
||||
FileBasedConfig cfg = new FileBasedConfig(site.gerrit_config.toFile(), FS.DETECTED);
|
||||
if (!cfg.getFile().exists()) {
|
||||
return DefaultSecureStore.class.getName();
|
||||
}
|
||||
|
||||
try {
|
||||
cfg.load();
|
||||
String className = cfg.getString("gerrit", null, "secureStoreClass");
|
||||
return nullToDefault(className);
|
||||
} catch (IOException | ConfigInvalidException e) {
|
||||
throw new ProvisionException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String nullToDefault(String className) {
|
||||
return className != null ? className : DefaultSecureStore.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SitePaths.class);
|
||||
|
@ -38,6 +38,9 @@ import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||
import org.bouncycastle.openpgp.PGPUtil;
|
||||
import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRingCollection;
|
||||
import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
|
||||
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -49,7 +52,6 @@ import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.sql.Timestamp;
|
||||
import java.text.SimpleDateFormat;
|
||||
@ -94,7 +96,7 @@ class EncryptedContactStore implements ContactStore {
|
||||
//
|
||||
try {
|
||||
encrypt("test", new Date(0), "test".getBytes("UTF-8"));
|
||||
} catch (NoSuchProviderException | PGPException | IOException e) {
|
||||
} catch (PGPException | IOException e) {
|
||||
throw new ProvisionException("PGP encryption not available", e);
|
||||
}
|
||||
}
|
||||
@ -107,7 +109,7 @@ class EncryptedContactStore implements ContactStore {
|
||||
private static PGPPublicKeyRingCollection readPubRing(Path pub) {
|
||||
try (InputStream fin = Files.newInputStream(pub);
|
||||
InputStream in = PGPUtil.getDecoderStream(fin)) {
|
||||
return new PGPPublicKeyRingCollection(in);
|
||||
return new BcPGPPublicKeyRingCollection(in);
|
||||
} catch (IOException | PGPException e) {
|
||||
throw new ProvisionException("Cannot read " + pub, e);
|
||||
}
|
||||
@ -150,23 +152,26 @@ class EncryptedContactStore implements ContactStore {
|
||||
u.put("account_id", String.valueOf(account.getId().get()));
|
||||
u.put("data", encStr);
|
||||
connFactory.open(storeUrl).store(u.toString().getBytes("UTF-8"));
|
||||
} catch (IOException | PGPException | NoSuchProviderException e) {
|
||||
} catch (IOException | PGPException e) {
|
||||
log.error("Cannot store encrypted contact information", e);
|
||||
throw new ContactInformationStoreException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private final PGPEncryptedDataGenerator cpk()
|
||||
throws NoSuchProviderException, PGPException {
|
||||
private final PGPEncryptedDataGenerator cpk() {
|
||||
final BcPGPDataEncryptorBuilder builder =
|
||||
new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5)
|
||||
.setSecureRandom(prng);
|
||||
PGPEncryptedDataGenerator cpk =
|
||||
new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, true, prng, "BC");
|
||||
cpk.addMethod(dest);
|
||||
new PGPEncryptedDataGenerator(builder, true);
|
||||
final BcPublicKeyKeyEncryptionMethodGenerator methodGenerator =
|
||||
new BcPublicKeyKeyEncryptionMethodGenerator(dest);
|
||||
cpk.addMethod(methodGenerator);
|
||||
return cpk;
|
||||
}
|
||||
|
||||
private byte[] encrypt(final String name, final Date date,
|
||||
final byte[] rawText) throws NoSuchProviderException, PGPException,
|
||||
final byte[] rawText) throws PGPException,
|
||||
IOException {
|
||||
final byte[] zText = compress(name, date, rawText);
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.server.git;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.ListenableFutureTask;
|
||||
import com.google.gerrit.extensions.events.LifecycleListener;
|
||||
import com.google.gerrit.lifecycle.LifecycleModule;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
@ -28,6 +29,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
@ -385,6 +387,26 @@ public class WorkQueue {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
//This is a workaround to be able to print a proper name when the task
|
||||
//is wrapped into a ListenableFutureTask.
|
||||
if (runnable instanceof ListenableFutureTask<?>) {
|
||||
String errorMessage;
|
||||
try {
|
||||
for (Field field : ListenableFutureTask.class.getSuperclass()
|
||||
.getDeclaredFields()) {
|
||||
if (field.getType().isAssignableFrom(Callable.class)) {
|
||||
field.setAccessible(true);
|
||||
return ((Callable<?>) field.get(runnable)).toString();
|
||||
}
|
||||
}
|
||||
errorMessage = "Cannot find wrapped Callable field";
|
||||
} catch (SecurityException | IllegalArgumentException
|
||||
| IllegalAccessException e) {
|
||||
errorMessage = "Cannot call toString on Callable field";
|
||||
}
|
||||
log.debug("Cannot get a proper name for ListenableFutureTask: {}",
|
||||
errorMessage);
|
||||
}
|
||||
return runnable.toString();
|
||||
}
|
||||
}
|
||||
|
@ -206,10 +206,10 @@ public class PatchListLoader extends CacheLoader<PatchListKey, PatchList> {
|
||||
} catch (InterruptedException | TimeoutException e) {
|
||||
log.warn(timeoutMillis + " ms timeout reached for Diff loader"
|
||||
+ " in project " + key.projectKey.get()
|
||||
+ " on commit " + key.getNewId()
|
||||
+ " on commit " + key.getNewId().name()
|
||||
+ " on path " + diffEntry.getNewPath()
|
||||
+ " comparing " + diffEntry.getOldId()
|
||||
+ ".." + diffEntry.getNewId());
|
||||
+ " comparing " + diffEntry.getOldId().name()
|
||||
+ ".." + diffEntry.getNewId().name());
|
||||
result.cancel(true);
|
||||
return toFileHeaderWithoutMyersDiff(diffFormatter, diffEntry);
|
||||
} catch (ExecutionException e) {
|
||||
|
@ -0,0 +1,72 @@
|
||||
// Copyright (C) 2014 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.sshd;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.apache.sshd.common.Session;
|
||||
import org.apache.sshd.common.SessionListener;
|
||||
import org.apache.sshd.server.PublickeyAuthenticator;
|
||||
import org.apache.sshd.server.session.ServerSession;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Singleton
|
||||
public class CachingPublicKeyAuthenticator implements PublickeyAuthenticator,
|
||||
SessionListener {
|
||||
|
||||
private final PublickeyAuthenticator authenticator;
|
||||
private final Map<ServerSession, Map<PublicKey, Boolean>> sessionCache;
|
||||
|
||||
@Inject
|
||||
public CachingPublicKeyAuthenticator(DatabasePubKeyAuth authenticator) {
|
||||
this.authenticator = authenticator;
|
||||
this.sessionCache = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean authenticate(String username, PublicKey key,
|
||||
ServerSession session) {
|
||||
Map<PublicKey, Boolean> m = sessionCache.get(session);
|
||||
if (m == null) {
|
||||
m = new HashMap<>();
|
||||
sessionCache.put(session, m);
|
||||
session.addListener(this);
|
||||
}
|
||||
if (m.containsKey(key)) {
|
||||
return m.get(key);
|
||||
}
|
||||
boolean r = authenticator.authenticate(username, key, session);
|
||||
m.put(key, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionCreated(Session session) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionEvent(Session sesssion, Event event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionClosed(Session session) {
|
||||
sessionCache.remove(session);
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.sshd;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import com.google.gerrit.common.FileUtil;
|
||||
import com.google.gerrit.reviewdb.client.AccountSshKey;
|
||||
@ -23,7 +24,6 @@ import com.google.gerrit.server.PeerDaemonUser;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.sshd.common.KeyPairProvider;
|
||||
@ -51,7 +51,6 @@ import java.util.Set;
|
||||
/**
|
||||
* Authenticates by public key through {@link AccountSshKey} entities.
|
||||
*/
|
||||
@Singleton
|
||||
class DatabasePubKeyAuth implements PublickeyAuthenticator {
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(DatabasePubKeyAuth.class);
|
||||
@ -96,10 +95,10 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean authenticate(String username,
|
||||
final PublicKey suppliedKey, final ServerSession session) {
|
||||
final SshSession sd = session.getAttribute(SshSession.KEY);
|
||||
|
||||
public boolean authenticate(String username, PublicKey suppliedKey,
|
||||
ServerSession session) {
|
||||
SshSession sd = session.getAttribute(SshSession.KEY);
|
||||
Preconditions.checkState(sd.getCurrentUser() == null);
|
||||
if (PeerDaemonUser.USER_NAME.equals(username)) {
|
||||
if (myHostKeys.contains(suppliedKey)
|
||||
|| getPeerKeys().contains(suppliedKey)) {
|
||||
@ -116,10 +115,10 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator {
|
||||
username = username.toLowerCase(Locale.US);
|
||||
}
|
||||
|
||||
final Iterable<SshKeyCacheEntry> keyList = sshKeyCache.get(username);
|
||||
final SshKeyCacheEntry key = find(keyList, suppliedKey);
|
||||
Iterable<SshKeyCacheEntry> keyList = sshKeyCache.get(username);
|
||||
SshKeyCacheEntry key = find(keyList, suppliedKey);
|
||||
if (key == null) {
|
||||
final String err;
|
||||
String err;
|
||||
if (keyList == SshKeyCacheImpl.NO_SUCH_USER) {
|
||||
err = "user-not-found";
|
||||
} else if (keyList == SshKeyCacheImpl.NO_KEYS) {
|
||||
@ -137,7 +136,7 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator {
|
||||
// security check to ensure there aren't two users sharing the same
|
||||
// user name on the server.
|
||||
//
|
||||
for (final SshKeyCacheEntry otherKey : keyList) {
|
||||
for (SshKeyCacheEntry otherKey : keyList) {
|
||||
if (!key.getAccount().equals(otherKey.getAccount())) {
|
||||
sd.authenticationError(username, "keys-cross-accounts");
|
||||
return false;
|
||||
|
@ -46,6 +46,7 @@ import org.apache.sshd.common.KeyExchange;
|
||||
import org.apache.sshd.common.KeyPairProvider;
|
||||
import org.apache.sshd.common.NamedFactory;
|
||||
import org.apache.sshd.common.Random;
|
||||
import org.apache.sshd.common.RequestHandler;
|
||||
import org.apache.sshd.common.Session;
|
||||
import org.apache.sshd.common.Signature;
|
||||
import org.apache.sshd.common.SshdSocketAddress;
|
||||
@ -68,10 +69,11 @@ import org.apache.sshd.common.forward.TcpipServerChannel;
|
||||
import org.apache.sshd.common.future.CloseFuture;
|
||||
import org.apache.sshd.common.future.SshFutureListener;
|
||||
import org.apache.sshd.common.io.IoAcceptor;
|
||||
import org.apache.sshd.common.io.IoServiceFactory;
|
||||
import org.apache.sshd.common.io.IoServiceFactoryFactory;
|
||||
import org.apache.sshd.common.io.IoSession;
|
||||
import org.apache.sshd.common.io.mina.MinaServiceFactory;
|
||||
import org.apache.sshd.common.io.mina.MinaServiceFactoryFactory;
|
||||
import org.apache.sshd.common.io.mina.MinaSession;
|
||||
import org.apache.sshd.common.io.nio2.Nio2ServiceFactoryFactory;
|
||||
import org.apache.sshd.common.mac.HMACMD5;
|
||||
import org.apache.sshd.common.mac.HMACMD596;
|
||||
import org.apache.sshd.common.mac.HMACSHA1;
|
||||
@ -80,7 +82,9 @@ import org.apache.sshd.common.random.BouncyCastleRandom;
|
||||
import org.apache.sshd.common.random.JceRandom;
|
||||
import org.apache.sshd.common.random.SingletonRandomFactory;
|
||||
import org.apache.sshd.common.session.AbstractSession;
|
||||
import org.apache.sshd.common.session.ConnectionService;
|
||||
import org.apache.sshd.common.signature.SignatureDSA;
|
||||
import org.apache.sshd.common.signature.SignatureECDSA;
|
||||
import org.apache.sshd.common.signature.SignatureRSA;
|
||||
import org.apache.sshd.common.util.Buffer;
|
||||
import org.apache.sshd.common.util.SecurityUtils;
|
||||
@ -92,6 +96,10 @@ import org.apache.sshd.server.auth.UserAuthPublicKey;
|
||||
import org.apache.sshd.server.auth.gss.GSSAuthenticator;
|
||||
import org.apache.sshd.server.auth.gss.UserAuthGSS;
|
||||
import org.apache.sshd.server.channel.ChannelSession;
|
||||
import org.apache.sshd.server.global.CancelTcpipForwardHandler;
|
||||
import org.apache.sshd.server.global.KeepAliveHandler;
|
||||
import org.apache.sshd.server.global.NoMoreSessionsHandler;
|
||||
import org.apache.sshd.server.global.TcpipForwardHandler;
|
||||
import org.apache.sshd.server.kex.DHG1;
|
||||
import org.apache.sshd.server.kex.DHG14;
|
||||
import org.apache.sshd.server.session.SessionFactory;
|
||||
@ -137,6 +145,7 @@ import java.util.List;
|
||||
*/
|
||||
@Singleton
|
||||
public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
|
||||
@SuppressWarnings("hiding") // Don't use AbstractCloseable's logger.
|
||||
private static final Logger log = LoggerFactory.getLogger(SshDaemon.class);
|
||||
|
||||
public static enum SshSessionBackend {
|
||||
@ -184,6 +193,15 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
|
||||
IDLE_TIMEOUT,
|
||||
String.valueOf(SECONDS.toMillis(idleTimeoutSeconds)));
|
||||
|
||||
long rekeyTimeLimit = ConfigUtil.getTimeUnit(cfg, "sshd", null,
|
||||
"rekeyTimeLimit", 3600, SECONDS);
|
||||
getProperties().put(
|
||||
REKEY_TIME_LIMIT,
|
||||
String.valueOf(SECONDS.toMillis(rekeyTimeLimit)));
|
||||
|
||||
getProperties().put(REKEY_BYTES_LIMIT,
|
||||
String.valueOf(cfg.getLong("sshd", "rekeyBytesLimit", 1024 * 1024 * 1024 /* 1GB */)));
|
||||
|
||||
final int maxConnectionsPerUser =
|
||||
cfg.getInt("sshd", "maxConnectionsPerUser", 64);
|
||||
if (0 < maxConnectionsPerUser) {
|
||||
@ -196,8 +214,13 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
|
||||
final String kerberosPrincipal = cfg.getString(
|
||||
"sshd", null, "kerberosPrincipal");
|
||||
|
||||
System.setProperty(IoServiceFactory.class.getName(),
|
||||
MinaServiceFactory.class.getName());
|
||||
SshSessionBackend backend = cfg.getEnum(
|
||||
"sshd", null, "backend", SshSessionBackend.MINA);
|
||||
|
||||
System.setProperty(IoServiceFactoryFactory.class.getName(),
|
||||
backend == SshSessionBackend.MINA
|
||||
? MinaServiceFactoryFactory.class.getName()
|
||||
: Nio2ServiceFactoryFactory.class.getName());
|
||||
|
||||
if (SecurityUtils.isBouncyCastleRegistered()) {
|
||||
initProviderBouncyCastle(cfg);
|
||||
@ -254,6 +277,12 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
|
||||
return new GerritServerSession(server, ioSession);
|
||||
}
|
||||
});
|
||||
setGlobalRequestHandlers(Arrays.<RequestHandler<ConnectionService>> asList(
|
||||
new KeepAliveHandler(),
|
||||
new NoMoreSessionsHandler(),
|
||||
new TcpipForwardHandler(),
|
||||
new CancelTcpipForwardHandler()
|
||||
));
|
||||
|
||||
hostKeys = computeHostKeys();
|
||||
}
|
||||
@ -303,8 +332,10 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
|
||||
public synchronized void stop() {
|
||||
if (daemonAcceptor != null) {
|
||||
try {
|
||||
daemonAcceptor.dispose();
|
||||
daemonAcceptor.close(true).await();
|
||||
log.info("Stopped Gerrit SSHD");
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Exception caught while closing", e);
|
||||
} finally {
|
||||
daemonAcceptor = null;
|
||||
}
|
||||
@ -405,6 +436,12 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
|
||||
public void fill(byte[] bytes, int start, int len) {
|
||||
random.nextBytes(bytes, start, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int random(int n) {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void initProviderJce() {
|
||||
@ -526,7 +563,11 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
|
||||
|
||||
private void initSignatures() {
|
||||
setSignatureFactories(Arrays.<NamedFactory<Signature>> asList(
|
||||
new SignatureDSA.Factory(), new SignatureRSA.Factory()));
|
||||
new SignatureDSA.Factory(),
|
||||
new SignatureRSA.Factory(),
|
||||
new SignatureECDSA.NISTP256Factory(),
|
||||
new SignatureECDSA.NISTP384Factory(),
|
||||
new SignatureECDSA.NISTP521Factory()));
|
||||
}
|
||||
|
||||
private void initCompression() {
|
||||
@ -621,6 +662,11 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
|
||||
@Override
|
||||
public SshFile getFile(String file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemView getNormalizedView() {
|
||||
return this;
|
||||
}};
|
||||
}
|
||||
});
|
||||
|
@ -81,7 +81,7 @@ public class SshModule extends LifecycleModule {
|
||||
bind(QueueProvider.class).to(CommandExecutorQueueProvider.class).in(SINGLETON);
|
||||
|
||||
bind(GSSAuthenticator.class).to(GerritGSSAuthenticator.class);
|
||||
bind(PublickeyAuthenticator.class).to(DatabasePubKeyAuth.class);
|
||||
bind(PublickeyAuthenticator.class).to(CachingPublicKeyAuthenticator.class);
|
||||
|
||||
bind(ModuleGenerator.class).to(SshAutoRegisterModuleGenerator.class);
|
||||
bind(SshPluginStarterCallback.class);
|
||||
|
@ -25,8 +25,8 @@ import com.google.gerrit.sshd.SshDaemon;
|
||||
import com.google.gerrit.sshd.SshSession;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.apache.sshd.common.future.CloseFuture;
|
||||
import org.apache.sshd.common.io.IoAcceptor;
|
||||
import org.apache.sshd.common.io.IoCloseFuture;
|
||||
import org.apache.sshd.common.io.IoSession;
|
||||
import org.apache.sshd.server.session.ServerSession;
|
||||
import org.kohsuke.args4j.Argument;
|
||||
@ -76,7 +76,7 @@ final class CloseConnection extends SshCommand {
|
||||
if (sshSession != null && sshSession.getSessionId() == id) {
|
||||
connectionFound = true;
|
||||
stdout.println("closing connection " + sessionId + "...");
|
||||
IoCloseFuture future = io.close(true);
|
||||
CloseFuture future = io.close(true);
|
||||
if (wait) {
|
||||
try {
|
||||
future.await();
|
||||
|
@ -56,6 +56,7 @@ import com.google.gerrit.server.schema.DataSourceType;
|
||||
import com.google.gerrit.server.schema.DatabaseModule;
|
||||
import com.google.gerrit.server.schema.SchemaModule;
|
||||
import com.google.gerrit.server.schema.SchemaVersionCheck;
|
||||
import com.google.gerrit.server.securestore.SecureStoreClassName;
|
||||
import com.google.gerrit.server.ssh.NoSshModule;
|
||||
import com.google.gerrit.server.ssh.SshAddressesModule;
|
||||
import com.google.gerrit.solr.SolrIndexModule;
|
||||
@ -74,6 +75,7 @@ import com.google.inject.name.Names;
|
||||
import com.google.inject.servlet.GuiceFilter;
|
||||
import com.google.inject.servlet.GuiceServletContextListener;
|
||||
import com.google.inject.spi.Message;
|
||||
import com.google.inject.util.Providers;
|
||||
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.slf4j.Logger;
|
||||
@ -207,6 +209,7 @@ public class WebAppInitializer extends GuiceServletContextListener
|
||||
|
||||
private Injector createDbInjector() {
|
||||
final List<Module> modules = new ArrayList<>();
|
||||
AbstractModule secureStore = createSecureStoreModule();
|
||||
if (sitePath != null) {
|
||||
Module sitePathModule = new AbstractModule() {
|
||||
@Override
|
||||
@ -219,13 +222,13 @@ public class WebAppInitializer extends GuiceServletContextListener
|
||||
Module configModule = new GerritServerConfigModule();
|
||||
modules.add(configModule);
|
||||
|
||||
Injector cfgInjector = Guice.createInjector(sitePathModule, configModule);
|
||||
Injector cfgInjector = Guice.createInjector(sitePathModule, configModule, secureStore);
|
||||
Config cfg = cfgInjector.getInstance(Key.get(Config.class,
|
||||
GerritServerConfig.class));
|
||||
String dbType = cfg.getString("database", null, "type");
|
||||
|
||||
final DataSourceType dst = Guice.createInjector(new DataSourceModule(),
|
||||
configModule, sitePathModule).getInstance(
|
||||
configModule, sitePathModule, secureStore).getInstance(
|
||||
Key.get(DataSourceType.class, Names.named(dbType.toLowerCase())));
|
||||
modules.add(new LifecycleModule() {
|
||||
@Override
|
||||
@ -240,6 +243,7 @@ public class WebAppInitializer extends GuiceServletContextListener
|
||||
});
|
||||
|
||||
} else {
|
||||
modules.add(secureStore);
|
||||
modules.add(new LifecycleModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
@ -376,4 +380,16 @@ public class WebAppInitializer extends GuiceServletContextListener
|
||||
manager = null;
|
||||
}
|
||||
}
|
||||
|
||||
private AbstractModule createSecureStoreModule() {
|
||||
return new AbstractModule() {
|
||||
@Override
|
||||
public void configure() {
|
||||
String secureStoreClassName =
|
||||
GerritServerConfigModule.getSecureStoreClassName(sitePath);
|
||||
bind(String.class).annotatedWith(SecureStoreClassName.class).toProvider(
|
||||
Providers.of(secureStoreClassName));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -2,19 +2,19 @@ include_defs('//lib/maven.defs')
|
||||
|
||||
# This version must match the version that also appears in
|
||||
# gerrit-pgm/src/main/resources/com/google/gerrit/pgm/libraries.config
|
||||
VERSION = '1.49'
|
||||
VERSION = '1.51'
|
||||
|
||||
maven_jar(
|
||||
name = 'bcprov',
|
||||
id = 'org.bouncycastle:bcprov-jdk15on:' + VERSION,
|
||||
sha1 = 'f5155f04330459104b79923274db5060c1057b99',
|
||||
sha1 = '9ab8afcc2842d5ef06eb775a0a2b12783b99aa80',
|
||||
license = 'DO_NOT_DISTRIBUTE', #'bouncycastle'
|
||||
)
|
||||
|
||||
maven_jar(
|
||||
name = 'bcpg',
|
||||
id = 'org.bouncycastle:bcpg-jdk15on:' + VERSION,
|
||||
sha1 = '081d84be5b125e1997ab0e2244d1a2276b5de76c',
|
||||
sha1 = 'b5fa4c280dfbf8bf7c260bc1e78044c7a1de5133',
|
||||
license = 'DO_NOT_DISTRIBUTE', #'bouncycastle'
|
||||
deps = [':bcprov'],
|
||||
)
|
||||
@ -22,7 +22,7 @@ maven_jar(
|
||||
maven_jar(
|
||||
name = 'bcpkix',
|
||||
id = 'org.bouncycastle:bcpkix-jdk15on:' + VERSION,
|
||||
sha1 = '924cc7ad2f589630c97b918f044296ebf1bb6855',
|
||||
sha1 = '6c8c1f61bf27a09f9b1a8abc201523669bba9597',
|
||||
license = 'DO_NOT_DISTRIBUTE', #'bouncycastle'
|
||||
deps = [':bcprov'],
|
||||
)
|
||||
|
@ -8,18 +8,17 @@ EXCLUDE = [
|
||||
|
||||
maven_jar(
|
||||
name = 'sshd',
|
||||
id = 'org.apache.sshd:sshd-core:0.9.0-4-g5967cfd',
|
||||
sha1 = '449ec11c4417b295dbf1661585a50c6ec7d9a452',
|
||||
id = 'org.apache.sshd:sshd-core:0.14.0',
|
||||
sha1 = 'cb12fa1b1b07fb5ce3aa4f99b189743897bd4fca',
|
||||
license = 'Apache2.0',
|
||||
deps = [':core'],
|
||||
exclude = EXCLUDE,
|
||||
repository = GERRIT,
|
||||
)
|
||||
|
||||
maven_jar(
|
||||
name = 'core',
|
||||
id = 'org.apache.mina:mina-core:2.0.7',
|
||||
sha1 = 'c878e2aa82de748474a624ec3933e4604e446dec',
|
||||
id = 'org.apache.mina:mina-core:2.0.8',
|
||||
sha1 = 'd6ff69fa049aeaecdf0c04cafbb1ab53b7487883',
|
||||
license = 'Apache2.0',
|
||||
exclude = EXCLUDE,
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user