{"action":"created","comment":{"url":"https://api.github.com/repos/nuxeo/nuxeo/pulls/comments/203677067","pull_request_review_id":138611810,"id":203677067,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIwMzY3NzA2Nw==","diff_hunk":"@@ -0,0 +1,323 @@\n+/*\n+ * (C) Copyright 2018 Nuxeo (http://nuxeo.com/) and others.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ *\n+ * Contributors:\n+ *     Florent Guillaume\n+ */\n+package org.nuxeo.ecm.jwt;\n+\n+import static org.apache.commons.lang3.StringUtils.isBlank;\n+\n+import java.io.IOException;\n+import java.io.UnsupportedEncodingException;\n+import java.lang.reflect.Field;\n+import java.time.Instant;\n+import java.util.ArrayList;\n+import java.util.Date;\n+import java.util.HashMap;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.Map.Entry;\n+import java.util.Objects;\n+\n+import org.apache.commons.lang3.StringUtils;\n+import org.apache.commons.logging.Log;\n+import org.apache.commons.logging.LogFactory;\n+import org.nuxeo.ecm.core.api.NuxeoException;\n+import org.nuxeo.ecm.core.api.local.ClientLoginModule;\n+import org.nuxeo.runtime.model.ComponentInstance;\n+import org.nuxeo.runtime.model.DefaultComponent;\n+import org.nuxeo.runtime.model.SimpleContributionRegistry;\n+\n+import com.auth0.jwt.JWT;\n+import com.auth0.jwt.JWTCreator.Builder;\n+import com.auth0.jwt.JWTVerifier;\n+import com.auth0.jwt.algorithms.Algorithm;\n+import com.auth0.jwt.exceptions.JWTCreationException;\n+import com.auth0.jwt.exceptions.JWTVerificationException;\n+import com.auth0.jwt.interfaces.DecodedJWT;\n+import com.fasterxml.jackson.core.JsonParser;\n+import com.fasterxml.jackson.core.type.TypeReference;\n+import com.fasterxml.jackson.databind.JsonNode;\n+import com.fasterxml.jackson.databind.ObjectMapper;\n+\n+/**\n+ * The JSON Web Token Service implementation.\n+ *\n+ * @since 10.3\n+ */\n+public class JWTServiceImpl extends DefaultComponent implements JWTService {\n+\n+    private static final Log log = LogFactory.getLog(JWTServiceImpl.class);\n+\n+    public static final String XP_CONFIGURATION = \"configuration\";\n+\n+    public static final String NUXEO_ISSUER = \"nuxeo\";\n+\n+    protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();\n+\n+    protected static final TypeReference<Map<String, Object>> MAP_STRING_OBJECT = new TypeReference<Map<String, Object>>() {\n+    };\n+\n+    protected static class JWTServiceConfigurationRegistry\n+            extends SimpleContributionRegistry<JWTServiceConfigurationDescriptor> {\n+\n+        protected static final String KEY = \"\"; // value doesn't matter as long as we use a fixed one\n+\n+        protected static final JWTServiceConfigurationDescriptor DEFAULT_CONTRIBUTION = new JWTServiceConfigurationDescriptor();\n+\n+        @Override\n+        public String getContributionId(JWTServiceConfigurationDescriptor contrib) {\n+            return KEY;\n+        }\n+\n+        @Override\n+        public boolean isSupportingMerge() {\n+            return true;\n+        }\n+\n+        @Override\n+        public JWTServiceConfigurationDescriptor clone(JWTServiceConfigurationDescriptor orig) {\n+            return new JWTServiceConfigurationDescriptor(orig);\n+        }\n+\n+        @Override\n+        public void merge(JWTServiceConfigurationDescriptor src, JWTServiceConfigurationDescriptor dst) {\n+            dst.merge(src);\n+        }\n+\n+        public JWTServiceConfigurationDescriptor getContribution() {\n+            JWTServiceConfigurationDescriptor contribution = getContribution(KEY);\n+            if (contribution == null) {\n+                contribution = DEFAULT_CONTRIBUTION;\n+            }\n+            return contribution;\n+        }\n+    }\n+\n+    protected final JWTServiceConfigurationRegistry registry = new JWTServiceConfigurationRegistry();\n+\n+    @Override\n+    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {\n+        if (XP_CONFIGURATION.equals(extensionPoint)) {\n+            registry.addContribution((JWTServiceConfigurationDescriptor) contribution);\n+        } else {\n+            throw new NuxeoException(\"Unknown extension point: \" + extensionPoint);\n+        }\n+    }\n+\n+    @Override\n+    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {\n+        if (XP_CONFIGURATION.equals(extensionPoint)) {\n+            registry.removeContribution((JWTServiceConfigurationDescriptor) contribution);\n+        }\n+    }\n+\n+    // -------------------- JWTService API --------------------\n+\n+    @Override\n+    public JWTBuilder newBuilder() {\n+        return new JWTBuilderImpl();\n+    }\n+\n+    /**\n+     * Implementation of {@link JWTBuilder} delegating to the auth0 JWT library.\n+     *\n+     * @since 10.3\n+     */\n+    public class JWTBuilderImpl implements JWTBuilder {\n+\n+        public final Builder builder;\n+\n+        public JWTBuilderImpl() {\n+            builder = JWT.create();\n+            // default Nuxeo issuer, checked during validation\n+            builder.withIssuer(NUXEO_ISSUER);\n+            // default to current principal as subject\n+            String subject = ClientLoginModule.getCurrentPrincipal().getActingUser();\n+            if (subject == null) {\n+                throw new NuxeoException(\"No currently logged-in user\");\n+            }\n+            builder.withSubject(subject);\n+            // default max TTL\n+            withTTL(0);\n+        }\n+\n+        @Override\n+        public JWTBuilderImpl withTTL(int ttlSeconds) {\n+            int maxTTL = getMaxTTL();\n+            int ttl = ttlSeconds <= 0 ? maxTTL : Math.min(ttlSeconds, maxTTL);\n+            builder.withExpiresAt(Date.from(Instant.now().plusSeconds(ttl)));\n+            return this;\n+        }\n+\n+        @Override\n+        public JWTBuilderImpl withClaim(String name, Object value) {\n+            if (value instanceof Boolean) {\n+                builder.withClaim(name, (Boolean) value);\n+            } else if (value instanceof Date) {\n+                builder.withClaim(name, (Date) value);\n+            } else if (value instanceof Double) {\n+                builder.withClaim(name, (Double) value);\n+            } else if (value instanceof Integer) {\n+                builder.withClaim(name, (Integer) value);\n+            } else if (value instanceof Long) {\n+                builder.withClaim(name, (Long) value);\n+            } else if (value instanceof String) {\n+                builder.withClaim(name, (String) value);\n+            } else if (value instanceof Integer[]) {\n+                builder.withArrayClaim(name, (Integer[]) value);\n+            } else if (value instanceof Long[]) {\n+                builder.withArrayClaim(name, (Long[]) value);\n+            } else if (value instanceof String[]) {\n+                builder.withArrayClaim(name, (String[]) value);\n+            } else {\n+                throw new NuxeoException(\"Unknown claim type: \" + value);\n+            }\n+            return this;\n+        }\n+\n+        @Override\n+        public String build() {\n+            try {\n+                Algorithm algorithm = getAlgorithm();\n+                if (algorithm == null) {\n+                    throw new NuxeoException(\"JWTService secret not configured\");\n+                }\n+                return builder.sign(algorithm);\n+            } catch (JWTCreationException e) {\n+                throw new NuxeoException(e);\n+            }\n+        }\n+    }\n+\n+    protected void builderWithClaim(Builder builder, String name, Object value) {\n+        if (value instanceof Boolean) {\n+            builder.withClaim(name, (Boolean) value);\n+        } else if (value instanceof Date) {\n+            builder.withClaim(name, (Date) value);\n+        } else if (value instanceof Double) {\n+            builder.withClaim(name, (Double) value);\n+        } else if (value instanceof Integer) {\n+            builder.withClaim(name, (Integer) value);\n+        } else if (value instanceof Long) {\n+            builder.withClaim(name, (Long) value);\n+        } else if (value instanceof String) {\n+            builder.withClaim(name, (String) value);\n+        } else if (value instanceof Integer[]) {\n+            builder.withArrayClaim(name, (Integer[]) value);\n+        } else if (value instanceof Long[]) {\n+            builder.withArrayClaim(name, (Long[]) value);\n+        } else if (value instanceof String[]) {\n+            builder.withArrayClaim(name, (String[]) value);\n+        } else {\n+            throw new NuxeoException(\"Unknown claim type: \" + value);\n+        }\n+    }\n+\n+    @Override\n+    public Map<String, Object> verifyToken(String token) {\n+        Objects.requireNonNull(token);\n+        Algorithm algorithm = getAlgorithm();\n+        if (algorithm == null) {\n+            log.debug(\"secret not configured, cannot verify token\");\n+            return null; // no secret\n+        }\n+        JWTVerifier verifier = JWT.require(algorithm) //\n+                                  .withIssuer(NUXEO_ISSUER)\n+                                  .build();\n+        DecodedJWT jwt;\n+        try {\n+            jwt = verifier.verify(token);\n+        } catch (JWTVerificationException e) {\n+            if (log.isTraceEnabled()) {\n+                log.trace(\"token verification failed: \" + e.toString());\n+            }\n+            return null; // invalid\n+        }\n+        Object payload = getFieldValue(jwt, \"payload\"); // com.auth0.jwt.impl.PayloadImpl\n+        Map<String, JsonNode> tree = getFieldValue(payload, \"tree\");\n+        Map<String, Object> claims = new HashMap<>();\n+        for (Entry<String, JsonNode> es : tree.entrySet()) {\n+            claims.put(es.getKey(), nodeToValue(es.getValue()));\n+        }","path":"nuxeo-services/login/nuxeo-platform-login-jwt/src/main/java/org/nuxeo/ecm/jwt/JWTServiceImpl.java","position":null,"original_position":255,"commit_id":"c6425a410557091f71eca694b7f0879c72af001a","original_commit_id":"492a81bae7dbaa31fab1cc1f7cbe100b2e46dd1f","user":{"login":"efge","id":592810,"node_id":"MDQ6VXNlcjU5MjgxMA==","avatar_url":"https://avatars3.githubusercontent.com/u/592810?v=4","gravatar_id":"","url":"https://api.github.com/users/efge","html_url":"https://github.com/efge","followers_url":"https://api.github.com/users/efge/followers","following_url":"https://api.github.com/users/efge/following{/other_user}","gists_url":"https://api.github.com/users/efge/gists{/gist_id}","starred_url":"https://api.github.com/users/efge/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/efge/subscriptions","organizations_url":"https://api.github.com/users/efge/orgs","repos_url":"https://api.github.com/users/efge/repos","events_url":"https://api.github.com/users/efge/events{/privacy}","received_events_url":"https://api.github.com/users/efge/received_events","type":"User","site_admin":false},"body":"👍 ","created_at":"2018-07-19T10:29:14Z","updated_at":"2018-07-19T10:29:14Z","html_url":"https://github.com/nuxeo/nuxeo/pull/2169#discussion_r203677067","pull_request_url":"https://api.github.com/repos/nuxeo/nuxeo/pulls/2169","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/nuxeo/nuxeo/pulls/comments/203677067"},"html":{"href":"https://github.com/nuxeo/nuxeo/pull/2169#discussion_r203677067"},"pull_request":{"href":"https://api.github.com/repos/nuxeo/nuxeo/pulls/2169"}},"in_reply_to_id":203667804},"pull_request":{"url":"https://api.github.com/repos/nuxeo/nuxeo/pulls/2169","id":202424842,"node_id":"MDExOlB1bGxSZXF1ZXN0MjAyNDI0ODQy","html_url":"https://github.com/nuxeo/nuxeo/pull/2169","diff_url":"https://github.com/nuxeo/nuxeo/pull/2169.diff","patch_url":"https://github.com/nuxeo/nuxeo/pull/2169.patch","issue_url":"https://api.github.com/repos/nuxeo/nuxeo/issues/2169","number":2169,"state":"open","locked":false,"title":"feature-NXP-24734-jwt-auth","user":{"login":"nuxeojenkins","id":925456,"node_id":"MDQ6VXNlcjkyNTQ1Ng==","avatar_url":"https://avatars3.githubusercontent.com/u/925456?v=4","gravatar_id":"","url":"https://api.github.com/users/nuxeojenkins","html_url":"https://github.com/nuxeojenkins","followers_url":"https://api.github.com/users/nuxeojenkins/followers","following_url":"https://api.github.com/users/nuxeojenkins/following{/other_user}","gists_url":"https://api.github.com/users/nuxeojenkins/gists{/gist_id}","starred_url":"https://api.github.com/users/nuxeojenkins/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nuxeojenkins/subscriptions","organizations_url":"https://api.github.com/users/nuxeojenkins/orgs","repos_url":"https://api.github.com/users/nuxeojenkins/repos","events_url":"https://api.github.com/users/nuxeojenkins/events{/privacy}","received_events_url":"https://api.github.com/users/nuxeojenkins/received_events","type":"User","site_admin":false},"body":"PR created from https://qa.nuxeo.org/jenkins/job/TestAndPush/job/ondemand-testandpush-fguillaume/426/","created_at":"2018-07-19T01:53:35Z","updated_at":"2018-07-19T10:29:14Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c556ef70841dc61149f38196b58a01d276f0177a","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/nuxeo/nuxeo/pulls/2169/commits","review_comments_url":"https://api.github.com/repos/nuxeo/nuxeo/pulls/2169/comments","review_comment_url":"https://api.github.com/repos/nuxeo/nuxeo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/nuxeo/nuxeo/issues/2169/comments","statuses_url":"https://api.github.com/repos/nuxeo/nuxeo/statuses/c6425a410557091f71eca694b7f0879c72af001a","head":{"label":"nuxeo:feature-NXP-24734-jwt-auth","ref":"feature-NXP-24734-jwt-auth","sha":"c6425a410557091f71eca694b7f0879c72af001a","user":{"login":"nuxeo","id":460607,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ2MDYwNw==","avatar_url":"https://avatars1.githubusercontent.com/u/460607?v=4","gravatar_id":"","url":"https://api.github.com/users/nuxeo","html_url":"https://github.com/nuxeo","followers_url":"https://api.github.com/users/nuxeo/followers","following_url":"https://api.github.com/users/nuxeo/following{/other_user}","gists_url":"https://api.github.com/users/nuxeo/gists{/gist_id}","starred_url":"https://api.github.com/users/nuxeo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nuxeo/subscriptions","organizations_url":"https://api.github.com/users/nuxeo/orgs","repos_url":"https://api.github.com/users/nuxeo/repos","events_url":"https://api.github.com/users/nuxeo/events{/privacy}","received_events_url":"https://api.github.com/users/nuxeo/received_events","type":"Organization","site_admin":false},"repo":{"id":2988101,"node_id":"MDEwOlJlcG9zaXRvcnkyOTg4MTAx","name":"nuxeo","full_name":"nuxeo/nuxeo","owner":{"login":"nuxeo","id":460607,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ2MDYwNw==","avatar_url":"https://avatars1.githubusercontent.com/u/460607?v=4","gravatar_id":"","url":"https://api.github.com/users/nuxeo","html_url":"https://github.com/nuxeo","followers_url":"https://api.github.com/users/nuxeo/followers","following_url":"https://api.github.com/users/nuxeo/following{/other_user}","gists_url":"https://api.github.com/users/nuxeo/gists{/gist_id}","starred_url":"https://api.github.com/users/nuxeo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nuxeo/subscriptions","organizations_url":"https://api.github.com/users/nuxeo/orgs","repos_url":"https://api.github.com/users/nuxeo/repos","events_url":"https://api.github.com/users/nuxeo/events{/privacy}","received_events_url":"https://api.github.com/users/nuxeo/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nuxeo/nuxeo","description":"Content management platform to build modern business applications","fork":false,"url":"https://api.github.com/repos/nuxeo/nuxeo","forks_url":"https://api.github.com/repos/nuxeo/nuxeo/forks","keys_url":"https://api.github.com/repos/nuxeo/nuxeo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nuxeo/nuxeo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nuxeo/nuxeo/teams","hooks_url":"https://api.github.com/repos/nuxeo/nuxeo/hooks","issue_events_url":"https://api.github.com/repos/nuxeo/nuxeo/issues/events{/number}","events_url":"https://api.github.com/repos/nuxeo/nuxeo/events","assignees_url":"https://api.github.com/repos/nuxeo/nuxeo/assignees{/user}","branches_url":"https://api.github.com/repos/nuxeo/nuxeo/branches{/branch}","tags_url":"https://api.github.com/repos/nuxeo/nuxeo/tags","blobs_url":"https://api.github.com/repos/nuxeo/nuxeo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nuxeo/nuxeo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nuxeo/nuxeo/git/refs{/sha}","trees_url":"https://api.github.com/repos/nuxeo/nuxeo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nuxeo/nuxeo/statuses/{sha}","languages_url":"https://api.github.com/repos/nuxeo/nuxeo/languages","stargazers_url":"https://api.github.com/repos/nuxeo/nuxeo/stargazers","contributors_url":"https://api.github.com/repos/nuxeo/nuxeo/contributors","subscribers_url":"https://api.github.com/repos/nuxeo/nuxeo/subscribers","subscription_url":"https://api.github.com/repos/nuxeo/nuxeo/subscription","commits_url":"https://api.github.com/repos/nuxeo/nuxeo/commits{/sha}","git_commits_url":"https://api.github.com/repos/nuxeo/nuxeo/git/commits{/sha}","comments_url":"https://api.github.com/repos/nuxeo/nuxeo/comments{/number}","issue_comment_url":"https://api.github.com/repos/nuxeo/nuxeo/issues/comments{/number}","contents_url":"https://api.github.com/repos/nuxeo/nuxeo/contents/{+path}","compare_url":"https://api.github.com/repos/nuxeo/nuxeo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nuxeo/nuxeo/merges","archive_url":"https://api.github.com/repos/nuxeo/nuxeo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nuxeo/nuxeo/downloads","issues_url":"https://api.github.com/repos/nuxeo/nuxeo/issues{/number}","pulls_url":"https://api.github.com/repos/nuxeo/nuxeo/pulls{/number}","milestones_url":"https://api.github.com/repos/nuxeo/nuxeo/milestones{/number}","notifications_url":"https://api.github.com/repos/nuxeo/nuxeo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nuxeo/nuxeo/labels{/name}","releases_url":"https://api.github.com/repos/nuxeo/nuxeo/releases{/id}","deployments_url":"https://api.github.com/repos/nuxeo/nuxeo/deployments","created_at":"2011-12-15T14:35:41Z","updated_at":"2018-07-17T11:05:06Z","pushed_at":"2018-07-19T10:29:10Z","git_url":"git://github.com/nuxeo/nuxeo.git","ssh_url":"git@github.com:nuxeo/nuxeo.git","clone_url":"https://github.com/nuxeo/nuxeo.git","svn_url":"https://github.com/nuxeo/nuxeo","homepage":"http://www.nuxeo.com","size":231076,"stargazers_count":229,"watchers_count":229,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":158,"mirror_url":null,"archived":false,"open_issues_count":16,"license":null,"forks":158,"open_issues":16,"watchers":229,"default_branch":"master"}},"base":{"label":"nuxeo:master","ref":"master","sha":"40ab260a3074a047b3377e7bbe415d3f0405b5f2","user":{"login":"nuxeo","id":460607,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ2MDYwNw==","avatar_url":"https://avatars1.githubusercontent.com/u/460607?v=4","gravatar_id":"","url":"https://api.github.com/users/nuxeo","html_url":"https://github.com/nuxeo","followers_url":"https://api.github.com/users/nuxeo/followers","following_url":"https://api.github.com/users/nuxeo/following{/other_user}","gists_url":"https://api.github.com/users/nuxeo/gists{/gist_id}","starred_url":"https://api.github.com/users/nuxeo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nuxeo/subscriptions","organizations_url":"https://api.github.com/users/nuxeo/orgs","repos_url":"https://api.github.com/users/nuxeo/repos","events_url":"https://api.github.com/users/nuxeo/events{/privacy}","received_events_url":"https://api.github.com/users/nuxeo/received_events","type":"Organization","site_admin":false},"repo":{"id":2988101,"node_id":"MDEwOlJlcG9zaXRvcnkyOTg4MTAx","name":"nuxeo","full_name":"nuxeo/nuxeo","owner":{"login":"nuxeo","id":460607,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ2MDYwNw==","avatar_url":"https://avatars1.githubusercontent.com/u/460607?v=4","gravatar_id":"","url":"https://api.github.com/users/nuxeo","html_url":"https://github.com/nuxeo","followers_url":"https://api.github.com/users/nuxeo/followers","following_url":"https://api.github.com/users/nuxeo/following{/other_user}","gists_url":"https://api.github.com/users/nuxeo/gists{/gist_id}","starred_url":"https://api.github.com/users/nuxeo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nuxeo/subscriptions","organizations_url":"https://api.github.com/users/nuxeo/orgs","repos_url":"https://api.github.com/users/nuxeo/repos","events_url":"https://api.github.com/users/nuxeo/events{/privacy}","received_events_url":"https://api.github.com/users/nuxeo/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nuxeo/nuxeo","description":"Content management platform to build modern business applications","fork":false,"url":"https://api.github.com/repos/nuxeo/nuxeo","forks_url":"https://api.github.com/repos/nuxeo/nuxeo/forks","keys_url":"https://api.github.com/repos/nuxeo/nuxeo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nuxeo/nuxeo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nuxeo/nuxeo/teams","hooks_url":"https://api.github.com/repos/nuxeo/nuxeo/hooks","issue_events_url":"https://api.github.com/repos/nuxeo/nuxeo/issues/events{/number}","events_url":"https://api.github.com/repos/nuxeo/nuxeo/events","assignees_url":"https://api.github.com/repos/nuxeo/nuxeo/assignees{/user}","branches_url":"https://api.github.com/repos/nuxeo/nuxeo/branches{/branch}","tags_url":"https://api.github.com/repos/nuxeo/nuxeo/tags","blobs_url":"https://api.github.com/repos/nuxeo/nuxeo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nuxeo/nuxeo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nuxeo/nuxeo/git/refs{/sha}","trees_url":"https://api.github.com/repos/nuxeo/nuxeo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nuxeo/nuxeo/statuses/{sha}","languages_url":"https://api.github.com/repos/nuxeo/nuxeo/languages","stargazers_url":"https://api.github.com/repos/nuxeo/nuxeo/stargazers","contributors_url":"https://api.github.com/repos/nuxeo/nuxeo/contributors","subscribers_url":"https://api.github.com/repos/nuxeo/nuxeo/subscribers","subscription_url":"https://api.github.com/repos/nuxeo/nuxeo/subscription","commits_url":"https://api.github.com/repos/nuxeo/nuxeo/commits{/sha}","git_commits_url":"https://api.github.com/repos/nuxeo/nuxeo/git/commits{/sha}","comments_url":"https://api.github.com/repos/nuxeo/nuxeo/comments{/number}","issue_comment_url":"https://api.github.com/repos/nuxeo/nuxeo/issues/comments{/number}","contents_url":"https://api.github.com/repos/nuxeo/nuxeo/contents/{+path}","compare_url":"https://api.github.com/repos/nuxeo/nuxeo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nuxeo/nuxeo/merges","archive_url":"https://api.github.com/repos/nuxeo/nuxeo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nuxeo/nuxeo/downloads","issues_url":"https://api.github.com/repos/nuxeo/nuxeo/issues{/number}","pulls_url":"https://api.github.com/repos/nuxeo/nuxeo/pulls{/number}","milestones_url":"https://api.github.com/repos/nuxeo/nuxeo/milestones{/number}","notifications_url":"https://api.github.com/repos/nuxeo/nuxeo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nuxeo/nuxeo/labels{/name}","releases_url":"https://api.github.com/repos/nuxeo/nuxeo/releases{/id}","deployments_url":"https://api.github.com/repos/nuxeo/nuxeo/deployments","created_at":"2011-12-15T14:35:41Z","updated_at":"2018-07-17T11:05:06Z","pushed_at":"2018-07-19T10:29:10Z","git_url":"git://github.com/nuxeo/nuxeo.git","ssh_url":"git@github.com:nuxeo/nuxeo.git","clone_url":"https://github.com/nuxeo/nuxeo.git","svn_url":"https://github.com/nuxeo/nuxeo","homepage":"http://www.nuxeo.com","size":231076,"stargazers_count":229,"watchers_count":229,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":158,"mirror_url":null,"archived":false,"open_issues_count":16,"license":null,"forks":158,"open_issues":16,"watchers":229,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/nuxeo/nuxeo/pulls/2169"},"html":{"href":"https://github.com/nuxeo/nuxeo/pull/2169"},"issue":{"href":"https://api.github.com/repos/nuxeo/nuxeo/issues/2169"},"comments":{"href":"https://api.github.com/repos/nuxeo/nuxeo/issues/2169/comments"},"review_comments":{"href":"https://api.github.com/repos/nuxeo/nuxeo/pulls/2169/comments"},"review_comment":{"href":"https://api.github.com/repos/nuxeo/nuxeo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/nuxeo/nuxeo/pulls/2169/commits"},"statuses":{"href":"https://api.github.com/repos/nuxeo/nuxeo/statuses/c6425a410557091f71eca694b7f0879c72af001a"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"efge"}}