{"action":"created","comment":{"url":"https://api.github.com/repos/healthbam/healthbam/pulls/comments/85629424","pull_request_review_id":6337852,"id":85629424,"diff_hunk":"@@ -0,0 +1,178 @@\n+package org.hmhb.authentication;\n+\n+import javax.annotation.Nonnull;\n+import javax.crypto.spec.SecretKeySpec;\n+import javax.servlet.http.HttpServletRequest;\n+import javax.xml.bind.DatatypeConverter;\n+\n+import java.util.Date;\n+import java.util.HashMap;\n+import java.util.Map;\n+\n+import io.jsonwebtoken.Claims;\n+import io.jsonwebtoken.Jwts;\n+import io.jsonwebtoken.SignatureAlgorithm;\n+import org.hmhb.exception.authentication.AuthHeaderHasTooManyPartsException;\n+import org.hmhb.exception.authentication.AuthHeaderMissingTokenException;\n+import org.hmhb.exception.authentication.AuthHeaderUnknownException;\n+import org.hmhb.exception.authentication.JwtTokenExpiredException;\n+import org.hmhb.exception.authentication.JwtTokenNotActiveYetException;\n+import org.hmhb.exception.authentication.JwtTokenNotSignedException;\n+import org.hmhb.user.HmhbUser;\n+import org.slf4j.Logger;\n+import org.slf4j.LoggerFactory;\n+import org.springframework.beans.factory.annotation.Autowired;\n+import org.springframework.core.env.Environment;\n+import org.springframework.stereotype.Service;\n+\n+import static java.util.Objects.requireNonNull;\n+\n+@Service\n+public class DefaultJwtAuthenticationService implements JwtAuthenticationService {\n+\n+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAuthenticationService.class);\n+\n+    private final Environment environment;\n+\n+    @Autowired\n+    public DefaultJwtAuthenticationService(\n+            @Nonnull Environment environment\n+    ) {\n+        this.environment = requireNonNull(environment, \"environment cannot be null\");\n+    }\n+\n+    private String getDomain() {\n+        return environment.getProperty(\"hmhb.jwt.domain\");\n+    }\n+\n+    private String getSecret() {\n+        return environment.getProperty(\"hmhb.jwt.secret\");\n+    }\n+\n+    private Date getTokenExpirationDate() {\n+        /* one week */\n+        return new Date(System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000);\n+    }\n+\n+    @Override\n+    public String generateJwtToken(\n+            @Nonnull HmhbUser user\n+    ) {\n+        Map<String, Object> claims = new HashMap<>();\n+        claims.put(\"admin\", user.isAdmin());\n+        claims.put(\"email\", user.getEmail());\n+        claims.put(\"userId\", user.getId());\n+\n+        String domainId = getDomain();\n+        String secret = getSecret();\n+\n+        String token = Jwts.builder()\n+                /* you must set claims before the others or else setClaims will clear your other properties */\n+                .setClaims(claims)\n+                .setIssuedAt(new Date())\n+                .setExpiration(getTokenExpirationDate())\n+                .setSubject(\"\" + user.getId())\n+                .setAudience(domainId)\n+                .setIssuer(domainId)\n+                .signWith(SignatureAlgorithm.HS256, secret)\n+                .compact();\n+\n+        LOGGER.debug(\"token: {}\", token);\n+\n+        return token;\n+    }\n+\n+    @Override\n+    public void validateAuthentication(\n+            @Nonnull HttpServletRequest request\n+    ) {\n+        requireNonNull(request, \"request cannot be null\");\n+\n+        String authHeader = request.getHeader(\"Authorization\");\n+\n+        LOGGER.debug(\"processing authentication: header={}\", authHeader);\n+\n+        if (authHeader != null) {\n+\n+            String[] authHeaderInfo = authHeader.split(\"[ ]\");\n+\n+            if (authHeaderInfo.length < 2) {\n+                throw new AuthHeaderMissingTokenException(authHeader);\n+            }\n+\n+            if (authHeaderInfo.length > 2) {\n+                throw new AuthHeaderHasTooManyPartsException(authHeader);\n+            }\n+\n+            if (!\"Bearer\".equals(authHeaderInfo[0])) {\n+                throw new AuthHeaderUnknownException(authHeader);\n+            }\n+\n+            String authToken = authHeaderInfo[1];\n+\n+            if (!Jwts.parser().isSigned(authToken)) {\n+                throw new JwtTokenNotSignedException();\n+            }\n+\n+            String domainId = getDomain();\n+            String secret = getSecret();\n+\n+            byte[] keyBytes = DatatypeConverter.parseBase64Binary(secret);\n+            /* I'm using setSigningKey with the SecretKeySpec so I can enforce which algorithm to use. */\n+            SecretKeySpec secretKey = new SecretKeySpec(keyBytes, SignatureAlgorithm.HS256.getJcaName());\n+\n+            LOGGER.debug(\"authHeader is: {}\", authHeader);\n+            LOGGER.debug(\"authToken is: {}\", authToken);\n+\n+            Claims claims = Jwts.parser()\n+                    .requireAudience(domainId)\n+                    .requireIssuer(domainId)\n+                    .setSigningKey(secretKey)\n+                    .parseClaimsJws(authToken)\n+                    .getBody();\n+\n+            String username = claims.getSubject();\n+            Date issuedAt = claims.getIssuedAt();\n+            Date expirationDate = claims.getExpiration();\n+            String issuer = claims.getIssuer();\n+            String audience = claims.getAudience();\n+            boolean admin = claims.get(\"admin\", Boolean.class);\n+            String email = claims.get(\"email\", String.class);\n+            Long userId = claims.get(\"userId\", Integer.class).longValue();\n+\n+            Date now = new Date();\n+\n+            if (issuedAt.after(now)) {\n+                throw new JwtTokenNotActiveYetException(issuedAt);\n+            }\n+\n+            if (expirationDate.before(now)) {\n+                throw new JwtTokenExpiredException(expirationDate);\n+            }\n+\n+            LOGGER.debug(\n+                    \"token accepted, standard info: issuer={}, audience={}, username={}, issuedAt={}, expiresOn={}\",\n+                    issuer,\n+                    audience,\n+                    username,\n+                    issuedAt,\n+                    expirationDate\n+            );\n+\n+            LOGGER.debug(\n+                    \"token accepted, custom info: admin={}, email={}, userId={}\",\n+                    admin,\n+                    email,\n+                    userId\n+            );\n+\n+            HmhbUser user = new HmhbUser();","path":"src/main/java/org/hmhb/authentication/DefaultJwtAuthenticationService.java","position":169,"original_position":169,"commit_id":"21bffdf2af0eb6529b78cc9e0d464b4d0289b906","original_commit_id":"21bffdf2af0eb6529b78cc9e0d464b4d0289b906","user":{"login":"RyanBard","id":2766717,"avatar_url":"https://avatars.githubusercontent.com/u/2766717?v=3","gravatar_id":"","url":"https://api.github.com/users/RyanBard","html_url":"https://github.com/RyanBard","followers_url":"https://api.github.com/users/RyanBard/followers","following_url":"https://api.github.com/users/RyanBard/following{/other_user}","gists_url":"https://api.github.com/users/RyanBard/gists{/gist_id}","starred_url":"https://api.github.com/users/RyanBard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RyanBard/subscriptions","organizations_url":"https://api.github.com/users/RyanBard/orgs","repos_url":"https://api.github.com/users/RyanBard/repos","events_url":"https://api.github.com/users/RyanBard/events{/privacy}","received_events_url":"https://api.github.com/users/RyanBard/received_events","type":"User","site_admin":false},"body":"Much better than looking up the user for every request or having to rely on http sessions.  This is stateless and works well with non-web clients.","created_at":"2016-10-29T00:39:54Z","updated_at":"2016-10-29T00:39:54Z","html_url":"https://github.com/healthbam/healthbam/pull/228#discussion_r85629424","pull_request_url":"https://api.github.com/repos/healthbam/healthbam/pulls/228","_links":{"self":{"href":"https://api.github.com/repos/healthbam/healthbam/pulls/comments/85629424"},"html":{"href":"https://github.com/healthbam/healthbam/pull/228#discussion_r85629424"},"pull_request":{"href":"https://api.github.com/repos/healthbam/healthbam/pulls/228"}}},"pull_request":{"url":"https://api.github.com/repos/healthbam/healthbam/pulls/228","id":91465722,"html_url":"https://github.com/healthbam/healthbam/pull/228","diff_url":"https://github.com/healthbam/healthbam/pull/228.diff","patch_url":"https://github.com/healthbam/healthbam/pull/228.patch","issue_url":"https://api.github.com/repos/healthbam/healthbam/issues/228","number":228,"state":"open","locked":false,"title":"closes #49: Implement authN & authZ","user":{"login":"RyanBard","id":2766717,"avatar_url":"https://avatars.githubusercontent.com/u/2766717?v=3","gravatar_id":"","url":"https://api.github.com/users/RyanBard","html_url":"https://github.com/RyanBard","followers_url":"https://api.github.com/users/RyanBard/followers","following_url":"https://api.github.com/users/RyanBard/following{/other_user}","gists_url":"https://api.github.com/users/RyanBard/gists{/gist_id}","starred_url":"https://api.github.com/users/RyanBard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RyanBard/subscriptions","organizations_url":"https://api.github.com/users/RyanBard/orgs","repos_url":"https://api.github.com/users/RyanBard/repos","events_url":"https://api.github.com/users/RyanBard/events{/privacy}","received_events_url":"https://api.github.com/users/RyanBard/received_events","type":"User","site_admin":false},"body":"* Adds auth modules, configs, components, etc. to support google oauth.\r\n* Adds user table and code to support partners and admins.\r\n* Adds admin requirement to all CRUD operations on programs.\r\n* Adds admin requirement to delete and updating of organizations.\r\n* Updates README with info about new required config.\r\n* Updates the unit tests.","created_at":"2016-10-28T23:01:40Z","updated_at":"2016-10-29T00:39:54Z","closed_at":null,"merged_at":null,"merge_commit_sha":"87fa6f4e94554014e8b0115e2b9722230ce2b039","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/healthbam/healthbam/pulls/228/commits","review_comments_url":"https://api.github.com/repos/healthbam/healthbam/pulls/228/comments","review_comment_url":"https://api.github.com/repos/healthbam/healthbam/pulls/comments{/number}","comments_url":"https://api.github.com/repos/healthbam/healthbam/issues/228/comments","statuses_url":"https://api.github.com/repos/healthbam/healthbam/statuses/21bffdf2af0eb6529b78cc9e0d464b4d0289b906","head":{"label":"RyanBard:ImplementOauth","ref":"ImplementOauth","sha":"21bffdf2af0eb6529b78cc9e0d464b4d0289b906","user":{"login":"RyanBard","id":2766717,"avatar_url":"https://avatars.githubusercontent.com/u/2766717?v=3","gravatar_id":"","url":"https://api.github.com/users/RyanBard","html_url":"https://github.com/RyanBard","followers_url":"https://api.github.com/users/RyanBard/followers","following_url":"https://api.github.com/users/RyanBard/following{/other_user}","gists_url":"https://api.github.com/users/RyanBard/gists{/gist_id}","starred_url":"https://api.github.com/users/RyanBard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RyanBard/subscriptions","organizations_url":"https://api.github.com/users/RyanBard/orgs","repos_url":"https://api.github.com/users/RyanBard/repos","events_url":"https://api.github.com/users/RyanBard/events{/privacy}","received_events_url":"https://api.github.com/users/RyanBard/received_events","type":"User","site_admin":false},"repo":{"id":67940746,"name":"healthbam","full_name":"RyanBard/healthbam","owner":{"login":"RyanBard","id":2766717,"avatar_url":"https://avatars.githubusercontent.com/u/2766717?v=3","gravatar_id":"","url":"https://api.github.com/users/RyanBard","html_url":"https://github.com/RyanBard","followers_url":"https://api.github.com/users/RyanBard/followers","following_url":"https://api.github.com/users/RyanBard/following{/other_user}","gists_url":"https://api.github.com/users/RyanBard/gists{/gist_id}","starred_url":"https://api.github.com/users/RyanBard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RyanBard/subscriptions","organizations_url":"https://api.github.com/users/RyanBard/orgs","repos_url":"https://api.github.com/users/RyanBard/repos","events_url":"https://api.github.com/users/RyanBard/events{/privacy}","received_events_url":"https://api.github.com/users/RyanBard/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/RyanBard/healthbam","description":"Healthy Mothers, Healthy Babies Coalition of Georgia Programs Map","fork":true,"url":"https://api.github.com/repos/RyanBard/healthbam","forks_url":"https://api.github.com/repos/RyanBard/healthbam/forks","keys_url":"https://api.github.com/repos/RyanBard/healthbam/keys{/key_id}","collaborators_url":"https://api.github.com/repos/RyanBard/healthbam/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/RyanBard/healthbam/teams","hooks_url":"https://api.github.com/repos/RyanBard/healthbam/hooks","issue_events_url":"https://api.github.com/repos/RyanBard/healthbam/issues/events{/number}","events_url":"https://api.github.com/repos/RyanBard/healthbam/events","assignees_url":"https://api.github.com/repos/RyanBard/healthbam/assignees{/user}","branches_url":"https://api.github.com/repos/RyanBard/healthbam/branches{/branch}","tags_url":"https://api.github.com/repos/RyanBard/healthbam/tags","blobs_url":"https://api.github.com/repos/RyanBard/healthbam/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/RyanBard/healthbam/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/RyanBard/healthbam/git/refs{/sha}","trees_url":"https://api.github.com/repos/RyanBard/healthbam/git/trees{/sha}","statuses_url":"https://api.github.com/repos/RyanBard/healthbam/statuses/{sha}","languages_url":"https://api.github.com/repos/RyanBard/healthbam/languages","stargazers_url":"https://api.github.com/repos/RyanBard/healthbam/stargazers","contributors_url":"https://api.github.com/repos/RyanBard/healthbam/contributors","subscribers_url":"https://api.github.com/repos/RyanBard/healthbam/subscribers","subscription_url":"https://api.github.com/repos/RyanBard/healthbam/subscription","commits_url":"https://api.github.com/repos/RyanBard/healthbam/commits{/sha}","git_commits_url":"https://api.github.com/repos/RyanBard/healthbam/git/commits{/sha}","comments_url":"https://api.github.com/repos/RyanBard/healthbam/comments{/number}","issue_comment_url":"https://api.github.com/repos/RyanBard/healthbam/issues/comments{/number}","contents_url":"https://api.github.com/repos/RyanBard/healthbam/contents/{+path}","compare_url":"https://api.github.com/repos/RyanBard/healthbam/compare/{base}...{head}","merges_url":"https://api.github.com/repos/RyanBard/healthbam/merges","archive_url":"https://api.github.com/repos/RyanBard/healthbam/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/RyanBard/healthbam/downloads","issues_url":"https://api.github.com/repos/RyanBard/healthbam/issues{/number}","pulls_url":"https://api.github.com/repos/RyanBard/healthbam/pulls{/number}","milestones_url":"https://api.github.com/repos/RyanBard/healthbam/milestones{/number}","notifications_url":"https://api.github.com/repos/RyanBard/healthbam/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/RyanBard/healthbam/labels{/name}","releases_url":"https://api.github.com/repos/RyanBard/healthbam/releases{/id}","deployments_url":"https://api.github.com/repos/RyanBard/healthbam/deployments","created_at":"2016-09-11T16:26:06Z","updated_at":"2016-10-12T01:55:39Z","pushed_at":"2016-10-29T00:31:48Z","git_url":"git://github.com/RyanBard/healthbam.git","ssh_url":"git@github.com:RyanBard/healthbam.git","clone_url":"https://github.com/RyanBard/healthbam.git","svn_url":"https://github.com/RyanBard/healthbam","homepage":null,"size":592,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"healthbam:master","ref":"master","sha":"af641f64d61831111520f96bcef45585d56d7bda","user":{"login":"healthbam","id":21698178,"avatar_url":"https://avatars.githubusercontent.com/u/21698178?v=3","gravatar_id":"","url":"https://api.github.com/users/healthbam","html_url":"https://github.com/healthbam","followers_url":"https://api.github.com/users/healthbam/followers","following_url":"https://api.github.com/users/healthbam/following{/other_user}","gists_url":"https://api.github.com/users/healthbam/gists{/gist_id}","starred_url":"https://api.github.com/users/healthbam/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/healthbam/subscriptions","organizations_url":"https://api.github.com/users/healthbam/orgs","repos_url":"https://api.github.com/users/healthbam/repos","events_url":"https://api.github.com/users/healthbam/events{/privacy}","received_events_url":"https://api.github.com/users/healthbam/received_events","type":"Organization","site_admin":false},"repo":{"id":67935826,"name":"healthbam","full_name":"healthbam/healthbam","owner":{"login":"healthbam","id":21698178,"avatar_url":"https://avatars.githubusercontent.com/u/21698178?v=3","gravatar_id":"","url":"https://api.github.com/users/healthbam","html_url":"https://github.com/healthbam","followers_url":"https://api.github.com/users/healthbam/followers","following_url":"https://api.github.com/users/healthbam/following{/other_user}","gists_url":"https://api.github.com/users/healthbam/gists{/gist_id}","starred_url":"https://api.github.com/users/healthbam/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/healthbam/subscriptions","organizations_url":"https://api.github.com/users/healthbam/orgs","repos_url":"https://api.github.com/users/healthbam/repos","events_url":"https://api.github.com/users/healthbam/events{/privacy}","received_events_url":"https://api.github.com/users/healthbam/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/healthbam/healthbam","description":"Healthy Mothers, Healthy Babies Coalition of Georgia Programs Map","fork":false,"url":"https://api.github.com/repos/healthbam/healthbam","forks_url":"https://api.github.com/repos/healthbam/healthbam/forks","keys_url":"https://api.github.com/repos/healthbam/healthbam/keys{/key_id}","collaborators_url":"https://api.github.com/repos/healthbam/healthbam/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/healthbam/healthbam/teams","hooks_url":"https://api.github.com/repos/healthbam/healthbam/hooks","issue_events_url":"https://api.github.com/repos/healthbam/healthbam/issues/events{/number}","events_url":"https://api.github.com/repos/healthbam/healthbam/events","assignees_url":"https://api.github.com/repos/healthbam/healthbam/assignees{/user}","branches_url":"https://api.github.com/repos/healthbam/healthbam/branches{/branch}","tags_url":"https://api.github.com/repos/healthbam/healthbam/tags","blobs_url":"https://api.github.com/repos/healthbam/healthbam/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/healthbam/healthbam/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/healthbam/healthbam/git/refs{/sha}","trees_url":"https://api.github.com/repos/healthbam/healthbam/git/trees{/sha}","statuses_url":"https://api.github.com/repos/healthbam/healthbam/statuses/{sha}","languages_url":"https://api.github.com/repos/healthbam/healthbam/languages","stargazers_url":"https://api.github.com/repos/healthbam/healthbam/stargazers","contributors_url":"https://api.github.com/repos/healthbam/healthbam/contributors","subscribers_url":"https://api.github.com/repos/healthbam/healthbam/subscribers","subscription_url":"https://api.github.com/repos/healthbam/healthbam/subscription","commits_url":"https://api.github.com/repos/healthbam/healthbam/commits{/sha}","git_commits_url":"https://api.github.com/repos/healthbam/healthbam/git/commits{/sha}","comments_url":"https://api.github.com/repos/healthbam/healthbam/comments{/number}","issue_comment_url":"https://api.github.com/repos/healthbam/healthbam/issues/comments{/number}","contents_url":"https://api.github.com/repos/healthbam/healthbam/contents/{+path}","compare_url":"https://api.github.com/repos/healthbam/healthbam/compare/{base}...{head}","merges_url":"https://api.github.com/repos/healthbam/healthbam/merges","archive_url":"https://api.github.com/repos/healthbam/healthbam/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/healthbam/healthbam/downloads","issues_url":"https://api.github.com/repos/healthbam/healthbam/issues{/number}","pulls_url":"https://api.github.com/repos/healthbam/healthbam/pulls{/number}","milestones_url":"https://api.github.com/repos/healthbam/healthbam/milestones{/number}","notifications_url":"https://api.github.com/repos/healthbam/healthbam/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/healthbam/healthbam/labels{/name}","releases_url":"https://api.github.com/repos/healthbam/healthbam/releases{/id}","deployments_url":"https://api.github.com/repos/healthbam/healthbam/deployments","created_at":"2016-09-11T14:56:30Z","updated_at":"2016-10-16T02:41:20Z","pushed_at":"2016-10-29T00:31:44Z","git_url":"git://github.com/healthbam/healthbam.git","ssh_url":"git@github.com:healthbam/healthbam.git","clone_url":"https://github.com/healthbam/healthbam.git","svn_url":"https://github.com/healthbam/healthbam","homepage":null,"size":516,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":32,"forks":5,"open_issues":32,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/healthbam/healthbam/pulls/228"},"html":{"href":"https://github.com/healthbam/healthbam/pull/228"},"issue":{"href":"https://api.github.com/repos/healthbam/healthbam/issues/228"},"comments":{"href":"https://api.github.com/repos/healthbam/healthbam/issues/228/comments"},"review_comments":{"href":"https://api.github.com/repos/healthbam/healthbam/pulls/228/comments"},"review_comment":{"href":"https://api.github.com/repos/healthbam/healthbam/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/healthbam/healthbam/pulls/228/commits"},"statuses":{"href":"https://api.github.com/repos/healthbam/healthbam/statuses/21bffdf2af0eb6529b78cc9e0d464b4d0289b906"}}}}
{"actor":{"display_login":"RyanBard"}}