diff --git a/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/GroupRolesBean.java b/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/GroupRolesBean.java index d79da30410..653310342d 100644 --- a/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/GroupRolesBean.java +++ b/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/GroupRolesBean.java @@ -16,6 +16,8 @@ package com.pinterest.deployservice.bean; import com.fasterxml.jackson.annotation.JsonProperty; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import org.apache.commons.lang.builder.ReflectionToStringBuilder; import javax.validation.constraints.NotEmpty; @@ -40,41 +42,41 @@ public class GroupRolesBean implements Updatable { private String resource_id; @JsonProperty("type") - private Resource.Type resource_type; + private AuthZResource.Type resource_type; @NotNull @JsonProperty("role") - private Role role; + private TeletraanPrincipalRole role; public String getGroup_name() { return group_name; } - public void setGroup_name(String user_name) { - this.group_name = user_name; + public void setGroup_name(String userName) { + this.group_name = userName; } public String getResource_id() { return resource_id; } - public void setResource_id(String resource_id) { - this.resource_id = resource_id; + public void setResource_id(String resourceId) { + this.resource_id = resourceId; } - public Resource.Type getResource_type() { + public AuthZResource.Type getResource_type() { return resource_type; } - public void setResource_type(Resource.Type resource_type) { - this.resource_type = resource_type; + public void setResource_type(AuthZResource.Type resourceType) { + this.resource_type = resourceType; } - public Role getRole() { + public TeletraanPrincipalRole getRole() { return role; } - public void setRole(Role role) { + public void setRole(TeletraanPrincipalRole role) { this.role = role; } diff --git a/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/Resource.java b/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/Resource.java deleted file mode 100644 index bf38cce26b..0000000000 --- a/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/Resource.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.deployservice.bean; - -public class Resource { - public enum Type { - ENV, GROUP, SYSTEM - } - - public final static String ALL = "*"; - public final static Resource SYSTEM_RESOURCE = new Resource(ALL, Type.SYSTEM); - - private String id; - private Type type; - - public Resource(String id, Type type) { - this.id = id; - this.type = type; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public Type getType() { - return type; - } - - public void setType(Type type) { - this.type = type; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - Resource resource = (Resource) o; - - if (!id.equals(resource.id)) { - return false; - } - return type == resource.type; - - } - - @Override - public int hashCode() { - int result = id.hashCode(); - result = 31 * result + type.hashCode(); - return result; - } -} diff --git a/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/Role.java b/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/Role.java deleted file mode 100644 index 6ae70bf9ea..0000000000 --- a/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/Role.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.deployservice.bean; - -/** - * READER: - * Default role, everyone who is able to use Teletraan has READER access. - * PINGER: - * Role required to ping server. - * PUBLISHER: - * Role required to publish artifacts. - * OPERATOR: - * Role where user can modify a specific environment's config and - * perform deploy related actions. - * ADMIN: - * Role that has the same environment specific privileges as OPERATOR - * plus the ability specify new OPERATORS and ADMINs for said environment. - * When a new environment is created the creating user is the designated the - * first ADMIN. - */ -public enum Role { - READER(0), - PINGER(1), - PUBLISHER(1), - OPERATOR(10), - ADMIN(20); - - private int value; - - Role(int value) { - this.value = value; - } - - public boolean isAuthorized(Role requiredRole) { - return this == requiredRole || this.value > requiredRole.value; - } -} diff --git a/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/TeletraanPrincipalRole.java b/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/TeletraanPrincipalRole.java new file mode 100644 index 0000000000..bc53c016a6 --- /dev/null +++ b/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/TeletraanPrincipalRole.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2024, Pinterest Inc. All rights reserved. + */ +package com.pinterest.deployservice.bean; + +import com.pinterest.teletraan.universal.security.bean.RoleEnum; +import com.pinterest.teletraan.universal.security.bean.ValueBasedRole; + + +/** + * READER: + * Default role, everyone who is able to use Teletraan has READER access. + * PINGER: + * Role required to ping server. + * PUBLISHER: + * Role required to publish artifacts. + * OPERATOR: + * Role where user can modify a specific environment's config and + * perform deploy related actions. + * ADMIN: + * Role that has the same environment specific privileges as OPERATOR + * plus the ability specify new OPERATORS and ADMINs for said environment. + * When a new environment is created the creating user is the designated the + * first ADMIN. + */ +public enum TeletraanPrincipalRole implements RoleEnum { + READ(-1), + READER(0), // legacy + PINGER(1), // legacy + PUBLISHER(1), // legacy + EXECUTE(9), + WRITE(9), + DELETE(9), + OPERATOR(10), // legacy + ADMIN(20); + + public class Names { + private Names() {} + public static final String PINGER = "PINGER"; + public static final String PUBLISHER = "PUBLISHER"; + public static final String READER = "READER"; + public static final String OPERATOR = "OPERATOR"; + public static final String ADMIN = "ADMIN"; + + public static final String READ = "READ"; + public static final String WRITE = "WRITE"; + public static final String EXECUTE = "EXECUTE"; + public static final String DELETE = "DELETE"; + } + + private final ValueBasedRole role; + + TeletraanPrincipalRole(int value) { + this.role = new ValueBasedRole(value); + } + + public ValueBasedRole getRole() { + return role; + } + + public boolean isEqualOrSuperior(TeletraanPrincipalRole otherRole) { + return this.role.isEqualOrSuperior(otherRole.getRole()); + } +} diff --git a/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/TokenRolesBean.java b/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/TokenRolesBean.java index 8ddc65a8fc..f57579c19e 100644 --- a/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/TokenRolesBean.java +++ b/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/TokenRolesBean.java @@ -16,8 +16,11 @@ package com.pinterest.deployservice.bean; import com.fasterxml.jackson.annotation.JsonProperty; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import javax.annotation.Nonnull; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; @@ -42,14 +45,14 @@ public class TokenRolesBean implements Updatable { private String resource_id; @JsonProperty("type") - private Resource.Type resource_type; + private AuthZResource.Type resource_type; @JsonProperty("token") private String token; @NotNull @JsonProperty("role") - private Role role; + private TeletraanPrincipalRole role; @JsonProperty("expireDate") private Long expire_date; @@ -66,31 +69,31 @@ public String getScript_name() { return script_name; } - public void setScript_name(String script_name) { - this.script_name = script_name; + public void setScript_name(String scriptName) { + this.script_name = scriptName; } - public String getResource_id() { + public @Nonnull String getResource_id() { return resource_id; } - public void setResource_id(String resource_id) { - this.resource_id = resource_id; + public void setResource_id(String resourceId) { + this.resource_id = resourceId; } - public Resource.Type getResource_type() { + public AuthZResource.Type getResource_type() { return resource_type; } - public void setResource_type(Resource.Type resource_type) { - this.resource_type = resource_type; + public void setResource_type(AuthZResource.Type resourceType) { + this.resource_type = resourceType; } - public Role getRole() { + public TeletraanPrincipalRole getRole() { return role; } - public void setRole(Role role) { + public void setRole(TeletraanPrincipalRole role) { this.role = role; } @@ -98,8 +101,8 @@ public Long getExpire_date() { return expire_date; } - public void setExpire_date(Long expire_date) { - this.expire_date = expire_date; + public void setExpire_date(Long expireDate) { + this.expire_date = expireDate; } @Override diff --git a/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/UserRolesBean.java b/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/UserRolesBean.java index 2a7055584a..06dd3d6428 100644 --- a/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/UserRolesBean.java +++ b/deploy-service/common/src/main/java/com/pinterest/deployservice/bean/UserRolesBean.java @@ -16,6 +16,8 @@ package com.pinterest.deployservice.bean; import com.fasterxml.jackson.annotation.JsonProperty; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import org.apache.commons.lang.builder.ReflectionToStringBuilder; import javax.validation.constraints.NotEmpty; @@ -40,45 +42,45 @@ public class UserRolesBean implements Updatable { private String resource_id; @JsonProperty("type") - private Resource.Type resource_type; + private AuthZResource.Type resource_type; @NotNull @JsonProperty("role") - private Role role; + private TeletraanPrincipalRole role; public String getUser_name() { return user_name; } - public void setUser_name(String user_name) { - this.user_name = user_name; + public void setUser_name(String userName) { + this.user_name = userName; } public String getResource_id() { return resource_id; } - public void setResource_id(String resource_id) { - this.resource_id = resource_id; + public void setResource_id(String resourceId) { + this.resource_id = resourceId; } - public Resource.Type getResource_type() { + public AuthZResource.Type getResource_type() { return resource_type; } - public void setResource_type(Resource.Type resource_type) { - this.resource_type = resource_type; + public void setResource_type(AuthZResource.Type resourceType) { + this.resource_type = resourceType; } - public Role getRole() { + public TeletraanPrincipalRole getRole() { return role; } - public void setRole(Role role) { + public void setRole(TeletraanPrincipalRole role) { this.role = role; } - public final static String UPDATE_CLAUSE = + public static final String UPDATE_CLAUSE = "user_name=VALUES(user_name)," + "resource_id=VALUES(resource_id)," + "resource_type=VALUES(resource_type)," + diff --git a/deploy-service/common/src/main/java/com/pinterest/deployservice/dao/GroupRolesDAO.java b/deploy-service/common/src/main/java/com/pinterest/deployservice/dao/GroupRolesDAO.java index 938e625b1a..a5be012bc1 100644 --- a/deploy-service/common/src/main/java/com/pinterest/deployservice/dao/GroupRolesDAO.java +++ b/deploy-service/common/src/main/java/com/pinterest/deployservice/dao/GroupRolesDAO.java @@ -4,9 +4,9 @@ * 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. @@ -16,22 +16,20 @@ package com.pinterest.deployservice.dao; import com.pinterest.deployservice.bean.GroupRolesBean; -import com.pinterest.deployservice.bean.Resource; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; import java.util.List; public interface GroupRolesDAO { void insert(GroupRolesBean bean) throws Exception; - void delete(String groupName, String resourceId, - Resource.Type resourceType) throws Exception; + void delete(String groupName, String resourceId, AuthZResource.Type resourceType) throws Exception; - void update(GroupRolesBean bean, String groupName, String resourceId, - Resource.Type resourceType) throws Exception; + void update(GroupRolesBean bean, String groupName, String resourceId, AuthZResource.Type resourceType) + throws Exception; - GroupRolesBean getByNameAndResource(String groupName, String resourceId, - Resource.Type resourceType) throws Exception; + GroupRolesBean getByNameAndResource(String groupName, String resourceId, AuthZResource.Type resourceType) + throws Exception; - List getByResource(String resourceId, - Resource.Type resourceType) throws Exception; + List getByResource(String resourceId, AuthZResource.Type resourceType) throws Exception; } diff --git a/deploy-service/common/src/main/java/com/pinterest/deployservice/dao/TokenRolesDAO.java b/deploy-service/common/src/main/java/com/pinterest/deployservice/dao/TokenRolesDAO.java index 15c1e19509..f69a55c172 100644 --- a/deploy-service/common/src/main/java/com/pinterest/deployservice/dao/TokenRolesDAO.java +++ b/deploy-service/common/src/main/java/com/pinterest/deployservice/dao/TokenRolesDAO.java @@ -4,9 +4,9 @@ * 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. @@ -15,25 +15,23 @@ */ package com.pinterest.deployservice.dao; -import com.pinterest.deployservice.bean.Resource; import com.pinterest.deployservice.bean.TokenRolesBean; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; import java.util.List; public interface TokenRolesDAO { void insert(TokenRolesBean bean) throws Exception; - void delete(String scriptName, String resourceId, - Resource.Type resourceType) throws Exception; + void delete(String scriptName, String resourceId, AuthZResource.Type resourceType) throws Exception; - void update(TokenRolesBean bean, String scriptName, String resourceId, - Resource.Type resourceType) throws Exception; + void update(TokenRolesBean bean, String scriptName, String resourceId, AuthZResource.Type resourceType) + throws Exception; TokenRolesBean getByToken(String token) throws Exception; - TokenRolesBean getByNameAndResource(String scriptName, String resourceId, - Resource.Type resourceType) throws Exception; + TokenRolesBean getByNameAndResource(String scriptName, String resourceId, AuthZResource.Type resourceType) + throws Exception; - List getByResource(String resourceId, - Resource.Type resourceType) throws Exception; + List getByResource(String resourceId, AuthZResource.Type resourceType) throws Exception; } diff --git a/deploy-service/common/src/main/java/com/pinterest/deployservice/dao/UserRolesDAO.java b/deploy-service/common/src/main/java/com/pinterest/deployservice/dao/UserRolesDAO.java index 4593c99a4d..b2a7e0616c 100644 --- a/deploy-service/common/src/main/java/com/pinterest/deployservice/dao/UserRolesDAO.java +++ b/deploy-service/common/src/main/java/com/pinterest/deployservice/dao/UserRolesDAO.java @@ -4,9 +4,9 @@ * 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. @@ -15,19 +15,21 @@ */ package com.pinterest.deployservice.dao; -import com.pinterest.deployservice.bean.Resource; import com.pinterest.deployservice.bean.UserRolesBean; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; import java.util.List; public interface UserRolesDAO { void insert(UserRolesBean bean) throws Exception; - void delete(String userName, String resourceId, Resource.Type resourceType) throws Exception; + void delete(String userName, String resourceId, AuthZResource.Type resourceType) throws Exception; - void update(UserRolesBean bean, String userName, String resourceId, Resource.Type resourceType) throws Exception; + void update(UserRolesBean bean, String userName, String resourceId, AuthZResource.Type resourceType) + throws Exception; - UserRolesBean getByNameAndResource(String userName, String resourceId, Resource.Type resourceType) throws Exception; + UserRolesBean getByNameAndResource(String userName, String resourceId, AuthZResource.Type resourceType) + throws Exception; - List getByResource(String resourceId, Resource.Type resourceType) throws Exception; + List getByResource(String resourceId, AuthZResource.Type resourceType) throws Exception; } diff --git a/deploy-service/common/src/main/java/com/pinterest/deployservice/db/DBGroupRolesDAOImpl.java b/deploy-service/common/src/main/java/com/pinterest/deployservice/db/DBGroupRolesDAOImpl.java index c0dde34c33..92b4d36710 100644 --- a/deploy-service/common/src/main/java/com/pinterest/deployservice/db/DBGroupRolesDAOImpl.java +++ b/deploy-service/common/src/main/java/com/pinterest/deployservice/db/DBGroupRolesDAOImpl.java @@ -4,9 +4,9 @@ * 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. @@ -16,9 +16,10 @@ package com.pinterest.deployservice.db; import com.pinterest.deployservice.bean.GroupRolesBean; -import com.pinterest.deployservice.bean.Resource; import com.pinterest.deployservice.bean.SetClause; import com.pinterest.deployservice.dao.GroupRolesDAO; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; @@ -66,14 +67,14 @@ public void insert(GroupRolesBean bean) throws Exception { @Override public void delete(String groupName, String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { new QueryRunner(dataSource).update(DELETE_TEMPLATE, groupName, resourceId, resourceType.toString()); } @Override public void update(GroupRolesBean bean, String groupName, String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { SetClause setClause = bean.genSetClause(); String clause = String.format(UPDATE_TEMPLATE, setClause.getClause()); setClause.addValue(groupName); @@ -84,7 +85,7 @@ public void update(GroupRolesBean bean, String groupName, String resourceId, @Override public GroupRolesBean getByNameAndResource(String groupName, String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { ResultSetHandler h = new BeanHandler<>(GroupRolesBean.class); return new QueryRunner(dataSource).query(GET_BY_NAME_AND_RESOURCE, h, groupName, resourceId, resourceType.toString()); @@ -92,7 +93,7 @@ public GroupRolesBean getByNameAndResource(String groupName, String resourceId, @Override public List getByResource(String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { ResultSetHandler> h = new BeanListHandler<>(GroupRolesBean.class); return new QueryRunner(dataSource).query(GET_BY_RESOURCE, h, resourceId, resourceType.toString()); diff --git a/deploy-service/common/src/main/java/com/pinterest/deployservice/db/DBTokenRolesDAOImpl.java b/deploy-service/common/src/main/java/com/pinterest/deployservice/db/DBTokenRolesDAOImpl.java index af6b45683f..cace700306 100644 --- a/deploy-service/common/src/main/java/com/pinterest/deployservice/db/DBTokenRolesDAOImpl.java +++ b/deploy-service/common/src/main/java/com/pinterest/deployservice/db/DBTokenRolesDAOImpl.java @@ -4,9 +4,9 @@ * 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. @@ -15,10 +15,11 @@ */ package com.pinterest.deployservice.db; -import com.pinterest.deployservice.bean.Resource; import com.pinterest.deployservice.bean.SetClause; import com.pinterest.deployservice.bean.TokenRolesBean; import com.pinterest.deployservice.dao.TokenRolesDAO; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; @@ -65,14 +66,14 @@ public void insert(TokenRolesBean bean) throws Exception { @Override public void delete(String userName, String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { new QueryRunner(dataSource).update(DELETE_TEMPLATE, userName, resourceId, resourceType.toString()); } @Override public void update(TokenRolesBean bean, String userName, String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { SetClause setClause = bean.genSetClause(); String clause = String.format(UPDATE_TEMPLATE, setClause.getClause()); setClause.addValue(userName); @@ -89,7 +90,7 @@ public TokenRolesBean getByToken(String token) throws Exception { @Override public TokenRolesBean getByNameAndResource(String userName, String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { ResultSetHandler h = new BeanHandler<>(TokenRolesBean.class); return new QueryRunner(dataSource).query(GET_BY_NAME_AND_RESOURCE, h, userName, resourceId, resourceType.toString()); @@ -97,7 +98,7 @@ public TokenRolesBean getByNameAndResource(String userName, String resourceId, @Override public List getByResource(String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { ResultSetHandler> h = new BeanListHandler<>(TokenRolesBean.class); return new QueryRunner(dataSource).query(GET_BY_RESOURCE, h, resourceId, resourceType.toString()); diff --git a/deploy-service/common/src/main/java/com/pinterest/deployservice/db/DBUserRolesDAOImpl.java b/deploy-service/common/src/main/java/com/pinterest/deployservice/db/DBUserRolesDAOImpl.java index 5b4bbed9a6..cfb485ad50 100644 --- a/deploy-service/common/src/main/java/com/pinterest/deployservice/db/DBUserRolesDAOImpl.java +++ b/deploy-service/common/src/main/java/com/pinterest/deployservice/db/DBUserRolesDAOImpl.java @@ -4,9 +4,9 @@ * 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. @@ -15,10 +15,11 @@ */ package com.pinterest.deployservice.db; -import com.pinterest.deployservice.bean.Resource; import com.pinterest.deployservice.bean.SetClause; import com.pinterest.deployservice.bean.UserRolesBean; import com.pinterest.deployservice.dao.UserRolesDAO; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; @@ -62,14 +63,14 @@ public void insert(UserRolesBean bean) throws Exception { @Override public void delete(String userName, String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { new QueryRunner(dataSource).update(DELETE_TEMPLATE, userName, resourceId, resourceType.toString()); } @Override public void update(UserRolesBean bean, String userName, String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { SetClause setClause = bean.genSetClause(); String clause = String.format(UPDATE_TEMPLATE, setClause.getClause()); setClause.addValue(userName); @@ -80,7 +81,7 @@ public void update(UserRolesBean bean, String userName, String resourceId, @Override public UserRolesBean getByNameAndResource(String userName, String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { ResultSetHandler h = new BeanHandler<>(UserRolesBean.class); return new QueryRunner(dataSource).query(GET_BY_NAME_AND_RESOURCE, h, userName, resourceId, resourceType.toString()); @@ -88,7 +89,7 @@ public UserRolesBean getByNameAndResource(String userName, String resourceId, @Override public List getByResource(String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { ResultSetHandler> h = new BeanListHandler<>(UserRolesBean.class); return new QueryRunner(dataSource).query(GET_BY_RESOURCE, h, resourceId, resourceType.toString()); diff --git a/deploy-service/common/src/test/java/com/pinterest/deployservice/db/DBDAOTest.java b/deploy-service/common/src/test/java/com/pinterest/deployservice/db/DBDAOTest.java index d4241deae4..623fb829b7 100644 --- a/deploy-service/common/src/test/java/com/pinterest/deployservice/db/DBDAOTest.java +++ b/deploy-service/common/src/test/java/com/pinterest/deployservice/db/DBDAOTest.java @@ -47,13 +47,12 @@ import com.pinterest.deployservice.bean.PromoteBean; import com.pinterest.deployservice.bean.PromoteType; import com.pinterest.deployservice.bean.RatingBean; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; import com.pinterest.deployservice.bean.ScheduleBean; import com.pinterest.deployservice.bean.ScheduleState; import com.pinterest.deployservice.bean.TagBean; import com.pinterest.deployservice.bean.TagTargetType; import com.pinterest.deployservice.bean.TagValue; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.bean.TokenRolesBean; import com.pinterest.deployservice.bean.UserRolesBean; import com.pinterest.deployservice.common.CommonUtils; @@ -75,6 +74,7 @@ import com.pinterest.deployservice.dao.TokenRolesDAO; import com.pinterest.deployservice.dao.UserRolesDAO; import com.pinterest.deployservice.dao.UtilDAO; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; import java.sql.Connection; import java.util.ArrayList; import java.util.Arrays; @@ -902,12 +902,13 @@ public void testUserRolesDAO() throws Exception { UserRolesBean bean = new UserRolesBean(); bean.setUser_name("test"); bean.setResource_id("envTest"); - bean.setResource_type(Resource.Type.ENV); - bean.setRole(Role.ADMIN); + bean.setResource_type(AuthZResource.Type.ENV); + bean.setRole(TeletraanPrincipalRole.ADMIN); userRolesDAO.insert(bean); - UserRolesBean bean2 = - userRolesDAO.getByNameAndResource("test", "envTest", Resource.Type.ENV); - assertEquals(bean2.getRole(), Role.ADMIN); + UserRolesBean + bean2 = + userRolesDAO.getByNameAndResource("test", "envTest", AuthZResource.Type.ENV); + assertEquals(bean2.getRole(), TeletraanPrincipalRole.ADMIN); } @Test @@ -915,12 +916,13 @@ public void testGroupRolesDAO() throws Exception { GroupRolesBean bean = new GroupRolesBean(); bean.setGroup_name("group"); bean.setResource_id("123"); - bean.setResource_type(Resource.Type.ENV); - bean.setRole(Role.ADMIN); + bean.setResource_type(AuthZResource.Type.ENV); + bean.setRole(TeletraanPrincipalRole.ADMIN); groupRolesDAO.insert(bean); - GroupRolesBean bean2 = - groupRolesDAO.getByNameAndResource("group", "123", Resource.Type.ENV); - assertEquals(bean2.getRole(), Role.ADMIN); + GroupRolesBean + bean2 = + groupRolesDAO.getByNameAndResource("group", "123", AuthZResource.Type.ENV); + assertEquals(bean2.getRole(), TeletraanPrincipalRole.ADMIN); } @Test @@ -929,13 +931,14 @@ public void testTokenRolesDAO() throws Exception { bean.setScript_name("test"); bean.setToken("token"); bean.setResource_id("envTest"); - bean.setResource_type(Resource.Type.ENV); - bean.setRole(Role.ADMIN); + bean.setResource_type(AuthZResource.Type.ENV); + bean.setRole(TeletraanPrincipalRole.ADMIN); bean.setExpire_date(System.currentTimeMillis()); tokenRolesDAO.insert(bean); - TokenRolesBean bean2 = - tokenRolesDAO.getByNameAndResource("test", "envTest", Resource.Type.ENV); - assertEquals(bean2.getRole(), Role.ADMIN); + TokenRolesBean + bean2 = + tokenRolesDAO.getByNameAndResource("test", "envTest", AuthZResource.Type.ENV); + assertEquals(bean2.getRole(), TeletraanPrincipalRole.ADMIN); } @Test diff --git a/deploy-service/teletraanservice/pom.xml b/deploy-service/teletraanservice/pom.xml index 88a37fa9a7..15ee614543 100644 --- a/deploy-service/teletraanservice/pom.xml +++ b/deploy-service/teletraanservice/pom.xml @@ -79,6 +79,7 @@ io.micrometer micrometer-core + io.dropwizard dropwizard-testing @@ -91,6 +92,12 @@ ${junit-jupiter.version} test + + com.squareup.okhttp3 + mockwebserver + 4.12.0 + test + diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/ConfigHelper.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/ConfigHelper.java index 59f35c0d03..13007e495c 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/ConfigHelper.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/ConfigHelper.java @@ -76,6 +76,7 @@ import com.pinterest.teletraan.config.RodimusFactory; import com.pinterest.teletraan.config.SourceControlFactory; import com.pinterest.teletraan.config.WorkerConfig; +import com.pinterest.teletraan.security.TeletraanAuthZResourceExtractorFactory; import com.pinterest.teletraan.universal.events.AppEventPublisher; import com.pinterest.teletraan.worker.AgentJanitor; import com.pinterest.teletraan.worker.AutoPromoter; @@ -133,7 +134,8 @@ public static TeletraanServiceContext setupContext(TeletraanServiceConfiguration context.setScheduleDAO(new DBScheduleDAOImpl(dataSource)); // Inject proper implementation based on config - context.setAuthorizer(configuration.getAuthorizationFactory().create(context)); + context.setAuthorizationFactory(configuration.getAuthorizationFactory()); + context.setAuthZResourceExtractorFactory(new TeletraanAuthZResourceExtractorFactory(context)); context.setChatManager(configuration.getChatFactory().create()); context.setMailManager(configuration.getEmailFactory().createMailManager()); context.setHostGroupDAO(configuration.getHostGroupFactory().createHostGroupDAO()); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/TeletraanAgentService.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/TeletraanAgentService.java index fe37538f44..a0333f054c 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/TeletraanAgentService.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/TeletraanAgentService.java @@ -1,5 +1,5 @@ /** - * Copyright 2016 Pinterest, Inc. + * Copyright (c) 2016-2024 Pinterest, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,11 +17,14 @@ import com.pinterest.teletraan.health.GenericHealthCheck; import com.pinterest.teletraan.resource.Pings; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfoFeature; import io.dropwizard.Application; -import io.dropwizard.setup.Environment; +import io.dropwizard.auth.AuthDynamicFeature; import io.dropwizard.configuration.EnvironmentVariableSubstitutor; import io.dropwizard.configuration.SubstitutingSourceProvider; import io.dropwizard.setup.Bootstrap; +import io.dropwizard.setup.Environment; +import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature; public class TeletraanAgentService extends Application { @Override @@ -40,10 +43,17 @@ public void initialize(Bootstrap bootstrap) { } @Override - public void run(TeletraanServiceConfiguration configuration, Environment environment) throws Exception { + public void run(TeletraanServiceConfiguration configuration, Environment environment) + throws Exception { TeletraanServiceContext context = ConfigHelper.setupContext(configuration); environment.jersey().register(context); - environment.jersey().register(configuration.getAuthenticationFactory().create(context)); + environment + .jersey() + .register( + new AuthDynamicFeature( + configuration.getAuthenticationFactory().create(context))); + environment.jersey().register(RolesAllowedDynamicFeature.class); + environment.jersey().register(ResourceAuthZInfoFeature.class); environment.jersey().register(Pings.class); environment.healthChecks().register("generic", new GenericHealthCheck(context)); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/TeletraanService.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/TeletraanService.java index f69f903c73..85ed37f22d 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/TeletraanService.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/TeletraanService.java @@ -1,5 +1,5 @@ /** - * Copyright 2016 Pinterest, Inc. + * Copyright (c) 2016-2024 Pinterest, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +19,9 @@ import com.pinterest.teletraan.health.GenericHealthCheck; import com.pinterest.teletraan.resource.*; import com.pinterest.teletraan.universal.security.PrincipalNameInjector; - +import com.pinterest.teletraan.universal.security.ResourceAuthZInfoFeature; import io.dropwizard.Application; +import io.dropwizard.auth.AuthDynamicFeature; import io.dropwizard.configuration.EnvironmentVariableSubstitutor; import io.dropwizard.configuration.SubstitutingSourceProvider; import io.dropwizard.jersey.jackson.JsonProcessingExceptionMapper; @@ -28,13 +29,12 @@ import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Environment; import io.swagger.jaxrs.config.BeanConfig; -import io.swagger.jaxrs.listing.ApiListingResource; import io.swagger.jaxrs.listing.SwaggerSerializers; -import org.eclipse.jetty.servlets.CrossOriginFilter; - import java.util.EnumSet; import javax.servlet.DispatcherType; import javax.servlet.FilterRegistration; +import org.eclipse.jetty.servlets.CrossOriginFilter; +import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature; public class TeletraanService extends Application { @Override @@ -53,11 +53,18 @@ public void initialize(Bootstrap bootstrap) { } @Override - public void run(TeletraanServiceConfiguration configuration, Environment environment) throws Exception { + public void run(TeletraanServiceConfiguration configuration, Environment environment) + throws Exception { TeletraanServiceContext context = ConfigHelper.setupContext(configuration); - environment.jersey().register(configuration.getAuthenticationFactory().create(context)); environment.jersey().register(context); + environment + .jersey() + .register( + new AuthDynamicFeature( + configuration.getAuthenticationFactory().create(context))); + environment.jersey().register(RolesAllowedDynamicFeature.class); + environment.jersey().register(ResourceAuthZInfoFeature.class); environment.jersey().register(Builds.class); environment.jersey().register(Commits.class); @@ -111,7 +118,7 @@ public void run(TeletraanServiceConfiguration configuration, Environment environ environment.jersey().register(PrincipalNameInjector.class); // Swagger API docs generation related - environment.jersey().register(ApiListingResource.class); + environment.jersey().register(SecureApiListingResource.class); environment.jersey().register(SwaggerSerializers.class); BeanConfig config = new BeanConfig(); config.setTitle("Teletraan API Docs"); @@ -128,10 +135,8 @@ public void run(TeletraanServiceConfiguration configuration, Environment environ filter.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true"); filter.setInitParameter("allowedHeaders", "Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin"); filter.setInitParameter("allowCredentials", "true"); - } - public static void main(String[] args) throws Exception { new TeletraanService().run(args); } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/TeletraanServiceContext.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/TeletraanServiceContext.java index ad5c323bf0..9b2cd0909d 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/TeletraanServiceContext.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/TeletraanServiceContext.java @@ -17,14 +17,16 @@ import com.pinterest.deployservice.ServiceContext; import com.pinterest.deployservice.alerts.ExternalAlertFactory; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.config.AuthorizationFactory; +import com.pinterest.teletraan.universal.security.AuthZResourceExtractor; public class TeletraanServiceContext extends ServiceContext { - private Authorizer authorizer; private int maxDaysToKeep; private int maxBuildsToKeep; private ExternalAlertFactory externalAlertsFactory; + private AuthorizationFactory authorizationFactory; + private AuthZResourceExtractor.Factory authZResourceExtractorFactory; public ExternalAlertFactory getExternalAlertsFactory() { return externalAlertsFactory; @@ -35,14 +37,6 @@ public void setExternalAlertsFactory( this.externalAlertsFactory = externalAlertsFactory; } - public Authorizer getAuthorizer() { - return authorizer; - } - - public void setAuthorizer(Authorizer authz) { - this.authorizer = authz; - } - public int getMaxDaysToKeep() { return maxDaysToKeep; } @@ -59,4 +53,20 @@ public void setMaxBuildsToKeep(int maxBuildsToKeep) { this.maxBuildsToKeep = maxBuildsToKeep; } + public void setAuthorizationFactory(AuthorizationFactory authorizationFactory) { + this.authorizationFactory = authorizationFactory; + } + + public AuthorizationFactory getAuthorizationFactory() { + return authorizationFactory; + } + + public AuthZResourceExtractor.Factory getAuthZResourceExtractorFactory() { + return authZResourceExtractorFactory; + } + + public void setAuthZResourceExtractorFactory( + AuthZResourceExtractor.Factory authZResourceExtractorFactory) { + this.authZResourceExtractorFactory = authZResourceExtractorFactory; + } } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/AnonymousAuthenticationFactory.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/AnonymousAuthenticationFactory.java index 2005b71ab5..cc4424fc54 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/AnonymousAuthenticationFactory.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/AnonymousAuthenticationFactory.java @@ -1,12 +1,12 @@ /** - * Copyright 2016 Pinterest, Inc. + * Copyright (c) 2016-2024 Pinterest, 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. @@ -17,8 +17,7 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.AnonymousAuthFilter; - +import com.pinterest.teletraan.universal.security.AnonymousAuthFilter; import javax.ws.rs.container.ContainerRequestFilter; @JsonTypeName("anonymous") diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/AuthorizationFactory.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/AuthorizationFactory.java index 7c1ebd7f3f..96da79f100 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/AuthorizationFactory.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/AuthorizationFactory.java @@ -1,12 +1,12 @@ /** - * Copyright 2016 Pinterest, Inc. + * Copyright (c) 2016-2024 Pinterest, 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. @@ -17,10 +17,17 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.bean.TeletraanPrincipal; +import io.dropwizard.auth.Authorizer; import io.dropwizard.jackson.Discoverable; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") public interface AuthorizationFactory extends Discoverable { - Authorizer create(TeletraanServiceContext context) throws Exception; +

Authorizer

create(TeletraanServiceContext context) + throws Exception; + + default

Authorizer create( + TeletraanServiceContext context, Class

principalClass) throws Exception { + return create(context); + } } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/OpenAuthorizationFactory.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/OpenAuthorizationFactory.java index 85b6ea0bba..adf179f6c6 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/OpenAuthorizationFactory.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/OpenAuthorizationFactory.java @@ -1,12 +1,12 @@ /** - * Copyright 2016 Pinterest, Inc. + * Copyright (c) 2016-2024 Pinterest, 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. @@ -15,26 +15,16 @@ */ package com.pinterest.teletraan.config; -import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeName; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.OpenAuthorizer; +import com.pinterest.teletraan.universal.security.bean.TeletraanPrincipal; +import io.dropwizard.auth.Authorizer; +import io.dropwizard.auth.PermitAllAuthorizer; @JsonTypeName("open") public class OpenAuthorizationFactory implements AuthorizationFactory { - @JsonProperty - private String roleCacheSpec; - - public String getRoleCacheSpec() { - return roleCacheSpec; - } - - public void setRoleCacheSpec(String roleCacheSpec) { - this.roleCacheSpec = roleCacheSpec; - } - @Override - public OpenAuthorizer create(TeletraanServiceContext context) throws Exception { - return new OpenAuthorizer(); + public Authorizer create(TeletraanServiceContext context) throws Exception { + return new PermitAllAuthorizer<>(); } } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/RoleAuthorizationFactory.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/RoleAuthorizationFactory.java new file mode 100644 index 0000000000..16c6296102 --- /dev/null +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/RoleAuthorizationFactory.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.config; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.pinterest.teletraan.TeletraanServiceContext; +import com.pinterest.teletraan.security.ScriptTokenRoleAuthorizer; +import com.pinterest.teletraan.security.UserRoleAuthorizer; +import com.pinterest.teletraan.universal.security.bean.ServicePrincipal; +import com.pinterest.teletraan.universal.security.bean.TeletraanPrincipal; +import com.pinterest.teletraan.universal.security.bean.UserPrincipal; +import io.dropwizard.auth.Authorizer; + +@JsonTypeName("role") +public class RoleAuthorizationFactory implements AuthorizationFactory { + @JsonProperty + private String roleCacheSpec; // Unused, for backwards compatibility + + @Override + public

Authorizer

create(TeletraanServiceContext context) + throws Exception { + throw new UnsupportedOperationException( + "RoleAuthorizationFactory does not support this method. Use create(TeletraanServiceContext, Class

) instead."); + } + + @Override + public

Authorizer create( + TeletraanServiceContext context, Class

principalClass) throws Exception { + if (ServicePrincipal.class.equals(principalClass)) { + return new ScriptTokenRoleAuthorizer(context.getAuthZResourceExtractorFactory()); + } else if (UserPrincipal.class.equals(principalClass)) { + return new UserRoleAuthorizer(context, context.getAuthZResourceExtractorFactory()); + } + throw new UnsupportedOperationException("Unsupported principal class: " + principalClass); + } +} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/TokenAuthenticationFactory.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/TokenAuthenticationFactory.java index b2296c1635..d9843116e7 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/TokenAuthenticationFactory.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/TokenAuthenticationFactory.java @@ -1,5 +1,5 @@ /** - * Copyright 2016 Pinterest, Inc. + * Copyright (c) 2016-2024 Pinterest, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,33 +15,41 @@ */ package com.pinterest.teletraan.config; - - +import com.codahale.metrics.SharedMetricRegistries; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeName; +import com.github.benmanes.caffeine.cache.Caffeine; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.TokenAuthFilter; - +import com.pinterest.teletraan.security.TeletraanScriptTokenProvider; +import com.pinterest.teletraan.universal.security.OAuthAuthenticator; +import com.pinterest.teletraan.universal.security.ScriptTokenAuthenticator; +import com.pinterest.teletraan.universal.security.bean.ScriptTokenPrincipal; +import com.pinterest.teletraan.universal.security.bean.ServicePrincipal; +import com.pinterest.teletraan.universal.security.bean.UserPrincipal; +import com.pinterest.teletraan.universal.security.bean.ValueBasedRole; +import io.dropwizard.auth.AuthFilter; +import io.dropwizard.auth.Authenticator; +import io.dropwizard.auth.Authorizer; +import io.dropwizard.auth.CachingAuthenticator; +import io.dropwizard.auth.chained.ChainedAuthFilter; +import io.dropwizard.auth.oauth.OAuthCredentialAuthFilter; +import java.util.Arrays; +import java.util.List; import javax.validation.constraints.NotEmpty; import javax.ws.rs.container.ContainerRequestFilter; +import org.apache.commons.lang3.StringUtils; @JsonTypeName("token") public class TokenAuthenticationFactory implements AuthenticationFactory { - @JsonProperty - @NotEmpty - private String userDataUrl; + @JsonProperty @NotEmpty private String userDataUrl; - @JsonProperty - private String groupDataUrl; + @JsonProperty private String groupDataUrl; - @JsonProperty - private String userNameKey; + @JsonProperty private String userNameKey; - @JsonProperty - private Boolean extractUserNameFromEmail; + @JsonProperty private Boolean extractUserNameFromEmail; - @JsonProperty - private String tokenCacheSpec; + @JsonProperty private String tokenCacheSpec; public String getUserDataUrl() { return userDataUrl; @@ -83,8 +91,72 @@ public void setGroupDataUrl(String groupDataUrl) { this.groupDataUrl = groupDataUrl; } + @SuppressWarnings({"rawtypes", "unchecked"}) @Override public ContainerRequestFilter create(TeletraanServiceContext context) throws Exception { - return new TokenAuthFilter(userDataUrl, groupDataUrl, userNameKey, extractUserNameFromEmail, tokenCacheSpec, context); + List filters = createAuthFilters(context); + + return new ChainedAuthFilter(filters); + } + + @SuppressWarnings("rawtypes") + List createAuthFilters(TeletraanServiceContext context) throws Exception { + return Arrays.asList(createScriptTokenAuthFilter(context), createOauthTokenAuthFilter(context), createJwtTokenAuthFilter(context)); + } + + @SuppressWarnings({ "unchecked" }) + private AuthFilter> createScriptTokenAuthFilter(TeletraanServiceContext context) throws Exception { + Authenticator> scriptTokenAuthenticator = + new ScriptTokenAuthenticator<>(new TeletraanScriptTokenProvider(context)); + if (StringUtils.isNotBlank(getTokenCacheSpec())) { + scriptTokenAuthenticator = + new CachingAuthenticator<>( + SharedMetricRegistries.getDefault(), scriptTokenAuthenticator, Caffeine.from(getTokenCacheSpec())); + } + return new OAuthCredentialAuthFilter.Builder>() + .setAuthenticator(scriptTokenAuthenticator) + .setAuthorizer( + (Authorizer>) + context.getAuthorizationFactory() + .create(context, ServicePrincipal.class)) + .setPrefix("token") + .buildAuthFilter(); + } + + // TODO: CDP-7837 remove this after all the clients are updated to use the new token scheme + @SuppressWarnings({ "unchecked" }) + private AuthFilter createOauthTokenAuthFilter(TeletraanServiceContext context) throws Exception { + Authenticator oauthAuthenticator = + new OAuthAuthenticator(getUserDataUrl(), getGroupDataUrl()); + if (StringUtils.isNotBlank(getTokenCacheSpec())) { + oauthAuthenticator = + new CachingAuthenticator<>( + SharedMetricRegistries.getDefault(), oauthAuthenticator, Caffeine.from(getTokenCacheSpec())); + } + return new OAuthCredentialAuthFilter.Builder() + .setAuthenticator(oauthAuthenticator) + .setAuthorizer( + (Authorizer) + context.getAuthorizationFactory() + .create(context, UserPrincipal.class)) + .setPrefix("token") + .buildAuthFilter(); + } + + @SuppressWarnings({ "unchecked" }) + private AuthFilter createJwtTokenAuthFilter(TeletraanServiceContext context) throws Exception { + Authenticator oauthJwtAuthenticator = new OAuthAuthenticator(getUserDataUrl(), + getGroupDataUrl()); + if (StringUtils.isNotBlank(getTokenCacheSpec())) { + oauthJwtAuthenticator = new CachingAuthenticator<>( + SharedMetricRegistries.getDefault(), oauthJwtAuthenticator, Caffeine.from(getTokenCacheSpec())); + } + return new OAuthCredentialAuthFilter.Builder() + .setAuthenticator(oauthJwtAuthenticator) + .setAuthorizer( + (Authorizer) context.getAuthorizationFactory() + .create(context, UserPrincipal.class)) + .setPrefix("Bearer") + .buildAuthFilter(); } } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/TokenAuthorizationFactory.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/TokenAuthorizationFactory.java deleted file mode 100644 index edd48e4251..0000000000 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/config/TokenAuthorizationFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.teletraan.config; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.RoleAuthorizer; - -@JsonTypeName("role") -public class TokenAuthorizationFactory implements AuthorizationFactory { - @JsonProperty - private String roleCacheSpec; - - public String getRoleCacheSpec() { - return roleCacheSpec; - } - - public void setRoleCacheSpec(String roleCacheSpec) { - this.roleCacheSpec = roleCacheSpec; - } - - @Override - public RoleAuthorizer create(TeletraanServiceContext context) throws Exception { - return new RoleAuthorizer(context, roleCacheSpec); - } -} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Agents.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Agents.java index 23b32606af..f27e7df904 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Agents.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Agents.java @@ -24,6 +24,7 @@ import io.swagger.annotations.*; +import javax.annotation.security.PermitAll; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.Context; @@ -33,6 +34,7 @@ import java.util.Collection; import java.util.List; +@PermitAll @Path("/v1/agents") @Api(tags = "Agents") @SwaggerDefinition( diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Builds.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Builds.java index 854512b745..b9b214dcde 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Builds.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Builds.java @@ -28,18 +28,23 @@ import com.pinterest.teletraan.TeletraanServiceContext; import com.pinterest.deployservice.events.BuildEventPublisher; import com.pinterest.deployservice.exception.TeletaanInternalException; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.*; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.*; import java.net.URI; import java.util.List; +@PermitAll @Path("/v1/builds") @Api(tags = "Builds") @SwaggerDefinition( @@ -57,14 +62,12 @@ public class Builds { private final TagDAO tagDAO; private final Allowlist buildAllowlist; private final SourceControlManagerProxy sourceControlManagerProxy; - private final Authorizer authorizer; private final BuildEventPublisher buildEventPublisher; public Builds(@Context TeletraanServiceContext context) { buildDAO = context.getBuildDAO(); tagDAO = context.getTagDAO(); sourceControlManagerProxy = context.getSourceControlManagerProxy(); - authorizer = context.getAuthorizer(); buildAllowlist = context.getBuildAllowlist(); buildEventPublisher = context.getBuildEventPublisher(); deployDAO = context.getDeployDAO(); @@ -177,6 +180,8 @@ public List getBuildsWithTags(@QueryParam("commit") String scmComm value = "Publish a build", notes = "Publish a build given a build object", response = Response.class) + @RolesAllowed(TeletraanPrincipalRole.Names.PUBLISHER) + @ResourceAuthZInfo(type = AuthZResource.Type.BUILD) public Response publish( @Context SecurityContext sc, @Context UriInfo uriInfo, @@ -209,13 +214,13 @@ public Response publish( buildBean.setPublisher(sc.getUserPrincipal().getName()); // Check if build is approved via our allow list of URLs - if (!buildAllowlist.approved(buildBean.getArtifact_url())) { + if (!Boolean.TRUE.equals(buildAllowlist.approved(buildBean.getArtifact_url()))) { throw new TeletaanInternalException(Response.Status.BAD_REQUEST, "Artifact URL points to unapproved location."); } // Check if build SCM is approved via allow list of SCMs - if (!sourceControlManagerProxy.hasSCMType(buildBean.getScm())) { + if (!Boolean.TRUE.equals(sourceControlManagerProxy.hasSCMType(buildBean.getScm()))) { throw new TeletaanInternalException(Response.Status.BAD_REQUEST, String.format("Unsupported SCM type. %s not in list %s.", buildBean.getScm(), sourceControlManagerProxy.getSCMs())); } @@ -245,10 +250,11 @@ public Response publish( @ApiOperation( value = "Delete a build", notes = "Deletes a build given a build id") + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo(type = AuthZResource.Type.BUILD) public void delete( @Context SecurityContext sc, @ApiParam(value = "BUILD id", required = true)@PathParam("id") String id) throws Exception { - authorizer.authorize(sc, new Resource(Resource.ALL, Resource.Type.SYSTEM), Role.OPERATOR); BuildBean buildBean = buildDAO.getById(id); if (buildBean == null) { throw new TeletaanInternalException(Response.Status.NOT_FOUND, String.format("BUILD %s does not exist.", id)); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Commits.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Commits.java index ec3070e71d..8c3733085f 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Commits.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Commits.java @@ -24,11 +24,13 @@ import com.pinterest.teletraan.TeletraanServiceContext; import io.swagger.annotations.*; +import javax.annotation.security.PermitAll; import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import java.util.List; +@PermitAll @Path("/v1/commits") @Api(tags="Commits") @SwaggerDefinition( @@ -76,9 +78,9 @@ public CommitBean getCommit( public List getCommits( @QueryParam("scm") Optional scm, @QueryParam("repo") String repo, - @QueryParam("startSha") String startSha, + @QueryParam("startSha") String startSha, @QueryParam("endSha") String endSha, - @QueryParam("size") Optional size, + @QueryParam("size") Optional size, @QueryParam("path") Optional path) throws Exception { return sourceControlManagerProxy.getCommits(scm.or(""), repo, startSha, endSha, size.or(DEFAULT_SIZE), path.or(DEFAULT_PATH)); } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/DeployCandidates.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/DeployCandidates.java index eb6a97debb..33724918a7 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/DeployCandidates.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/DeployCandidates.java @@ -4,12 +4,12 @@ import com.pinterest.deployservice.bean.PingRequestBean; import com.pinterest.deployservice.bean.PingResponseBean; import com.pinterest.deployservice.bean.PingResult; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.handler.GoalAnalyst; import com.pinterest.deployservice.handler.PingHandler; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -17,6 +17,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.POST; @@ -27,6 +29,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; +@PermitAll @Path("/v1/system") @Api(tags = "Hosts and Systems") @Produces(MediaType.APPLICATION_JSON) @@ -34,11 +37,9 @@ public class DeployCandidates { private static final Logger LOG = LoggerFactory.getLogger(DeployCandidates.class); private PingHandler pingHandler; - private final Authorizer authorizer; public DeployCandidates(@Context TeletraanServiceContext context) { pingHandler = new PingHandler(context); - authorizer = context.getAuthorizer(); } @POST @@ -47,11 +48,12 @@ public DeployCandidates(@Context TeletraanServiceContext context) { value = "Get a set of deploy candidates to deploy", notes = "Returns a list of build bean", response = DeployCandidatesResponse.class) + @RolesAllowed(TeletraanPrincipalRole.Names.PINGER) + @ResourceAuthZInfo(type = AuthZResource.Type.GROUP) public DeployCandidatesResponse getDeployCandidates(@Context SecurityContext sc, @Context HttpHeaders headers, @ApiParam(value = "Ping request object", required = true)@Valid PingRequestBean requestBean) throws Exception { LOG.info("Receive ping request " + requestBean); - authorizer.authorize(sc, new Resource(Resource.ALL, Resource.Type.SYSTEM), Role.PINGER); boolean rate_limited = Boolean.parseBoolean(headers.getRequestHeaders().getFirst("x-envoy-low-watermark")); PingResult result = pingHandler.ping(requestBean, rate_limited); DeployCandidatesResponse resp = new DeployCandidatesResponse(); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/DeployConstraints.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/DeployConstraints.java index e2ad2bc50a..994fd56cd2 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/DeployConstraints.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/DeployConstraints.java @@ -6,13 +6,16 @@ import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.deployservice.db.DatabaseUtil; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; import com.pinterest.teletraan.worker.DeployTagWorker; import io.swagger.annotations.*; import org.apache.commons.dbcp.BasicDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.Context; @@ -22,6 +25,7 @@ import java.util.List; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/deploy_constraint") @Api(tags = "Deploy Constraints") @SwaggerDefinition( @@ -36,7 +40,6 @@ public class DeployConstraints { private DeployConstraintDAO deployConstraintDAO; private EnvironDAO environDAO; - private Authorizer authorizer; private BasicDataSource dataSource; private TeletraanServiceContext serviceContext; @@ -44,7 +47,6 @@ public DeployConstraints(@Context TeletraanServiceContext context) { serviceContext = context; deployConstraintDAO = context.getDeployConstraintDAO(); environDAO = context.getEnvironDAO(); - authorizer = context.getAuthorizer(); dataSource = context.getDataSource(); } @@ -57,7 +59,6 @@ public DeployConstraintBean get(@PathParam("envName") String envName, @PathParam("stageName") String stageName, @Context SecurityContext sc) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String deployConstraintId = envBean.getDeploy_constraint_id(); if (deployConstraintId == null) { LOG.warn("Environment {} does not have deploy constraint set up.", envBean); @@ -67,13 +68,14 @@ public DeployConstraintBean get(@PathParam("envName") String envName, } @POST + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) public void update(@PathParam("envName") String envName, @PathParam("stageName") String stageName, @ApiParam(value = "Deploy Constraint Object to update in database", required = true) @Valid DeployConstraintBean deployConstraintBean, @Context SecurityContext sc) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String operator = sc.getUserPrincipal().getName(); String constraintId = envBean.getDeploy_constraint_id(); @@ -122,11 +124,12 @@ public void update(@PathParam("envName") String envName, @DELETE + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) public void delete(@PathParam("envName") String envName, @PathParam("stageName") String stageName, @Context SecurityContext sc) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String operator = sc.getUserPrincipal().getName(); String constraintId = envBean.getDeploy_constraint_id(); if (constraintId == null) { diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Deploys.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Deploys.java index a907e45393..d738aa6eab 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Deploys.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Deploys.java @@ -26,22 +26,23 @@ import com.pinterest.deployservice.bean.DeployQueryResultBean; import com.pinterest.deployservice.bean.DeployState; import com.pinterest.deployservice.bean.DeployType; -import com.pinterest.deployservice.bean.EnvironBean; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.dao.DeployDAO; -import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.deployservice.db.DeployQueryFilter; import com.pinterest.deployservice.handler.DeployHandler; import com.pinterest.teletraan.TeletraanServiceContext; import com.pinterest.deployservice.exception.TeletaanInternalException; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo.Location; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; @@ -61,6 +62,7 @@ import io.swagger.annotations.SwaggerDefinition; import io.swagger.annotations.Tag; +@PermitAll @Path("/v1/deploys") @Api(tags = "Deploys") @SwaggerDefinition( @@ -72,17 +74,13 @@ @Consumes(MediaType.APPLICATION_JSON) public class Deploys { private static final Logger LOG = LoggerFactory.getLogger(Deploys.class); - private final static int DEFAULT_SIZE = 30; - private EnvironDAO environDAO; + private static final int DEFAULT_SIZE = 30; private DeployDAO deployDAO; private DeployHandler deployHandler; - private final Authorizer authorizer; public Deploys(@Context TeletraanServiceContext context) { - environDAO = context.getEnvironDAO(); deployDAO = context.getDeployDAO(); deployHandler = new DeployHandler(context); - authorizer = context.getAuthorizer(); } @GET @@ -147,14 +145,13 @@ public DeployQueryResultBean search( value = "Update deploy", notes = "Update deploy given a deploy id and a deploy object. Current only " + "acceptanceStatus and description are allowed to change.") + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.BODY, beanClass = DeployBean.class) public void update( @Context SecurityContext sc, @ApiParam(value = "Deploy id", required = true)@PathParam("id") String id, @ApiParam(value = "Partially populated deploy object", required = true) DeployBean deployBean) throws Exception { - DeployBean originBean = Utils.getDeploy(deployDAO, id); - EnvironBean environBean = Utils.getEnvStage(environDAO, originBean.getEnv_id()); - authorizer.authorize(sc, new Resource(environBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String userName = sc.getUserPrincipal().getName(); deployHandler.update(id, deployBean); LOG.info("{} successfully updated deploy {} with {}", @@ -168,12 +165,11 @@ public void update( @ApiOperation( value = "Delete deploy info", notes = "Delete deploy info given a deploy id") + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.BODY, beanClass = DeployBean.class) public void delete( @Context SecurityContext sc, @ApiParam(value = "Deploy id", required = true)@PathParam("id") String id) throws Exception { - DeployBean deployBean = Utils.getDeploy(deployDAO, id); - EnvironBean environBean = Utils.getEnvStage(environDAO, deployBean.getEnv_id()); - authorizer.authorize(sc, new Resource(environBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String userName = sc.getUserPrincipal().getName(); deployDAO.delete(id); LOG.info("Successfully deleted deploy {} by {}", id, userName); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAgentConfigs.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAgentConfigs.java index 8737117096..2ab1d2ea72 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAgentConfigs.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAgentConfigs.java @@ -4,9 +4,9 @@ * 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. @@ -16,20 +16,23 @@ package com.pinterest.teletraan.resource; import com.pinterest.deployservice.bean.EnvironBean; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.common.Constants; import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.deployservice.handler.ConfigHistoryHandler; import com.pinterest.deployservice.handler.EnvironHandler; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.Context; @@ -37,6 +40,7 @@ import javax.ws.rs.core.SecurityContext; import java.util.Map; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/agent_configs") @Api(value = "/Environments", description = "Environment info APIs") @Produces(MediaType.APPLICATION_JSON) @@ -46,13 +50,11 @@ public class EnvAgentConfigs { private EnvironDAO environDAO; private EnvironHandler environHandler; private ConfigHistoryHandler configHistoryHandler; - private Authorizer authorizer; public EnvAgentConfigs(@Context TeletraanServiceContext context) { environDAO = context.getEnvironDAO(); environHandler = new EnvironHandler(context); configHistoryHandler = new ConfigHistoryHandler(context); - authorizer = context.getAuthorizer(); } @GET @@ -72,13 +74,14 @@ public Map get( value = "Update agent configs", notes = "Updates environment agent configs given an environment name and stage name with a map of " + "name,value agent configs") + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) public void update( @ApiParam(value = "Environment name", required = true)@PathParam("envName") String envName, @ApiParam(value = "Stage name", required = true)@PathParam("stageName") String stageName, @ApiParam(value = "Map of configs to update with", required = true)@Valid Map configs, @Context SecurityContext sc) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String userName = sc.getUserPrincipal().getName(); Utils.trimMapValues(configs); environHandler.updateAdvancedConfigs(envBean, configs, userName); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAgents.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAgents.java index c7ba89a9e1..eea0d46d2b 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAgents.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAgents.java @@ -4,9 +4,9 @@ * 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. @@ -21,9 +21,13 @@ import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.teletraan.TeletraanServiceContext; import com.pinterest.deployservice.exception.TeletaanInternalException; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.*; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.constraints.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,6 +39,7 @@ import javax.ws.rs.core.SecurityContext; import java.util.List; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/agents") @Api(tags = "Agents") @Produces(MediaType.APPLICATION_JSON) @@ -44,7 +49,6 @@ public class EnvAgents { private EnvironDAO environDAO; private AgentDAO agentDAO; private AgentErrorDAO agentErrorDAO; - private Authorizer authorizer; public enum CountActionType { SERVING, @@ -55,7 +59,6 @@ public enum CountActionType { public EnvAgents(@Context TeletraanServiceContext context) { environDAO = context.getEnvironDAO(); - authorizer = context.getAuthorizer(); agentDAO = context.getAgentDAO(); agentErrorDAO = context.getAgentErrorDAO(); } @@ -96,6 +99,8 @@ public AgentErrorBean getAgentError( value = "Update host agent", notes = "Updates host agent specified by given environment name, stage name, and host id with given " + "agent object") + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) public void update( @Context SecurityContext sc, @ApiParam(value = "Environment name", required = true)@PathParam("envName") String envName, @@ -103,7 +108,6 @@ public void update( @ApiParam(value = "Host id", required = true)@PathParam("hostId") String hostId, @ApiParam(value = "Agent object to update with", required = true)AgentBean agentBean) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String operator = sc.getUserPrincipal().getName(); agentDAO.update(hostId, envBean.getEnv_id(), agentBean); LOG.info("Successfully updated agent {} with {} in env {}/{} by {}.", @@ -115,13 +119,14 @@ public void update( @ApiOperation( value = "Reset failed deploys", notes = "Resets failing deploys given an environment name, stage name, and deploy id") + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) public void resetFailedDeploys( @Context SecurityContext sc, @ApiParam(value = "Environment name", required = true)@PathParam("envName") String envName, @ApiParam(value = "Stage name", required = true)@PathParam("stageName") String stageName, @ApiParam(value = "Deploy id", required = true)@PathParam("deployId") String deployId) throws Exception { EnvironBean environBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(environBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String operator = sc.getUserPrincipal().getName(); agentDAO.resetFailedAgents(environBean.getEnv_id(), deployId); LOG.info("Successfully reset failed agents for deploy {} in env {}/{} by {}.", diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAlarms.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAlarms.java index 62727a854a..bb6c25db74 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAlarms.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAlarms.java @@ -4,9 +4,9 @@ * 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. @@ -17,19 +17,22 @@ import com.pinterest.deployservice.bean.AlarmBean; import com.pinterest.deployservice.bean.EnvironBean; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.common.Constants; import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.deployservice.handler.ConfigHistoryHandler; import com.pinterest.deployservice.handler.EnvironHandler; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; + +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -41,6 +44,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/alarms") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @@ -49,13 +53,11 @@ public class EnvAlarms { private EnvironHandler environHandler; private EnvironDAO environDAO; private ConfigHistoryHandler configHistoryHandler; - private Authorizer authorizer; public EnvAlarms(@Context TeletraanServiceContext context) { environHandler = new EnvironHandler(context); configHistoryHandler = new ConfigHistoryHandler(context); environDAO = context.getEnvironDAO(); - authorizer = context.getAuthorizer(); } @GET @@ -66,10 +68,11 @@ public List get(@PathParam("envName") String envName, } @PUT + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) public void update(@PathParam("envName") String envName, @PathParam("stageName") String stageName, - @Valid List alarmBeans, @Context SecurityContext sc) throws Exception { + @Valid List alarmBeans, @Context SecurityContext sc) throws Exception { EnvironBean environBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(environBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String userName = sc.getUserPrincipal().getName(); environHandler.updateAlarms(environBean, alarmBeans, userName); configHistoryHandler.updateConfigHistory(environBean.getEnv_id(), Constants.TYPE_ENV_ALARM, alarmBeans, userName); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAlerts.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAlerts.java index 9d25b2ba16..42229f2fa2 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAlerts.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvAlerts.java @@ -27,14 +27,14 @@ import com.pinterest.deployservice.bean.EnvironBean; import com.pinterest.deployservice.bean.EnvironState; import com.pinterest.deployservice.bean.ExternalAlert; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.deployservice.handler.DeployHandler; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; - +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; import com.google.common.collect.ImmutableMap; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang.StringUtils; @@ -47,6 +47,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; + +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -58,6 +61,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/alerts") @Api("ExternalAlerts") @Produces(MediaType.APPLICATION_JSON) @@ -68,7 +72,6 @@ public class EnvAlerts { private EnvironDAO environDAO; private DeployHandler deployHandler; - private Authorizer authorizer; private ExternalAlertFactory externalAlertFactory; private ServiceContext serviceContext; private Map supportActions; @@ -76,7 +79,6 @@ public class EnvAlerts { public EnvAlerts(@Context TeletraanServiceContext context) { environDAO = context.getEnvironDAO(); - authorizer = context.getAuthorizer(); externalAlertFactory = context.getExternalAlertsFactory(); deployHandler = new DeployHandler(context); supportActions = new ImmutableMap.Builder() @@ -101,6 +103,8 @@ public void setAlertContextBuilder( value = "The alert response", notes = "Return the alert checking result", response = Response.class) + @RolesAllowed(TeletraanPrincipalRole.Names.EXECUTE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) /** * This method is supposed to be triggered by the alerting system from a webhook. It means * the environment has received an alert @@ -114,8 +118,6 @@ public Response alertsTriggered(@PathParam("envName") String envName, @Context SecurityContext sc, String alertBody) throws Exception { EnvironBean environBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer - .authorize(sc, new Resource(environBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String userName = sc.getUserPrincipal().getName(); LOG.info("Get Alert for env {} stage {} actionWindow {} actions {} with alert body {}", envName, stageName, actionWindow, actions, alertBody); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvCapacities.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvCapacities.java index 32bb54efae..0d95244f56 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvCapacities.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvCapacities.java @@ -16,21 +16,23 @@ package com.pinterest.teletraan.resource; import com.pinterest.deployservice.bean.EnvironBean; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.common.Constants; import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.deployservice.dao.GroupDAO; import com.pinterest.deployservice.handler.ConfigHistoryHandler; import com.pinterest.deployservice.handler.EnvironHandler; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; - +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; import com.google.common.base.Optional; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.StringUtils; import javax.validation.constraints.NotEmpty; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.constraints.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,6 +51,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/capacity") @Api(tags = "Environments") @Produces(MediaType.APPLICATION_JSON) @@ -61,14 +64,12 @@ public class EnvCapacities { private ConfigHistoryHandler configHistoryHandler; private EnvironDAO environDAO; private GroupDAO groupDAO; - private Authorizer authorizer; public EnvCapacities(@Context TeletraanServiceContext context) { environHandler = new EnvironHandler(context); configHistoryHandler = new ConfigHistoryHandler(context); environDAO = context.getEnvironDAO(); groupDAO = context.getGroupDAO(); - authorizer = context.getAuthorizer(); } @GET @@ -91,12 +92,13 @@ public List get(@PathParam("envName") String envName, @ApiOperation( value = "Update the capacities for Group and hosts", notes = "Update the capacities for Group and hosts") + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) public void update(@PathParam("envName") String envName, @PathParam("stageName") String stageName, @QueryParam("capacityType") Optional capacityType, @NotNull List names, @Context SecurityContext sc) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String operator = sc.getUserPrincipal().getName(); if (capacityType.or(CapacityType.GROUP) == CapacityType.GROUP) { environHandler.updateGroups(envBean, names, operator); @@ -121,12 +123,13 @@ public void update(@PathParam("envName") String envName, @ApiOperation( value = "Create the capacities for Group and hosts", notes = "Create the capacities for Group and hosts") + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) public void add(@PathParam("envName") String envName, @PathParam("stageName") String stageName, @QueryParam("capacityType") Optional capacityType, @NotEmpty String name, @Context SecurityContext sc) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String operator = sc.getUserPrincipal().getName(); name = name.replaceAll("\"", ""); if (capacityType.or(CapacityType.GROUP) == CapacityType.GROUP) { @@ -142,12 +145,13 @@ public void add(@PathParam("envName") String envName, @ApiOperation( value = "Delete the capacities for Group and hosts", notes = "Delete the capacities for Group and hosts") + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) public void delete(@PathParam("envName") String envName, @PathParam("stageName") String stageName, @QueryParam("capacityType") Optional capacityType, @NotEmpty String name, @Context SecurityContext sc) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String operator = sc.getUserPrincipal().getName(); name = name.replaceAll("\"", ""); if (capacityType.or(CapacityType.GROUP) == CapacityType.GROUP) { diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvDeploys.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvDeploys.java index 16bce6bde2..91e54109e8 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvDeploys.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvDeploys.java @@ -22,19 +22,21 @@ import com.pinterest.deployservice.common.Constants; import com.pinterest.deployservice.dao.DeployDAO; import com.pinterest.deployservice.dao.EnvironDAO; -import com.pinterest.deployservice.dao.BuildDAO; import com.pinterest.deployservice.dao.AgentDAO; import com.pinterest.deployservice.handler.ConfigHistoryHandler; import com.pinterest.deployservice.handler.DeployHandler; import com.pinterest.deployservice.handler.EnvironHandler; import com.pinterest.teletraan.TeletraanServiceContext; import com.pinterest.deployservice.exception.TeletaanInternalException; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import javax.validation.constraints.NotEmpty; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.constraints.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,6 +48,7 @@ import java.util.Collections; import java.util.List; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/deploys") @Api(tags = "Deploys") @Produces(MediaType.APPLICATION_JSON) @@ -61,9 +64,7 @@ public enum HostActions { private static final Logger LOG = LoggerFactory.getLogger(EnvDeploys.class); private EnvironDAO environDAO; - private BuildDAO buildDAO; private DeployDAO deployDAO; - private Authorizer authorizer; private AgentDAO agentDAO; private EnvironHandler environHandler; private DeployHandler deployHandler; @@ -71,9 +72,7 @@ public enum HostActions { public EnvDeploys(@Context TeletraanServiceContext context) throws Exception { environDAO = context.getEnvironDAO(); - buildDAO = context.getBuildDAO(); deployDAO = context.getDeployDAO(); - authorizer = context.getAuthorizer(); agentDAO = context.getAgentDAO(); environHandler = new EnvironHandler(context); deployHandler = new DeployHandler(context); @@ -103,6 +102,8 @@ public DeployBean get( value = "Take deploy action", notes = "Take an action on a deploy such as RESTART or PAUSE", response = Response.class) + @RolesAllowed(TeletraanPrincipalRole.Names.EXECUTE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) public Response action( @Context SecurityContext sc, @Context UriInfo uriInfo, @@ -113,7 +114,6 @@ public Response action( @ApiParam(value = "Upper bound deploy id", required = true)@QueryParam("toDeployId") String toDeployId, @ApiParam(value = "Description", required = true)@QueryParam("description") String description) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String operator = sc.getUserPrincipal().getName(); String newDeployId; switch (actionType) { @@ -153,6 +153,9 @@ public Response action( value = "Take a deploy action", notes = "Take an action on a deploy using host information", response = Response.class) + @RolesAllowed(TeletraanPrincipalRole.Names.EXECUTE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) + // TODO: sidecar owners can perform actions on non-sidecar agents public void update( @Context SecurityContext sc, @ApiParam(value = "Environment name", required = true)@PathParam("envName") String envName, @@ -160,7 +163,6 @@ public void update( @ApiParam(value = "Agent object to update with", required = true)@NotNull @QueryParam("actionType") HostActions actionType, @NotNull List hostIds) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); AgentBean agentBean = new AgentBean(); switch (actionType) { case PAUSED_BY_USER: @@ -193,6 +195,8 @@ public void update( value = "Create a deploy", notes = "Creates a deploy given an environment name, stage name, build id and description", response = Response.class) + @RolesAllowed(TeletraanPrincipalRole.Names.EXECUTE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) public Response create( @Context SecurityContext sc, @Context UriInfo uriInfo, @@ -202,7 +206,6 @@ public Response create( @ApiParam(value = "Description", required = true)@QueryParam("description") String description, @ApiParam(value = "Delivery type", required = false)@QueryParam("deliveryType") String deliveryType) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String operator = sc.getUserPrincipal().getName(); String deployId = deployHandler.deploy(envBean, buildId, description, deliveryType, operator); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvGroupRoles.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvGroupRoles.java index 41515d4dd8..9223de6e59 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvGroupRoles.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvGroupRoles.java @@ -4,9 +4,9 @@ * 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. @@ -16,16 +16,22 @@ package com.pinterest.teletraan.resource; import com.pinterest.deployservice.bean.GroupRolesBean; -import com.pinterest.deployservice.bean.Resource; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.teletraan.TeletraanServiceContext; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.*; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.*; import java.util.List; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/group_roles") @Api(tags = "Group Roles") @SwaggerDefinition( @@ -36,7 +42,7 @@ @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class EnvGroupRoles extends GroupRoles { - private static final Resource.Type RESOURCE_TYPE = Resource.Type.ENV; + private static final AuthZResource.Type RESOURCE_TYPE = AuthZResource.Type.ENV; public EnvGroupRoles(@Context TeletraanServiceContext context) { super(context); @@ -69,10 +75,11 @@ public GroupRolesBean getByNameAndResource(@PathParam("envName") String envName, value = "Update an environment's group role", notes = "Updates a GroupRoles object for given group and environment names with given GroupRoles object.", response = GroupRolesBean.class) - public void update(@Context SecurityContext sc, @PathParam("envName") String envName, - @PathParam("groupName") String groupName, - GroupRolesBean bean) throws Exception { - super.update(sc, bean, groupName, envName, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV, idLocation = ResourceAuthZInfo.Location.PATH) + public void update(@PathParam("envName") String envName, @PathParam("groupName") String groupName, + GroupRolesBean bean) throws Exception { + super.update(bean, groupName, envName, RESOURCE_TYPE); } @POST @@ -80,11 +87,12 @@ public void update(@Context SecurityContext sc, @PathParam("envName") String env value = "Create a group role for an environment", notes = "Creates a new GroupRoles object for a given environment name.", response = Response.class) - public Response create(@Context SecurityContext sc, - @Context UriInfo uriInfo, - @ApiParam(value = "Environment name.", required = true)@PathParam("envName") String envName, - @ApiParam(value = "GroupRolesBean object.", required = true)@Valid GroupRolesBean bean) throws Exception { - return super.create(sc, uriInfo, bean, envName, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV, idLocation = ResourceAuthZInfo.Location.PATH) + public Response create(@Context UriInfo uriInfo, + @ApiParam(value = "Environment name.", required = true) @PathParam("envName") String envName, + @ApiParam(value = "GroupRolesBean object.", required = true)@Valid GroupRolesBean bean) throws Exception { + return super.create(uriInfo, bean, envName, RESOURCE_TYPE); } @DELETE @@ -92,8 +100,10 @@ public Response create(@Context SecurityContext sc, @ApiOperation( value = "Deletes a group role from an environment", notes = "Deletes a GroupRoles object by given group and environment names.") - public void delete(@Context SecurityContext sc, @PathParam("envName") String envName, - @PathParam("groupName") String groupName) throws Exception { - super.delete(sc, groupName, envName, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV, idLocation = ResourceAuthZInfo.Location.PATH) + public void delete(@PathParam("envName") String envName, + @PathParam("groupName") String groupName) throws Exception { + super.delete(groupName, envName, RESOURCE_TYPE); } } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvHistory.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvHistory.java index 674825c8c7..765384645f 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvHistory.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvHistory.java @@ -4,9 +4,9 @@ * 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. @@ -26,6 +26,8 @@ import io.swagger.annotations.ApiOperation; import java.util.List; + +import javax.annotation.security.PermitAll; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.Path; @@ -35,6 +37,7 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/history") @Api(tags = "Environments") @Produces(MediaType.APPLICATION_JSON) diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvHostTags.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvHostTags.java index 00e64f427e..3455b1de49 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvHostTags.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvHostTags.java @@ -7,7 +7,10 @@ import com.pinterest.deployservice.dao.HostTagDAO; import com.pinterest.deployservice.rodimus.RodimusManager; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo.Location; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.SwaggerDefinition; @@ -15,6 +18,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; @@ -22,6 +27,7 @@ import java.util.*; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/host_tags") @Api(tags = "Hosts Tags") @SwaggerDefinition( @@ -37,14 +43,12 @@ public class EnvHostTags { private EnvironDAO environDAO; private HostDAO hostDAO; private final RodimusManager rodimusManager; - private Authorizer authorizer; public EnvHostTags(@Context TeletraanServiceContext context) { hostDAO = context.getHostDAO(); hostTagDAO = context.getHostTagDAO(); environDAO = context.getEnvironDAO(); - authorizer = context.getAuthorizer(); rodimusManager = context.getRodimusManager(); } @@ -54,12 +58,13 @@ public EnvHostTags(@Context TeletraanServiceContext context) { value = "List all the hosts tags", notes = "Returns a list the host tags in an environment", response = HostTagInfo.class) + @RolesAllowed(TeletraanPrincipalRole.Names.EXECUTE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.PATH) public Collection get(@PathParam("envName") String envName, @PathParam("stageName") String stageName, @QueryParam("ec2Tags") Optional ecTags, @Context SecurityContext sc) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); Boolean loadEc2Tags = ecTags.or(false); if (loadEc2Tags) { @@ -77,13 +82,14 @@ public Collection get(@PathParam("envName") String envName, value = "List all the hosts that are tagged with tagName in an environment, and group by tagValue", notes = "Returns a map group by tagValue and hosts tagged with tagName:tagValue in an environment", response = HostTagInfo.class) + @RolesAllowed(TeletraanPrincipalRole.Names.EXECUTE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.PATH) public Map> get(@PathParam("envName") String envName, @PathParam("stageName") String stageName, @PathParam("tagName") String tagName, @QueryParam("ec2Tags") Optional ecTags, @Context SecurityContext sc) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); Boolean loadEc2Tags = ecTags.or(false); if (loadEc2Tags) { @@ -105,13 +111,13 @@ public Map> get(@PathParam("envName") String env @DELETE @Path("/{tagName : [a-zA-Z0-9\\-:_]+}") + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.PATH) public void removeHostTags(@PathParam("envName") String envName, @PathParam("stageName") String stageName, @PathParam("tagName") String tagName, @Context SecurityContext sc) throws Exception { - EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String envId = envBean.getEnv_id(); hostTagDAO.deleteAllByEnvId(envId, tagName); LOG.info("Successfully removed all host tagged by {} in env {}", tagName, envId); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvHosts.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvHosts.java index f9f6463d1f..1a15f6cfb6 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvHosts.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvHosts.java @@ -20,15 +20,16 @@ import com.google.common.base.Optional; import com.pinterest.deployservice.bean.EnvironBean; import com.pinterest.deployservice.bean.HostBean; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.common.Constants; import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.deployservice.dao.HostDAO; import com.pinterest.deployservice.handler.ConfigHistoryHandler; import com.pinterest.deployservice.handler.EnvironHandler; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo.Location; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -37,6 +38,9 @@ import org.slf4j.LoggerFactory; import java.util.Collection; + +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -49,20 +53,19 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/hosts") @Api(tags = "Hosts") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class EnvHosts { private static final Logger LOG = LoggerFactory.getLogger(EnvHosts.class); - private final Authorizer authorizer; private final EnvironDAO environDAO; private final HostDAO hostDAO; private final EnvironHandler environHandler; private final ConfigHistoryHandler configHistoryHandler; public EnvHosts(@Context TeletraanServiceContext context) { - authorizer = context.getAuthorizer(); environDAO = context.getEnvironDAO(); hostDAO = context.getHostDAO(); environHandler = new EnvironHandler(context); @@ -96,6 +99,9 @@ public Collection getHostByHostName( } @DELETE + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.PATH) + // TODO: this allows sidecar owners to stop hosts public void stopServiceOnHost(@Context SecurityContext sc, @PathParam("envName") String envName, @PathParam("stageName") String stageName, @@ -104,7 +110,6 @@ public void stopServiceOnHost(@Context SecurityContext sc, throws Exception { String operator = sc.getUserPrincipal().getName(); EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); environHandler.stopServiceOnHosts(hostIds, replaceHost.or(true)); configHistoryHandler.updateConfigHistory(envBean.getEnv_id(), Constants.TYPE_HOST_ACTION, String.format("STOP %s", hostIds.toString()), operator); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvMetrics.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvMetrics.java index f7cc961ef1..d25c1c8b4c 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvMetrics.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvMetrics.java @@ -4,9 +4,9 @@ * 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. @@ -17,20 +17,24 @@ import com.pinterest.deployservice.bean.EnvironBean; import com.pinterest.deployservice.bean.MetricsConfigBean; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.common.Constants; import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.deployservice.handler.ConfigHistoryHandler; import com.pinterest.deployservice.handler.EnvironHandler; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo.Location; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.Context; @@ -38,6 +42,7 @@ import javax.ws.rs.core.SecurityContext; import java.util.List; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/metrics") @Api(tags = "Environments") @Produces(MediaType.APPLICATION_JSON) @@ -47,13 +52,11 @@ public class EnvMetrics { private EnvironHandler environHandler; private ConfigHistoryHandler configHistoryHandler; private EnvironDAO environDAO; - private Authorizer authorizer; public EnvMetrics(@Context TeletraanServiceContext context) { environDAO = context.getEnvironDAO(); environHandler = new EnvironHandler(context); configHistoryHandler = new ConfigHistoryHandler(context); - authorizer = context.getAuthorizer(); } @GET @@ -74,13 +77,14 @@ public List get( value = "Update environment metrics", notes = "Updates an environment's metrics configs given an environment name, stage name, and list of " + "MetricsConfig objects to update with") + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.PATH) public void update( @ApiParam(value = "Environment name", required = true)@PathParam("envName") String envName, @ApiParam(value = "Stage name", required = true)@PathParam("stageName") String stageName, @ApiParam(value = "List of MetricsConfigBean objects", required = true)@Valid List metrics, @Context SecurityContext sc) throws Exception { EnvironBean environBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(environBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String userName = sc.getUserPrincipal().getName(); environHandler.updateMetrics(environBean, metrics, userName); configHistoryHandler.updateConfigHistory(environBean.getEnv_id(), Constants.TYPE_ENV_METRIC, metrics, userName); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvPromotes.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvPromotes.java index 8887ae8549..baa87fd776 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvPromotes.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvPromotes.java @@ -17,26 +17,31 @@ import com.pinterest.deployservice.bean.EnvironBean; import com.pinterest.deployservice.bean.PromoteBean; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.common.Constants; import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.deployservice.handler.ConfigHistoryHandler; import com.pinterest.deployservice.handler.EnvironHandler; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo.Location; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/promotes") @Api(tags = "Environments") @Produces(MediaType.APPLICATION_JSON) @@ -46,13 +51,11 @@ public class EnvPromotes { private EnvironHandler environHandler; private ConfigHistoryHandler configHistoryHandler; private EnvironDAO environDAO; - private Authorizer authorizer; public EnvPromotes(@Context TeletraanServiceContext context) { environDAO = context.getEnvironDAO(); environHandler = new EnvironHandler(context); configHistoryHandler = new ConfigHistoryHandler(context); - authorizer = context.getAuthorizer(); } @GET @@ -70,13 +73,14 @@ public PromoteBean get(@ApiParam(value = "Environment name", required = true)@Pa @ApiOperation( value = "Update promote info", notes = "Updates promote info given environment and stage names by given promote info object") + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.PATH) public void update(@Context SecurityContext sc, @ApiParam(value = "Environment name", required = true) @PathParam("envName") String envName, @ApiParam(value = "Stage name", required = true) @PathParam("stageName") String stageName, @ApiParam(value = "Promote object to update with", required = true) @Valid PromoteBean promoteBean) throws Exception { EnvironBean environBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(environBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String operator = sc.getUserPrincipal().getName(); environHandler.updateEnvPromote(environBean, promoteBean, operator); configHistoryHandler.updateConfigHistory(environBean.getEnv_id(), Constants.TYPE_ENV_PROMOTE, promoteBean, operator); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvScriptConfigs.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvScriptConfigs.java index 7c48f67f07..58dec1d9e0 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvScriptConfigs.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvScriptConfigs.java @@ -4,9 +4,9 @@ * 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. @@ -16,20 +16,24 @@ package com.pinterest.teletraan.resource; import com.pinterest.deployservice.bean.EnvironBean; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.common.Constants; import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.deployservice.handler.ConfigHistoryHandler; import com.pinterest.deployservice.handler.EnvironHandler; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo.Location; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.Context; @@ -37,6 +41,7 @@ import javax.ws.rs.core.SecurityContext; import java.util.Map; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/script_configs") @Api(tags = "Environments") @Produces(MediaType.APPLICATION_JSON) @@ -46,13 +51,11 @@ public class EnvScriptConfigs { private EnvironDAO environDAO; private EnvironHandler environHandler; private ConfigHistoryHandler configHistoryHandler; - private Authorizer authorizer; public EnvScriptConfigs(@Context TeletraanServiceContext context) { environDAO = context.getEnvironDAO(); environHandler = new EnvironHandler(context); configHistoryHandler = new ConfigHistoryHandler(context); - authorizer = context.getAuthorizer(); } @GET @@ -73,13 +76,14 @@ public Map get( notes = "Updates script configs given environment and stage names with given name:value map of new " + "script configs", response = String.class, responseContainer = "Map") + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.PATH) public void update(@Context SecurityContext sc, @ApiParam(value = "Environment name", required = true)@PathParam("envName") String envName, @ApiParam(value = "Stage name", required = true)@PathParam("stageName") String stageName, @ApiParam(value = "New configs to update with", required = true) @Valid Map configs) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String operator = sc.getUserPrincipal().getName(); environHandler.updateScriptConfigs(envBean, configs, operator); configHistoryHandler.updateConfigHistory(envBean.getEnv_id(), Constants.TYPE_ENV_SCRIPT, configs, operator); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvStages.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvStages.java index 44cbe8ff8e..c0710b01e8 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvStages.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvStages.java @@ -15,39 +15,52 @@ */ package com.pinterest.teletraan.resource; +import java.util.UUID; + +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.SecurityContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.pinterest.deployservice.bean.EnvType; import com.pinterest.deployservice.bean.EnvironBean; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; import com.pinterest.deployservice.bean.TagBean; import com.pinterest.deployservice.bean.TagTargetType; import com.pinterest.deployservice.bean.TagValue; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.common.Constants; import com.pinterest.deployservice.dao.EnvironDAO; +import com.pinterest.deployservice.exception.TeletaanInternalException; import com.pinterest.deployservice.handler.ConfigHistoryHandler; import com.pinterest.deployservice.handler.EnvTagHandler; import com.pinterest.deployservice.handler.EnvironHandler; import com.pinterest.deployservice.handler.TagHandler; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.deployservice.exception.TeletaanInternalException; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo.Location; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.ws.rs.*; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; - -import java.util.UUID; - +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}") @Api(tags = "Environments") @Produces(MediaType.APPLICATION_JSON) @@ -63,14 +76,13 @@ public enum ActionType { private EnvironHandler environHandler; private ConfigHistoryHandler configHistoryHandler; private TagHandler tagHandler; - private Authorizer authorizer; - public EnvStages(@Context TeletraanServiceContext context) throws Exception { + + public EnvStages(@Context TeletraanServiceContext context) { environDAO = context.getEnvironDAO(); environHandler = new EnvironHandler(context); configHistoryHandler = new ConfigHistoryHandler(context); tagHandler = new EnvTagHandler(context); - authorizer = context.getAuthorizer(); } @GET @@ -88,21 +100,23 @@ public EnvironBean get( @ApiOperation( value = "Update an environment", notes = "Update an environment given environment and stage names with a environment object") + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.PATH) public void update(@Context SecurityContext sc, @ApiParam(value = "Environment name", required = true)@PathParam("envName") String envName, @ApiParam(value = "Stage name", required = true)@PathParam("stageName") String stageName, @ApiParam(value = "Desired Environment object with updates", required = true) EnvironBean environBean) throws Exception { - EnvironBean origBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(origBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); - // We must use default null Boolean to know that the user did not change from true->false - if (environBean.getIs_sox() != null && origBean.getIs_sox() != environBean.getIs_sox()) { - authorizer.authorize(sc, new Resource(Resource.ALL, Resource.Type.SYSTEM), Role.ADMIN); - } - // TODO: If is_sox is not provided, set it, this support existing PATCH style usages of the endpoint + final EnvironBean origBean = Utils.getEnvStage(environDAO, envName, stageName); + // treat null as false + boolean originalIsSox = origBean.getIs_sox() != null && origBean.getIs_sox(); + if (environBean.getIs_sox() == null) { - environBean.setIs_sox(origBean.getIs_sox()); + environBean.setIs_sox(originalIsSox); + } else if (!environBean.getIs_sox().equals(originalIsSox)) { + throw new TeletaanInternalException(Response.Status.FORBIDDEN, "Modification of isSox flag is not allowed!"); } + String operator = sc.getUserPrincipal().getName(); try { environBean.validate(); @@ -134,16 +148,36 @@ public void update(@Context SecurityContext sc, envName, stageName, environBean, operator); } + @PUT + @Path("/is-sox/{booleanValue}") + @ApiOperation( + value = "Update an environment/stage's isSox flag" + ) + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.SYSTEM, idLocation = Location.PATH) + public void updateIsSox(@Context SecurityContext sc, + @ApiParam(value = "Environment name", required = true)@PathParam("envName") String envName, + @ApiParam(value = "Stage name", required = true)@PathParam("stageName") String stageName, + @ApiParam(value = "Is sox flag", required = true)@PathParam("booleanValue") boolean isSox) throws Exception { + EnvironBean origBean = Utils.getEnvStage(environDAO, envName, stageName); + origBean.setIs_sox(isSox); + String operator = sc.getUserPrincipal().getName(); + environHandler.updateStage(origBean, operator); + configHistoryHandler.updateConfigHistory(origBean.getEnv_id(), Constants.TYPE_ENV_GENERAL, origBean, operator); + configHistoryHandler.updateChangeFeed(Constants.CONFIG_TYPE_ENV, origBean.getEnv_id(), Constants.TYPE_ENV_GENERAL, operator, origBean.getExternal_id()); + LOG.info("Successfully updated env {}/{} isSox flag to {} by {}.", envName, stageName, isSox, operator); + } + @DELETE @ApiOperation( value = "Delete an environment", notes = "Deletes an environment given a environment and stage names") + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.PATH) public void delete(@Context SecurityContext sc, @ApiParam(value = "Environment name", required = true)@PathParam("envName") String envName, @ApiParam(value = "Stage name", required = true)@PathParam("stageName") String stageName) throws Exception { - EnvironBean origBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(origBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String operator = sc.getUserPrincipal().getName(); environHandler.deleteEnvStage(envName, stageName, operator); LOG.info("Successfully deleted env {}/{} by {}.", envName, stageName, operator); @@ -156,6 +190,8 @@ public void delete(@Context SecurityContext sc, response = EnvironBean.class ) @Path("/external_id") + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = ResourceAuthZInfo.Location.PATH) public EnvironBean setExternalId( @ApiParam(value = "Environment name", required = true)@PathParam("envName") String envName, @ApiParam(value = "Stage name", required = true)@PathParam("stageName") String stageName, @@ -184,13 +220,14 @@ public EnvironBean setExternalId( @POST @Path("/actions") + @RolesAllowed(TeletraanPrincipalRole.Names.EXECUTE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.PATH) public void action(@Context SecurityContext sc, @PathParam("envName") String envName, @PathParam("stageName") String stageName, @NotNull @QueryParam("actionType") ActionType actionType, @NotEmpty @QueryParam("description") String description) throws Exception { EnvironBean envBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(envBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String operator = sc.getUserPrincipal().getName(); TagBean tagBean = new TagBean(); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvTokenRoles.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvTokenRoles.java index 5f02b2bec1..ab725f9af3 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvTokenRoles.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvTokenRoles.java @@ -15,24 +15,30 @@ */ package com.pinterest.teletraan.resource; -import com.pinterest.deployservice.bean.Resource; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.bean.TokenRolesBean; import com.pinterest.teletraan.TeletraanServiceContext; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.*; import java.util.List; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/token_roles") @Api(value = "Script Tokens") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class EnvTokenRoles extends TokenRoles { - private static final Resource.Type RESOURCE_TYPE = Resource.Type.ENV; + private static final AuthZResource.Type RESOURCE_TYPE = AuthZResource.Type.ENV; public EnvTokenRoles(@Context TeletraanServiceContext context) { super(context); @@ -43,9 +49,12 @@ public EnvTokenRoles(@Context TeletraanServiceContext context) { value = "Get environment TokenRoles objects", notes = "Returns all the TokenRoles objects for a given environment.", response = TokenRolesBean.class, responseContainer = "List") - public List getByResource(@Context SecurityContext sc, - @ApiParam(value = "Environment name.", required = true)@PathParam("envName") String envName) throws Exception { - return super.getByResource(sc, envName, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.READ) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV, idLocation = ResourceAuthZInfo.Location.PATH) + public List getByResource( + @ApiParam(value = "Environment name.", required = true) @PathParam("envName") String envName) + throws Exception { + return super.getByResource(envName, RESOURCE_TYPE); } @GET @@ -54,10 +63,12 @@ public List getByResource(@Context SecurityContext sc, value = "Get TokenRoles object by script and environment names", notes = "Returns a TokenRoles object given a script and environment name.", response = TokenRolesBean.class) - public TokenRolesBean getByNameAndResource(@Context SecurityContext sc, - @ApiParam(value = "Environment name.", required = true)@PathParam("envName") String envName, - @ApiParam(value = "Script name.", required = true)@PathParam("scriptName") String scriptName) throws Exception { - return super.getByNameAndResource(sc, scriptName, envName, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.READ) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV, idLocation = ResourceAuthZInfo.Location.PATH) + public TokenRolesBean getByNameAndResource( + @ApiParam(value = "Environment name.", required = true) @PathParam("envName") String envName, + @ApiParam(value = "Script name.", required = true) @PathParam("scriptName") String scriptName) throws Exception { + return super.getByNameAndResource(scriptName, envName, RESOURCE_TYPE); } @PUT @@ -65,10 +76,11 @@ public TokenRolesBean getByNameAndResource(@Context SecurityContext sc, @ApiOperation( value = "Update an envrionment's script token", notes = "Update a specific environment script token given environment and script names.") - public void update(@Context SecurityContext sc, - @ApiParam(value = "Environment name.", required = true)@PathParam("envName") String envName, - @ApiParam(value = "Script name.", required = true)@PathParam("scriptName") String scriptName, TokenRolesBean bean) throws Exception { - super.update(sc, bean, scriptName, envName, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV, idLocation = ResourceAuthZInfo.Location.PATH) + public void update(@ApiParam(value = "Environment name.", required = true) @PathParam("envName") String envName, + @ApiParam(value = "Script name.", required = true)@PathParam("scriptName") String scriptName, TokenRolesBean bean) throws Exception { + super.update(bean, scriptName, envName, RESOURCE_TYPE); } @POST @@ -76,11 +88,12 @@ public void update(@Context SecurityContext sc, value = "Create an environment script token", notes = "Creates an environment script token with given environment name and TokenRoles object.", response = Response.class) - public Response create(@Context SecurityContext sc, - @Context UriInfo uriInfo, - @ApiParam(value = "Environment name.", required = true)@PathParam("envName") String envName, - @ApiParam(value = "TokenRolesBean object.", required = true)@Valid TokenRolesBean bean) throws Exception { - return super.create(sc, uriInfo, bean, envName, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV, idLocation = ResourceAuthZInfo.Location.PATH) + public Response create(@Context UriInfo uriInfo, + @ApiParam(value = "Environment name.", required = true) @PathParam("envName") String envName, + @ApiParam(value = "TokenRolesBean object.", required = true)@Valid TokenRolesBean bean) throws Exception { + return super.create(uriInfo, bean, envName, RESOURCE_TYPE); } @DELETE @@ -88,9 +101,10 @@ public Response create(@Context SecurityContext sc, @ApiOperation( value = "Delete an environment script token", notes = "Deletes a script token by given environment and script name.") - public void delete(@Context SecurityContext sc, - @ApiParam(value = "Environment name.", required = true)@PathParam("envName") String envName, - @ApiParam(value = "Script name.", required = true)@PathParam("scriptName") String scriptName) throws Exception { - super.delete(sc, scriptName, envName, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV, idLocation = ResourceAuthZInfo.Location.PATH) + public void delete(@ApiParam(value = "Environment name.", required = true) @PathParam("envName") String envName, + @ApiParam(value = "Script name.", required = true)@PathParam("scriptName") String scriptName) throws Exception { + super.delete(scriptName, envName, RESOURCE_TYPE); } } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvUserRoles.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvUserRoles.java index c78c1441ec..3053b9d8a9 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvUserRoles.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvUserRoles.java @@ -4,9 +4,9 @@ * 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. @@ -15,18 +15,22 @@ */ package com.pinterest.teletraan.resource; -import com.pinterest.deployservice.bean.Resource; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.bean.UserRolesBean; import com.pinterest.teletraan.TeletraanServiceContext; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.*; import java.util.List; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/user_roles") @Api(tags = "User Roles") @SwaggerDefinition( @@ -37,10 +41,9 @@ @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class EnvUserRoles extends UserRoles { - private static final Resource.Type RESOURCE_TYPE = Resource.Type.ENV; - private static final Logger LOG = LoggerFactory.getLogger(EnvUserRoles.class); + private static final AuthZResource.Type RESOURCE_TYPE = AuthZResource.Type.ENV; - public EnvUserRoles(@Context TeletraanServiceContext context) throws Exception { + public EnvUserRoles(@Context TeletraanServiceContext context) { super(context); } @@ -72,10 +75,11 @@ public UserRolesBean getByNameAndResource( value = "Update a user's environment role", notes = "Updates a UserRoles object for given user and environment names with given UserRoles object.", response = UserRolesBean.class) - public void update(@Context SecurityContext sc, - @ApiParam(value = "Environment name.", required = true)@PathParam("envName") String envName, - @ApiParam(value = "User name.", required = true)@PathParam("userName") String userName, UserRolesBean bean) throws Exception { - super.update(sc, bean, userName, envName, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV, idLocation = ResourceAuthZInfo.Location.PATH) + public void update(@ApiParam(value = "Environment name.", required = true) @PathParam("envName") String envName, + @ApiParam(value = "User name.", required = true)@PathParam("userName") String userName, UserRolesBean bean) throws Exception { + super.update(bean, userName, envName, RESOURCE_TYPE); } @POST @@ -83,11 +87,12 @@ public void update(@Context SecurityContext sc, value = "Create a user for an environment", notes = "Creates a new UserRoles object for a given environment name.", response = Response.class) - public Response create(@Context SecurityContext sc, - @Context UriInfo uriInfo, - @ApiParam(value = "Environment name.", required = true)@PathParam("envName") String envName, - @ApiParam(value = "UserRolesBean object.", required = true)@Valid UserRolesBean bean) throws Exception { - return super.create(sc, uriInfo, bean, envName, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV, idLocation = ResourceAuthZInfo.Location.PATH) + public Response create(@Context UriInfo uriInfo, + @ApiParam(value = "Environment name.", required = true) @PathParam("envName") String envName, + @ApiParam(value = "UserRolesBean object.", required = true)@Valid UserRolesBean bean) throws Exception { + return super.create(uriInfo, bean, envName, RESOURCE_TYPE); } @DELETE @@ -95,9 +100,10 @@ public Response create(@Context SecurityContext sc, @ApiOperation( value = "Deletes a user's roles from an environment", notes = "Deletes a UserRoles object by given user and environment names.") - public void delete(@Context SecurityContext sc, - @ApiParam(value = "Host name.", required = true)@PathParam("envName") String envName, - @PathParam("userName") String userName) throws Exception { - super.delete(sc, userName, envName, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV, idLocation = ResourceAuthZInfo.Location.PATH) + public void delete(@ApiParam(value = "Host name.", required = true) @PathParam("envName") String envName, + @PathParam("userName") String userName) throws Exception { + super.delete(userName, envName, RESOURCE_TYPE); } } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvWebHooks.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvWebHooks.java index 805af8d566..5ef9040785 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvWebHooks.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/EnvWebHooks.java @@ -21,18 +21,24 @@ import com.pinterest.deployservice.handler.ConfigHistoryHandler; import com.pinterest.deployservice.handler.EnvironHandler; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo.Location; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; +@PermitAll @Path("/v1/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/web_hooks") @Api(tags = "Environments") @Produces(MediaType.APPLICATION_JSON) @@ -42,13 +48,11 @@ public class EnvWebHooks { private EnvironDAO environDAO; private EnvironHandler environHandler; private ConfigHistoryHandler configHistoryHandler; - private Authorizer authorizer; public EnvWebHooks(@Context TeletraanServiceContext context) { environDAO = context.getEnvironDAO(); environHandler = new EnvironHandler(context); configHistoryHandler = new ConfigHistoryHandler(context); - authorizer = context.getAuthorizer(); } @GET @@ -68,12 +72,13 @@ public EnvWebHookBean get( @ApiOperation( value = "Update webhooks", notes = "Updates pre/deploy webhooks by given environment and stage names with given webhooks object") + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.PATH) public void update(@Context SecurityContext sc, @ApiParam(value = "Environment name", required = true)@PathParam("envName") String envName, @ApiParam(value = "Stage name", required = true)@PathParam("stageName") String stageName, EnvWebHookBean hookBean) throws Exception { EnvironBean environBean = Utils.getEnvStage(environDAO, envName, stageName); - authorizer.authorize(sc, new Resource(environBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); String userName = sc.getUserPrincipal().getName(); environHandler.updateHooks(environBean, hookBean, userName); configHistoryHandler.updateConfigHistory(environBean.getEnv_id(), Constants.TYPE_ENV_WEBHOOK, hookBean, userName); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Environs.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Environs.java index 6750a83067..fbad4c92e3 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Environs.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Environs.java @@ -17,11 +17,10 @@ import com.google.common.base.Optional; import com.pinterest.deployservice.bean.EnvironBean; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; import com.pinterest.deployservice.bean.TagBean; import com.pinterest.deployservice.bean.TagTargetType; import com.pinterest.deployservice.bean.TagValue; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.bean.UserRolesBean; import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.deployservice.dao.UserRolesDAO; @@ -30,8 +29,11 @@ import com.pinterest.deployservice.handler.TagHandler; import com.pinterest.teletraan.TeletraanServiceContext; import com.pinterest.deployservice.exception.TeletaanInternalException; -import com.pinterest.teletraan.security.Authorizer; -import com.pinterest.teletraan.security.OpenAuthorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo.Location; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import com.pinterest.teletraan.universal.security.bean.UserPrincipal; + import io.swagger.annotations.*; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -40,6 +42,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.*; @@ -48,6 +52,7 @@ import java.util.Collection; import java.util.List; +@PermitAll @Path("/v1/envs") @Api(tags = "Environments") @SwaggerDefinition( @@ -70,14 +75,12 @@ public enum ActionType { private EnvironHandler environHandler; private TagHandler tagHandler; private UserRolesDAO userRolesDAO; - private final Authorizer authorizer; public Environs(@Context TeletraanServiceContext context) throws Exception { environDAO = context.getEnvironDAO(); environHandler = new EnvironHandler(context); tagHandler = new EnvTagHandler(context); userRolesDAO = context.getUserRolesDAO(); - authorizer = context.getAuthorizer(); } @GET @@ -144,28 +147,27 @@ public List getAll( value = "Create environment", notes = "Creates a new environment given an environment object", response = Response.class) + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.BODY, beanClass = EnvironBean.class) public Response create( @Context SecurityContext sc, @Context UriInfo uriInfo, - @ApiParam(value = "Environemnt object to create in database", required = true)@Valid EnvironBean environBean) throws Exception { - String operator = sc.getUserPrincipal().getName(); - String envName = environBean.getEnv_name(); - List environBeans = environDAO.getByName(envName); - if (!CollectionUtils.isEmpty(environBeans)) { - authorizer.authorize(sc, new Resource(envName, Resource.Type.ENV), Role.OPERATOR); - } + @ApiParam(value = "Environment object to create in database", required = true)@Valid EnvironBean environBean) throws Exception { try { environBean.validate(); } catch (Exception e) { - throw new TeletaanInternalException(Response.Status.BAD_REQUEST, e.toString()); + throw new IllegalArgumentException("Environment bean validation failed", e); } + String operator = sc.getUserPrincipal().getName(); + String envName = environBean.getEnv_name(); + List environBeans = environDAO.getByName(envName); String id = environHandler.createEnvStage(environBean, operator); - if (!(authorizer instanceof OpenAuthorizer) && CollectionUtils.isEmpty(environBeans)) { + if (sc.getUserPrincipal() instanceof UserPrincipal && CollectionUtils.isEmpty(environBeans)) { // This is the first stage for this env, let's make operator ADMIN of this env UserRolesBean rolesBean = new UserRolesBean(); rolesBean.setResource_id(environBean.getEnv_name()); - rolesBean.setResource_type(Resource.Type.ENV); - rolesBean.setRole(Role.ADMIN); + rolesBean.setResource_type(AuthZResource.Type.ENV); + rolesBean.setRole(TeletraanPrincipalRole.ADMIN); rolesBean.setUser_name(operator); userRolesDAO.insert(rolesBean); LOG.info("Make {} admin for the new env {}", operator, envName); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/GroupRoles.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/GroupRoles.java index 20f484b3dc..3b9a039751 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/GroupRoles.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/GroupRoles.java @@ -4,9 +4,9 @@ * 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. @@ -16,11 +16,9 @@ package com.pinterest.teletraan.resource; import com.pinterest.deployservice.bean.GroupRolesBean; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; import com.pinterest.deployservice.dao.GroupRolesDAO; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; import javax.ws.rs.core.*; import java.net.URI; @@ -28,35 +26,28 @@ public abstract class GroupRoles { private final GroupRolesDAO groupRolesDAO; - private final Authorizer authorizer; - public GroupRoles(TeletraanServiceContext context) { + protected GroupRoles(TeletraanServiceContext context) { groupRolesDAO = context.getGroupRolesDAO(); - authorizer = context.getAuthorizer(); } public List getByResource(String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { return groupRolesDAO.getByResource(resourceId, resourceType); } public GroupRolesBean getByNameAndResource(String groupName, String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { return groupRolesDAO.getByNameAndResource(groupName, resourceId, resourceType); } - public void update(SecurityContext sc, GroupRolesBean bean, String groupName, - String resourceId, Resource.Type resourceType) throws Exception { - authorizer.authorize(sc, new Resource(resourceId, resourceType), Role.ADMIN); + public void update(GroupRolesBean bean, String groupName, String resourceId, AuthZResource.Type resourceType) + throws Exception { groupRolesDAO.update(bean, groupName, resourceId, resourceType); } - public Response create(SecurityContext sc, - UriInfo uriInfo, - GroupRolesBean bean, - String resourceId, - Resource.Type resourceType) throws Exception { - authorizer.authorize(sc, new Resource(resourceId, resourceType), Role.ADMIN); + public Response create(UriInfo uriInfo, GroupRolesBean bean, String resourceId, AuthZResource.Type resourceType) + throws Exception { bean.setResource_id(resourceId); bean.setResource_type(resourceType); groupRolesDAO.insert(bean); @@ -66,9 +57,7 @@ public Response create(SecurityContext sc, return Response.created(roleUri).entity(newBean).build(); } - public void delete(SecurityContext sc, String groupName, String resourceId, - Resource.Type resourceType) throws Exception { - authorizer.authorize(sc, new Resource(resourceId, resourceType), Role.ADMIN); + public void delete(String groupName, String resourceId, AuthZResource.Type resourceType) throws Exception { groupRolesDAO.delete(groupName, resourceId, resourceType); } } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Groups.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Groups.java index c75d412e02..af581df613 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Groups.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Groups.java @@ -4,9 +4,9 @@ * 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. @@ -23,6 +23,7 @@ import com.pinterest.teletraan.TeletraanServiceContext; import com.pinterest.deployservice.exception.TeletaanInternalException; +import javax.annotation.security.PermitAll; import javax.validation.constraints.NotNull; import javax.ws.rs.*; @@ -34,6 +35,7 @@ import java.util.List; import io.swagger.annotations.*; +@PermitAll @Path("/v1/groups") @Api(tags = "Hosts and Systems") @Produces(MediaType.APPLICATION_JSON) diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Hosts.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Hosts.java index 90e328f645..9d561197ce 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Hosts.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Hosts.java @@ -27,6 +27,7 @@ import io.swagger.annotations.*; +import javax.annotation.security.PermitAll; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.Context; @@ -36,6 +37,7 @@ import java.util.Collection; import java.util.List; +@PermitAll @Path("/v1/hosts") @Api(tags = "Hosts and Systems") @SwaggerDefinition( @@ -45,6 +47,7 @@ ) @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) +// TODO: CDP-7701 Add authorization to hosts endpoints public class Hosts { private static final Logger LOG = LoggerFactory.getLogger(Hosts.class); private HostDAO hostDAO; diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Hotfixs.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Hotfixs.java index 00624bca84..b571c3a37b 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Hotfixs.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Hotfixs.java @@ -4,9 +4,9 @@ * 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. @@ -23,33 +23,37 @@ import com.pinterest.deployservice.dao.HotfixDAO; import com.pinterest.teletraan.TeletraanServiceContext; import com.pinterest.deployservice.exception.TeletaanInternalException; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo.Location; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.*; import java.net.URI; import java.util.List; +@PermitAll @Path("/v1/hotfixs") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class Hotfixs { private static final Logger LOG = LoggerFactory.getLogger(Hotfixs.class); - private final static int DEFAULT_TIME_OUT = 30; // minutes - private final static int DEFAULT_SIZE = 30; + private static final int DEFAULT_TIME_OUT = 30; // minutes + private static final int DEFAULT_SIZE = 30; private DeployDAO deployDAO; private BuildDAO buildDAO; private HotfixDAO hotfixDAO; - private final Authorizer authorizer; public Hotfixs(@Context TeletraanServiceContext context) { deployDAO = context.getDeployDAO(); buildDAO = context.getBuildDAO(); hotfixDAO = context.getHotfixDAO(); - authorizer = context.getAuthorizer(); } private HotfixBean getHotfixBean(String id) throws Exception { @@ -75,9 +79,10 @@ public List getAll(@QueryParam("envName") String envName, @PUT @Path("/{id : [a-zA-Z0-9\\-_]+}") - public void update(@Context SecurityContext sc, @PathParam("id") String id, + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.BODY, beanClass = HotfixBean.class) + public void update(@PathParam("id") String id, HotfixBean hotfixBean) throws Exception { - authorizer.authorize(sc, new Resource(Resource.ALL, Resource.Type.SYSTEM), Role.PUBLISHER); hotfixDAO.update(id, hotfixBean); LOG.info("Successfully updated hotfix {} with {}.", id, hotfixBean); } @@ -92,10 +97,10 @@ private String generateJobName(String repo) { } @POST - public Response create(@Context SecurityContext sc, - @Context UriInfo uriInfo, - @Valid HotfixBean hotfixBean) throws Exception { - authorizer.authorize(sc, new Resource(hotfixBean.getEnv_name(), Resource.Type.ENV), Role.OPERATOR); + @RolesAllowed(TeletraanPrincipalRole.Names.EXECUTE) + @ResourceAuthZInfo(type = AuthZResource.Type.ENV_STAGE, idLocation = Location.BODY, beanClass = HotfixBean.class) + public Response create(@Context SecurityContext sc, @Context UriInfo uriInfo, @Valid HotfixBean hotfixBean) + throws Exception { String hotfixId = CommonUtils.getBase64UUID(); hotfixBean.setId(hotfixId); hotfixBean.setState(HotfixState.INITIAL); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Pings.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Pings.java index 3d746c9c73..b7afbe470e 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Pings.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Pings.java @@ -4,9 +4,9 @@ * 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. @@ -19,17 +19,20 @@ import com.pinterest.deployservice.bean.PingRequestBean; import com.pinterest.deployservice.bean.PingResponseBean; import com.pinterest.deployservice.bean.PingResult; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.handler.PingHandler; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.POST; @@ -40,6 +43,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; +@PermitAll @Path("/v1/system") @Api(tags = "Hosts and Systems") @Produces(MediaType.APPLICATION_JSON) @@ -47,11 +51,9 @@ public class Pings { private static final Logger LOG = LoggerFactory.getLogger(Pings.class); private PingHandler pingHandler; - private final Authorizer authorizer; public Pings(@Context TeletraanServiceContext context) { pingHandler = new PingHandler(context); - authorizer = context.getAuthorizer(); } @POST @@ -60,11 +62,12 @@ public Pings(@Context TeletraanServiceContext context) { value = "Ping operation for agent ", notes = "Returns a deploy goal object given a ping request object", response = PingResponseBean.class) + @RolesAllowed(TeletraanPrincipalRole.Names.PINGER) + @ResourceAuthZInfo(type = AuthZResource.Type.GROUP) public PingResponseBean ping(@Context SecurityContext sc, @Context HttpHeaders headers, @ApiParam(value = "Ping request object", required = true)@Valid PingRequestBean requestBean) throws Exception { LOG.info("Receive ping request " + requestBean); - authorizer.authorize(sc, new Resource(Resource.ALL, Resource.Type.SYSTEM), Role.PINGER); boolean rate_limited = Boolean.parseBoolean(headers.getRequestHeaders().getFirst("x-envoy-low-watermark")); PingResult result= pingHandler.ping(requestBean, rate_limited); LOG.info("Send ping response " + result.getResponseBean()); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Ratings.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Ratings.java index 9e17d9cd7d..86b52b9c92 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Ratings.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Ratings.java @@ -4,9 +4,9 @@ * 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. @@ -20,12 +20,14 @@ import com.pinterest.deployservice.handler.RatingsHandler; import com.pinterest.teletraan.TeletraanServiceContext; +import javax.annotation.security.PermitAll; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.*; import java.net.URI; import java.util.List; +@PermitAll @Path("/v1/ratings") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Schedules.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Schedules.java index 70416fc123..4e285c01bc 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Schedules.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Schedules.java @@ -4,9 +4,9 @@ * 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. @@ -27,14 +27,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.security.PermitAll; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; +@PermitAll @Path("/v1/schedules") - @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class Schedules { @@ -64,7 +65,7 @@ public ScheduleBean getSchedule( } return scheduleBean; } - + @PUT @Path("/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}/schedules") public void updateSchedule( @@ -78,7 +79,7 @@ public void updateSchedule( String cooldownTimes = bean.getCooldown_times(); String hostNumbers = bean.getHost_numbers(); Integer totalSessions = bean.getTotal_sessions(); - if (totalSessions > 0) { // there is a schedule + if (totalSessions > 0) { // there is a schedule ScheduleBean scheduleBean = new ScheduleBean(); scheduleBean.setState_start_time(System.currentTimeMillis()); scheduleBean.setCooldown_times(cooldownTimes); @@ -98,7 +99,7 @@ public void updateSchedule( LOG.info(String.format("Successfully updated one env %s (%s)'s schedule by %s: %s", envName, stageName, operator, scheduleBean.toString())); } } else if (scheduleId != null) { //there are no sessions, so delete the schedule - scheduleDAO.delete(scheduleId); + scheduleDAO.delete(scheduleId); environDAO.deleteSchedule(envName, stageName); LOG.info(String.format("Successfully deleted env %s (%s)'s schedule by %s", envName, stageName, operator)); } @@ -122,17 +123,17 @@ public void overrideSession( Integer currentSession = scheduleBean.getCurrent_session(); Integer totalSessions = scheduleBean.getTotal_sessions(); if (sessionNumber != currentSession) { - LOG.info(String.format("Overriding session %d is now invalid as deploy is already on session %d", sessionNumber, currentSession)); - return; + LOG.info(String.format("Overriding session %d is now invalid as deploy is already on session %d", sessionNumber, currentSession)); + return; } if (sessionNumber == totalSessions) { scheduleBean.setState(ScheduleState.FINAL); - LOG.info(String.format("Overrided session %d and currently working on the final deploy session", sessionNumber)); + LOG.info(String.format("Overrided session %d and currently working on the final deploy session", sessionNumber)); } else { scheduleBean.setCurrent_session(sessionNumber+1); scheduleBean.setState(ScheduleState.RUNNING); - LOG.info(String.format("Overrided session %d and currently working on session %d", sessionNumber, currentSession+1)); - } + LOG.info(String.format("Overrided session %d and currently working on session %d", sessionNumber, currentSession+1)); + } scheduleBean.setState_start_time(System.currentTimeMillis()); scheduleDAO.update(scheduleBean, scheduleId); } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SecureApiListingResource.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SecureApiListingResource.java new file mode 100644 index 0000000000..e9d14a8435 --- /dev/null +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SecureApiListingResource.java @@ -0,0 +1,8 @@ +package com.pinterest.teletraan.resource; + +import javax.annotation.security.PermitAll; + +import io.swagger.jaxrs.listing.ApiListingResource; + +@PermitAll +public class SecureApiListingResource extends ApiListingResource {} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SystemGroupRoles.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SystemGroupRoles.java index c5596b37a2..40c8385725 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SystemGroupRoles.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SystemGroupRoles.java @@ -4,9 +4,9 @@ * 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. @@ -16,23 +16,27 @@ package com.pinterest.teletraan.resource; import com.pinterest.deployservice.bean.GroupRolesBean; -import com.pinterest.deployservice.bean.Resource; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.teletraan.TeletraanServiceContext; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import java.util.List; +@PermitAll @Path("/v1/system/group_roles") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class SystemGroupRoles extends GroupRoles { - private final static Resource.Type RESOURCE_TYPE = Resource.Type.SYSTEM; - private final static String RESOURCE_ID = Resource.ALL; + private static final AuthZResource.Type RESOURCE_TYPE = AuthZResource.Type.SYSTEM; + private static final String RESOURCE_ID = AuthZResource.ALL; public SystemGroupRoles(@Context TeletraanServiceContext context) { super(context); @@ -51,19 +55,24 @@ public GroupRolesBean getByNameAndResource(@PathParam("groupName") String groupN @PUT @Path("/{groupName : [a-zA-Z0-9\\-_]+}") - public void update(@Context SecurityContext sc, @PathParam("groupName") String groupName, - GroupRolesBean bean) throws Exception { - super.update(sc, bean, groupName, RESOURCE_ID, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.SYSTEM) + public void update(@PathParam("groupName") String groupName, GroupRolesBean bean) throws Exception { + super.update(bean, groupName, RESOURCE_ID, RESOURCE_TYPE); } @POST - public void create(@Context SecurityContext sc, @Context UriInfo uriInfo, @Valid GroupRolesBean bean) throws Exception { - super.create(sc, uriInfo, bean, RESOURCE_ID, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.SYSTEM) + public void create(@Context UriInfo uriInfo, @Valid GroupRolesBean bean) throws Exception { + super.create(uriInfo, bean, RESOURCE_ID, RESOURCE_TYPE); } @DELETE @Path("/{groupName : [a-zA-Z0-9\\-_]+}") - public void delete(@Context SecurityContext sc, @PathParam("groupName") String groupName) throws Exception { - super.delete(sc, groupName, RESOURCE_ID, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo(type = AuthZResource.Type.SYSTEM) + public void delete(@PathParam("groupName") String groupName) throws Exception { + super.delete(groupName, RESOURCE_ID, RESOURCE_TYPE); } } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SystemTokenRoles.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SystemTokenRoles.java index f80bf6b703..5b817507bc 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SystemTokenRoles.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SystemTokenRoles.java @@ -4,9 +4,9 @@ * 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. @@ -15,16 +15,22 @@ */ package com.pinterest.teletraan.resource; -import com.pinterest.deployservice.bean.Resource; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.bean.TokenRolesBean; import com.pinterest.teletraan.TeletraanServiceContext; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.*; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.*; import java.util.List; +@PermitAll @Path("/v1/system/token_roles") @Api(tags = "Script Tokens") @SwaggerDefinition( @@ -35,8 +41,8 @@ @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class SystemTokenRoles extends TokenRoles { - private static final Resource.Type RESOURCE_TYPE = Resource.Type.SYSTEM; - private final static String RESOURCE_ID = Resource.ALL; + private static final AuthZResource.Type RESOURCE_TYPE = AuthZResource.Type.SYSTEM; + private static final String RESOURCE_ID = AuthZResource.ALL; public SystemTokenRoles(TeletraanServiceContext context) { super(context); @@ -47,8 +53,10 @@ public SystemTokenRoles(TeletraanServiceContext context) { value = "Get system script tokens", notes = "Returns all system TokenRoles objects", response = TokenRolesBean.class, responseContainer = "List") - public List getByResource(@Context SecurityContext sc) throws Exception { - return super.getByResource(sc, RESOURCE_ID, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.READ) + @ResourceAuthZInfo(type = AuthZResource.Type.SYSTEM) + public List getByResource() throws Exception { + return super.getByResource(RESOURCE_ID, RESOURCE_TYPE); } @GET @@ -57,9 +65,12 @@ public List getByResource(@Context SecurityContext sc) throws Ex value = "Get system TokenRoles object by script name", notes = "Returns a TokenRoles object for given script name", response = TokenRolesBean.class) - public TokenRolesBean getByNameAndResource(@Context SecurityContext sc, - @ApiParam(value = "Script name.", required = true)@PathParam("scriptName") String scriptName) throws Exception { - return super.getByNameAndResource(sc, scriptName, RESOURCE_ID, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.READ) + @ResourceAuthZInfo(type = AuthZResource.Type.SYSTEM) + public TokenRolesBean getByNameAndResource( + @ApiParam(value = "Script name.", required = true) @PathParam("scriptName") String scriptName) + throws Exception { + return super.getByNameAndResource(scriptName, RESOURCE_ID, RESOURCE_TYPE); } @PUT @@ -67,10 +78,11 @@ public TokenRolesBean getByNameAndResource(@Context SecurityContext sc, @ApiOperation( value = "Update a system script token", notes = "Updates a TokenRoles object by given script name and replacement TokenRoles object") - public void update(@Context SecurityContext sc, - @ApiParam(value = "Script name.", required = true)@PathParam("scriptName") String scriptName, - @ApiParam(value = "TokenRolesBean object.", required = true)TokenRolesBean bean) throws Exception { - super.update(sc, bean, scriptName, RESOURCE_ID, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.SYSTEM) + public void update(@ApiParam(value = "Script name.", required = true) @PathParam("scriptName") String scriptName, + @ApiParam(value = "TokenRolesBean object.", required = true) TokenRolesBean bean) throws Exception { + super.update(bean, scriptName, RESOURCE_ID, RESOURCE_TYPE); } @POST @@ -78,10 +90,11 @@ public void update(@Context SecurityContext sc, value = "Create a system script token", notes = "Creates a specified system wide TokenRole and returns a Response object", response = Response.class) - public Response create(@Context SecurityContext sc, - @Context UriInfo uriInfo, - @ApiParam(value = "TokenRolesBean object.", required = true)@Valid TokenRolesBean bean) throws Exception { - return super.create(sc, uriInfo, bean, RESOURCE_ID, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.SYSTEM) + public Response create(@Context UriInfo uriInfo, + @ApiParam(value = "TokenRolesBean object.", required = true)@Valid TokenRolesBean bean) throws Exception { + return super.create(uriInfo, bean, RESOURCE_ID, RESOURCE_TYPE); } @DELETE @@ -89,8 +102,10 @@ public Response create(@Context SecurityContext sc, @ApiOperation( value = "Delete a system wide script token", notes = "Deletes a system wide TokenRoles object by specified script name") - public void delete(@Context SecurityContext sc, - @ApiParam(value = "Script name.", required = true)@PathParam("scriptName") String scriptName) throws Exception { - super.delete(sc, scriptName, RESOURCE_ID, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo(type = AuthZResource.Type.SYSTEM) + public void delete( + @ApiParam(value = "Script name.", required = true)@PathParam("scriptName") String scriptName) throws Exception { + super.delete(scriptName, RESOURCE_ID, RESOURCE_TYPE); } } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SystemUserRoles.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SystemUserRoles.java index 6707dd616e..4f1a3f59fc 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SystemUserRoles.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/SystemUserRoles.java @@ -4,9 +4,9 @@ * 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. @@ -15,25 +15,31 @@ */ package com.pinterest.teletraan.resource; -import com.pinterest.deployservice.bean.Resource; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; import com.pinterest.deployservice.bean.UserRolesBean; import com.pinterest.teletraan.TeletraanServiceContext; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; import javax.validation.Valid; import javax.ws.rs.*; import javax.ws.rs.core.*; import java.util.List; +@PermitAll @Path("/v1/system/user_roles") @Api(tags = "User Roles") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class SystemUserRoles extends UserRoles { - private final static Resource.Type RESOURCE_TYPE = Resource.Type.SYSTEM; - private final static String RESOURCE_ID = Resource.ALL; + private static final AuthZResource.Type RESOURCE_TYPE = AuthZResource.Type.SYSTEM; + private static final String RESOURCE_ID = AuthZResource.ALL; public SystemUserRoles(TeletraanServiceContext context) { super(context); @@ -65,10 +71,11 @@ public UserRolesBean getByNameAndResource(@ApiParam(value = "Name of user", requ value = "Update a system level user's role", notes = "Updates a system level user's role given specified user name and replacement UserRoles object", response = UserRolesBean.class) - public void update(@Context SecurityContext sc, - @ApiParam(value = "Name of user.", required = true)@PathParam("userName") String userName, - @ApiParam(value = "UserRolesBean object", required = true)UserRolesBean bean) throws Exception { - super.update(sc, bean, userName, RESOURCE_ID, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.SYSTEM) + public void update(@ApiParam(value = "Name of user.", required = true) @PathParam("userName") String userName, + @ApiParam(value = "UserRolesBean object", required = true)UserRolesBean bean) throws Exception { + super.update(bean, userName, RESOURCE_ID, RESOURCE_TYPE); } @POST @@ -76,11 +83,11 @@ public void update(@Context SecurityContext sc, value = "Create a new system level user", notes = "Creates a system level user for given UserRoles object", response = Response.class) - public Response create(@Context SecurityContext sc, - @Context UriInfo uriInfo, - @ApiParam(value = "UserRolesBean object.", required = true) - @Valid UserRolesBean bean) throws Exception { - return super.create(sc, uriInfo, bean, RESOURCE_ID, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo(type = AuthZResource.Type.SYSTEM) + public Response create(@Context UriInfo uriInfo, + @ApiParam(value = "UserRolesBean object.", required = true) @Valid UserRolesBean bean) throws Exception { + return super.create(uriInfo, bean, RESOURCE_ID, RESOURCE_TYPE); } @DELETE @@ -88,9 +95,10 @@ public Response create(@Context SecurityContext sc, value = "Delete a system level user", notes = "Deletes a system level user by specified user name") @Path("/{userName : [a-zA-Z0-9\\-_]+}") - public void delete(@Context SecurityContext sc, - @ApiParam(value = "User name", required = true) - @PathParam("userName") String userName) throws Exception { - super.delete(sc, userName, RESOURCE_ID, RESOURCE_TYPE); + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo(type = AuthZResource.Type.SYSTEM) + public void delete( + @ApiParam(value = "User name", required = true) @PathParam("userName") String userName) throws Exception { + super.delete(userName, RESOURCE_ID, RESOURCE_TYPE); } } diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Systems.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Systems.java index 99ffc42fb0..8e99c9e8b9 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Systems.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Systems.java @@ -4,9 +4,9 @@ * 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. @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.List; +import javax.annotation.security.PermitAll; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -46,6 +47,7 @@ import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +@PermitAll @Path("/v1/system") @Api(tags = "Hosts and Systems") @Produces(MediaType.APPLICATION_JSON) diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Tags.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Tags.java index 6f88b92ac5..be5216b51a 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Tags.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Tags.java @@ -38,6 +38,8 @@ import java.net.URI; import java.util.HashMap; import java.util.List; + +import javax.annotation.security.PermitAll; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -53,6 +55,7 @@ import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; +@PermitAll @Api(tags="Tags") @Path("/v1/tags") @Produces(MediaType.APPLICATION_JSON) diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/TokenRoles.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/TokenRoles.java index c5b981ef49..702f63fd3b 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/TokenRoles.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/TokenRoles.java @@ -4,9 +4,9 @@ * 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. @@ -15,13 +15,12 @@ */ package com.pinterest.teletraan.resource; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; import com.pinterest.deployservice.bean.TokenRolesBean; import com.pinterest.deployservice.common.CommonUtils; import com.pinterest.deployservice.dao.TokenRolesDAO; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,40 +30,32 @@ public abstract class TokenRoles { private static final Logger LOG = LoggerFactory.getLogger(TokenRoles.class); - final static public long VALIDATE_TIME = 10 * 365 * 24 * 60 * 60 * 1000L; + public static final long VALIDATE_TIME = 10 * 365 * 24 * 60 * 60 * 1000L; private final TokenRolesDAO tokenRolesDAO; - private final Authorizer authorizer; - public TokenRoles(TeletraanServiceContext context) { + protected TokenRoles(TeletraanServiceContext context) { tokenRolesDAO = context.getTokenRolesDAO(); - authorizer = context.getAuthorizer(); } - public List getByResource(SecurityContext sc, String resourceId, - Resource.Type resourceType) throws Exception { - authorizer.authorize(sc, new Resource(resourceId, resourceType), Role.ADMIN); + public List getByResource(String resourceId, + AuthZResource.Type resourceType) throws Exception { return tokenRolesDAO.getByResource(resourceId, resourceType); } - public TokenRolesBean getByNameAndResource(SecurityContext sc, String scriptName, - String resourceId, Resource.Type resourceType) throws Exception { - authorizer.authorize(sc, new Resource(resourceId, resourceType), Role.ADMIN); + public TokenRolesBean getByNameAndResource(String scriptName, + String resourceId, AuthZResource.Type resourceType) throws Exception { return tokenRolesDAO.getByNameAndResource(scriptName, resourceId, resourceType); } - public void update(SecurityContext sc, TokenRolesBean bean, String scriptName, - String resourceId, Resource.Type resourceType) throws Exception { - authorizer.authorize(sc, new Resource(resourceId, resourceType), Role.ADMIN); + public void update(TokenRolesBean bean, String scriptName, + String resourceId, AuthZResource.Type resourceType) throws Exception { tokenRolesDAO.update(bean, scriptName, resourceId, resourceType); LOG.info("Successfully updated script {} permission for resource {} with {}", scriptName, resourceId, bean); } - public Response create(SecurityContext sc, - UriInfo uriInfo, - TokenRolesBean bean, String resourceId, - Resource.Type resourceType) throws Exception { - authorizer.authorize(sc, new Resource(resourceId, resourceType), Role.ADMIN); + public Response create(UriInfo uriInfo, TokenRolesBean bean, String resourceId, + AuthZResource.Type resourceType) throws Exception { String token = CommonUtils.getBase64UUID(); bean.setToken(token); bean.setResource_id(resourceId); @@ -80,9 +71,8 @@ public Response create(SecurityContext sc, return Response.created(roleUri).entity(newBean).build(); } - public void delete(SecurityContext sc, String scriptName, String resourceId, - Resource.Type resourceType) throws Exception { - authorizer.authorize(sc, new Resource(resourceId, resourceType), Role.ADMIN); + public void delete(String scriptName, String resourceId, + AuthZResource.Type resourceType) throws Exception { tokenRolesDAO.delete(scriptName, resourceId, resourceType); LOG.info("Successfully deleted script {} permission for resource {}", scriptName, resourceId); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/UserRoles.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/UserRoles.java index 527f84391f..7bf00378ef 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/UserRoles.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/UserRoles.java @@ -4,9 +4,9 @@ * 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. @@ -18,9 +18,8 @@ import com.pinterest.deployservice.bean.UserRolesBean; import com.pinterest.deployservice.dao.UserRolesDAO; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,37 +30,30 @@ public abstract class UserRoles { private static final Logger LOG = LoggerFactory.getLogger(UserRoles.class); private final UserRolesDAO userRolesDAO; - private final Authorizer authorizer; - public UserRoles(TeletraanServiceContext context) { + protected UserRoles(TeletraanServiceContext context) { userRolesDAO = context.getUserRolesDAO(); - authorizer = context.getAuthorizer(); } public List getByResource(String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { return userRolesDAO.getByResource(resourceId, resourceType); } public UserRolesBean getByNameAndResource(String userName, String resourceId, - Resource.Type resourceType) throws Exception { + AuthZResource.Type resourceType) throws Exception { return userRolesDAO.getByNameAndResource(userName, resourceId, resourceType); } - public void update(SecurityContext sc, UserRolesBean bean, String userName, - String resourceId, Resource.Type resourceType) throws Exception { - authorizer.authorize(sc, new Resource(resourceId, resourceType), Role.ADMIN); + public void update(UserRolesBean bean, String userName, String resourceId, AuthZResource.Type resourceType) + throws Exception { userRolesDAO.update(bean, userName, resourceId, resourceType); LOG.info("Successfully updated user {} permission for resource {} with {}", userName, resourceId, bean); } - public Response create(SecurityContext sc, - UriInfo uriInfo, - UserRolesBean bean, - String resourceId, - Resource.Type resourceType) throws Exception { - authorizer.authorize(sc, new Resource(resourceId, resourceType), Role.ADMIN); + public Response create(UriInfo uriInfo, UserRolesBean bean, String resourceId, AuthZResource.Type resourceType) + throws Exception { bean.setResource_id(resourceId); bean.setResource_type(resourceType); userRolesDAO.insert(bean); @@ -73,9 +65,7 @@ public Response create(SecurityContext sc, return Response.created(roleUri).entity(newBean).build(); } - public void delete(SecurityContext sc, String userName, String resourceId, - Resource.Type resourceType) throws Exception { - authorizer.authorize(sc, new Resource(resourceId, resourceType), Role.ADMIN); + public void delete(String userName, String resourceId, AuthZResource.Type resourceType) throws Exception { userRolesDAO.delete(userName, resourceId, resourceType); LOG.info("Successfully deleted user {} permission for resource {}", userName, resourceId); diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Utils.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Utils.java index 2911a39f76..0c3833e780 100644 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Utils.java +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/resource/Utils.java @@ -4,9 +4,9 @@ * 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. @@ -15,23 +15,18 @@ */ package com.pinterest.teletraan.resource; +import java.util.Map; + +import javax.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.pinterest.deployservice.bean.DeployBean; import com.pinterest.deployservice.bean.EnvironBean; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; import com.pinterest.deployservice.dao.DeployDAO; import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.deployservice.exception.TeletaanInternalException; -import com.pinterest.teletraan.security.Authorizer; -import org.apache.commons.collections.CollectionUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.util.Arrays; -import java.util.List; -import java.util.Map; public class Utils { private static final Logger LOG = LoggerFactory.getLogger(Utils.class); @@ -55,46 +50,6 @@ public static EnvironBean getEnvStage(EnvironDAO environDAO, String envId) throw return environBean; } - private static void authorizeEnvirons(List environBeans, - SecurityContext sc, Authorizer authorizer, Role role) throws Exception { - for (EnvironBean environBean : environBeans) { - try { - Resource resource = new Resource(environBean.getEnv_name(), Resource.Type.ENV); - authorizer.authorize(sc, resource, role); - return; - } catch (Exception e) { - // Not authorized, for whatever reason - LOG.info("Failed to authorize {} for resource {} and role {}", - sc.getUserPrincipal().getName(), environBean.getEnv_name(), role); - } - } - throw new TeletaanInternalException(Response.Status.FORBIDDEN, "Not authorized!"); - } - - public static void authorizeGroup(EnvironDAO environDAO, String groupName, - SecurityContext sc, Authorizer authorizer, Role role) throws Exception { - List environBeans = environDAO.getEnvsByGroups(Arrays.asList(groupName)); - if (CollectionUtils.isEmpty(environBeans)) { - // For groups not associate with environ yet, just pass it - LOG.warn("Group {} is not managed by Teletraan yet, authorize the action for now", - groupName); - return; - } - authorizeEnvirons(environBeans, sc, authorizer, role); - } - - public static void authorizeHost(EnvironDAO environDAO, String hostName, - SecurityContext sc, Authorizer authorizer, Role role) throws Exception { - List environBeans = environDAO.getEnvsByHost(hostName); - if (CollectionUtils.isEmpty(environBeans)) { - // For groups not associate with environ yet, just pass it - LOG.warn("Host {} is not managed by Teletraan yet, authorize the action for now", - hostName); - return; - } - authorizeEnvirons(environBeans, sc, authorizer, role); - } - public static DeployBean getDeploy(DeployDAO deployDAO, String deployId) throws Exception { DeployBean deployBean = deployDAO.getById(deployId); if (deployBean == null) { diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/AnonymousAuthFilter.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/AnonymousAuthFilter.java deleted file mode 100644 index 697cb9b32b..0000000000 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/AnonymousAuthFilter.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.teletraan.security; - -import javax.annotation.Priority; -import javax.ws.rs.Priorities; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerRequestFilter; -import javax.ws.rs.core.SecurityContext; -import javax.ws.rs.ext.Provider; -import java.io.IOException; -import java.security.Principal; - -@Provider -@Priority(Priorities.AUTHENTICATION) -public class AnonymousAuthFilter implements ContainerRequestFilter { - private static final AnonymousUser user = new AnonymousUser(); - private SecurityContext securityContext; - - public AnonymousAuthFilter() { - securityContext = new SecurityContext() { - - @Override - public Principal getUserPrincipal() { - return AnonymousAuthFilter.user; - } - - @Override - public boolean isUserInRole(String s) { - return true; - } - - @Override - public boolean isSecure() { - return true; - } - - @Override - public String getAuthenticationScheme() { - return "Anonymous"; - } - }; - } - - @Override - public void filter(ContainerRequestContext containerRequestContext) throws IOException { - containerRequestContext.setSecurityContext(securityContext); - } -} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/AnonymousUser.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/AnonymousUser.java deleted file mode 100644 index b334e8ff54..0000000000 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/AnonymousUser.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.teletraan.security; - -import java.security.Principal; -import java.util.Collections; -import java.util.List; - -public class AnonymousUser implements Principal { - private String user; - private List groups; - - public AnonymousUser() { - this.user = "Anonymous"; - this.groups = Collections.emptyList(); - } - - @Override - public String getName() { - return user; - } - - public String getUser() { - return user; - } - - public List getGroups() { - return groups; - } -} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/Authorizer.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/Authorizer.java deleted file mode 100644 index c1f0555712..0000000000 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/Authorizer.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.teletraan.security; - - -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; - -import javax.ws.rs.core.SecurityContext; - -public interface Authorizer { - void authorize(SecurityContext securityContext, Resource resource, Role requiredRole) throws Exception; -} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/EnvPathExtractor.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/EnvPathExtractor.java new file mode 100644 index 0000000000..051d72ecbd --- /dev/null +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/EnvPathExtractor.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.security; + +import com.pinterest.teletraan.universal.security.AuthZResourceExtractor; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import javax.ws.rs.container.ContainerRequestContext; + +public class EnvPathExtractor implements AuthZResourceExtractor { + private static final String ENV_NAME = "envName"; + + @Override + public AuthZResource extractResource(ContainerRequestContext requestContext) + throws ExtractionException { + String envName = requestContext.getUriInfo().getPathParameters().getFirst(ENV_NAME); + if (envName == null) { + throw new ExtractionException("Failed to extract environment name"); + } + return new AuthZResource(envName, AuthZResource.Type.ENV); + } +} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/EnvStageBodyExtractor.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/EnvStageBodyExtractor.java new file mode 100644 index 0000000000..d314a9f330 --- /dev/null +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/EnvStageBodyExtractor.java @@ -0,0 +1,133 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.security; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pinterest.deployservice.ServiceContext; +import com.pinterest.deployservice.bean.AgentBean; +import com.pinterest.deployservice.bean.DeployBean; +import com.pinterest.deployservice.bean.EnvironBean; +import com.pinterest.deployservice.bean.HotfixBean; +import com.pinterest.deployservice.dao.EnvironDAO; +import com.pinterest.teletraan.universal.security.AuthZResourceExtractor; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; + +import java.io.IOException; +import java.io.InputStream; +import javax.ws.rs.container.ContainerRequestContext; + +import org.glassfish.jersey.server.ContainerRequest; + +public class EnvStageBodyExtractor implements AuthZResourceExtractor { + private final EnvironDAO environDAO; + + public EnvStageBodyExtractor(ServiceContext context) { + this.environDAO = context.getEnvironDAO(); + } + + @Override + public AuthZResource extractResource(ContainerRequestContext requestContext, Class beanClass) + throws ExtractionException { + return extractResource(requestContext, beanClass, false); + } + + @Override + public AuthZResource extractResource(ContainerRequestContext requestContext) + throws ExtractionException { + return extractResource(requestContext, Object.class, true); + } + + AuthZResource extractResource( + ContainerRequestContext requestContext, Class beanClass, boolean tryAll) + throws ExtractionException { + ContainerRequest request = (ContainerRequest) requestContext; + request.bufferEntity(); + InputStream inputStream = request.getEntityStream(); + if (EnvironBean.class.equals(beanClass) || tryAll) { + try { + return extractEnvironResource(inputStream); + } catch (IOException e) { + if (!tryAll) { + throw new BeanClassExtractionException(beanClass, e); + } + } + } + + if (DeployBean.class.equals(beanClass) || tryAll) { + try { + return extractDeployResource(inputStream); + } catch (Exception e) { + if (!tryAll) { + throw new BeanClassExtractionException(beanClass, e); + } + } + } + + if (HotfixBean.class.equals(beanClass) || tryAll) { + try { + return extractHotfixResource(inputStream); + } catch (IOException e) { + if (!tryAll) { + throw new BeanClassExtractionException(beanClass, e); + } + } + } + + if (AgentBean.class.equals(beanClass) || tryAll) { + try { + return extractAgentResource(inputStream); + } catch (Exception e) { + if (!tryAll) { + throw new BeanClassExtractionException(beanClass, e); + } + } + } + + if (tryAll) { + throw new ExtractionException( + "Failed to extract environment resource using all supported classes"); + } + throw new UnsupportedOperationException("Failed to extract environment resource"); + } + + private AuthZResource extractEnvironResource(InputStream inputStream) throws IOException { + EnvironBean envBean = new ObjectMapper().readValue(inputStream, EnvironBean.class); + return new AuthZResource(envBean.getEnv_name(), envBean.getStage_name()); + } + + private AuthZResource extractDeployResource(InputStream inputStream) throws Exception { + DeployBean deployBean = new ObjectMapper().readValue(inputStream, DeployBean.class); + EnvironBean envBean = environDAO.getById(deployBean.getEnv_id()); + return new AuthZResource(envBean.getEnv_name(), envBean.getStage_name()); + } + + private AuthZResource extractHotfixResource(InputStream inputStream) throws IOException { + HotfixBean hotfixBean = new ObjectMapper().readValue(inputStream, HotfixBean.class); + return new AuthZResource(hotfixBean.getEnv_name(), ""); + } + + private AuthZResource extractAgentResource(InputStream inputStream) throws Exception { + AgentBean agentBean = new ObjectMapper().readValue(inputStream, AgentBean.class); + EnvironBean envBean = environDAO.getById(agentBean.getEnv_id()); + return new AuthZResource(envBean.getEnv_name(), envBean.getStage_name()); + } + + class BeanClassExtractionException extends ExtractionException { + public BeanClassExtractionException(Class beanClass, Throwable cause) { + super(String.format("failed to extract as %s", beanClass.getName()), cause); + } + } +} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/EnvStagePathExtractor.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/EnvStagePathExtractor.java new file mode 100644 index 0000000000..ef817757b2 --- /dev/null +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/EnvStagePathExtractor.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.security; + +import com.pinterest.teletraan.universal.security.AuthZResourceExtractor; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import javax.ws.rs.container.ContainerRequestContext; + +public class EnvStagePathExtractor implements AuthZResourceExtractor { + private static final String ENV_NAME = "envName"; + private static final String STAGE_NAME = "stageName"; + + @Override + public AuthZResource extractResource(ContainerRequestContext requestContext) + throws ExtractionException { + String envName = requestContext.getUriInfo().getPathParameters().getFirst(ENV_NAME); + String stageName = requestContext.getUriInfo().getPathParameters().getFirst(STAGE_NAME); + if (envName == null) { + throw new ExtractionException("Failed to extract environment resource"); + } + return new AuthZResource(envName, stageName); + } +} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/OpenAuthorizer.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/OpenAuthorizer.java deleted file mode 100644 index 3f63294cf2..0000000000 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/OpenAuthorizer.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.teletraan.security; - -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; - -import javax.ws.rs.core.SecurityContext; - -public class OpenAuthorizer implements Authorizer { - @Override - public void authorize(SecurityContext securityContext, Resource resource, Role requiredRole) throws Exception { - } -} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/RoleAuthorizer.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/RoleAuthorizer.java deleted file mode 100644 index 6419bf1f2b..0000000000 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/RoleAuthorizer.java +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.teletraan.security; - -import com.pinterest.deployservice.ServiceContext; -import com.pinterest.deployservice.bean.*; -import com.pinterest.deployservice.dao.GroupRolesDAO; -import com.pinterest.deployservice.dao.UserRolesDAO; -import com.pinterest.deployservice.exception.TeletaanInternalException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.util.*; - -public class RoleAuthorizer implements Authorizer { - private static final Logger LOG = LoggerFactory.getLogger(RoleAuthorizer.class); - private UserRolesDAO userRolesDAO; - private GroupRolesDAO groupRolesDAO; - - public RoleAuthorizer(ServiceContext context, String roleCacheSpec) throws Exception { - this.userRolesDAO = context.getUserRolesDAO(); - this.groupRolesDAO = context.getGroupRolesDAO(); - } - - public void checkUserPermission(String userName, Resource resource, List groups, Role requiredRole) throws Exception { - // Consider group role(s) - Set groupsSet = new HashSet<>(); - if(groups != null && !groups.isEmpty()) { - // Convert to Set for lookup convenience - groupsSet.addAll(groups); - - List resourceGroupBeans = groupRolesDAO.getByResource(resource.getId(), resource.getType()); - for (GroupRolesBean resourceGroupBean : resourceGroupBeans) { - if (groupsSet.contains(resourceGroupBean.getGroup_name())) { - if(resourceGroupBean.getRole().isAuthorized(requiredRole)) { - return; - } - } - } - } - - // Consider user role(s) - UserRolesBean userBean = userRolesDAO.getByNameAndResource(userName, resource.getId(), resource.getType()); - if(userBean != null) { - if(userBean.getRole().isAuthorized(requiredRole)) { - return; - } - } - - // Check SYSTEM wide group role - if(groups != null && !groups.isEmpty()) { - List systemGroupBeans = groupRolesDAO.getByResource(Resource.ALL, Resource.Type.SYSTEM); - for(GroupRolesBean group : systemGroupBeans) { - if(groupsSet.contains(group.getGroup_name())) { - if (group.getRole().isAuthorized(requiredRole)) { - return; - } - } - } - } - - // Consider SYSTEM wide role - UserRolesBean systemBean = userRolesDAO.getByNameAndResource(userName, Resource.ALL, Resource.Type.SYSTEM); - if (systemBean != null) { - if(systemBean.getRole().isAuthorized(requiredRole)) { - return; - } - } - - // Otherwise not authorized - throw new TeletaanInternalException(Response.Status.FORBIDDEN, "Not authorized!"); - } - - public void checkAPITokenPermission(TokenRolesBean bean, Resource requiredResource, Role requiredRole) throws Exception { - Resource myResource = new Resource(bean.getResource_id(), bean.getResource_type()); - - if ((myResource.equals(requiredResource) && bean.getRole().isAuthorized(requiredRole))) { - // An exact match - return; - } - - if (myResource.getType() == Resource.Type.SYSTEM && bean.getRole().isAuthorized(requiredRole)) { - // More than enough - return; - } - - // Otherwise, no way - throw new TeletaanInternalException(Response.Status.FORBIDDEN, "Not authorized!"); - } - - @Override - public void authorize(SecurityContext securityContext, Resource resource, Role requiredRole) throws Exception { - UserPrincipal principal = (UserPrincipal) securityContext.getUserPrincipal(); - - // Check if script token - if (principal.getTokenRolesBean() != null) { - checkAPITokenPermission(principal.getTokenRolesBean(), resource, requiredRole); - } - // Check user roles if not a script - else { - checkUserPermission(principal.getName(), resource, principal.getGroups(), requiredRole); - } - } -} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/ScriptTokenRoleAuthorizer.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/ScriptTokenRoleAuthorizer.java new file mode 100644 index 0000000000..c32582cb6d --- /dev/null +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/ScriptTokenRoleAuthorizer.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.security; + +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; +import com.pinterest.teletraan.universal.security.AuthZResourceExtractor; +import com.pinterest.teletraan.universal.security.BaseAuthorizer; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import com.pinterest.teletraan.universal.security.bean.ScriptTokenPrincipal; +import com.pinterest.teletraan.universal.security.bean.ValueBasedRole; +import javax.annotation.Nullable; +import javax.ws.rs.container.ContainerRequestContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ScriptTokenRoleAuthorizer + extends BaseAuthorizer> { + private static final Logger LOG = LoggerFactory.getLogger(ScriptTokenRoleAuthorizer.class); + + public ScriptTokenRoleAuthorizer(AuthZResourceExtractor.Factory authZResourceExtractorFactory) { + super(authZResourceExtractorFactory); + } + + @Override + public boolean authorize( + ScriptTokenPrincipal principal, + String role, + AuthZResource requestedResource, + @Nullable ContainerRequestContext context) { + if (!principal + .getRole() + .isEqualOrSuperior(TeletraanPrincipalRole.valueOf(role).getRole())) { + LOG.info("Principal role does not match required role"); + return false; + } + + if (requestedResource.getType().equals(AuthZResource.Type.ENV_STAGE)) { + if (requestedResource.getEnvName().equals(principal.getResource().getName())) { + return true; + } + } else if (requestedResource.getType().equals(AuthZResource.Type.ENV) + && !(TeletraanPrincipalRole.ADMIN.getRole().equals(principal.getRole()) + || principal.getResource().getType().equals(AuthZResource.Type.SYSTEM))) { + return false; + } + + if (requestedResource.equals(principal.getResource()) + || principal.getResource().getType().equals(AuthZResource.Type.SYSTEM)) { + LOG.debug("Authorized"); + return true; + } + + LOG.info("Requested resource does not match principal resource"); + return false; + } +} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/TeletraanAuthZResourceExtractorFactory.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/TeletraanAuthZResourceExtractorFactory.java new file mode 100644 index 0000000000..b8b012a2d1 --- /dev/null +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/TeletraanAuthZResourceExtractorFactory.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.security; + +import com.pinterest.deployservice.ServiceContext; +import com.pinterest.teletraan.universal.security.AuthZResourceExtractor; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; + +public class TeletraanAuthZResourceExtractorFactory implements AuthZResourceExtractor.Factory { + + private static final AuthZResourceExtractor ENV_PATH_EXTRACTOR = new EnvPathExtractor(); + private static final AuthZResourceExtractor ENV_STAGE_PATH_EXTRACTOR = + new EnvStagePathExtractor(); + private final AuthZResourceExtractor ENV_STAGE_BODY_EXTRACTOR; + + public TeletraanAuthZResourceExtractorFactory(ServiceContext serviceContext) { + ENV_STAGE_BODY_EXTRACTOR = new EnvStageBodyExtractor(serviceContext); + } + + @Override + public AuthZResourceExtractor create(ResourceAuthZInfo authZInfo) { + switch (authZInfo.type()) { + case ENV: + switch (authZInfo.idLocation()) { + case PATH: + return ENV_PATH_EXTRACTOR; + default: + throw new UnsupportedResourceIDLocationException(authZInfo); + } + case ENV_STAGE: + switch (authZInfo.idLocation()) { + case PATH: + return ENV_STAGE_PATH_EXTRACTOR; + case BODY: + return ENV_STAGE_BODY_EXTRACTOR; + default: + throw new UnsupportedResourceIDLocationException(authZInfo); + } + default: + throw new IllegalArgumentException( + "Unsupported resource type: " + authZInfo.type()); + } + } + + class UnsupportedResourceIDLocationException extends IllegalArgumentException { + public UnsupportedResourceIDLocationException(ResourceAuthZInfo authZInfo) { + super( + String.format( + "Unsupported resource ID location %s for type %s", + authZInfo.idLocation(), authZInfo.type())); + } + } +} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/TeletraanScriptTokenProvider.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/TeletraanScriptTokenProvider.java new file mode 100644 index 0000000000..351fe71b8d --- /dev/null +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/TeletraanScriptTokenProvider.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.security; + +import com.pinterest.deployservice.ServiceContext; +import com.pinterest.deployservice.bean.TokenRolesBean; +import com.pinterest.teletraan.universal.security.ScriptTokenProvider; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import com.pinterest.teletraan.universal.security.bean.ScriptTokenPrincipal; +import com.pinterest.teletraan.universal.security.bean.ValueBasedRole; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TeletraanScriptTokenProvider implements ScriptTokenProvider { + private static final Logger LOG = LoggerFactory.getLogger(TeletraanScriptTokenProvider.class); + + private ServiceContext context; + + public TeletraanScriptTokenProvider(ServiceContext context) { + this.context = context; + } + + @Override + public Optional> getPrincipal(String token) { + try { + TokenRolesBean tokenRolesBean = context.getTokenRolesDAO().getByToken(token); + + if (tokenRolesBean != null) { + return Optional.of( + new ScriptTokenPrincipal( + tokenRolesBean.getScript_name(), + tokenRolesBean.getRole().getRole(), + new AuthZResource( + tokenRolesBean.getResource_id(), + tokenRolesBean.getResource_type()))); + } + } catch (Exception e) { + LOG.error("failed to get Script token principal", e); + } + return Optional.empty(); + } +} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/TokenAuthFilter.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/TokenAuthFilter.java deleted file mode 100644 index 97c1ce6315..0000000000 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/TokenAuthFilter.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.teletraan.security; - -import com.pinterest.deployservice.common.DeployInternalException; -import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.deployservice.exception.TeletaanInternalException; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; - -import javax.annotation.Priority; -import javax.ws.rs.Priorities; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerRequestFilter; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import javax.ws.rs.ext.Provider; -import javax.ws.rs.core.UriInfo; - -@Provider -@Priority(Priorities.AUTHENTICATION) -public class TokenAuthFilter implements ContainerRequestFilter { - - private static final String AUTHENTICATION_HEADER = "Authorization"; - private static final String TELETRAAN_TOKEN_SCHEME = "token"; - private static final Logger LOG = LoggerFactory.getLogger(TokenAuthFilter.class); - - private UserDataHelper userDataHelper; - - public TokenAuthFilter(String userDataUrl, String groupDataUrl, String userNameKey, Boolean extractUserNameFromEmail, String tokenCacheSpec, TeletraanServiceContext context) throws Exception { - userDataHelper = new UserDataHelper(userDataUrl, groupDataUrl, userNameKey, extractUserNameFromEmail, tokenCacheSpec, context); - } - - @Override - public void filter(ContainerRequestContext context) throws IOException { - if(!context.getMethod().equals("OPTIONS")) { - SecurityContext securityContext; - try { - securityContext = authenticate(context); - if ( securityContext != null ) { - LOG.info(String.format("{\"requestMethod\": \"%s\", \"requestUri\": \"%s\", \"userName\": \"%s\"}", context.getMethod(), context.getUriInfo().getRequestUri(), securityContext.getUserPrincipal().getName())); - } - } catch (Exception e) { - LOG.info("Authentication failed. Reason: " + e.getMessage()); - throw new TeletaanInternalException(Response.Status.UNAUTHORIZED, - "Failed to authenticate user. " + e.getMessage()); - } - context.setSecurityContext(securityContext); - } - } - - private SecurityContext authenticate(ContainerRequestContext context) throws Exception { - String authCredentials = context.getHeaderString(AUTHENTICATION_HEADER); - UriInfo uriInfo = context.getUriInfo(); - if (StringUtils.isEmpty(authCredentials)) { - if (!uriInfo.getAbsolutePath().equals("healthcheck") && - !uriInfo.getAbsolutePath().equals("/healthcheck") && - !uriInfo.getPath().equals("healthcheck") && - !uriInfo.getPath().equals("/healthcheck")) { - throw new DeployInternalException("Can not find HTTP header: Authorization!"); - } - return null; - } - - String[] schemeAndToken = authCredentials.trim().split(" "); - String scheme = schemeAndToken[0].trim(); - String token = schemeAndToken[1].trim(); - - if (scheme.equalsIgnoreCase(TELETRAAN_TOKEN_SCHEME)) { - return userDataHelper.getUserSecurityContext(token); - } - - throw new DeployInternalException("Authorization scheme " + scheme + " is not supported!"); - } -} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserDataHelper.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserDataHelper.java deleted file mode 100644 index 2db5e3c57e..0000000000 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserDataHelper.java +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.teletraan.security; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableMap; -import com.google.gson.*; -import com.pinterest.deployservice.bean.TokenRolesBean; -import com.pinterest.deployservice.common.HTTPClient; -import com.pinterest.teletraan.TeletraanServiceContext; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.*; - -public class UserDataHelper { - private String userDataUrl; - private String groupDataUrl; - private String[] userNameKeys; - private Boolean extractUserNameFromEmail; - private LoadingCache tokenCache; - private TeletraanServiceContext context; - private static final Logger LOG = LoggerFactory.getLogger(UserDataHelper.class); - - public UserDataHelper(String userDataUrl, String groupDataUrl, String userNameKey, Boolean extractUserNameFromEmail, String tokenCacheSpec, TeletraanServiceContext context) { - this.userDataUrl = userDataUrl; - this.groupDataUrl = groupDataUrl; - this.context = context; - this.extractUserNameFromEmail = extractUserNameFromEmail; - if (!StringUtils.isEmpty(tokenCacheSpec)) { - tokenCache = CacheBuilder.from(tokenCacheSpec) - .build(new CacheLoader() { - @Override - public UserSecurityContext load(String token) throws Exception { - return loadOauthUserData(token); - } - }); - } - - // prepare username keys - if (StringUtils.isEmpty(userNameKey)) { - userNameKeys = null; - } else { - userNameKeys = userNameKey.split("\\s+"); - } - } - - public List getUserGroups(String token) throws Exception { - if (StringUtils.isEmpty(groupDataUrl)) { - return Collections.emptyList(); - } - - // Get user groups through auth server with user oauth token - HTTPClient client = new HTTPClient(); - HashMap params = new HashMap<>(); - params.put("access_token", token); - String jsonResponse = client.get(groupDataUrl, null, params, null, 3); - - // Parse response - Gson gson = new Gson(); - JsonParser parser = new JsonParser(); - JsonElement element = parser.parse(jsonResponse); - - if (element.getAsJsonObject().has("groups")) { - JsonArray jsonArray = element.getAsJsonObject().getAsJsonArray("groups"); - String[] groups = gson.fromJson(jsonArray, String[].class); - LOG.info("Retrieved groups " + Arrays.asList(groups).toString() + " from token."); - return Arrays.asList(groups); - } - - return null; - } - - public String getUsername(String token) throws Exception { - HTTPClient httpClient = new HTTPClient(); - Map params = ImmutableMap.of("access_token", token); - String jsonPayload = httpClient.get(userDataUrl, null, params, null, 3); - JsonElement e = new JsonParser().parse(jsonPayload); - String userName; - if (userNameKeys != null && userNameKeys.length > 0) { - JsonObject jsonObject = e.getAsJsonObject(); - int i = 0; - for (; i < userNameKeys.length - 1; i++) { - jsonObject = jsonObject.getAsJsonObject(userNameKeys[i]); - } - userName = jsonObject.get(userNameKeys[i]).getAsString(); - } else { - userName = e.getAsString(); - } - if (extractUserNameFromEmail != null && extractUserNameFromEmail) { - userName = userName.split("@")[0]; - } - LOG.info("Retrieved username " + userName + " from token."); - return userName; - } - - public UserSecurityContext loadOauthUserData(String token) throws Exception { - TokenRolesBean tokenRolesBean = context.getTokenRolesDAO().getByToken(token); - - // Script token - if (tokenRolesBean != null) { - return new UserSecurityContext(tokenRolesBean.getScript_name(), tokenRolesBean, null); - } - - // User token - String username = getUsername(token); - List groups = getUserGroups(token); - return new UserSecurityContext(username, null, groups); - } - - public UserSecurityContext getUserSecurityContext(String token) throws Exception { - if (tokenCache == null) { - return loadOauthUserData(token); - } else { - return tokenCache.get(token); - } - } -} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserPrincipal.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserPrincipal.java deleted file mode 100644 index 1cc140307d..0000000000 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserPrincipal.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.teletraan.security; - -import com.pinterest.deployservice.bean.TokenRolesBean; - -import java.security.Principal; -import java.util.List; - -public class UserPrincipal implements Principal { - private String user; - private TokenRolesBean tokenRolesBean; - private List groups; - - public UserPrincipal(String user, TokenRolesBean tokenRolesBean, List groups) { - this.user = user; - this.tokenRolesBean = tokenRolesBean; - this.groups = groups; - } - - @Override - public String getName() { - return user; - } - - public String getUser() { - return user; - } - - public List getGroups() { - return groups; - } - - public TokenRolesBean getTokenRolesBean() { - return tokenRolesBean; - } -} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserResourceKey.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserResourceKey.java deleted file mode 100644 index 8d8fa871bd..0000000000 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserResourceKey.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.teletraan.security; - -import com.pinterest.deployservice.bean.Resource; - -public class UserResourceKey { - private String userName; - private Resource resource; - - public UserResourceKey(String userName, Resource resource) { - this.userName = userName; - this.resource = resource; - } - - public String getUserName() { - return userName; - } - - public Resource getResource() { - return resource; - } -} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserRoleAuthorizer.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserRoleAuthorizer.java new file mode 100644 index 0000000000..ed4f886aed --- /dev/null +++ b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserRoleAuthorizer.java @@ -0,0 +1,155 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.security; + +import com.pinterest.deployservice.ServiceContext; +import com.pinterest.deployservice.bean.EnvironBean; +import com.pinterest.deployservice.bean.GroupRolesBean; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; +import com.pinterest.deployservice.bean.UserRolesBean; +import com.pinterest.deployservice.dao.EnvironDAO; +import com.pinterest.deployservice.dao.GroupRolesDAO; +import com.pinterest.deployservice.dao.UserRolesDAO; +import com.pinterest.teletraan.universal.security.AuthZResourceExtractor; +import com.pinterest.teletraan.universal.security.BaseAuthorizer; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import com.pinterest.teletraan.universal.security.bean.UserPrincipal; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.Nullable; +import javax.ws.rs.container.ContainerRequestContext; +import org.apache.commons.collections.CollectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Deprecated +public class UserRoleAuthorizer extends BaseAuthorizer { + private static final Logger LOG = LoggerFactory.getLogger(UserRoleAuthorizer.class); + private final UserRolesDAO userRolesDAO; + private final GroupRolesDAO groupRolesDAO; + private final EnvironDAO environDAO; + + public UserRoleAuthorizer( + ServiceContext context, AuthZResourceExtractor.Factory authZResourceExtractorFactory) { + super(authZResourceExtractorFactory); + userRolesDAO = context.getUserRolesDAO(); + groupRolesDAO = context.getGroupRolesDAO(); + environDAO = context.getEnvironDAO(); + } + + @Override + public boolean authorize( + UserPrincipal principal, + String role, + AuthZResource requestedResource, + @Nullable ContainerRequestContext context) { + try { + TeletraanPrincipalRole requiredRole = TeletraanPrincipalRole.valueOf(role); + AuthZResource convertedRequestedResource = requestedResource; + if (AuthZResource.Type.ENV_STAGE.equals(requestedResource.getType())) { + // Convert to ENV for backward compatibility + convertedRequestedResource = + new AuthZResource(requestedResource.getEnvName(), AuthZResource.Type.ENV); + } + + // Consider group role(s) + Set groupsSet = new HashSet<>(); + if (principal.getGroups() != null && !principal.getGroups().isEmpty()) { + // Convert to Set for lookup convenience + groupsSet.addAll(principal.getGroups()); + + List resourceGroupBeans = + groupRolesDAO.getByResource( + convertedRequestedResource.getName(), + convertedRequestedResource.getType()); + for (GroupRolesBean resourceGroupBean : resourceGroupBeans) { + if (groupsSet.contains(resourceGroupBean.getGroup_name()) + && hasPermission( + resourceGroupBean.getRole(), requiredRole, requestedResource)) { + return true; + } + } + } + + // Consider user role(s) + UserRolesBean userBean = + userRolesDAO.getByNameAndResource( + principal.getName(), + convertedRequestedResource.getName(), + convertedRequestedResource.getType()); + if (userBean != null + && hasPermission(userBean.getRole(), requiredRole, requestedResource)) { + return true; + } + + // Check SYSTEM wide group role + if (principal.getGroups() != null && !principal.getGroups().isEmpty()) { + List systemGroupBeans = + groupRolesDAO.getByResource(AuthZResource.ALL, AuthZResource.Type.SYSTEM); + for (GroupRolesBean group : systemGroupBeans) { + if (groupsSet.contains(group.getGroup_name()) + && group.getRole().isEqualOrSuperior(requiredRole)) { + return true; + } + } + } + + // Consider SYSTEM wide role + UserRolesBean systemBean = + userRolesDAO.getByNameAndResource( + principal.getName(), AuthZResource.ALL, AuthZResource.Type.SYSTEM); + if (systemBean != null && systemBean.getRole().isEqualOrSuperior(requiredRole)) { + return true; + } + + // Special case for creating a new environment + if (AuthZResource.Type.ENV_STAGE.equals(requestedResource.getType()) + && requiredRole.equals(TeletraanPrincipalRole.WRITE)) { + List environBeans = + environDAO.getByName(convertedRequestedResource.getEnvName()); + if (CollectionUtils.isEmpty(environBeans)) { + return true; + } + } + + // Special case for the build resource + // Authorize PUBLISHER role for build resource here and let the build resource + // handle + // the rest of the authorization + if (AuthZResource.Type.BUILD.equals(convertedRequestedResource.getType())) { + return TeletraanPrincipalRole.PUBLISHER.equals(requiredRole); + } + + return false; + } catch (Exception ex) { + LOG.error("Authorization failed", ex); + return false; + } + } + + // Special handling for ENV type because they represent environment level + // resources + private boolean hasPermission( + TeletraanPrincipalRole principalRole, + TeletraanPrincipalRole requiredRole, + AuthZResource requestedResource) { + if (AuthZResource.Type.ENV.equals(requestedResource.getType())) { + return TeletraanPrincipalRole.ADMIN.equals(principalRole); + } + return principalRole.isEqualOrSuperior(requiredRole); + } +} diff --git a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserSecurityContext.java b/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserSecurityContext.java deleted file mode 100644 index c449166682..0000000000 --- a/deploy-service/teletraanservice/src/main/java/com/pinterest/teletraan/security/UserSecurityContext.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.teletraan.security; - -import com.pinterest.deployservice.bean.TokenRolesBean; - -import javax.ws.rs.core.SecurityContext; -import java.security.Principal; -import java.util.List; - -public class UserSecurityContext implements SecurityContext { - private UserPrincipal userPrincipal; - - public UserSecurityContext(String user, TokenRolesBean tokenRolesBean, List ldapGroups) { - userPrincipal = new UserPrincipal(user, tokenRolesBean, ldapGroups); - } - - @Override - public Principal getUserPrincipal() { - return userPrincipal; - } - - @Override - public boolean isUserInRole(String requiredRole) { - return false; - } - - @Override - public boolean isSecure() { - return false; - } - - @Override - public String getAuthenticationScheme() { - return null; - } -} diff --git a/deploy-service/teletraanservice/src/main/resources/META-INF/services/com.pinterest.teletraan.config.AuthorizationFactory b/deploy-service/teletraanservice/src/main/resources/META-INF/services/com.pinterest.teletraan.config.AuthorizationFactory index d427e0ddda..7e642207db 100644 --- a/deploy-service/teletraanservice/src/main/resources/META-INF/services/com.pinterest.teletraan.config.AuthorizationFactory +++ b/deploy-service/teletraanservice/src/main/resources/META-INF/services/com.pinterest.teletraan.config.AuthorizationFactory @@ -1,2 +1,3 @@ com.pinterest.teletraan.config.OpenAuthorizationFactory -com.pinterest.teletraan.config.TokenAuthorizationFactory +com.pinterest.teletraan.config.RoleAuthorizationFactory +com.pinterest.teletraan.config.CompositeAuthorizationFactory diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/config/RoleAuthorizationFactoryTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/config/RoleAuthorizationFactoryTest.java new file mode 100644 index 0000000000..8864e9c865 --- /dev/null +++ b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/config/RoleAuthorizationFactoryTest.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +import com.pinterest.teletraan.TeletraanServiceContext; +import com.pinterest.teletraan.security.ScriptTokenRoleAuthorizer; +import com.pinterest.teletraan.security.UserRoleAuthorizer; +import com.pinterest.teletraan.universal.security.bean.ServicePrincipal; +import com.pinterest.teletraan.universal.security.bean.TeletraanPrincipal; +import com.pinterest.teletraan.universal.security.bean.UserPrincipal; + +class RoleAuthorizationFactoryTest { + private static RoleAuthorizationFactory sut = new RoleAuthorizationFactory(); + private static TeletraanServiceContext context = new TeletraanServiceContext(); + + @Test + void testCreate() { + assertThrows(UnsupportedOperationException.class, () -> { + sut.create(context); + }); + } + + @Test + void testCreate_servicePrincipal() throws Exception { + assertEquals(ScriptTokenRoleAuthorizer.class, sut.create(context, ServicePrincipal.class).getClass()); + } + + @Test + void testCreate_userPrincipal() throws Exception { + assertEquals(UserRoleAuthorizer.class, sut.create(context, UserPrincipal.class).getClass()); + } + + @Test + void testCreate_otherPrincipal() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + sut.create(context, TeletraanPrincipal.class); + }); + } +} \ No newline at end of file diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/fixture/EnvironBeanFixture.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/fixture/EnvironBeanFixture.java new file mode 100644 index 0000000000..b213355547 --- /dev/null +++ b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/fixture/EnvironBeanFixture.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.fixture; + +import com.pinterest.deployservice.bean.AcceptanceType; +import com.pinterest.deployservice.bean.EnvironBean; +import com.pinterest.deployservice.bean.EnvironState; +import com.pinterest.deployservice.bean.OverridePolicy; +import java.util.UUID; + +public class EnvironBeanFixture { + public static EnvironBean createRandomEnvironBean() { + EnvironBean environBean = new EnvironBean(); + environBean.setEnv_name(UUID.randomUUID().toString()); + environBean.setStage_name(UUID.randomUUID().toString()); + environBean.setEnv_id(UUID.randomUUID().toString()); + environBean.setDeploy_id(UUID.randomUUID().toString()); + environBean.setState(EnvironState.NORMAL); + environBean.setSuccess_th(10000); + environBean.setDescription(UUID.randomUUID().toString()); + environBean.setAdv_config_id(UUID.randomUUID().toString()); + environBean.setSc_config_id(UUID.randomUUID().toString()); + environBean.setLast_operator(UUID.randomUUID().toString()); + environBean.setLast_update(System.currentTimeMillis()); + environBean.setAccept_type(AcceptanceType.AUTO); + environBean.setNotify_authors(false); + environBean.setWatch_recipients(UUID.randomUUID().toString()); + environBean.setMax_deploy_num(5100); + environBean.setMax_deploy_day(366); + environBean.setIs_docker(false); + environBean.setMax_parallel_pct(0); + environBean.setState(EnvironState.NORMAL); + environBean.setMax_parallel_rp(1); + environBean.setOverride_policy(OverridePolicy.OVERRIDE); + environBean.setAllow_private_build(false); + environBean.setEnsure_trusted_build(false); + return environBean; + } +} diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/handler/EnvStagesTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/handler/EnvStagesTest.java index 15df4eb3aa..47ad0e84aa 100644 --- a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/handler/EnvStagesTest.java +++ b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/handler/EnvStagesTest.java @@ -18,7 +18,6 @@ import com.pinterest.deployservice.dao.EnvironDAO; import com.pinterest.teletraan.TeletraanServiceContext; import com.pinterest.teletraan.resource.EnvStages; -import com.pinterest.teletraan.security.Authorizer; public class EnvStagesTest { private EnvStages envStages; @@ -27,9 +26,7 @@ public class EnvStagesTest { @Before public void setup() throws Exception { environDAO = mock(EnvironDAO.class); - Authorizer auth = mock(Authorizer.class); TeletraanServiceContext tsc = new TeletraanServiceContext(); - tsc.setAuthorizer(auth); tsc.setEnvironDAO(environDAO); envStages = new EnvStages(tsc); } diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/resource/EnvAlertsTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/resource/EnvAlertsTest.java index 02113a7d25..273fb18a86 100644 --- a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/resource/EnvAlertsTest.java +++ b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/resource/EnvAlertsTest.java @@ -17,7 +17,6 @@ import com.pinterest.deployservice.bean.EnvironBean; import com.pinterest.deployservice.bean.EnvironState; import com.pinterest.deployservice.bean.TagBean; -import com.pinterest.deployservice.bean.TokenRolesBean; import com.pinterest.deployservice.buildtags.BuildTagsManager; import com.pinterest.deployservice.common.Constants; import com.pinterest.deployservice.dao.BuildDAO; @@ -27,8 +26,7 @@ import com.pinterest.deployservice.handler.DeployHandlerInterface; import com.pinterest.deployservice.handler.TagHandler; import com.pinterest.teletraan.TeletraanServiceContext; -import com.pinterest.teletraan.security.Authorizer; -import com.pinterest.teletraan.security.UserPrincipal; +import com.pinterest.teletraan.universal.security.bean.UserPrincipal; import org.joda.time.DateTime; import org.junit.Assert; @@ -44,7 +42,6 @@ public class EnvAlertsTest { TeletraanServiceContext context; - Authorizer authorizer; DeployBean recent = new DeployBean(); DeployBean lastKnownGoodDeploy = new DeployBean(); EnvironBean environBean = new EnvironBean(); @@ -67,7 +64,6 @@ public void setUp() throws Exception { environDAO = mock(EnvironDAO.class); deployDAO = mock(DeployDAO.class); tagDAO = mock(TagDAO.class); - authorizer = mock(Authorizer.class); buildTagsManager = mock(BuildTagsManager.class); alertContextBuilder = mock(AlertContextBuilder.class); deployHandler = mock(DeployHandlerInterface.class); @@ -76,7 +72,6 @@ public void setUp() throws Exception { context.setBuildDAO(buildDAO); context.setEnvironDAO(environDAO); context.setDeployDAO(deployDAO); - context.setAuthorizer(authorizer); context.setExternalAlertsFactory(new PinterestExternalAlertFactory()); buildBean.setBuild_id("0000001"); buildBean.setBuild_name("BuildOne"); @@ -99,7 +94,7 @@ public void setUp() throws Exception { when(deployDAO.getById("recentdeploy")).thenReturn(recent); when(deployDAO.getById("lastGoodDeploy")).thenReturn(recent); sc = mock(SecurityContext.class); - when(sc.getUserPrincipal()).thenReturn(new UserPrincipal(Constants.AUTO_PROMOTER_NAME, new TokenRolesBean(),new ArrayList())); + when(sc.getUserPrincipal()).thenReturn(new UserPrincipal(Constants.AUTO_PROMOTER_NAME, new ArrayList())); when(buildDAO.getById(recent.getBuild_id())).thenReturn(buildBean); alertContext = new AlertContext(); alertContext.setTagHandler(mock(TagHandler.class)); diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/resource/EnvStagesTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/resource/EnvStagesTest.java new file mode 100644 index 0000000000..5b2e6c246f --- /dev/null +++ b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/resource/EnvStagesTest.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.resource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Response; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import com.pinterest.deployservice.bean.EnvironBean; +import com.pinterest.deployservice.dao.EnvironDAO; +import com.pinterest.teletraan.TeletraanServiceContext; +import com.pinterest.teletraan.fixture.EnvironBeanFixture; + +import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; +import io.dropwizard.testing.junit5.ResourceExtension; + +@ExtendWith(DropwizardExtensionsSupport.class) +class EnvStagesTest { + private static final String ENV1 = "env1"; + private static final String STAGE1 = "stage1"; + private static final String TARGET = "/v1/envs/"; + private static final ResourceExtension EXT; + private static EnvironDAO environDAO = mock(EnvironDAO.class); + + static { + TeletraanServiceContext context = new TeletraanServiceContext(); + context.setEnvironDAO(environDAO); + EXT = ResourceExtension.builder() + .addResource(new EnvStages(context)) + .build(); + } + + @Test + void testUpdate_cannotChangeIsSox() throws Exception { + EnvironBean originalBean = EnvironBeanFixture.createRandomEnvironBean(); + EnvironBean updatedBean = EnvironBeanFixture.createRandomEnvironBean(); + + Boolean[] originalSox = {null, true, false}; + Boolean[] newSox = {true, false, true}; + for (int i = 0; i < originalSox.length; i++) { + originalBean.setIs_sox(originalSox[i]); + updatedBean.setIs_sox(newSox[i]); + when(environDAO.getByStage(ENV1, STAGE1)).thenReturn(originalBean); + + Response response = EXT.target(TARGET + ENV1 + "/" + STAGE1) + .request() + .put(Entity.json(updatedBean)); + + assertEquals(Response.Status.FORBIDDEN.getStatusCode(), response.getStatus()); + } + } +} diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/resource/ResourceAuthorizationTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/resource/ResourceAuthorizationTest.java new file mode 100644 index 0000000000..a99bcd7006 --- /dev/null +++ b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/resource/ResourceAuthorizationTest.java @@ -0,0 +1,378 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.resource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.codahale.metrics.SharedMetricRegistries; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; +import com.pinterest.deployservice.bean.TokenRolesBean; +import com.pinterest.deployservice.bean.UserRolesBean; +import com.pinterest.deployservice.dao.EnvironDAO; +import com.pinterest.deployservice.dao.GroupRolesDAO; +import com.pinterest.deployservice.dao.TokenRolesDAO; +import com.pinterest.deployservice.dao.UserRolesDAO; +import com.pinterest.teletraan.TeletraanServiceContext; +import com.pinterest.teletraan.config.RoleAuthorizationFactory; +import com.pinterest.teletraan.config.TokenAuthenticationFactory; +import com.pinterest.teletraan.security.TeletraanAuthZResourceExtractorFactory; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfo; +import com.pinterest.teletraan.universal.security.ResourceAuthZInfoFeature; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import io.dropwizard.auth.AuthDynamicFeature; +import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; +import io.dropwizard.testing.junit5.ResourceExtension; +import javax.annotation.security.DenyAll; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import okhttp3.mockwebserver.Dispatcher; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.eclipse.jetty.http.HttpHeader; +import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +@ExtendWith(DropwizardExtensionsSupport.class) +public class ResourceAuthorizationTest { + private static final String SCRIPT_TOKEN = "script_token"; + private static final String JWT_TOKEN = "jwt_token"; + private static final String JWT_ADMIN_TOKEN = "jwt_admin_token"; + private static final String TEST_USER = "testUser"; + private static final String TEST_ADMIN_USER = "testAdminUser"; + private static final String GROUP_PATH = "/group/"; + private static final String USER_PATH = "/user/"; + private static final String ENV_STAGE_SUFFIX = + "/envs/{envName : [a-zA-Z0-9\\-_]+}/{stageName : [a-zA-Z0-9\\-_]+}"; + private static final String TEST_ENV_STAGE_ID = "testEnv/testStage"; + private static final String TEST_ENV_STAGE_PATH = "/envs/" + TEST_ENV_STAGE_ID; + + private static ResourceExtension EXT; + private static MockWebServer mockWebServer; + + private static class Targets { + public static final String root = "/"; + public static final String read = "/read"; + public static final String write = "/write"; + public static final String delete = "/delete"; + public static final String execute = "/execute"; + public static final String none = "/none"; + public static final String all = "/all"; + public static final String admin = "/admin"; + } + + private static String[] protectedResourceTargetsProvider() { + return new String[] { + Targets.read + TEST_ENV_STAGE_PATH, + Targets.write + TEST_ENV_STAGE_PATH, + Targets.delete + TEST_ENV_STAGE_PATH, + Targets.execute + TEST_ENV_STAGE_PATH + }; + } + + static { + SharedMetricRegistries.setDefault("test"); + TeletraanServiceContext context = new TeletraanServiceContext(); + TokenAuthenticationFactory authenticationFactory = new TokenAuthenticationFactory(); + RoleAuthorizationFactory authorizationFactory = new RoleAuthorizationFactory(); + TokenRolesDAO tokenRolesDAO = mock(TokenRolesDAO.class); + UserRolesDAO userRolesDAO = mock(UserRolesDAO.class); + GroupRolesDAO groupRolesDAO = mock(GroupRolesDAO.class); + EnvironDAO environDAO = mock(EnvironDAO.class); + UserRolesBean adminRolesBean = new UserRolesBean(); + TokenRolesBean tokenRolesBean = new TokenRolesBean(); + + mockWebServer = new MockWebServer(); + mockWebServer.setDispatcher(new AuthDispatcher()); + authenticationFactory.setGroupDataUrl(mockWebServer.url(GROUP_PATH).toString()); + authenticationFactory.setUserDataUrl(mockWebServer.url(USER_PATH).toString()); + adminRolesBean.setRole(TeletraanPrincipalRole.ADMIN); + tokenRolesBean.setRole(TeletraanPrincipalRole.OPERATOR); + tokenRolesBean.setResource_id("testEnv"); + tokenRolesBean.setResource_type(AuthZResource.Type.ENV); + + context.setAuthorizationFactory(authorizationFactory); + context.setTokenRolesDAO(tokenRolesDAO); + context.setUserRolesDAO(userRolesDAO); + context.setGroupRolesDAO(groupRolesDAO); + context.setEnvironDAO(environDAO); + context.setAuthZResourceExtractorFactory( + new TeletraanAuthZResourceExtractorFactory(context)); + + try { + when(userRolesDAO.getByNameAndResource( + TEST_ADMIN_USER, AuthZResource.ALL, AuthZResource.Type.SYSTEM)) + .thenReturn(adminRolesBean); + when(tokenRolesDAO.getByToken(SCRIPT_TOKEN)).thenReturn(tokenRolesBean); + EXT = + ResourceExtension.builder() + .addProvider( + new AuthDynamicFeature(authenticationFactory.create(context))) + .addProvider(RolesAllowedDynamicFeature.class) + .addResource(TestResource.class) + .addResource(ResourceAuthZInfoFeature.class) + .build(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @AfterAll + public static void tearDown() throws Exception { + mockWebServer.shutdown(); + } + + @Test + void noAuthHeader_unprotected_200() { + Response response = + EXT.target(Targets.root).request().accept(MediaType.APPLICATION_JSON_TYPE).get(); + assertEquals(Status.OK.getStatusCode(), response.getStatus()); + } + + @Test + void noAuthHeader_permitAll_401() { + Response response = + EXT.target(Targets.all).request().accept(MediaType.APPLICATION_JSON_TYPE).get(); + assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus()); + } + + @Test + void unsupportedTokenType_401() { + Response response = + EXT.target(Targets.all) + .request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .header(HttpHeader.AUTHORIZATION.asString(), "Basic TOKEN") + .get(); + assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus()); + } + + // OAuth legacy token tests + @Test + void invalidToken_401() { + Response response = + EXT.target(Targets.all) + .request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .header(HttpHeader.AUTHORIZATION.asString(), "token TOKEN") + .get(); + assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus()); + } + + @Test + void validToken_200() { + Response response = + EXT.target(Targets.root) + .request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .header(HttpHeader.AUTHORIZATION.asString(), "token " + JWT_TOKEN) + .get(); + assertEquals(Status.OK.getStatusCode(), response.getStatus()); + } + + @Test + void validToken_permitAll_200() { + Response response = + EXT.target(Targets.all) + .request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .header(HttpHeader.AUTHORIZATION.asString(), "token " + JWT_TOKEN) + .get(); + assertEquals(Status.OK.getStatusCode(), response.getStatus()); + } + + @ParameterizedTest + @MethodSource("protectedResourceTargetsProvider") + void validToken_protectedResource_403(String target) { + Response response = + EXT.target(target) + .request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .header(HttpHeader.AUTHORIZATION.asString(), "token " + JWT_TOKEN) + .get(); + assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus()); + } + + @ParameterizedTest + @MethodSource("protectedResourceTargetsProvider") + void adminToken_protectedResource_200(String target) { + Response response = + EXT.target(target) + .request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .header(HttpHeader.AUTHORIZATION.asString(), "token " + JWT_ADMIN_TOKEN) + .get(); + assertEquals(Status.OK.getStatusCode(), response.getStatus()); + } + + // Script token tests + @Test + void validScriptToken_200() { + Response response = + EXT.target(Targets.root) + .request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .header(HttpHeader.AUTHORIZATION.asString(), "token " + SCRIPT_TOKEN) + .get(); + assertEquals(Status.OK.getStatusCode(), response.getStatus()); + } + + @ParameterizedTest + @MethodSource("protectedResourceTargetsProvider") + void validScriptToken_protectedResource_200(String target) { + Response response = + EXT.target(target) + .request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .header(HttpHeader.AUTHORIZATION.asString(), "token " + SCRIPT_TOKEN) + .get(); + assertEquals(Status.OK.getStatusCode(), response.getStatus()); + } + + @Test + void validScriptToken_protectedAdminResource_403() { + Response response = + EXT.target(Targets.admin + TEST_ENV_STAGE_PATH) + .request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .header(HttpHeader.AUTHORIZATION.asString(), "token " + SCRIPT_TOKEN) + .get(); + assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus()); + } + + @Test + void validScriptToken_noAccessResource_403() { + Response response = + EXT.target(Targets.none) + .request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .header(HttpHeader.AUTHORIZATION.asString(), "token " + SCRIPT_TOKEN) + .get(); + assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus()); + } + + @Produces(MediaType.APPLICATION_JSON) + @Path(Targets.root) + public static class TestResource { + @GET + public Response unprotected() { + return Response.ok().build(); + } + + @GET + @Path(Targets.read + ENV_STAGE_SUFFIX) + @RolesAllowed(TeletraanPrincipalRole.Names.READ) + @ResourceAuthZInfo( + type = AuthZResource.Type.ENV_STAGE, + idLocation = ResourceAuthZInfo.Location.PATH) + public Response protectedReadResource() { + return Response.ok().build(); + } + + @GET + @Path(Targets.write + ENV_STAGE_SUFFIX) + @RolesAllowed(TeletraanPrincipalRole.Names.WRITE) + @ResourceAuthZInfo( + type = AuthZResource.Type.ENV_STAGE, + idLocation = ResourceAuthZInfo.Location.PATH) + public Response protectedWriteResource() { + return Response.ok().build(); + } + + @GET + @Path(Targets.execute + ENV_STAGE_SUFFIX) + @RolesAllowed(TeletraanPrincipalRole.Names.EXECUTE) + @ResourceAuthZInfo( + type = AuthZResource.Type.ENV_STAGE, + idLocation = ResourceAuthZInfo.Location.PATH) + public Response protectedExecuteResource() { + return Response.ok().build(); + } + + @GET + @Path(Targets.delete + ENV_STAGE_SUFFIX) + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo( + type = AuthZResource.Type.ENV_STAGE, + idLocation = ResourceAuthZInfo.Location.PATH) + public Response protectedDeleteResource() { + return Response.ok().build(); + } + + @GET + @Path(Targets.admin + ENV_STAGE_SUFFIX) + @RolesAllowed(TeletraanPrincipalRole.Names.DELETE) + @ResourceAuthZInfo( + type = AuthZResource.Type.ENV, + idLocation = ResourceAuthZInfo.Location.PATH) + public Response protectedAdminResource() { + return Response.ok().build(); + } + + @GET + @Path(Targets.none) + @DenyAll + public Response deadResource() { + return Response.ok().build(); + } + + @GET + @Path(Targets.all) + @PermitAll + public Response permitAllResource() { + return Response.ok().build(); + } + } + + public static class AuthDispatcher extends Dispatcher { + + @Override + public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + if (request.getPath().contains(JWT_TOKEN)) { + if (request.getPath().contains(USER_PATH)) { + return new MockResponse() + .setBody( + String.format("{\"user\": {\"username\": \"%s\"}}", TEST_USER)); + } else if (request.getPath().contains(GROUP_PATH)) { + return new MockResponse().setBody("{\"groups\": [\"group1\", \"group2\"]}"); + } + } else if (request.getPath().contains(JWT_ADMIN_TOKEN)) { + if (request.getPath().contains(USER_PATH)) { + return new MockResponse() + .setBody( + String.format( + "{\"user\": {\"username\": \"%s\"}}", TEST_ADMIN_USER)); + } else if (request.getPath().contains(GROUP_PATH)) { + return new MockResponse() + .setBody("{\"groups\": [\"admingroup1\", \"admingroup2\"]}"); + } + } + return new MockResponse().setResponseCode(404); + } + } +} diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/BasePathExtractorTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/BasePathExtractorTest.java new file mode 100644 index 0000000000..0aaf7847f1 --- /dev/null +++ b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/BasePathExtractorTest.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.security; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriInfo; +import org.junit.jupiter.api.BeforeEach; + +abstract class BasePathExtractorTest { + protected ContainerRequestContext context; + protected MultivaluedMap pathParameters; + + @BeforeEach + void setUp() { + UriInfo uriInfo = mock(UriInfo.class); + + context = mock(ContainerRequestContext.class); + pathParameters = new MultivaluedHashMap<>(); + + when(context.getUriInfo()).thenReturn(uriInfo); + when(uriInfo.getPathParameters()).thenReturn(pathParameters); + } +} diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/EnvPathExtractorTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/EnvPathExtractorTest.java new file mode 100644 index 0000000000..dfbbdd6dfe --- /dev/null +++ b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/EnvPathExtractorTest.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.pinterest.teletraan.universal.security.AuthZResourceExtractor.ExtractionException; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class EnvPathExtractorTest extends BasePathExtractorTest { + private EnvPathExtractor sut; + + @BeforeEach + void setUp() { + super.setUp(); + sut = new EnvPathExtractor(); + } + + @Test + void testExtractResource_noPathParams_exception() { + assertThrows(ExtractionException.class, () -> sut.extractResource(context)); + } + + @Test + void testExtractResource_0EnvName() { + pathParameters.add("nonEnvName", "testEnv"); + + assertThrows(ExtractionException.class, () -> sut.extractResource(context)); + } + + @Test + void testExtractResource_1EnvName() throws ExtractionException { + pathParameters.add("envName", "testEnv"); + + AuthZResource resource = sut.extractResource(context); + assertNotNull(resource); + assertEquals("testEnv", resource.getName()); + assertEquals(AuthZResource.Type.ENV, resource.getType()); + } + + @Test + void testExtractResource_2EnvNames() throws ExtractionException { + pathParameters.add("envName", "testEnv"); + pathParameters.add("envName", "testEnv2"); + + AuthZResource resource = sut.extractResource(context); + assertNotNull(resource); + assertEquals("testEnv", resource.getName()); + assertEquals(AuthZResource.Type.ENV, resource.getType()); + } +} diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/EnvStageBodyExtractorTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/EnvStageBodyExtractorTest.java new file mode 100644 index 0000000000..9415444e91 --- /dev/null +++ b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/EnvStageBodyExtractorTest.java @@ -0,0 +1,187 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pinterest.deployservice.ServiceContext; +import com.pinterest.deployservice.bean.AgentBean; +import com.pinterest.deployservice.bean.DeployBean; +import com.pinterest.deployservice.bean.EnvironBean; +import com.pinterest.deployservice.bean.HotfixBean; +import com.pinterest.deployservice.dao.EnvironDAO; +import com.pinterest.teletraan.fixture.EnvironBeanFixture; +import com.pinterest.teletraan.security.EnvStageBodyExtractor.BeanClassExtractionException; +import com.pinterest.teletraan.universal.security.AuthZResourceExtractor.ExtractionException; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import javax.ws.rs.container.ContainerRequestContext; + +import org.glassfish.jersey.server.ContainerRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class EnvStageBodyExtractorTest { + + private ContainerRequestContext context; + private EnvStageBodyExtractor sut; + private EnvironDAO environDAO; + private InputStream inputStream; + private ObjectMapper objectMapper = new ObjectMapper(); + private static final Class[] BEAN_CLASSES = { + EnvironBean.class, HotfixBean.class, DeployBean.class, AgentBean.class + }; + + @BeforeEach + void setUp() { + environDAO = mock(EnvironDAO.class); + context = mock(ContainerRequest.class); + + ServiceContext serviceContext = new ServiceContext(); + serviceContext.setEnvironDAO(environDAO); + sut = new EnvStageBodyExtractor(serviceContext); + } + + @Test + void testExtractResource_nothing_exception() throws Exception { + inputStream = mock(InputStream.class); + when(context.getEntityStream()).thenReturn(inputStream); + + assertThrows(ExtractionException.class, () -> sut.extractResource(context)); + } + + @ParameterizedTest + @MethodSource("getSupportedClassed") + void testExtractResource_specificBeanClass_emptyStream(Class beanClass) throws Exception { + inputStream = mock(InputStream.class); + when(context.getEntityStream()).thenReturn(inputStream); + + assertThrows( + BeanClassExtractionException.class, () -> sut.extractResource(context, beanClass)); + } + + @ParameterizedTest + @MethodSource("getSupportedClassed") + void testExtractResource_environBean_success(Class beanClass) throws Exception { + EnvironBean envBean = EnvironBeanFixture.createRandomEnvironBean(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + objectMapper.writeValue(out, envBean); + inputStream = new ByteArrayInputStream(out.toByteArray()); + + when(context.getEntityStream()).thenReturn(inputStream); + + if (beanClass.equals(EnvironBean.class)) { + AuthZResource resource = sut.extractResource(context, EnvironBean.class); + assertTrue(resource.getName().contains(envBean.getEnv_name())); + assertTrue(resource.getName().contains(envBean.getStage_name())); + assertEquals(AuthZResource.Type.ENV_STAGE, resource.getType()); + } else { + assertThrows( + BeanClassExtractionException.class, + () -> sut.extractResource(context, beanClass)); + } + } + + @ParameterizedTest + @MethodSource("getSupportedClassed") + void testExtractResource_hotFixBean_success(Class beanClass) throws Exception { + HotfixBean hotfixBean = new HotfixBean(); + hotfixBean.setEnv_name("env_name"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + objectMapper.writeValue(out, hotfixBean); + inputStream = new ByteArrayInputStream(out.toByteArray()); + + when(context.getEntityStream()).thenReturn(inputStream); + + if (beanClass.equals(HotfixBean.class)) { + + AuthZResource resource = sut.extractResource(context, HotfixBean.class); + assertTrue(resource.getName().contains(hotfixBean.getEnv_name())); + assertEquals(AuthZResource.Type.ENV_STAGE, resource.getType()); + } else { + assertThrows( + BeanClassExtractionException.class, + () -> sut.extractResource(context, beanClass)); + } + } + + @ParameterizedTest + @MethodSource("getSupportedClassed") + void testExtractResource_deployFixBean_success(Class beanClass) throws Exception { + EnvironBean envBean = EnvironBeanFixture.createRandomEnvironBean(); + DeployBean deployBean = new DeployBean(); + String envId = "env_id"; + deployBean.setEnv_id(envId); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + objectMapper.writeValue(out, deployBean); + inputStream = new ByteArrayInputStream(out.toByteArray()); + + when(context.getEntityStream()).thenReturn(inputStream); + when(environDAO.getById(envId)).thenReturn(envBean); + + if (beanClass.equals(DeployBean.class)) { + AuthZResource resource = sut.extractResource(context, DeployBean.class); + assertTrue(resource.getName().contains(envBean.getEnv_name())); + assertTrue(resource.getName().contains(envBean.getStage_name())); + assertEquals(AuthZResource.Type.ENV_STAGE, resource.getType()); + } else { + assertThrows( + BeanClassExtractionException.class, + () -> sut.extractResource(context, beanClass)); + } + } + + @ParameterizedTest + @MethodSource("getSupportedClassed") + void testExtractResource_agentBean_success(Class beanClass) throws Exception { + EnvironBean envBean = EnvironBeanFixture.createRandomEnvironBean(); + AgentBean agentBean = new AgentBean(); + String envId = "env_id"; + agentBean.setEnv_id(envId); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + objectMapper.writeValue(out, agentBean); + inputStream = new ByteArrayInputStream(out.toByteArray()); + + when(context.getEntityStream()).thenReturn(inputStream); + when(environDAO.getById(envId)).thenReturn(envBean); + + if (beanClass.equals(AgentBean.class)) { + AuthZResource resource = sut.extractResource(context, AgentBean.class); + assertTrue(resource.getName().contains(envBean.getEnv_name())); + assertTrue(resource.getName().contains(envBean.getStage_name())); + assertEquals(AuthZResource.Type.ENV_STAGE, resource.getType()); + } else { + assertThrows( + BeanClassExtractionException.class, + () -> sut.extractResource(context, beanClass)); + } + } + + static Class[] getSupportedClassed() { + return BEAN_CLASSES; + } +} diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/EnvStagePathExtractorTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/EnvStagePathExtractorTest.java new file mode 100644 index 0000000000..91442e7b03 --- /dev/null +++ b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/EnvStagePathExtractorTest.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.pinterest.teletraan.universal.security.AuthZResourceExtractor.ExtractionException; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class EnvStagePathExtractorTest extends BasePathExtractorTest { + private EnvStagePathExtractor sut; + private static final String ENV_NAME_KEY = "envName"; + private static final String STAGE_NAME_KEY = "stageName"; + + @BeforeEach + void setUp() { + super.setUp(); + sut = new EnvStagePathExtractor(); + } + + @Test + void testExtractResource_noPathParams_exception() { + assertThrows(ExtractionException.class, () -> sut.extractResource(context)); + } + + @Test + void testExtractResource_0EnvName() { + pathParameters.add("nonEnvName", "testEnv"); + + assertThrows(ExtractionException.class, () -> sut.extractResource(context)); + } + + @Test + void testExtractResource_onlyStageName() { + pathParameters.add(STAGE_NAME_KEY, "testEnv"); + + assertThrows(ExtractionException.class, () -> sut.extractResource(context)); + } + + @Test + void testExtractResource_1EnvName() throws ExtractionException { + pathParameters.add(ENV_NAME_KEY, "testEnv"); + + AuthZResource resource = sut.extractResource(context); + assertNotNull(resource); + assertTrue(resource.getName().contains("testEnv")); + assertEquals(AuthZResource.Type.ENV_STAGE, resource.getType()); + } + + @Test + void testExtractResource_1EnvName1StageName() throws ExtractionException { + pathParameters.add(ENV_NAME_KEY, "testEnv"); + pathParameters.add(STAGE_NAME_KEY, "testStage"); + + AuthZResource resource = sut.extractResource(context); + assertNotNull(resource); + assertTrue(resource.getName().contains("testEnv")); + assertTrue(resource.getName().contains("testStage")); + assertEquals(AuthZResource.Type.ENV_STAGE, resource.getType()); + } + + @Test + void testExtractResource_2EnvNames1StageName() throws ExtractionException { + pathParameters.add(ENV_NAME_KEY, "testEnv"); + pathParameters.add(ENV_NAME_KEY, "testEnv2"); + pathParameters.add(STAGE_NAME_KEY, "testStage"); + + AuthZResource resource = sut.extractResource(context); + assertNotNull(resource); + assertTrue(resource.getName().contains("testEnv")); + assertTrue(resource.getName().contains("testStage")); + assertEquals(AuthZResource.Type.ENV_STAGE, resource.getType()); + } +} diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/ScriptTokenAuthorizerTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/ScriptTokenAuthorizerTest.java deleted file mode 100644 index 01b5adfbde..0000000000 --- a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/ScriptTokenAuthorizerTest.java +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.teletraan.security; - -import com.pinterest.deployservice.ServiceContext; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; -import com.pinterest.deployservice.bean.TokenRolesBean; -import com.pinterest.deployservice.exception.TeletaanInternalException; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertFalse; - -public class ScriptTokenAuthorizerTest { - private ServiceContext context; - private RoleAuthorizer authorizer; - - private TokenRolesBean sysAdmin; - private TokenRolesBean sysOperator; - private TokenRolesBean sysReader; - - private TokenRolesBean envAdmin; - private TokenRolesBean envOperator; - private TokenRolesBean envReader; - - private Resource env1; - private Resource envX; - - @Before - public void setUp() throws Exception { - context = new ServiceContext(); - context.setUserRolesDAO(null); - authorizer = new RoleAuthorizer(context, null); - - sysAdmin = new TokenRolesBean(); - sysAdmin.setResource_id(Resource.ALL); - sysAdmin.setResource_type(Resource.Type.SYSTEM); - sysAdmin.setRole(Role.ADMIN); - - sysOperator = new TokenRolesBean(); - sysOperator.setResource_id(Resource.ALL); - sysOperator.setResource_type(Resource.Type.SYSTEM); - sysOperator.setRole(Role.OPERATOR); - - sysReader = new TokenRolesBean(); - sysReader.setResource_id(Resource.ALL); - sysReader.setResource_type(Resource.Type.SYSTEM); - sysReader.setRole(Role.READER); - - envAdmin = new TokenRolesBean(); - envAdmin.setResource_id("envX"); - envAdmin.setResource_type(Resource.Type.ENV); - envAdmin.setRole(Role.ADMIN); - - envOperator = new TokenRolesBean(); - envOperator.setResource_id("envX"); - envOperator.setResource_type(Resource.Type.ENV); - envOperator.setRole(Role.OPERATOR); - - envReader = new TokenRolesBean(); - envReader.setResource_id("envX"); - envReader.setResource_type(Resource.Type.ENV); - envReader.setRole(Role.READER); - - env1 = new Resource("env1", Resource.Type.ENV); - envX = new Resource("envX", Resource.Type.ENV); - } - - private void checkPositive(TokenRolesBean bean, Resource resource, Role role) throws Exception { - authorizer.checkAPITokenPermission(bean, resource, role); - } - - private void checkNegative(TokenRolesBean bean, Resource resource, Role role) throws Exception { - try { - authorizer.checkAPITokenPermission(bean, resource, role); - } catch (TeletaanInternalException e) { - // expected - return; - } - assertFalse("Expecting exception", true); - } - - @Test - public void testSysEnv() throws Exception { - checkPositive(sysAdmin, env1, Role.OPERATOR); - checkPositive(sysAdmin, env1, Role.ADMIN); - - checkPositive(sysOperator, env1, Role.OPERATOR); - checkNegative(sysOperator, env1, Role.ADMIN); - - checkNegative(sysReader, env1, Role.OPERATOR); - checkNegative(sysReader, env1, Role.ADMIN); - } - - @Test - public void testSysSys() throws Exception { - checkPositive(sysAdmin, Resource.SYSTEM_RESOURCE, Role.OPERATOR); - checkPositive(sysAdmin, Resource.SYSTEM_RESOURCE, Role.ADMIN); - - checkPositive(sysOperator, Resource.SYSTEM_RESOURCE, Role.OPERATOR); - checkNegative(sysOperator, Resource.SYSTEM_RESOURCE, Role.ADMIN); - - checkNegative(sysReader, Resource.SYSTEM_RESOURCE, Role.OPERATOR); - checkNegative(sysReader, Resource.SYSTEM_RESOURCE, Role.ADMIN); - } - - @Test - public void testEnvSys() throws Exception { - checkNegative(envAdmin, Resource.SYSTEM_RESOURCE, Role.OPERATOR); - checkNegative(envAdmin, Resource.SYSTEM_RESOURCE, Role.ADMIN); - - checkNegative(envOperator, Resource.SYSTEM_RESOURCE, Role.OPERATOR); - checkNegative(envOperator, Resource.SYSTEM_RESOURCE, Role.ADMIN); - - checkNegative(envReader, Resource.SYSTEM_RESOURCE, Role.OPERATOR); - checkNegative(envReader, Resource.SYSTEM_RESOURCE, Role.ADMIN); - } - - @Test - public void testEnvXEnv1() throws Exception { - checkNegative(envAdmin, env1, Role.OPERATOR); - checkNegative(envAdmin, env1, Role.ADMIN); - - checkNegative(envOperator, env1, Role.OPERATOR); - checkNegative(envOperator, env1, Role.ADMIN); - - checkNegative(envReader, env1, Role.OPERATOR); - checkNegative(envReader, env1, Role.ADMIN); - } - - @Test - public void testEnvXEnvX() throws Exception { - checkPositive(envAdmin, envX, Role.OPERATOR); - checkPositive(envAdmin, envX, Role.ADMIN); - - checkPositive(envOperator, envX, Role.OPERATOR); - checkNegative(envOperator, envX, Role.ADMIN); - - checkNegative(envReader, envX, Role.OPERATOR); - checkNegative(envReader, envX, Role.ADMIN); - } -} diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/ScriptTokenRoleAuthorizerTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/ScriptTokenRoleAuthorizerTest.java new file mode 100644 index 0000000000..c471b594cd --- /dev/null +++ b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/ScriptTokenRoleAuthorizerTest.java @@ -0,0 +1,206 @@ +/** + * Copyright (c) 2016-2024 Pinterest, 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.pinterest.teletraan.security; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.pinterest.deployservice.ServiceContext; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; +import com.pinterest.deployservice.bean.TokenRolesBean; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import com.pinterest.teletraan.universal.security.bean.ScriptTokenPrincipal; +import com.pinterest.teletraan.universal.security.bean.ValueBasedRole; +import org.junit.Before; +import org.junit.Test; + +public class ScriptTokenRoleAuthorizerTest { + private ServiceContext context; + private ScriptTokenRoleAuthorizer authorizer; + + private TokenRolesBean sysAdmin; + private TokenRolesBean sysOperator; + private TokenRolesBean sysReader; + + private TokenRolesBean envAdmin; + private TokenRolesBean envOperator; + private TokenRolesBean envReader; + + private AuthZResource env1; + private AuthZResource envX; + private AuthZResource env1Stage; + private AuthZResource envXStage; + + @Before + public void setUp() throws Exception { + context = new ServiceContext(); + context.setUserRolesDAO(null); + authorizer = new ScriptTokenRoleAuthorizer(null); + + sysAdmin = new TokenRolesBean(); + sysAdmin.setResource_id(AuthZResource.ALL); + sysAdmin.setResource_type(AuthZResource.Type.SYSTEM); + sysAdmin.setRole(TeletraanPrincipalRole.ADMIN); + + sysOperator = new TokenRolesBean(); + sysOperator.setResource_id(AuthZResource.ALL); + sysOperator.setResource_type(AuthZResource.Type.SYSTEM); + sysOperator.setRole(TeletraanPrincipalRole.OPERATOR); + + sysReader = new TokenRolesBean(); + sysReader.setResource_id(AuthZResource.ALL); + sysReader.setResource_type(AuthZResource.Type.SYSTEM); + sysReader.setRole(TeletraanPrincipalRole.READER); + + envAdmin = new TokenRolesBean(); + envAdmin.setResource_id("envX"); + envAdmin.setResource_type(AuthZResource.Type.ENV); + envAdmin.setRole(TeletraanPrincipalRole.ADMIN); + + envOperator = new TokenRolesBean(); + envOperator.setResource_id("envX"); + envOperator.setResource_type(AuthZResource.Type.ENV); + envOperator.setRole(TeletraanPrincipalRole.OPERATOR); + + envReader = new TokenRolesBean(); + envReader.setResource_id("envX"); + envReader.setResource_type(AuthZResource.Type.ENV); + envReader.setRole(TeletraanPrincipalRole.READER); + + env1 = new AuthZResource("env1", AuthZResource.Type.ENV); + envX = new AuthZResource("envX", AuthZResource.Type.ENV); + env1Stage = new AuthZResource("env1", "stage1"); + envXStage = new AuthZResource("envX", "stageX"); + } + + private void checkPositive( + TokenRolesBean bean, AuthZResource resource, TeletraanPrincipalRole role) + throws Exception { + ScriptTokenPrincipal principal = + new ScriptTokenPrincipal<>( + "testPrincipal", + bean.getRole().getRole(), + new AuthZResource(bean.getResource_id(), bean.getResource_type())); + assertTrue(authorizer.authorize(principal, role.name(), resource, null)); + } + + private void checkNegative( + TokenRolesBean bean, AuthZResource resource, TeletraanPrincipalRole requiredRole) + throws Exception { + ScriptTokenPrincipal principal = + new ScriptTokenPrincipal<>( + "testPrincipal", + bean.getRole().getRole(), + new AuthZResource(bean.getResource_id(), bean.getResource_type())); + assertFalse(authorizer.authorize(principal, requiredRole.name(), resource, null)); + } + + @Test + public void testSysEnv() throws Exception { + checkPositive(sysAdmin, env1, TeletraanPrincipalRole.OPERATOR); + checkPositive(sysAdmin, env1, TeletraanPrincipalRole.ADMIN); + + checkPositive(sysOperator, env1, TeletraanPrincipalRole.OPERATOR); + checkNegative(sysOperator, env1, TeletraanPrincipalRole.ADMIN); + + checkNegative(sysReader, env1, TeletraanPrincipalRole.OPERATOR); + checkNegative(sysReader, env1, TeletraanPrincipalRole.ADMIN); + } + + @Test + public void testSysSys() throws Exception { + checkPositive(sysAdmin, AuthZResource.SYSTEM_RESOURCE, TeletraanPrincipalRole.OPERATOR); + checkPositive(sysAdmin, AuthZResource.SYSTEM_RESOURCE, TeletraanPrincipalRole.ADMIN); + + checkPositive(sysOperator, AuthZResource.SYSTEM_RESOURCE, TeletraanPrincipalRole.OPERATOR); + checkNegative(sysOperator, AuthZResource.SYSTEM_RESOURCE, TeletraanPrincipalRole.ADMIN); + + checkNegative(sysReader, AuthZResource.SYSTEM_RESOURCE, TeletraanPrincipalRole.OPERATOR); + checkNegative(sysReader, AuthZResource.SYSTEM_RESOURCE, TeletraanPrincipalRole.ADMIN); + } + + @Test + public void testSysEnvStage() throws Exception { + checkPositive(sysAdmin, env1Stage, TeletraanPrincipalRole.OPERATOR); + checkPositive(sysAdmin, env1Stage, TeletraanPrincipalRole.ADMIN); + + checkPositive(sysOperator, env1Stage, TeletraanPrincipalRole.OPERATOR); + checkNegative(sysOperator, env1Stage, TeletraanPrincipalRole.ADMIN); + + checkNegative(sysReader, env1Stage, TeletraanPrincipalRole.OPERATOR); + checkNegative(sysReader, env1Stage, TeletraanPrincipalRole.ADMIN); + } + + @Test + public void testEnvSys() throws Exception { + checkNegative(envAdmin, AuthZResource.SYSTEM_RESOURCE, TeletraanPrincipalRole.OPERATOR); + checkNegative(envAdmin, AuthZResource.SYSTEM_RESOURCE, TeletraanPrincipalRole.ADMIN); + + checkNegative(envOperator, AuthZResource.SYSTEM_RESOURCE, TeletraanPrincipalRole.OPERATOR); + checkNegative(envOperator, AuthZResource.SYSTEM_RESOURCE, TeletraanPrincipalRole.ADMIN); + + checkNegative(envReader, AuthZResource.SYSTEM_RESOURCE, TeletraanPrincipalRole.OPERATOR); + checkNegative(envReader, AuthZResource.SYSTEM_RESOURCE, TeletraanPrincipalRole.ADMIN); + } + + @Test + public void testEnvXEnv1() throws Exception { + checkNegative(envAdmin, env1, TeletraanPrincipalRole.OPERATOR); + checkNegative(envAdmin, env1, TeletraanPrincipalRole.ADMIN); + + checkNegative(envOperator, env1, TeletraanPrincipalRole.OPERATOR); + checkNegative(envOperator, env1, TeletraanPrincipalRole.ADMIN); + + checkNegative(envReader, env1, TeletraanPrincipalRole.OPERATOR); + checkNegative(envReader, env1, TeletraanPrincipalRole.ADMIN); + } + + @Test + public void testEnvXEnvX() throws Exception { + checkPositive(envAdmin, envX, TeletraanPrincipalRole.OPERATOR); + checkPositive(envAdmin, envX, TeletraanPrincipalRole.ADMIN); + + checkNegative(envOperator, envX, TeletraanPrincipalRole.OPERATOR); + checkNegative(envOperator, envX, TeletraanPrincipalRole.ADMIN); + + checkNegative(envReader, envX, TeletraanPrincipalRole.OPERATOR); + checkNegative(envReader, envX, TeletraanPrincipalRole.ADMIN); + } + + @Test + public void testEnvXEnv1Stage() throws Exception { + checkNegative(envAdmin, env1Stage, TeletraanPrincipalRole.OPERATOR); + checkNegative(envAdmin, env1Stage, TeletraanPrincipalRole.ADMIN); + + checkNegative(envOperator, env1Stage, TeletraanPrincipalRole.OPERATOR); + checkNegative(envOperator, env1Stage, TeletraanPrincipalRole.ADMIN); + + checkNegative(envReader, env1Stage, TeletraanPrincipalRole.OPERATOR); + checkNegative(envReader, env1Stage, TeletraanPrincipalRole.ADMIN); + } + + @Test + public void testEnvXEnvXStage() throws Exception { + checkPositive(envAdmin, envXStage, TeletraanPrincipalRole.OPERATOR); + checkPositive(envAdmin, envXStage, TeletraanPrincipalRole.ADMIN); + + checkPositive(envOperator, envXStage, TeletraanPrincipalRole.OPERATOR); + checkNegative(envOperator, envXStage, TeletraanPrincipalRole.ADMIN); + + checkNegative(envReader, envXStage, TeletraanPrincipalRole.OPERATOR); + checkNegative(envReader, envXStage, TeletraanPrincipalRole.ADMIN); + } +} diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/TeletraanScriptTokenProviderTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/TeletraanScriptTokenProviderTest.java new file mode 100644 index 0000000000..2aede074ab --- /dev/null +++ b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/TeletraanScriptTokenProviderTest.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2024 Pinterest, 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.pinterest.teletraan.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.pinterest.deployservice.ServiceContext; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; +import com.pinterest.deployservice.bean.TokenRolesBean; +import com.pinterest.deployservice.dao.TokenRolesDAO; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import com.pinterest.teletraan.universal.security.bean.ScriptTokenPrincipal; +import com.pinterest.teletraan.universal.security.bean.ValueBasedRole; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class TeletraanScriptTokenProviderTest { + private static final String GOOD_TOKEN = "goodToken"; + private ServiceContext context; + private TokenRolesDAO tokenRolesDAO; + private TeletraanScriptTokenProvider sut; + private TokenRolesBean tokenRolesBean; + + @BeforeEach + void setUp() throws Exception { + context = new ServiceContext(); + tokenRolesDAO = mock(TokenRolesDAO.class); + context.setTokenRolesDAO(tokenRolesDAO); + sut = new TeletraanScriptTokenProvider(context); + tokenRolesBean = new TokenRolesBean(); + tokenRolesBean.setScript_name("scriptName"); + tokenRolesBean.setResource_id("resourceId"); + tokenRolesBean.setRole(TeletraanPrincipalRole.ADMIN); + tokenRolesBean.setResource_type(AuthZResource.Type.SYSTEM); + + when(tokenRolesDAO.getByToken(GOOD_TOKEN)).thenReturn(tokenRolesBean); + } + + @ParameterizedTest + @ValueSource(strings = {"", "badToken"}) + void testGetPrincipal_invalidToken(String token) { + Optional principal = sut.getPrincipal(token); + assertFalse(principal.isPresent()); + } + + @Test + void testGetPrincipal_validToken() { + ScriptTokenPrincipal principal = sut.getPrincipal(GOOD_TOKEN).get(); + assertEquals(TeletraanPrincipalRole.ADMIN.getRole(), principal.getRole()); + assertEquals(AuthZResource.Type.SYSTEM, principal.getResource().getType()); + assertEquals(tokenRolesBean.getResource_id(), principal.getResource().getName()); + assertEquals(tokenRolesBean.getScript_name(), principal.getName()); + } +} diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/UserRoleAuthorizerTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/UserRoleAuthorizerTest.java new file mode 100644 index 0000000000..5da303b4b4 --- /dev/null +++ b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/UserRoleAuthorizerTest.java @@ -0,0 +1,351 @@ +/** + * Copyright (c) 2016-2024 Pinterest, 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.pinterest.teletraan.security; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import com.pinterest.deployservice.ServiceContext; +import com.pinterest.deployservice.bean.EnvironBean; +import com.pinterest.deployservice.bean.GroupRolesBean; +import com.pinterest.deployservice.bean.TeletraanPrincipalRole; +import com.pinterest.deployservice.bean.UserRolesBean; +import com.pinterest.deployservice.dao.EnvironDAO; +import com.pinterest.deployservice.dao.GroupRolesDAO; +import com.pinterest.deployservice.dao.UserRolesDAO; +import com.pinterest.teletraan.universal.security.bean.AuthZResource; +import com.pinterest.teletraan.universal.security.bean.UserPrincipal; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class UserRoleAuthorizerTest { + private static final Logger LOG = LoggerFactory.getLogger(UserRoleAuthorizerTest.class); + private static final String envXName = "envX"; + private static final String env1Name = "env1"; + private static final String stageName = "stage"; + private static final AuthZResource env1AuthZResource = new AuthZResource(env1Name, stageName); + private static final AuthZResource env1AdminAuthZResource = + new AuthZResource(env1Name, AuthZResource.Type.ENV); + private static final AuthZResource envXAuthZResource = new AuthZResource(envXName, stageName); + private static final AuthZResource buildResource = + new AuthZResource("build1", AuthZResource.Type.BUILD); + private static final MultivaluedMap + legacyToNewRoles = new MultivaluedHashMap<>(); + private static final List newRoles = + Arrays.asList( + TeletraanPrincipalRole.READ, + TeletraanPrincipalRole.WRITE, + TeletraanPrincipalRole.EXECUTE, + TeletraanPrincipalRole.DELETE); + + @BeforeAll + static void setUpAll() { + legacyToNewRoles.add(TeletraanPrincipalRole.READER, TeletraanPrincipalRole.READ); + legacyToNewRoles.add(TeletraanPrincipalRole.OPERATOR, TeletraanPrincipalRole.READ); + legacyToNewRoles.add(TeletraanPrincipalRole.OPERATOR, TeletraanPrincipalRole.WRITE); + legacyToNewRoles.add(TeletraanPrincipalRole.OPERATOR, TeletraanPrincipalRole.EXECUTE); + legacyToNewRoles.add(TeletraanPrincipalRole.OPERATOR, TeletraanPrincipalRole.DELETE); + legacyToNewRoles.add(TeletraanPrincipalRole.ADMIN, TeletraanPrincipalRole.READ); + legacyToNewRoles.add(TeletraanPrincipalRole.ADMIN, TeletraanPrincipalRole.WRITE); + legacyToNewRoles.add(TeletraanPrincipalRole.ADMIN, TeletraanPrincipalRole.EXECUTE); + legacyToNewRoles.add(TeletraanPrincipalRole.ADMIN, TeletraanPrincipalRole.DELETE); + } + + private static AuthZResource[] resourceProvider() { + return new AuthZResource[] { + AuthZResource.SYSTEM_RESOURCE, + env1AuthZResource, + env1AdminAuthZResource, + envXAuthZResource, + }; + } + + private ServiceContext context; + private EnvironDAO environDAO; + + private UserRolesDAO userRolesDAO; + private GroupRolesDAO groupRolesDAO; + private UserRoleAuthorizer authorizer; + private UserPrincipal sysAdmin; + private UserPrincipal sysOperator; + private UserPrincipal sysReader; + + private UserPrincipal sysAdminByGroup; + private UserPrincipal sysOperatorByGroup; + private UserPrincipal sysReaderByGroup; + private UserPrincipal envAdmin; + private UserPrincipal envOperator; + private UserPrincipal envReader; + + private UserPrincipal envAdminByGroup; + private UserPrincipal envOperatorByGroup; + private UserPrincipal envReaderByGroup; + + private String adminGroupName = "admin"; + + private String operatorGroupName = "operator"; + + private String readerGroupName = "reader"; + + @BeforeEach + void setUp() throws Exception { + context = new ServiceContext(); + userRolesDAO = Mockito.mock(UserRolesDAO.class); + groupRolesDAO = Mockito.mock(GroupRolesDAO.class); + environDAO = Mockito.mock(EnvironDAO.class); + context.setUserRolesDAO(userRolesDAO); + context.setGroupRolesDAO(groupRolesDAO); + context.setEnvironDAO(environDAO); + authorizer = new UserRoleAuthorizer(context, null); + + setUpUserPrincipals(); + setUpGroupRolesBeans(); + + when(environDAO.getByName(anyString())) + .thenReturn(Collections.singletonList(new EnvironBean())); + } + + @ParameterizedTest + @MethodSource("resourceProvider") + void testSystemUserOnResources(AuthZResource requestedResource) { + checkAllRequiredRoles(sysAdmin, TeletraanPrincipalRole.ADMIN, requestedResource); + checkAllRequiredRoles(sysOperator, TeletraanPrincipalRole.OPERATOR, requestedResource); + checkAllRequiredRoles(sysReader, TeletraanPrincipalRole.READER, requestedResource); + } + + @ParameterizedTest + @MethodSource("resourceProvider") + void testSystemUserOnResources_viaGroup(AuthZResource requestedResource) throws Exception { + GroupRolesBean sysAdminBean = + createGroupRolesBean(TeletraanPrincipalRole.ADMIN, adminGroupName); + when(groupRolesDAO.getByResource(AuthZResource.ALL, AuthZResource.Type.SYSTEM)) + .thenReturn(Collections.singletonList(sysAdminBean)); + checkAllRequiredRoles(sysAdminByGroup, TeletraanPrincipalRole.ADMIN, requestedResource); + + GroupRolesBean sysOperatorBean = + createGroupRolesBean(TeletraanPrincipalRole.OPERATOR, operatorGroupName); + when(groupRolesDAO.getByResource(AuthZResource.ALL, AuthZResource.Type.SYSTEM)) + .thenReturn(Collections.singletonList(sysOperatorBean)); + checkAllRequiredRoles( + sysOperatorByGroup, TeletraanPrincipalRole.OPERATOR, requestedResource); + + GroupRolesBean sysReaderBean = + createGroupRolesBean(TeletraanPrincipalRole.READER, readerGroupName); + when(groupRolesDAO.getByResource(AuthZResource.ALL, AuthZResource.Type.SYSTEM)) + .thenReturn(Collections.singletonList(sysReaderBean)); + checkAllRequiredRoles(sysReaderByGroup, TeletraanPrincipalRole.READER, requestedResource); + } + + @Test + void testEnvUserOnEnvResource() { + checkAllRequiredRoles(envAdmin, TeletraanPrincipalRole.ADMIN, env1AuthZResource); + checkAllRequiredRoles(envOperator, TeletraanPrincipalRole.OPERATOR, env1AuthZResource); + checkAllRequiredRoles(envReader, TeletraanPrincipalRole.READER, env1AuthZResource); + + checkAllRequiredRoles(envAdmin, TeletraanPrincipalRole.ADMIN, env1AdminAuthZResource); + checkAllNegative(envOperator, env1AdminAuthZResource); + checkAllNegative(envReader, env1AdminAuthZResource); + } + + @Test + void testEnvUserOnEnvXResource_viaGroup() throws Exception { + GroupRolesBean envAdminBean = + createGroupRolesBean(TeletraanPrincipalRole.ADMIN, adminGroupName); + when(groupRolesDAO.getByResource(env1Name, AuthZResource.Type.ENV)) + .thenReturn(Collections.singletonList(envAdminBean)); + checkAllNegative(envAdminByGroup, envXAuthZResource); + + GroupRolesBean envOperatorBean = + createGroupRolesBean(TeletraanPrincipalRole.OPERATOR, operatorGroupName); + when(groupRolesDAO.getByResource(env1Name, AuthZResource.Type.ENV)) + .thenReturn(Collections.singletonList(envOperatorBean)); + checkAllNegative(envOperatorByGroup, envXAuthZResource); + + GroupRolesBean envReaderBean = + createGroupRolesBean(TeletraanPrincipalRole.READER, readerGroupName); + when(groupRolesDAO.getByResource(env1Name, AuthZResource.Type.ENV)) + .thenReturn(Collections.singletonList(envReaderBean)); + checkAllNegative(envReaderByGroup, envXAuthZResource); + } + + @ParameterizedTest + @MethodSource("resourceProvider") + void testRandomUserOnDisallowedResources(AuthZResource requestedResource) { + UserPrincipal randomUser = + new UserPrincipal("randomUser", Collections.singletonList("someGroup")); + checkAllNegative(randomUser, requestedResource); + } + + @Test + void testRandomUserOnOtherResources() throws Exception { + UserPrincipal randomUser = + new UserPrincipal("randomUser", Collections.singletonList("someGroup")); + + // Special case for creating a new environment + AuthZResource envXResource = new AuthZResource(envXName, AuthZResource.Type.ENV_STAGE); + when(environDAO.getByName(envXName)).thenReturn(null); + checkPositive(randomUser, envXResource, TeletraanPrincipalRole.WRITE); + + when(environDAO.getByName(envXName)) + .thenReturn(Collections.singletonList(new EnvironBean())); + checkNegative(randomUser, envXResource, TeletraanPrincipalRole.WRITE); + + checkPositive(randomUser, buildResource, TeletraanPrincipalRole.PUBLISHER); + } + + @Test + void testBuildResource() { + checkPositive(sysAdmin, buildResource, TeletraanPrincipalRole.PUBLISHER); + checkPositive(sysOperator, buildResource, TeletraanPrincipalRole.PUBLISHER); + checkPositive(sysReader, buildResource, TeletraanPrincipalRole.PUBLISHER); + checkPositive(envAdmin, buildResource, TeletraanPrincipalRole.PUBLISHER); + checkPositive(envOperator, buildResource, TeletraanPrincipalRole.PUBLISHER); + checkPositive(envReader, buildResource, TeletraanPrincipalRole.PUBLISHER); + } + + private void checkAllRequiredRoles( + UserPrincipal principal, + TeletraanPrincipalRole legacyRole, + AuthZResource requestedResource) { + for (TeletraanPrincipalRole requiredRole : newRoles) { + if (legacyToNewRoles.get(legacyRole).contains(requiredRole)) { + checkPositive(principal, requestedResource, requiredRole); + } else { + checkNegative(principal, requestedResource, requiredRole); + } + } + } + + private void checkAllNegative(UserPrincipal principal, AuthZResource requestedResource) { + for (TeletraanPrincipalRole requiredRole : newRoles) { + checkNegative(principal, requestedResource, requiredRole); + } + } + + private void setUpGroupRolesBeans() throws Exception { + sysAdminByGroup = + new UserPrincipal("sysAdminByGroup", Collections.singletonList(adminGroupName)); + sysOperatorByGroup = + new UserPrincipal( + "sysOperatorByGroup", Collections.singletonList(operatorGroupName)); + sysReaderByGroup = + new UserPrincipal("sysReaderByGroup", Collections.singletonList(readerGroupName)); + + envAdminByGroup = + new UserPrincipal("env1AdminByGroup", Collections.singletonList(adminGroupName)); + envOperatorByGroup = + new UserPrincipal( + "env1OperatorByGroup", Collections.singletonList(operatorGroupName)); + envReaderByGroup = + new UserPrincipal("env1ReaderByGroup", Collections.singletonList(readerGroupName)); + } + + private void setUpUserPrincipals() throws Exception { + UserRolesBean sysAdminBean = createUserRolesBean(TeletraanPrincipalRole.ADMIN, "sysAdmin"); + when(userRolesDAO.getByNameAndResource( + sysAdminBean.getUser_name(), AuthZResource.ALL, AuthZResource.Type.SYSTEM)) + .thenReturn(sysAdminBean); + sysAdmin = new UserPrincipal(sysAdminBean.getUser_name(), null); + + UserRolesBean sysOperatorBean = + createUserRolesBean(TeletraanPrincipalRole.OPERATOR, "sysOperator"); + when(userRolesDAO.getByNameAndResource( + sysOperatorBean.getUser_name(), + AuthZResource.ALL, + AuthZResource.Type.SYSTEM)) + .thenReturn(sysOperatorBean); + sysOperator = new UserPrincipal(sysOperatorBean.getUser_name(), null); + + UserRolesBean sysReaderBean = + createUserRolesBean(TeletraanPrincipalRole.READER, "sysReader"); + when(userRolesDAO.getByNameAndResource( + sysReaderBean.getUser_name(), AuthZResource.ALL, AuthZResource.Type.SYSTEM)) + .thenReturn(sysReaderBean); + sysReader = new UserPrincipal(sysReaderBean.getUser_name(), null); + + UserRolesBean envAdminBean = + createUserRolesBean(TeletraanPrincipalRole.ADMIN, "env1Admin"); + when(userRolesDAO.getByNameAndResource( + envAdminBean.getUser_name(), env1Name, AuthZResource.Type.ENV)) + .thenReturn(envAdminBean); + envAdmin = new UserPrincipal(envAdminBean.getUser_name(), null); + + UserRolesBean envOperatorBean = + createUserRolesBean(TeletraanPrincipalRole.OPERATOR, "env1Operator"); + when(userRolesDAO.getByNameAndResource( + envOperatorBean.getUser_name(), env1Name, AuthZResource.Type.ENV)) + .thenReturn(envOperatorBean); + envOperator = new UserPrincipal(envOperatorBean.getUser_name(), null); + + UserRolesBean envReaderBean = + createUserRolesBean(TeletraanPrincipalRole.READER, "env1Reader"); + when(userRolesDAO.getByNameAndResource( + envReaderBean.getUser_name(), env1Name, AuthZResource.Type.ENV)) + .thenReturn(envReaderBean); + envReader = new UserPrincipal(envReaderBean.getUser_name(), null); + } + + private UserRolesBean createUserRolesBean(TeletraanPrincipalRole role, String userName) { + UserRolesBean userRolesBean = new UserRolesBean(); + userRolesBean.setRole(role); + userRolesBean.setUser_name(userName); + return userRolesBean; + } + + private GroupRolesBean createGroupRolesBean(TeletraanPrincipalRole role, String groupName) { + GroupRolesBean groupRolesBean = new GroupRolesBean(); + groupRolesBean.setRole(role); + groupRolesBean.setGroup_name(groupName); + return groupRolesBean; + } + + private void checkPositive( + UserPrincipal user, + AuthZResource requestedResource, + TeletraanPrincipalRole requiredRole) { + LOG.info( + "Checking positive {} for {} on {}", + user.getName(), + requiredRole, + requestedResource); + assertTrue(authorizer.authorize(user, requiredRole.name(), requestedResource, null)); + } + + private void checkNegative( + UserPrincipal user, + AuthZResource requestedResource, + TeletraanPrincipalRole requiredRole) { + LOG.info( + "Checking negative {} for {} on {}", + user.getName(), + requiredRole, + requestedResource); + assertFalse(authorizer.authorize(user, requiredRole.name(), requestedResource, null)); + } +} diff --git a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/UserTokenAuthorizerTest.java b/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/UserTokenAuthorizerTest.java deleted file mode 100644 index b7a06d0b3c..0000000000 --- a/deploy-service/teletraanservice/src/test/java/com/pinterest/teletraan/security/UserTokenAuthorizerTest.java +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Copyright 2016 Pinterest, 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.pinterest.teletraan.security; - -import com.pinterest.deployservice.ServiceContext; -import com.pinterest.deployservice.bean.Resource; -import com.pinterest.deployservice.bean.Role; -import com.pinterest.deployservice.bean.UserRolesBean; -import com.pinterest.deployservice.dao.UserRolesDAO; -import com.pinterest.deployservice.exception.TeletaanInternalException; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import static org.junit.Assert.assertFalse; - -public class UserTokenAuthorizerTest { - private ServiceContext context; - private UserRolesDAO userRolesDAO; - private RoleAuthorizer authorizer; - - private UserRolesBean sysAdmin; - private UserRolesBean sysOperator; - private UserRolesBean sysReader; - - private UserRolesBean envAdmin; - private UserRolesBean envOperator; - private UserRolesBean envReader; - - private String envXName = "envX"; - private String env1Name = "env1"; - private Resource env1; - private Resource envX; - - @Before - public void setUp() throws Exception { - context = new ServiceContext(); - userRolesDAO = Mockito.mock(UserRolesDAO.class); - context.setUserRolesDAO(userRolesDAO); - authorizer = new RoleAuthorizer(context, null); - - sysAdmin = new UserRolesBean(); - sysAdmin.setRole(Role.ADMIN); - - sysOperator = new UserRolesBean(); - sysOperator.setRole(Role.OPERATOR); - - sysReader = new UserRolesBean(); - sysReader.setRole(Role.READER); - - envAdmin = new UserRolesBean(); - envAdmin.setRole(Role.ADMIN); - - envOperator = new UserRolesBean(); - envOperator.setRole(Role.OPERATOR); - - envReader = new UserRolesBean(); - envReader.setRole(Role.READER); - - env1 = new Resource(env1Name, Resource.Type.ENV); - envX = new Resource(envXName, Resource.Type.ENV); - } - - private void checkPositive(String userName, Resource resource, Role role) throws Exception { - authorizer.checkUserPermission(userName, resource, null, role); - } - - private void checkNegative(String userName, Resource resource, Role role) throws Exception { - try { - authorizer.checkUserPermission(userName, resource, null, role); - } catch (TeletaanInternalException e) { - // expected - return; - } - assertFalse("Expecting exception", true); - } - - @Test - public void testSysSys() throws Exception { - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(sysAdmin); - checkPositive(envXName, Resource.SYSTEM_RESOURCE, Role.READER); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(sysAdmin); - checkPositive(envXName, Resource.SYSTEM_RESOURCE, Role.OPERATOR); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(sysAdmin); - checkPositive(envXName, Resource.SYSTEM_RESOURCE, Role.ADMIN); - - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(sysOperator); - checkPositive(envXName, Resource.SYSTEM_RESOURCE, Role.READER); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(sysOperator); - checkPositive(envXName, Resource.SYSTEM_RESOURCE, Role.OPERATOR); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(sysOperator); - checkNegative(envXName, Resource.SYSTEM_RESOURCE, Role.ADMIN); - - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(sysReader); - checkPositive(envXName, Resource.SYSTEM_RESOURCE, Role.READER); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(sysReader); - checkNegative(envXName, Resource.SYSTEM_RESOURCE, Role.OPERATOR); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(sysReader); - checkNegative(envXName, Resource.SYSTEM_RESOURCE, Role.ADMIN); - } - - @Test - public void testEnvSys() throws Exception { - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(null); - checkNegative(envXName, Resource.SYSTEM_RESOURCE, Role.OPERATOR); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(null); - checkNegative(envXName, Resource.SYSTEM_RESOURCE, Role.ADMIN); - } - - @Test - public void testEnvXEnv1() throws Exception { - Mockito.when(userRolesDAO.getByNameAndResource(envXName, env1Name, Resource.Type.ENV)).thenReturn(null); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(null); - checkNegative(envXName, env1, Role.OPERATOR); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, env1Name, Resource.Type.ENV)).thenReturn(null); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(sysAdmin); - checkPositive(envXName, env1, Role.ADMIN); - } - - @Test - public void testEnvXEnvX() throws Exception { - Mockito.when(userRolesDAO.getByNameAndResource(envXName, envXName, Resource.Type.ENV)).thenReturn(envAdmin); - checkPositive(envXName, envX, Role.OPERATOR); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, envXName, Resource.Type.ENV)).thenReturn(envAdmin); - checkPositive(envXName, envX, Role.ADMIN); - - Mockito.when(userRolesDAO.getByNameAndResource(envXName, envXName, Resource.Type.ENV)).thenReturn(envOperator); - checkPositive(envXName, envX, Role.OPERATOR); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, envXName, Resource.Type.ENV)).thenReturn(envOperator); - checkNegative(envXName, envX, Role.ADMIN); - - Mockito.when(userRolesDAO.getByNameAndResource(envXName, envXName, Resource.Type.ENV)).thenReturn(envReader); - checkNegative(envXName, envX, Role.OPERATOR); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, envXName, Resource.Type.ENV)).thenReturn(envReader); - checkNegative(envXName, envX, Role.ADMIN); - } - - @Test - public void testSysEnvX() throws Exception { - Mockito.when(userRolesDAO.getByNameAndResource(envXName, envXName, Resource.Type.ENV)).thenReturn(envAdmin); - checkPositive(envXName, envX, Role.OPERATOR); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, envXName, Resource.Type.ENV)).thenReturn(envAdmin); - checkPositive(envXName, envX, Role.ADMIN); - - Mockito.when(userRolesDAO.getByNameAndResource(envXName, envXName, Resource.Type.ENV)).thenReturn(envOperator); - checkPositive(envXName, envX, Role.OPERATOR); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, envXName, Resource.Type.ENV)).thenReturn(envOperator); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(sysAdmin); - checkPositive(envXName, envX, Role.ADMIN); - - Mockito.when(userRolesDAO.getByNameAndResource(envXName, envXName, Resource.Type.ENV)).thenReturn(envReader); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(null); - checkNegative(envXName, envX, Role.OPERATOR); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, envXName, Resource.Type.ENV)).thenReturn(envReader); - Mockito.when(userRolesDAO.getByNameAndResource(envXName, Resource.ALL, Resource.Type.SYSTEM)).thenReturn(sysOperator); - checkNegative(envXName, envX, Role.ADMIN); - } -}