Skip to content

Commit

Permalink
x-okta-multi-operation bug (#561)
Browse files Browse the repository at this point in the history
x-okta-multi-operation fixed and migration guide updated
  • Loading branch information
sergiishamrai-okta authored Mar 25, 2021
1 parent 19f11b8 commit 94a97a1
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 3 deletions.
14 changes: 14 additions & 0 deletions MIGRATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ Below methods have been added.
- `UserSchema updateApplicationUserProfile(String appInstanceId, UserSchema userSchema)`
- `UserSchema updateApplicationUserProfile(String appInstanceId)`
- `UserSchema updateUserProfile(String schemaId, UserSchema userSchema)`

Below method has been removed.
- `ForgotPasswordResponse apiV1UsersUserIdCredentialsForgotPasswordPost(String userId)`

One of below listed methods could be used instead:
- `User.forgotPasswordGenerateOneTimeToken(Boolean sendEmail)`
- `User.forgotPasswordGenerateOneTimeToken()`
- `User.forgotPasswordSetNewPassword(UserCredentials userCredentials, Boolean sendEmail)`
- `User.forgotPasswordSetNewPassword(UserCredentials userCredentials)`

### Package `com.okta.sdk.resource.user.type.UserType`

Expand Down Expand Up @@ -304,6 +313,11 @@ Below method has undergone a signature change in the interest of naming consiste
- `void addAllAppsAsTargetToRole()` to `void addAllAppsAsTarget(String roleId)`
- `void deleteFactor()` to `void deleteFactor(String factorId)`

Below methods have been added.
- `ForgotPasswordResponse forgotPasswordGenerateOneTimeToken(Boolean sendEmail)`
- `ForgotPasswordResponse forgotPasswordGenerateOneTimeToken()`
- `ForgotPasswordResponse forgotPasswordSetNewPassword(UserCredentials userCredentials, Boolean sendEmail)`
- `ForgotPasswordResponse forgotPasswordSetNewPassword(UserCredentials userCredentials)`

## Migrating from 2.x.x to 3.0.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import com.okta.sdk.resource.role.AssignRoleRequest
import com.okta.sdk.resource.role.RoleType
import com.okta.sdk.resource.user.AuthenticationProviderType
import com.okta.sdk.resource.user.ChangePasswordRequest
import com.okta.sdk.resource.user.ForgotPasswordResponse
import com.okta.sdk.resource.user.PasswordCredential
import com.okta.sdk.resource.user.RecoveryQuestionCredential
import com.okta.sdk.resource.user.ResetPasswordToken
Expand Down Expand Up @@ -761,6 +762,67 @@ class UsersIT extends ITSupport implements CrudTestSupport {
assertThat user.getCredentials().getProvider().getType(), is(AuthenticationProviderType.IMPORT)
}

@Test
@Scenario("user-forgot-password-generate-one-time-token")
void forgotPasswordGenerateOttTest() {

def password = 'Passw0rd!2@3#'
def firstName = 'John'
def lastName = 'Forgot-Password'
def email = "john-${uniqueTestName}@example.com"

// 1. Create a user
User user = UserBuilder.instance()
.setEmail(email)
.setFirstName(firstName)
.setLastName(lastName)
.setPassword(password.toCharArray())
.setActive(true)
.setSecurityQuestion("How many roads must a man walk down?")
.setSecurityQuestionAnswer("forty two")
.buildAndCreate(client)
registerForCleanup(user)
validateUser(user, firstName, lastName, email)

ForgotPasswordResponse response = user.forgotPasswordGenerateOneTimeToken(false)
assertThat response.getResetPasswordUrl(), containsString("/signin/reset-password/")
}

@Test
@Scenario("user-forgot-password-set-new-password")
void forgotPasswordSetNewPasswordTest() {

def password = 'OldPassw0rd!2@3#'
def firstName = 'John'
def lastName = 'Forgot-Password'
def email = "john-${uniqueTestName}@example.com"

// 1. Create a user
User user = UserBuilder.instance()
.setEmail(email)
.setFirstName(firstName)
.setLastName(lastName)
.setPassword(password.toCharArray())
.setActive(true)
.setSecurityQuestion("How many roads must a man walk down?")
.setSecurityQuestionAnswer("forty two")
.buildAndCreate(client)
registerForCleanup(user)
validateUser(user, firstName, lastName, email)

UserCredentials userCredentials = client.instantiate(UserCredentials)
.setPassword(client.instantiate(PasswordCredential)
.setValue('NewPassw0rd!2@3#'.toCharArray()))
.setRecoveryQuestion(client.instantiate(RecoveryQuestionCredential)
.setQuestion('How many roads must a man walk down?')
.setAnswer('forty two'))

ForgotPasswordResponse response = user.forgotPasswordSetNewPassword(userCredentials, false)
assertThat response.get("recovery_question")["question"], equalTo("How many roads must a man walk down?")
assertThat response.get("provider")["type"], equalTo("OKTA")
assertThat response.get("provider")["name"], equalTo("OKTA")
}

private void ensureCustomProperties() {
def userSchemaUri = "/api/v1/meta/schemas/user/default"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@
import io.swagger.models.Response;
import io.swagger.models.Swagger;
import io.swagger.models.parameters.BodyParameter;
import io.swagger.models.parameters.Parameter;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.Property;
import io.swagger.models.properties.RefProperty;
import io.swagger.parser.SwaggerException;
import io.swagger.util.Json;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
Expand Down Expand Up @@ -338,17 +340,36 @@ private void handleOktaLinkedOperations(Swagger swagger) {
swagger.getPaths().forEach((pathName, path) -> {
Optional<Map.Entry<HttpMethod, Operation>> operationEntry =
path.getOperationMap().entrySet().stream().filter(
e -> e.getValue().getOperationId() != null &&
e.getValue().getOperationId().equals(operationId)).findFirst();
oper -> {
//Looking for an operationId in paths:path:operationId
if (oper.getValue().getOperationId() != null
&& oper.getValue().getOperationId().equals(operationId)) {
return true;
}
//Looking for an operationId in paths:path:method:x-okta-multi-operation:operationId
List<Operation> xOktaMultiOperation = getOktaMultiOperationObject(oper.getValue());
if (xOktaMultiOperation != null &&
xOktaMultiOperation
.stream()
.anyMatch(multiOper -> multiOper.getOperationId().equals(operationId))
) {
return true;
}
return false;
}
).findFirst();

if (operationEntry.isPresent()) {

Operation operation = operationEntry.get().getValue();

//Trying to get an Operation from x-okta-multi-operation
Operation xOktaMultiOperation = produceOperationFromXOktaMultiOperation(operation, operationId);

CodegenOperation cgOperation = fromOperation(
pathName,
operationEntry.get().getKey().name().toLowerCase(),
operation,
xOktaMultiOperation != null ? xOktaMultiOperation : operation,
swagger.getDefinitions(),
swagger);

Expand Down Expand Up @@ -465,6 +486,77 @@ else if ("read".equals(alias) || "create".equals(alias)) {
});
}

private List<Operation> getOktaMultiOperationObject(Operation operation) {
Object multiOperationObject = operation.getVendorExtensions().get("x-okta-multi-operation");
List<Operation> xOktaMultiOperationList = new ArrayList<>();
if (multiOperationObject instanceof List) {
for(Object node : (List)multiOperationObject) {
Operation multiOperation = Json.mapper().convertValue(node, Operation.class);
xOktaMultiOperationList.add(multiOperation);
}
return xOktaMultiOperationList;
}
return null;
}

private Operation produceOperationFromXOktaMultiOperation(Operation operation, String operationId) {

Operation xOktaMultiOperation = null;

List<Operation> xOktaMultiOperationList = getOktaMultiOperationObject(operation);
if (xOktaMultiOperationList != null) {
Optional<Operation> operationFromXOktaMultiOperation = xOktaMultiOperationList.stream()
.filter(multiOper -> multiOper.getOperationId().equals(operationId)).findFirst();

if (operationFromXOktaMultiOperation.isPresent()) {
Operation xOktaMultiOperationTmp = operationFromXOktaMultiOperation.get();
xOktaMultiOperation = new Operation();

// VendorExtensions deep copy
Map<String, Object> vendorExtensions = new LinkedHashMap<>(operation.getVendorExtensions());
xOktaMultiOperation.setVendorExtensions(vendorExtensions);

// Tags deep copy
List<String> tags = new ArrayList<>(operation.getTags());
xOktaMultiOperation.setTags(tags);

xOktaMultiOperation.setSummary(operation.getSummary());
xOktaMultiOperation.setDescription(xOktaMultiOperationTmp.getDescription());
xOktaMultiOperation.setOperationId(xOktaMultiOperationTmp.getOperationId());

// Consumes deep copy
List<String> consumes = new ArrayList<>(operation.getConsumes());
xOktaMultiOperation.setConsumes(consumes);

// Produces deep copy
List<String> produces = new ArrayList<>(operation.getProduces());
xOktaMultiOperation.setProduces(produces);

// Parameters deep copy
List<Parameter> parameters = new ArrayList<>(operation.getParameters());
xOktaMultiOperation.setParameters(parameters);

// Responses deep copy
Map<String, Response> responses = new LinkedHashMap<>(operation.getResponses());
xOktaMultiOperation.setResponses(responses);

// Security deep copy
List<Map<String, List<String>>> security = new ArrayList<>(operation.getSecurity());
xOktaMultiOperation.setSecurity(security);

//Add params defined in x-okta-multi-operation
for(Parameter p: xOktaMultiOperationTmp.getParameters()) {
if (p instanceof BodyParameter && ((BodyParameter) p).getSchema() != null) {
xOktaMultiOperation.getParameters().add(p);
} else if (!(p instanceof BodyParameter)) {
xOktaMultiOperation.getParameters().add(p);
}
}
}
}
return xOktaMultiOperation;
}

private Map<String, String> createArgMap(ObjectNode n) {

Map<String, String> argMap = new LinkedHashMap<>();
Expand Down

0 comments on commit 94a97a1

Please sign in to comment.