Skip to content
This repository has been archived by the owner on Jan 12, 2024. It is now read-only.

Commit

Permalink
Feat: JWT auth and feature flags (#428)
Browse files Browse the repository at this point in the history
Co-authored-by: Shawn Sherwood <[email protected]>
Co-authored-by: Sam Lichlyter <[email protected]>
  • Loading branch information
3 people authored Apr 23, 2021
1 parent 826914c commit efa8bb0
Show file tree
Hide file tree
Showing 42 changed files with 2,035 additions and 82 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.nike.cerberus.error;

public class AuthTokenTooLongException extends Exception {

private AuthTokenTooLongException() {
super();
}

public AuthTokenTooLongException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public enum DefaultApiError implements ApiError {
/** Supplied credentials are invalid. */
AUTH_BAD_CREDENTIALS(99106, "Invalid credentials", SC_UNAUTHORIZED),

/** User belongs to too many groups, so the jwt token would make header too large */
AUTH_TOKEN_TOO_LONG(
99107, "X-Cerberus-Token header would be too long.", SC_INTERNAL_SERVER_ERROR),

/** Category display name is blank. */
CATEGORY_DISPLAY_NAME_BLANK(99200, "Display name may not be blank.", SC_BAD_REQUEST),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ public String getToken() {
return cerberusAuthToken.getToken();
}

public String getTokenId() {
return cerberusAuthToken.getId();
}

public Set<String> getUserGroups() {
if (cerberusAuthToken.getGroups() == null) {
return new HashSet<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void testCheckAuthTokenIsEmptyIfPrincipleIsNotInstanceOfCerberusAuthToken

@Test
public void testCheckAuthTokenIsEmptyIfPrincipleIsInstanceOfCerberusAuthToken() {
CerberusAuthToken cerberusAuthToken = new CerberusAuthToken();
CerberusAuthToken cerberusAuthToken = CerberusAuthToken.Builder.create().build();
auditableEventContext.setPrincipal(cerberusAuthToken);
Optional<CerberusAuthToken> principalAsCerberusPrincipal =
auditableEventContext.getPrincipalAsCerberusPrincipal();
Expand All @@ -36,9 +36,9 @@ public void testCheckAuthTokenIsEmptyIfPrincipleIsInstanceOfCerberusAuthToken()

@Test
public void testGetPrincipalNameIfPrincipleIsInstanceOfCerberusAuthToken() {
CerberusAuthToken cerberusAuthToken = new CerberusAuthToken();
String cerberusPrinciple = "cerberusPrinciple";
cerberusAuthToken.setPrincipal(cerberusPrinciple);
CerberusAuthToken cerberusAuthToken =
CerberusAuthToken.Builder.create().withPrincipal(cerberusPrinciple).build();
auditableEventContext.setPrincipal(cerberusAuthToken);
String principalName = auditableEventContext.getPrincipalName();
Assert.assertEquals(cerberusPrinciple, principalName);
Expand Down
5 changes: 3 additions & 2 deletions cerberus-dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
"axios": "^0.21.1",
"cookie": "0.4.1",
"downloadjs": "1.4.7",
"handlebars": "^4.7.7",
"humps": "2.0.1",
"lodash": "^4.17.21",
"loglevel": "1.7.1",
"node-sass": "5.0.0",
"prop-types": "^15.7.2",
"react": "15.7",
"react-addons-create-fragment": "15.6.2",
"react-transition-group": "2.9.0",
"react-addons-shallow-compare": "15.6.3",
"react-copy-to-clipboard": "5.0.3",
"react-dom": "15.7.0",
Expand All @@ -39,6 +39,7 @@
"react-router-redux": "4.0.8",
"react-select": "1.3.0",
"react-simple-file-input": "2.1.0",
"react-transition-group": "2.9.0",
"redux": "3.7.2",
"redux-form": "5.3.6",
"redux-logger": "3.0.6",
Expand All @@ -52,7 +53,7 @@
"eslint-loader": "4.0.2",
"eslint-plugin-react": "7.22.0",
"estraverse-fb": "1.3.2",
"react-scripts": "4.0.1",
"react-scripts": "4.0.3",
"redux-devtools": "3.7.0",
"bufferutil": "^4.0.3",
"utf-8-validate": "^5.0.4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,28 @@
font-size: 16px;
font-weight: bold;
}

.view-token-modal-data-token-value {
height: 100px;
width: 600px;
overflow-y: scroll;
overflow-wrap: break-word;
padding-left: 5px;
margin-left: 140px;
margin-top: 3px;
}

.view-token-modal-data-date-value {
.view-token-modal-data-token-value::-webkit-scrollbar {
width: 7px;
background-color: $snkrs_medium_grey;
}

.view-token-modal-data-token-value::-webkit-scrollbar-thumb {
height: 18px;
border-radius: 5px;
background-color: $snkrs_dark_grey;
}

.view-token-modal-data-date-value {
padding-left: 5px;
margin-left: 17px;
margin-top: 3px;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2021 Nike, Inc.
*
* 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.nike.cerberus.domain;

/** Enum used to distinguish between JWT and session token */
public enum AuthTokenAcceptType {
JWT,
SESSION,
ALL
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.nike.cerberus.domain;

import java.time.OffsetDateTime;

public interface AuthTokenInfo {
String getId();

AuthTokenInfo setId(String id);

OffsetDateTime getCreatedTs();

AuthTokenInfo setCreatedTs(OffsetDateTime createdTs);

OffsetDateTime getExpiresTs();

AuthTokenInfo setExpiresTs(OffsetDateTime expiresTs);

String getPrincipal();

AuthTokenInfo setPrincipal(String principal);

String getPrincipalType();

AuthTokenInfo setPrincipalType(String principalType);

Boolean getIsAdmin();

AuthTokenInfo setIsAdmin(Boolean admin);

String getGroups();

AuthTokenInfo setGroups(String groups);

Integer getRefreshCount();

AuthTokenInfo setRefreshCount(Integer refreshCount);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2021 Nike, Inc.
*
* 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.nike.cerberus.domain;

/** Enum used to distinguish between JWT and session token */
public enum AuthTokenIssueType {
JWT,
SESSION
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
* Copyright (c) 2020 Nike, inc.
* Copyright (c) 2017 Nike, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* 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
* 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,
Expand All @@ -19,24 +19,96 @@
import com.nike.cerberus.PrincipalType;
import java.io.Serializable;
import java.time.OffsetDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
import lombok.Getter;

public class CerberusAuthToken implements Serializable {

private static final long serialVersionUID = 703097175899198451L;

private String token;
private OffsetDateTime created;
private OffsetDateTime expires;
private String principal;
private PrincipalType principalType;
private boolean isAdmin;
private String groups;
private int refreshCount;
@Getter private String token;
@Getter private OffsetDateTime created;
@Getter private OffsetDateTime expires;
@Getter private String principal;
@Getter private PrincipalType principalType;
@Getter private boolean isAdmin;
@Getter private String groups;
@Getter private int refreshCount;
@Getter private String id;

public static final class Builder {
private String token;
private OffsetDateTime created;
private OffsetDateTime expires;
private String principal;
private PrincipalType principalType;
private boolean isAdmin;
private String groups;
private int refreshCount;
private String id;

private Builder() {}

public static Builder create() {
return new Builder();
}

public Builder withToken(String token) {
this.token = token;
return this;
}

public Builder withCreated(OffsetDateTime created) {
this.created = created;
return this;
}

public Builder withExpires(OffsetDateTime expires) {
this.expires = expires;
return this;
}

public Builder withPrincipal(String principal) {
this.principal = principal;
return this;
}

public Builder withPrincipalType(PrincipalType principalType) {
this.principalType = principalType;
return this;
}

public Builder withIsAdmin(boolean isAdmin) {
this.isAdmin = isAdmin;
return this;
}

public Builder withGroups(String groups) {
this.groups = groups;
return this;
}

public Builder withRefreshCount(int refreshCount) {
this.refreshCount = refreshCount;
return this;
}

public Builder withId(String id) {
this.id = id;
return this;
}

public CerberusAuthToken build() {
CerberusAuthToken generateTokenResult = new CerberusAuthToken();
generateTokenResult.refreshCount = this.refreshCount;
generateTokenResult.principal = this.principal;
generateTokenResult.token = this.token;
generateTokenResult.isAdmin = this.isAdmin;
generateTokenResult.expires = this.expires;
generateTokenResult.groups = this.groups;
generateTokenResult.principalType = this.principalType;
generateTokenResult.created = this.created;
generateTokenResult.id = this.id;
return generateTokenResult;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ public void test_pojo_structure_and_behavior() {
List<PojoClass> pojoClasses = PojoClassFactory.getPojoClasses("com.nike.cerberus.domain");

pojoClasses.remove(PojoClassFactory.getPojoClass(CerberusAuthToken.class));
pojoClasses.remove(
PojoClassFactory.getPojoClass(CerberusAuthToken.CerberusAuthTokenBuilder.class));
pojoClasses.remove(PojoClassFactory.getPojoClass(CerberusAuthToken.Builder.class));
pojoClasses.remove(PojoClassFactory.getPojoClass(VaultStyleErrorResponse.Builder.class));
pojoClasses.remove(PojoClassFactory.getPojoClass(IamPrincipalPermission.Builder.class));
pojoClasses.remove(PojoClassFactory.getPojoClass(UserGroupPermission.Builder.class));
Expand Down
8 changes: 8 additions & 0 deletions cerberus-web/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,17 @@ dependencies {
implementation "com.amazonaws:aws-java-sdk-core:${versions.awsSdkVersion}"
implementation "com.amazonaws:aws-java-sdk-kms:${versions.awsSdkVersion}"
implementation "com.amazonaws:aws-java-sdk-sts:${versions.awsSdkVersion}"
implementation "com.amazonaws:aws-java-sdk-s3:${versions.awsSdkVersion}"
implementation "com.amazonaws:aws-java-sdk-secretsmanager:${versions.awsSdkVersion}"
implementation 'com.amazonaws:aws-encryption-sdk-java:1.6.1'


// JWT
implementation "io.jsonwebtoken:jjwt-api:0.10.8"
implementation "io.jsonwebtoken:jjwt-impl:0.10.8"
implementation "io.jsonwebtoken:jjwt-jackson:0.10.8"


//dist tracing
implementation 'com.nike.wingtips:wingtips-spring-boot:0.23.1'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ public RevokeAuthenticationController(AuthenticationService authenticationServic
@RequestMapping(method = DELETE)
public void revokeAuthentication(Authentication authentication) {
var cerberusPrincipal = (CerberusPrincipal) authentication;
authenticationService.revoke(cerberusPrincipal.getToken());
authenticationService.revoke(cerberusPrincipal, cerberusPrincipal.getTokenExpires());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ public int createAuthToken(AuthTokenRecord record) {
}

public Optional<AuthTokenRecord> getAuthTokenFromHash(String hash) {
return Optional.ofNullable(authTokenMapper.getAuthTokenFromHash(hash));
Optional<AuthTokenRecord> authTokenRecord =
Optional.ofNullable(authTokenMapper.getAuthTokenFromHash(hash));
if (authTokenRecord.isEmpty()) {
logger.warn("Failed to get auth token from hash");
}
return authTokenRecord;
}

public void deleteAuthTokenFromHash(String hash) {
Expand Down
Loading

0 comments on commit efa8bb0

Please sign in to comment.