diff --git a/okta/models/access_policy_constraint.py b/okta/models/access_policy_constraint.py index ccbd38c6..62b4c537 100644 --- a/okta/models/access_policy_constraint.py +++ b/okta/models/access_policy_constraint.py @@ -99,24 +99,24 @@ def methods_validate_enum(cls, value): for i in value: if i not in set( [ - "PASSWORD", - "SECURITY_QUESTION", - "SMS", - "VOICE", - "EMAIL", - "PUSH", - "SIGNED_NONCE", - "OTP", - "TOTP", - "WEBAUTHN", - "DUO", - "IDP", - "CERT", + "password", + "security_question", + "sms", + "voice", + "email", + "push", + "signed_nonce", + "otp", + "totp", + "webauthn", + "duo", + "idp", + "cert", ] ): raise ValueError( - "each list item must be one of ('PASSWORD', 'SECURITY_QUESTION', 'SMS', 'VOICE', 'EMAIL', 'PUSH', " - "'SIGNED_NONCE', 'OTP', 'TOTP', 'WEBAUTHN', 'DUO', 'IDP', 'CERT')" + "each list item must be one of ('password', 'security_question', 'sms', 'voice', 'email', 'push', " + "'signed_nonce', 'otp', 'totp', 'webauthn', 'duo', 'idp', 'cert')" ) return value @@ -129,18 +129,18 @@ def types_validate_enum(cls, value): for i in value: if i not in set( [ - "SECURITY_KEY", - "PHONE", - "EMAIL", - "PASSWORD", - "SECURITY_QUESTION", - "APP", - "FEDERATED", + "security_key", + "phone", + "email", + "password", + "security_question", + "app", + "federated", ] ): raise ValueError( - "each list item must be one of ('SECURITY_KEY', 'PHONE', 'EMAIL', 'PASSWORD', 'SECURITY_QUESTION', " - "'APP', 'FEDERATED')" + "each list item must be one of ('security_key', 'phone', 'email', 'password', 'security_question', " + "'app', 'federated')" ) return value diff --git a/okta/models/knowledge_constraint.py b/okta/models/knowledge_constraint.py index c14c8e76..d3cfbb6e 100644 --- a/okta/models/knowledge_constraint.py +++ b/okta/models/knowledge_constraint.py @@ -96,24 +96,24 @@ def methods_validate_enum(cls, value): for i in value: if i not in set( [ - "PASSWORD", - "SECURITY_QUESTION", - "SMS", - "VOICE", - "EMAIL", - "PUSH", - "SIGNED_NONCE", - "OTP", - "TOTP", - "WEBAUTHN", - "DUO", - "IDP", - "CERT", + "password", + "security_question", + "sms", + "voice", + "email", + "push", + "signed_nonce", + "otp", + "totp", + "webauthn", + "duo", + "idp", + "cert", ] ): raise ValueError( - "each list item must be one of ('PASSWORD', 'SECURITY_QUESTION', 'SMS', 'VOICE', 'EMAIL', 'PUSH', " - "'SIGNED_NONCE', 'OTP', 'TOTP', 'WEBAUTHN', 'DUO', 'IDP', 'CERT')" + "each list item must be one of ('password', 'security_question', 'sms', 'voice', 'email', 'push', " + "'signed_nonce', 'otp', 'totp', 'webauthn', 'duo', 'idp', 'cert')" ) return value @@ -126,18 +126,18 @@ def types_validate_enum(cls, value): for i in value: if i not in set( [ - "SECURITY_KEY", - "PHONE", - "EMAIL", - "PASSWORD", - "SECURITY_QUESTION", - "APP", - "FEDERATED", + "security_key", + "phone", + "email", + "password", + "security_question", + "app", + "federated", ] ): raise ValueError( - "each list item must be one of ('SECURITY_KEY', 'PHONE', 'EMAIL', 'PASSWORD', 'SECURITY_QUESTION', " - "'APP', 'FEDERATED')" + "each list item must be one of ('security_key', 'phone', 'email', 'password', 'security_question', " + "'app', 'federated')" ) return value diff --git a/okta/models/possession_constraint.py b/okta/models/possession_constraint.py index b8ca7eec..3c611bab 100644 --- a/okta/models/possession_constraint.py +++ b/okta/models/possession_constraint.py @@ -137,24 +137,24 @@ def methods_validate_enum(cls, value): for i in value: if i not in set( [ - "PASSWORD", - "SECURITY_QUESTION", - "SMS", - "VOICE", - "EMAIL", - "PUSH", - "SIGNED_NONCE", - "OTP", - "TOTP", - "WEBAUTHN", - "DUO", - "IDP", - "CERT", + "password", + "security_question", + "sms", + "voice", + "email", + "push", + "signed_nonce", + "otp", + "totp", + "webauthn", + "duo", + "idp", + "cert", ] ): raise ValueError( - "each list item must be one of ('PASSWORD', 'SECURITY_QUESTION', 'SMS', 'VOICE', 'EMAIL', 'PUSH', " - "'SIGNED_NONCE', 'OTP', 'TOTP', 'WEBAUTHN', 'DUO', 'IDP', 'CERT')" + "each list item must be one of ('password', 'security_question', 'sms', 'voice', 'email', 'push', " + "'signed_nonce', 'otp', 'totp', 'webauthn', 'duo', 'idp', 'cert')" ) return value @@ -167,18 +167,18 @@ def types_validate_enum(cls, value): for i in value: if i not in set( [ - "SECURITY_KEY", - "PHONE", - "EMAIL", - "PASSWORD", - "SECURITY_QUESTION", - "APP", - "FEDERATED", + "security_key", + "phone", + "email", + "password", + "security_question", + "app", + "federated", ] ): raise ValueError( - "each list item must be one of ('SECURITY_KEY', 'PHONE', 'EMAIL', 'PASSWORD', 'SECURITY_QUESTION', " - "'APP', 'FEDERATED')" + "each list item must be one of ('security_key', 'phone', 'email', 'password', 'security_question', " + "'app', 'federated')" ) return value diff --git a/openapi/api.yaml b/openapi/api.yaml index d07ef21e..050b0fbb 100644 --- a/openapi/api.yaml +++ b/openapi/api.yaml @@ -57789,19 +57789,19 @@ components: items: type: string enum: - - PASSWORD - - SECURITY_QUESTION - - SMS - - VOICE - - EMAIL - - PUSH - - SIGNED_NONCE - - OTP - - TOTP - - WEBAUTHN - - DUO - - IDP - - CERT + - password + - security_question + - sms + - voice + - email + - push + - signed_nonce + - otp + - totp + - webauthn + - duo + - idp + - cert type: array reauthenticateIn: description: The duration after which the user must re-authenticate regardless of user activity. This re-authentication interval overrides the Verification Method object's `reauthenticateIn` interval. The supported values use ISO 8601 period format for recurring time intervals (for example, `PT1H`). @@ -57814,13 +57814,13 @@ components: items: type: string enum: - - SECURITY_KEY - - PHONE - - EMAIL - - PASSWORD - - SECURITY_QUESTION - - APP - - FEDERATED + - security_key + - phone + - email + - password + - security_question + - app + - federated type: array AccessPolicyConstraints: description: Specifies constraints for the authenticator. Constraints are logically evaluated such that only one constraint object needs to be satisfied. But, within a constraint object, each constraint property must be satisfied. diff --git a/tests/test_access_policy_constraint_enums.py b/tests/test_access_policy_constraint_enums.py new file mode 100644 index 00000000..ae5ae91b --- /dev/null +++ b/tests/test_access_policy_constraint_enums.py @@ -0,0 +1,126 @@ +# The Okta software accompanied by this notice is provided pursuant to the following terms: +# Copyright © 2025-Present, Okta, 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. +# coding: utf-8 + +""" +Unit tests verifying that `methods` and `types` enum validators on +`AccessPolicyConstraint`, `KnowledgeConstraint`, and `PossessionConstraint` +accept lowercase values (matching the live Okta API response casing) and +reject UPPERCASE / invalid values. + +Related: Alignment of OAS3 enum casing with the API. +""" + +import pytest +from pydantic import ValidationError + +from okta.models.access_policy_constraint import AccessPolicyConstraint +from okta.models.knowledge_constraint import KnowledgeConstraint +from okta.models.possession_constraint import PossessionConstraint + + +VALID_METHODS = [ + "password", "security_question", "sms", "voice", "email", + "push", "signed_nonce", "otp", "totp", "webauthn", "duo", "idp", "cert", +] +VALID_TYPES = [ + "security_key", "phone", "email", "password", + "security_question", "app", "federated", +] + + +class TestAccessPolicyConstraintEnums: + """Validator tests for the base `AccessPolicyConstraint` model.""" + + def test_lowercase_methods_accepted(self): + obj = AccessPolicyConstraint(methods=["password", "push", "webauthn"]) + assert obj.methods == ["password", "push", "webauthn"] + + def test_lowercase_types_accepted(self): + obj = AccessPolicyConstraint(types=["password", "security_key", "phone"]) + assert obj.types == ["password", "security_key", "phone"] + + def test_all_valid_methods_accepted(self): + obj = AccessPolicyConstraint(methods=list(VALID_METHODS)) + assert obj.methods == VALID_METHODS + + def test_all_valid_types_accepted(self): + obj = AccessPolicyConstraint(types=list(VALID_TYPES)) + assert obj.types == VALID_TYPES + + def test_uppercase_methods_rejected(self): + with pytest.raises(ValidationError): + AccessPolicyConstraint(methods=["PASSWORD"]) + + def test_uppercase_types_rejected(self): + with pytest.raises(ValidationError): + AccessPolicyConstraint(types=["PASSWORD"]) + + def test_unknown_method_rejected(self): + with pytest.raises(ValidationError): + AccessPolicyConstraint(methods=["not_a_real_method"]) + + def test_unknown_type_rejected(self): + with pytest.raises(ValidationError): + AccessPolicyConstraint(types=["not_a_real_type"]) + + def test_from_dict_lowercase_payload(self): + """Simulates a real API response payload.""" + payload = { + "methods": ["password", "push"], + "types": ["password"], + "required": True, + "reauthenticateIn": "PT2H", + } + obj = AccessPolicyConstraint.from_dict(payload) + assert obj.methods == ["password", "push"] + assert obj.types == ["password"] + assert obj.required is True + assert obj.reauthenticate_in == "PT2H" + + +class TestKnowledgeConstraintEnums: + """Validator tests for `KnowledgeConstraint` (inherits the same enums).""" + + def test_lowercase_methods_accepted(self): + obj = KnowledgeConstraint(methods=["password"]) + assert obj.methods == ["password"] + + def test_lowercase_types_accepted(self): + obj = KnowledgeConstraint(types=["password", "security_question"]) + assert obj.types == ["password", "security_question"] + + def test_uppercase_methods_rejected(self): + with pytest.raises(ValidationError): + KnowledgeConstraint(methods=["PASSWORD"]) + + def test_uppercase_types_rejected(self): + with pytest.raises(ValidationError): + KnowledgeConstraint(types=["PASSWORD"]) + + +class TestPossessionConstraintEnums: + """Validator tests for `PossessionConstraint` (inherits the same enums).""" + + def test_lowercase_methods_accepted(self): + obj = PossessionConstraint(methods=["push", "signed_nonce", "webauthn"]) + assert obj.methods == ["push", "signed_nonce", "webauthn"] + + def test_lowercase_types_accepted(self): + obj = PossessionConstraint(types=["security_key", "phone"]) + assert obj.types == ["security_key", "phone"] + + def test_uppercase_methods_rejected(self): + with pytest.raises(ValidationError): + PossessionConstraint(methods=["PUSH"]) + + def test_uppercase_types_rejected(self): + with pytest.raises(ValidationError): + PossessionConstraint(types=["SECURITY_KEY"]) +