cubewise-code / tm1py-samples

Do more with TM1 with these ready to use TM1py samples.
https://github.com/cubewise-code/TM1py
MIT License
59 stars 45 forks source link

TM1py.Exceptions.Exceptions.TM1pyRestException: Text: Status Code: 401 #80

Closed EdouarL closed 3 years ago

EdouarL commented 3 years ago

Hi team,

I stuck on this. Could you help ?

I take a look on https://github.com/cubewise-code/tm1py/issues/124 but Cognos is not configured to use SSO. We log with LDAP. But there is also a custom java provider

Here my configuration TM1 and Cognos both on 2 different Windows environment

TM1s.cfg

SecurityPackageName=Kerberos
IntegratedSecurityMode=5
UseSSL=T
HTTPPortNumber=8160
ClientCAMURI=http://sub.domain.com:9300/bi/v1/disp

config.ini

[tm1srv01]
address=localhost
port=8160
user=myusername
password=mypasswordbase64encoded
ssl=True
decode_b64=True
gateway=http://sub.domain.com:9300/bi/v1/disp
namespace=Cognos Namespace

Here the code with python.exe

If gateway is not empty in config.ini I get

C:\temp\tm1py-samples-master>python
Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from TM1py.Services import TM1Service
>>> import configparser
>>> config = configparser.ConfigParser()
>>> config.read(r'.\config.ini')
['.\\config.ini']
>>> with TM1Service(**config['tm1srv01']) as tm1:
...     for chore in tm1.chores.get_all():
...         chore.reschedule(hours=-1)
...         tm1.chores.update(chore)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\userBob\AppData\Local\Programs\Python\Python38\lib\site-packages\tm1py-1.5.0-py3.8.egg\TM1py\Services\TM1Service.py", line 13, in __init__
  File "C:\Users\userBob\AppData\Local\Programs\Python\Python38\lib\site-packages\tm1py-1.5.0-py3.8.egg\TM1py\Services\RestService.py", line 165, in __init__
  File "C:\Users\userBob\AppData\Local\Programs\Python\Python38\lib\site-packages\tm1py-1.5.0-py3.8.egg\TM1py\Services\RestService.py", line 293, in _start_session
  File "C:\Users\userBob\AppData\Local\Programs\Python\Python38\lib\site-packages\tm1py-1.5.0-py3.8.egg\TM1py\Services\RestService.py", line 383, in _build_authorization_token
  File "C:\Users\userBob\AppData\Local\Programs\Python\Python38\lib\site-packages\tm1py-1.5.0-py3.8.egg\TM1py\Services\RestService.py", line 404, in _build_authorization_token_cam
RuntimeError: Failed to authenticate through CAM. HTTP response does not contain 'cam_passport' cookie

If gateway is empty in config.ini I get :

>>> from TM1py.Services import TM1Service
>>> import configparser
>>> config = configparser.ConfigParser()
>>> config.read(r'.\config.ini')
['.\\config.ini']
>>> with TM1Service(**config['tm1srv01']) as tm1:
...     for chore in tm1.chores.get_all():
...         chore.reschedule(hours=-1)
...         tm1.chores.update(chore)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\userBob\AppData\Local\Programs\Python\Python38\lib\site-packages\tm1py-1.5.0-py3.8.egg\TM1py\Services\TM1Service.py", line 13, in __init__
  File "C:\Users\userBob\AppData\Local\Programs\Python\Python38\lib\site-packages\tm1py-1.5.0-py3.8.egg\TM1py\Services\RestService.py", line 165, in __init__
  File "C:\Users\userBob\AppData\Local\Programs\Python\Python38\lib\site-packages\tm1py-1.5.0-py3.8.egg\TM1py\Services\RestService.py", line 302, in _start_session
  File "C:\Users\userBob\AppData\Local\Programs\Python\Python38\lib\site-packages\tm1py-1.5.0-py3.8.egg\TM1py\Services\RestService.py", line 76, in wrapper
  File "C:\Users\userBob\AppData\Local\Programs\Python\Python38\lib\site-packages\tm1py-1.5.0-py3.8.egg\TM1py\Services\RestService.py", line 372, in verify_response
