def test_owncloud(self):

        # prepare service
        storage = Storage()

        redirect = "https://10.14.29.60/owncloud/index.php/apps/rds/oauth"
        owncloud = OAuth2Service(
            servicename="owncloud-local",
            implements=["metadata"],
            authorize_url=
            "https://10.14.29.60/owncloud/index.php/apps/oauth2/authorize?response_type=code&client_id={}&redirect_uri={}"
            .format(os.getenv("OWNCLOUD_OAUTH_CLIENT_ID"), redirect),
            refresh_url=
            "https://10.14.29.60/owncloud/index.php/apps/oauth2/api/v1/token",
            client_id=os.getenv("OWNCLOUD_OAUTH_CLIENT_ID"),
            client_secret=os.getenv("OWNCLOUD_OAUTH_CLIENT_SECRET"),
        )

        storage.addService(owncloud)

        # prepare user, which wants to make the whole oauth workflow
        user1 = User("user")
        token1 = Token(user1, owncloud, "user")

        storage.addUser(user1)
        storage.addTokenToUser(token1, user1)

        def get_access_token(user, token):
            nonlocal owncloud, storage

            self.driver.get(owncloud.authorize_url)

            if self.driver.current_url.startswith(
                    "https://10.14.29.60/owncloud/index.php/login"):
                # it redirects to login form
                field_username = self.driver.find_element_by_xpath(
                    '//*[@id="user"]')
                field_password = self.driver.find_element_by_xpath(
                    '//*[@id="password"]')
                field_username.clear()
                field_username.send_keys(user.username)

                field_password.clear()
                field_password.send_keys(token.access_token)

                old_url = self.driver.current_url
                url = self.driver.current_url

                field_password.send_keys(Keys.RETURN)

                retry = 0
                while old_url == url and retry < 5:
                    sleep(1)
                    retry += 1
                    url = self.driver.current_url
                    logger.info("url: {}".format(url))

                if retry >= 5:
                    raise Exception("url not redirect!")

            btn = self.driver.find_element_by_xpath(
                "/html/body/div[1]/div/span/form/button")
            old_url = self.driver.current_url
            url = self.driver.current_url

            btn.click()

            retry = 0
            while old_url == url and retry < 5:
                sleep(1)
                retry += 1
                url = self.driver.current_url
                logger.info("url: {}".format(url))

            if retry >= 5:
                raise Exception("url not redirect!")

            self.driver.delete_all_cookies()  # remove all cookies

            from urllib.parse import urlparse, parse_qs

            query = urlparse(url).query
            logger.info("query: {}".format(query))

            parse = parse_qs(query)
            logger.info("parse: {}".format(parse))

            code = parse["code"]

            data = {
                "grant_type": "authorization_code",
                "code": code,
                "redirect_uri": redirect,
            }

            req = requests.post(
                owncloud.refresh_url,
                data=data,
                auth=(owncloud.client_id, owncloud.client_secret),
                verify=False,
            ).json()
            oauthtoken = OAuth2Token(
                user,
                token.service,
                req["access_token"],
                req["refresh_token"],
                datetime.now() + timedelta(seconds=req["expires_in"]),
            )
            return oauthtoken

        oauthtoken1 = get_access_token(user1, token1)
        storage.addTokenToUser(oauthtoken1, user1, Force=True)

        ######## test a refresh token #######
        # prepare user, which wants to get a refresh token

        oauthuser2 = User("user_refresh")

        # check if there is already a file, which has an oauth2token to reuse it.
        oauthtoken2 = None
        filepath = "https://zivgitlab.uni-muenster.de/{}/{}/-/jobs/artifacts/{}/raw/{}/user_refresh.token?job_token={}&job={}".format(
            os.getenv("CI_PROJECT_NAMESPACE"),
            os.getenv("CI_PROJECT_NAME"),
            os.getenv("CI_COMMIT_REF_NAME"),
            os.getenv("FOLDER"),
            os.getenv("CI_JOB_TOKEN"),
            os.getenv("CI_JOB_NAME"),
        )
        try:
            headers = {"JOB-TOKEN": os.getenv("CI_JOB_TOKEN")}
            req = requests.get(filepath, headers=headers)
            if req.status_code != 200:
                raise Exception(
                    "Artifact not found, filepath: {filepath}, headers: {headers}"
                )

            try:
                oauthtoken2 = initialize_object_from_json(req.text)
            except Exception as e:
                raise Exception(f"{str(e)} + \n req: {req.text}")
        except Exception as e:
            logger.error(e)
            logger.warning(
                "No refresh token from previous test run was found, so we collect a new one. \nFilepath: {}"
                .format(filepath))
            # initialize like user1 with password
            token2 = Token(oauthuser2, owncloud, "user_refresh")

            # generate an oauthtoken like before and overwrite oauthtoken1
            oauthtoken2 = get_access_token(oauthuser2, token2)

        storage.addUser(oauthuser2)
        storage.addTokenToUser(oauthtoken2, oauthuser2)

        # try to refresh it now
        storage.refresh_service(owncloud)
        tokens = storage.getTokens(oauthuser2)
        checkToken = tokens[0]
        self.assertEqual(checkToken, oauthtoken2)

        # safe the current oauthtoken for reuse to test refresh token after a bigger period.
        with open("user_refresh.token", "w") as f:
            f.write(json.dumps(checkToken))