TM1py.Exceptions.Exceptions.TM1pyRestException: Text:  Status Code: 401 Reason: Unauthorized Headers: {'Content-Type': 'text/plain', 'Content-Length': '0', 'Connection': 'keep-alive', 'Set-Cookie': 'TM1SessionId=SU3hJNbbeou9-bFb1y95mg; Path=/api/; HttpOnly; Secure', 'WWW-Authenticate': 'CAMPassport http://sub.domain.com:9300/bi/v1/disp, CAMNamespace'}

many thanks for any help you could give me ! Ed

EdouarL commented 3 years ago

Hi team,

I was stuck because for unknow reason cam_passport is not return. Here what I had

[
  {
    'domain': 'sub.domain.com', 
    'httpOnly': False, 
    'name': 'usersessionid', 
    'path': '/bi', 
    'secure': False, 
    'value': 'AggAAAD96qtfAAAAAAoAAACdxQF/nck+xD+LFAAAABpQ0lFc1cNrzvrPMIDIJGmxkYbfBwAAAFNIQS0yNTYgAAAAski3zI/7W5b/HBJN6qNgIPdg3vSUB2p+8BwSDFixh10='
  }, 
  {
    'domain': 'sub.domain.com', 
    'httpOnly': False, 
    'name': 'TS_PSWD', 
    'path': '/bi', 
    'secure': False, 
    'value': 'myPassword'
  }, 
  {
    'domain': 'sub.domain.com', 
    'httpOnly': False, 
    'name': 'TS_LOGIN', 
    'path': '/bi', 
    'secure': False, 
    'value': 'MyLogin'
  },
  {
    'domain': 'sub.domain.com', 
    'httpOnly': False, 
    'name': 'MRUStorage', 
    'path': '/bi', 
    'secure': False, 
    'value': '%7B%22xZGVmaWhhYjp1OnVpZD1wdmdlMDY4MjEsb3U9dXNlcnM_%22%3Atrue%7D'
  }, 
  {
    'domain': 'sub.domain.com', 
    'httpOnly': False, 
    'name': 'XSRF-TOKEN', 
    'path': '/bi', 
    'secure': False, 
    'value': '5j2px0HOQt8Blh8-kUIjGAnItphgCX1B'
  }
]

After looking deeper into [https://github.com/cubewise-code/tm1py/issues/124] I finally found it very usefull. It is a nice workaround for the issue I raise. Due to specific authentification (LDAP + CJP) a custom TM1web.html file had been developp to send back cam_passport. So here the code I have use to change _build_authorization_token_cam function from [https://github.com/cubewise-code/tm1py/blob/master/TM1py/Services/RestService.py]

import time
import re

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument("--window-size=1920x1080")
chrome_options.add_argument("--ignore-certificate-error")
chrome_options.add_argument("--ignore-ssl-errors")

driver = webdriver.Chrome(options = chrome_options, executable_path = r'C:\temp\chromedriver_win32\chromedriver.exe')

driver.get('http://sub.domain.com:9300/bi/?legacyLogin=%2fbi%2fv1%2fdisp%3f%26b_action%3dxts.run%26c_cmd%3d..%252ftm1%252fweb%252ftm1web.html%26server%3dTOTO%26ps%3dhttps%253a%252f%252fsub2.domain.com%252app%26pg%3dapplications.jsp%253fAdminHost%253dsub.domain.com%2526TM1Server%253dTOTO%26host%3dsub.domain.com%26encoding%3dUTF-8%26m%3dportal%252fbridge.xts%26c_env%3dportal%252fvariables_TM1.xml')
time.sleep(3)
driver.find_element_by_id("username").send_keys('MyLogin')
driver.find_element_by_id("password").send_keys('MyPassword')
driver.find_element_by_id("login").click()
time.sleep(3)
cam_passport = re.search('cam_passport=(.+?)$', driver.current_url).group(1)

driver.close()

return 'CAMPassport ' + cam_passport

Hope it help.

MariusWirtz commented 3 years ago

Hi @EdouarL,

thank you for posting your solution. Very much appreciated.

It would be great if you create a Pull Request so that we can incorporate your change into the RestService in TM1py.