Example #2
0
class TestStorageService(unittest.TestCase):
    def setUp(self):
        self.empty_storage = Storage()

        self.user1 = User("Max Mustermann")
        self.user2 = User("Mimi Mimikri")
        self.user3 = User("Karla Kolumda")

        self.service1 = LoginService(servicename="MusterService",
                                     implements=["metadata"])
        self.service2 = LoginService(servicename="BetonService",
                                     implements=["metadata"])
        self.service3 = LoginService(servicename="FahrService",
                                     implements=["metadata"])

        # owncloud
        self.oauthservice1 = OAuth2Service.from_service(
            self.service1,
            f"{pact_host_fqdn}/owncloud/index.php/apps/oauth2/authorize",
            f"{pact_host_fqdn}/owncloud/index.php/apps/oauth2/api/v1/token",
            "ABC",
            "XYZ",
        )

        # zenodo
        self.oauthservice2 = OAuth2Service.from_service(
            self.service2,
            f"{pact_host_fqdn}/oauth/authorize",
            f"{pact_host_fqdn}/oauth/token",
            "DEF",
            "UVW",
        )

        self.oauthservice3 = OAuth2Service.from_service(
            self.service3,
            f"{pact_host_fqdn}/api/authorize",
            f"{pact_host_fqdn}/api/token",
            "GHI",
            "MNO",
        )

        self.token1 = Token(self.user1, self.service1, "ABC")
        self.token_like_token1 = Token(self.user1, self.service1, "DEF")
        self.token2 = Token(self.user2, self.service2, "XYZ")
        self.token3 = Token(self.user1, self.service3, "GHI")

        self.oauthtoken1 = OAuth2Token(self.user3, self.oauthservice1, "ABC",
                                       "X_ABC")
        self.oauthtoken_like_token1 = OAuth2Token(self.user3,
                                                  self.oauthservice1, "ABC",
                                                  "X_DEF")
        self.oauthtoken2 = OAuth2Token(self.user1, self.oauthservice2, "XYZ",
                                       "X_XYZ")
        self.oauthtoken3 = OAuth2Token(self.user3, self.oauthservice3, "GHI",
                                       "X_GHI")
        self.oauthtoken4 = OAuth2Token(self.user1, self.oauthservice1, "AMZ",
                                       "X_AMZ")

        self.services = [
            self.service1,
            self.service2,
            self.service3,
            self.oauthservice1,
            self.oauthservice2,
            self.oauthservice3,
        ]

        self.filled_storage_without_tokens = Storage()
        self.filled_storage_without_tokens.addUser(self.user1)
        self.filled_storage_without_tokens.addUser(self.user2)
        self.filled_storage_without_tokens.addUser(self.user3)

        self.filled_storage = Storage()
        # user1 is filled with mixed token and oauth2token
        self.filled_storage.addUser(self.user1)
        self.filled_storage.addService(self.oauthservice1)
        self.filled_storage.addService(self.oauthservice2)
        self.filled_storage.addService(self.oauthservice3)
        self.filled_storage.addTokenToUser(self.token1, self.user1)
        self.filled_storage.addTokenToUser(self.token3, self.user1)
        self.filled_storage.addTokenToUser(self.oauthtoken2, self.user1)

        # user2 is only filled with token
        self.filled_storage.addUser(self.user2)
        self.filled_storage.addTokenToUser(self.token2, self.user2)

        # user3 is only filled with oauth2token
        self.filled_storage.addUser(self.user3)
        self.filled_storage.addTokenToUser(self.oauthtoken1, self.user3)
        self.filled_storage.addTokenToUser(self.oauthtoken3, self.user3)

    def test_internal_find_services(self):
        self.assertEqual(
            self.empty_storage.internal_find_service(self.service1.servicename,
                                                     [self.service1]),
            0,
        )
        self.assertEqual(
            self.empty_storage.internal_find_service(
                self.service1.servicename, [self.service1, self.service2]),
            0,
        )
        self.assertEqual(
            self.empty_storage.internal_find_service(
                self.service1.servicename, [self.service2, self.service1]),
            1,
        )
        self.assertEqual(
            self.empty_storage.internal_find_service(
                self.service2.servicename, [self.service1, self.service2]),
            1,
        )

        self.assertEqual(
            self.empty_storage.internal_find_service(
                self.oauthservice1.servicename, [self.oauthservice1]),
            0,
        )
        self.assertEqual(
            self.empty_storage.internal_find_service(
                self.oauthservice1.servicename,
                [self.oauthservice1, self.oauthservice2]),
            0,
        )
        self.assertEqual(
            self.empty_storage.internal_find_service(
                self.oauthservice1.servicename,
                [self.oauthservice2, self.oauthservice1]),
            1,
        )
        self.assertEqual(
            self.empty_storage.internal_find_service(
                self.oauthservice2.servicename,
                [self.oauthservice1, self.oauthservice2]),
            1,
        )

        self.assertEqual(
            self.empty_storage.internal_find_service(
                self.oauthservice2.servicename,
                [self.service1, self.oauthservice2]),
            1,
        )

        with self.assertRaises(ValueError):
            self.empty_storage.internal_find_service(self.service1.servicename,
                                                     [self.service2])

        with self.assertRaises(ValueError):
            self.empty_storage.internal_find_service(
                self.service1.servicename, [self.service2, self.service3])

        with self.assertRaises(ValueError):
            self.empty_storage.internal_find_service(
                self.service1.servicename,
                [self.oauthservice2, self.oauthservice3])

        with self.assertRaises(ValueError):
            self.empty_storage.internal_find_service(
                self.oauthservice1.servicename, [self.service2, self.service3])

    def test_refresh_all_tokens(self):
        # works without any tokens
        self.assertFalse(
            self.filled_storage_without_tokens.refresh_service(self.service1))
        self.assertFalse(
            self.filled_storage_without_tokens.refresh_services(
                self.oauthservice1))
        self.assertFalse(
            self.filled_storage_without_tokens.refresh_services(self.services))

        # false because only a simple service
        self.assertFalse(self.filled_storage.refresh_service(self.service1),
                         msg=self.filled_storage)

        # works with a request to a provider
        expected_user = self.user3
        expected_service = self.oauthservice1
        expires_in = 3600

        # example taken from https://github.com/owncloud/oauth2
        json_expected = {
            "access_token":
            "1vtnuo1NkIsbndAjVnhl7y0wJha59JyaAiFIVQDvcBY2uvKmj5EPBEhss0pauzdQ",
            "token_type":
            "Bearer",
            "expires_in":
            expires_in,
            "refresh_token":
            "7y0wJuvKmj5E1vjVnhlPBEhha59JyaAiFIVQDvcBY2ss0pauzdQtnuo1NkIsbndA",
            "user_id":
            expected_user.username,
            "message_url":
            f"{pact_host_fqdn}/owncloud/index.php/apps/oauth2/authorization-successful",
        }

        from base64 import b64encode

        auth = f"{expected_service.client_id}:{expected_service.client_secret}"
        b64 = b64encode(auth.encode("utf-8")).decode("utf-8")

        pact.given(
            "Username can refresh given oauth2token to service",
            username=expected_service.client_id,
            service=expected_service,
        ).upon_receiving("An invalid response.").with_request(
            "POST", "/oauth/token").will_respond_with(404, body={})

        pact.given(
            "Username can refresh given oauth2token to service",
            username=expected_service.client_id,
            service=expected_service,
        ).upon_receiving("A valid refresh token response.").with_request(
            "POST",
            "/owncloud/index.php/apps/oauth2/api/v1/token",
            headers={
                "Authorization": f"Basic {b64}"
            },
        ).will_respond_with(200, body=json_expected)

        pact.given("Username can refresh given oauth2token to service"
                   ).upon_receiving("An invalid response for another service."
                                    ).with_request(
                                        "POST",
                                        "/api/token").will_respond_with(
                                            404, body={})

        result = None
        with pact:
            result = self.filled_storage.refresh_service(expected_service)

        self.assertTrue(result)

        # test for missing service
        self.assertFalse(
            self.filled_storage.refresh_services([
                BaseService(servicename="NotFoundService",
                            implements=["metadata"])
            ]))

    """ Currently not implemented and no idea how to solve.
    def test_valid_token(self):
        # Test, if token can be used in service
        self.assertTrue(self.service1.is_valid(self.token1, self.user1))
        self.assertTrue(self.oauthservice1.is_valid(
            self.oauthtoken1, self.user1))

        self.assertFalse(self.service1.is_valid(self.token2, self.user2))
        self.assertFalse(self.oauthservice1.is_valid(
            self.oauthtoken2, self.user2))

        # try to use the token to raise an exception
        with self.assertRaises(TokenNotValidError):
            self.oauthservice1.status(self.token1)"""

    def test_storage_refresh_save_mechanism(self):
        expires_in = 3600
        expected = OAuth2Token(self.user1, self.oauthservice1, "ABC", "XYZ")
        expected._expiration_date = datetime.fromtimestamp(time() + expires_in)

        # example taken from https://github.com/owncloud/oauth2
        json_expected = {
            "access_token":
            expected.access_token,
            "token_type":
            "Bearer",
            "expires_in":
            expires_in,
            "refresh_token":
            expected.refresh_token,
            "user_id":
            self.user1.username,
            "message_url":
            f"{pact_host_fqdn}/owncloud/index.php/apps/oauth2/authorization-successful",
        }

        pact.given(
            "Storage can refresh given oauth2token and saves it in storage"
        ).upon_receiving(
            "A valid refresh token response with a higher expiration date."
        ).with_request(
            "POST",
            "/owncloud/index.php/apps/oauth2/api/v1/token").will_respond_with(
                200, body=json_expected)

        result = None
        with pact:
            self.empty_storage.addService(self.oauthservice1)
            self.empty_storage.addUser(self.user1)
            self.empty_storage.addTokenToUser(self.oauthtoken4, self.user1)
            self.empty_storage.refresh_service(self.oauthservice1)
            result = self.empty_storage.getTokens()

            # there should only be one element in list, so it is ours
            self.assertEqual(len(result), 1)
            self.assertEqual(
                result[0],
                expected,
                msg=f"\nresult: {result[0]}\nexpected: {expected}")
            self.assertGreaterEqual(result[0].expiration_date,
                                    expected.expiration_date)

    def test_refresh_oauth2token(self):
        expires_in = 3600
        expected = self.oauthtoken1
        expected._access_token = "XYZABCTESTKORREKT"
        expected._refresh_token = "PLASDPJAPSJFSNIDFN"
        expected._exiration_date = datetime.fromtimestamp(time() + expires_in)

        # example taken from https://github.com/owncloud/oauth2
        json_expected = {
            "access_token":
            expected.access_token,
            "token_type":
            "Bearer",
            "expires_in":
            expires_in,
            "refresh_token":
            expected.refresh_token,
            "user_id":
            expected.user.username,
            "message_url":
            f"{pact_host_fqdn}/owncloud/index.php/apps/oauth2/authorization-successful",
        }

        pact.given(
            "Username can refresh given oauth2token",
            username=self.user1.username
        ).upon_receiving("A valid refresh token response.").with_request(
            "POST",
            "/owncloud/index.php/apps/oauth2/api/v1/token").will_respond_with(
                200, body=json_expected)

        result = None
        with pact:
            result = self.oauthtoken1.refresh()
            self.assertEqual(result,
                             expected,
                             msg=f"\nresult: {result}\nexpected: {expected}")
        """ obsolete
        # this needs to be here, because it counts the given interactions,
        # so if this is missing, you get an error, when you do the following assertion.
        pact.given(
            "Username can refresh given oauth2token", username=self.user1.username
        ).upon_receiving(
            "A valid refresh token response."
        ).with_request(
            "POST", "/owncloud/index.php/apps/oauth2/api/v1/token"
        ).will_respond_with(200, body=json_expected)

        with self.assertRaises(TokenNotValidError):
            with pact:
                self.oauthservice1.refresh(self.token1)
        """

        pact.given(
            "Username can't refresh given oauth2token",
            username=self.user1.username
        ).upon_receiving("A bad request was made.").with_request(
            "POST",
            "/owncloud/index.php/apps/oauth2/api/v1/token").will_respond_with(
                400, body=json_expected)

        with self.assertRaises(OAuth2UnsuccessfulResponseError):
            with pact:
                self.oauthservice1.refresh(self.oauthtoken1)

        self.make_bad_request_for_oauth_provider("invalid_request",
                                                 OAuth2InvalidRequestError)
        self.make_bad_request_for_oauth_provider("invalid_client",
                                                 OAuth2InvalidClientError)
        self.make_bad_request_for_oauth_provider("invalid_grant",
                                                 OAuth2InvalidGrantError)
        self.make_bad_request_for_oauth_provider("unauthorized_client",
                                                 OAuth2UnauthorizedClient)
        self.make_bad_request_for_oauth_provider("unsupported_grant_type",
                                                 OAuth2UnsupportedGrantType)

    def make_bad_request_for_oauth_provider(self, error_code, error):
        """
        For convenience.
        """
        json_expected = {"error": error_code}

        pact.given("Username made a bad request",
                   username=self.user1.username).upon_receiving(
                       f"A bad request with error {error_code} was made."
                   ).with_request(
                       "POST", "/owncloud/index.php/apps/oauth2/api/v1/token"
                   ).will_respond_with(400, body=json_expected)

        with self.assertRaises(error):
            with pact:
                self.oauthservice1.refresh(self.oauthtoken1)

    def test_service_list(self):
        expected = []
        # there should be no services
        self.assertEqual(self.empty_storage.getServices(), expected)

        # add an invalid service
        with self.assertRaises(ValueError):
            self.empty_storage.addService(123)

        with self.assertRaises(ValueError):
            self.empty_storage.addService(self.token1)

        with self.assertRaises(ValueError):
            self.empty_storage.addService(self.user1)

        with self.assertRaises(ValueError):
            self.empty_storage.getService(123)

        with self.assertRaises(ValueError):
            self.empty_storage.getService(self.token1)

        with self.assertRaises(ValueError):
            self.empty_storage.getService(self.user1)

        # add a service
        expected.append(self.service1)
        self.empty_storage.addService(self.service1)
        self.assertEqual(self.empty_storage.getServices(), expected)

        self.assertIsInstance(
            self.empty_storage.getService(self.service1.servicename),
            BaseService)
        self.assertEqual(
            self.empty_storage.getService(self.service1.servicename),
            self.service1,
            msg=self.empty_storage.getServices(),
        )
        self.assertEqual(self.empty_storage.getService(self.service1),
                         self.service1)

        from RDS.ServiceException import ServiceExistsAlreadyError

        with self.assertRaises(ServiceExistsAlreadyError):
            self.empty_storage.addService(self.service1)

        # add oauthservice for a already exists service, first with error, then with force
        with self.assertRaises(ServiceExistsAlreadyError):
            self.empty_storage.addService(self.oauthservice1)

        expected = [self.oauthservice1]
        self.empty_storage.addService(self.oauthservice1, Force=True)
        self.assertEqual(self.empty_storage.getServices(), expected)

        # add the next service
        expected.append(self.service2)
        self.empty_storage.addService(self.service2)
        self.assertEqual(self.empty_storage.getServices(), expected)

        # remove a not existing service
        self.assertFalse(self.empty_storage.removeService(self.service3))
        self.assertTrue(self.empty_storage.removeService(self.service2))
        self.assertTrue(
            self.empty_storage.removeService(self.service1),
            msg="{}".format(",".join(map(str, self.empty_storage._services))),
        )

        # should be empty now
        self.assertEqual(self.empty_storage.getServices(), [])