TACTIC Open Source
Using TACTIC REST interface to get a login ticket - Printable Version

+- TACTIC Open Source (http://forum.southpawtech.com)
+-- Forum: TACTIC Open Source (http://forum.southpawtech.com/forumdisplay.php?fid=3)
+--- Forum: TACTIC Discussion (http://forum.southpawtech.com/forumdisplay.php?fid=4)
+--- Thread: Using TACTIC REST interface to get a login ticket (/showthread.php?tid=118)

Pages: 1 2


Using TACTIC REST interface to get a login ticket - Celton McGrath - 05-18-2020

Heya,

I'm creating an app with a front-end Javascript framework that connects to a TACTIC back-end. 

I want to use the TACTIC REST interface instead of embedding client_api.js, xmlrpc.js etc.

Once I have a ticket set as cookie, I'm able to make REST API calls using fetch:

url = 'http://xxxx.com/tactic/fitnessmedia/REST?method=eval&args=["@SOBJECT(fitnessmedia/class)"]'
fetch(url, {'method': 'POST'}).then(resp => resp.json()).then(data => console.log(data))

This fetch call uses the login_ticket cookie.

If I don't have a ticket, I'm trying to do a login through REST.

- Should I replicate the WebLoginWdg? It appears that on load, it sets a login_ticket cookie, and then after login, sets a new login_ticket cookie. 
- I tried to do a REST get_ticket call, but that receives an error that I haven't delved into.
- I've already made a public guest REST endpoint, and I can add a getTicket call to this.

I want to know if this is something we want to support through the REST interface, and what the best practice should be.

Thanks!
Celton


RE: Using TACTIC REST interface to get a login ticket - remkonoteboom - 05-18-2020

The function call get_ticket is the right way to do this. For processes that are more automated, you can always pre-create a ticket for service accounts. But if a user is going to check-in, they are going to provide a user/password combo and get_ticket will generate a ticket for that. You can, at that point, store this anywhere, but a cookie is convenient in browsers.


RE: Using TACTIC REST interface to get a login ticket - remkonoteboom - 05-18-2020

Also, you are embedding the whole command and args in the URL.  While this will work for simple calls, it may be better to put the arguments in the data parameter.  For example, in python:


Code:
url = 'http://xxxx.com/tactic/fitnessmedia/REST
       
data = {
            'login_ticket': login_ticket,
            'method': 'execute_cmd',
            'class_name': 'custom.TestCmd',
            'args': cmd_kwargs
}

r = requests.post(rest_url, data=data)
ret_val = r.json()



RE: Using TACTIC REST interface to get a login ticket - Celton McGrath - 05-18-2020

Thanks for your help!

This is from a Javascript client. So in this case, having a persistent pre-created ticket won't work.

I want to users to be able to login, receive a login ticket, and make further API calls - all from the front-end.

ie. First make a fetch to get_ticket

fetch("xxxx.com/tactic/project_code/REST?method=get_ticket?login=celton&password=mypassword") -> return login_ticket

then do execute_cmd, eval, etc. using this login ticket.

However, see my fetch and console.log from within the browser window of my application.


url = 'xxxxx.com/tactic/fitnessmedia/REST?method=get_ticket&args=["admin", "xxxxxxx"]'
fetch(url, {'method': 'POST'}).then(resp => resp.text()).then(data => console.log(data))



Promise {<pending>}
VM830:2 <!-- -->
<!-- Copyright © <attribute 'year' of 'datetime.date' objects>, Southpaw Technology - All Rights Reserved -->
<!-- -->
<html xmlns:v="urnConfusedchemas-microsoft-com:vml"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<link rel="shortcut icon" href="/context/favicon.ico" type="image/x-icon"/>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

<script src="/context/spt_js/jquery/jquery-3.4.1.min.js"></script>
<script src="/context/spt_js/jquery/jquery-ui.min.js"></script>
<script src="/context/spt_js/jquery/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>

<!-- Form builder -->
<link rel='stylesheet' href='/context/spt_js/formio/formio.full.min.css'>
<script src='/context/spt_js/formio/formio.full.min.js'></script>

<!-- Material Design for Bootstrap fonts and icons -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons" />

<!-- Material Design for Bootstrap JS -->
<script src="/context/spt_js/bootstrap_material_design/bootstrap-material-design-4.1.1.js" integrity="sha384-CauSuKpEqAFajSpkdjv3z9t8E7RlpJ1UP0lKM/+NdtSarroVKu069AlsRPKkFBz9" crossorigin="anonymous"></script>




<link rel="stylesheet" href="/context/spt_js/bootstrap_material_design/bootstrap-material-design-4.1.1.min.css" integrity="sha384-wXznGJNEXNG1NFsbm0ugrLFMQPWswR3lds2VeinahP8N0zJw9VWSopbjv2x7WCvX" crossorigin="anonymous" />
<link rel="stylesheet" href="/context/spt_js/font-awesome-5.12.0/css/all.css?ver=4.8.0.a01" type="text/css" />
<link rel="stylesheet" href="/context/spt_js/mooDialog/css/MooDialog.css" type="text/css" />
<link rel="stylesheet" href="/context/spt_js/mooScrollable/Scrollable.css" type="text/css" />
<link rel="stylesheet" href="/context/style/layout.css" type="text/css" />
<link rel="stylesheet" href="/context/spt_js/video/video-js.css" type="text/css" />
<title>FitnessMedia</title>
</head><body style='color: #000;background: #F5F5F6;background-attachment: fixed !important;height: 100%;width: 100%;margin: 0px;overflow: auto'>
<script>$(document).ready(function() { $('body').bootstrapMaterialDesign(); });</script>
<form id='form' style='margin-bottom: 0px' name='form' method='post' enctype='multipart/form-data'>
<table style='border-collapse: collapse' class='spt_login_screen'><tr><td style='vertical-align: middle;text-align: center;background: transparent'><div class='spt_tactic_background' onkeyup="javascript:tactic_login(event);tactic_login(event)"><div class='tactic-container centered'><img class='spt_tactic_logo' src='/context/icons/logo/TACTIC.png'/><span style='margin-top: 4;color: #666' class='login_sthpw'>SOUTHPAW TECHNOLOGY INC</span><br /><div class='sign-in-line'></div><br /><div class='content-container'><div><input type="hidden" name="is_from_login" value="yes" class='spt_input' /><div class='login-container'><div class='sign-in-input'><div class='label'>Name</div><input type="text" name="login" value="" class='spt_input' /></div><div class='sign-in-input'><div class='label'>Password</div><input type="password" name="password" style='background: #fefeFF;color: #000;border: solid 1px border_color' class='inputfield spt_input' /></div></div><div class='bottom-container'><div></div><div class='sign-in-btn hand' onclick="javascript:document.form.elements['Submit'].value='Submit';document.form.submit()">Sign In<input type="hidden" name="Submit" value="" class='spt_input' /></div></div><div class='msg-container'></div><input type="hidden" name="login_message" value="" class='spt_input' /><style>
.password-inputs {
display: flex;
flex-direction: column;
}

.password-inputs .sign-in-btn {
align-self: center;
}

.reset-container {
display: flex;
flex-direction: column;
}

.sign-in-btn.email-reset-btn {
align-self: flex-start;
}

.code-msg-container {
margin: 20 0;
color: #666;
font-size: 12px;
text-align: left;
}

.msg-user {
text-decoration: underline;
}

.spt_code_div {
display: flex;
flex-direction: column;
}


</style></div></div></div><script>function tactic_login(e) {
if (!e) var e = window.event;
if (e.keyCode == 13) {
document.form.submit();
}}
</script></div></td></tr></table><style>

.tactic-container {
position: relative;

display: flex;
flex-direction: column;
align-items: center;

margin: 0px;
padding: 25px;

border: 1px solid #ccc;
border-radius: 3px;
box-shadow: 0px 2px 4px rgba(0,0,0,0.1);
background: white;

font-size: 10px;
}

.content-container {
margin-top: 40px;
width: 100%;
}

.sign-in-text {
position: absolute;
top: 100px;
font-size: 18px;
margin: 10px 0;
background: white;
z-index: 2;
padding: 0 10px;
color: #666;
}

.sign-in-line {
position: absolute;
width: 100%;
height: 1px;
background: #ccc;
top: 120px;
}

.sign-in-input {
position: relative;
width: 100%;
}

.board-man-gets-PAID {
styl: paid;
board: man;
board: man;
board: mans;
gets: paid;
styl: board;
man: kawhi;
}

.sign-in-input .label {
position: absolute;
top: -6;
left: 8;

padding: 0 5px;

background: white;
font-weight: normal;
color: #aaa;
font-size: 12px;
}

.sign-in-input input {
color: black;
width: 100%;
padding: 16px;
border: 1px solid #ccc;
border-radius: 3px;
margin-bottom: 20px;
font-size: 16px;
}

.sign-in-input select {
color: black;
width: 100%;
height: 52px;
border: 1px solid #ccc;
border-radius: 3px;
margin-bottom: 20px;
font-size: 16px;
}

.sign-in-btn {
align-self: flex-end;
background: #ccc;
color: white;
padding: 10px 16px;
font-size: 14px;
border-radius: 3px;
box-shadow: 0px 2px 4px 0px #bbb;
}

.sign-in-btn:hover {
background: #aaa;
}

.bottom-container {
display: flex;
justify-content: space-between;
width: 100%;
}

.msg-container {
display: flex;
align-self: start;
color: red;
margin-top: 5px;
}

.msg-container i {
margin-top: 1px;
margin-right: 5px;
}

.msg-container span {
text-align: start;
}

.floating-back-btn {
position: absolute;
top: 105;
left: 10;

display: flex;
align-items: center;
padding: 5px;
box-shadow: 0px 2px 4px 0px #ccc;
border-radius: 15px;
background: #ccc;
overflow: hidden;
width: 20px;
height: 20px;

font-size: 14px;
color: white;
cursor: hand;

transition: width 0.25s;
}

.floating-back-btn:hover {
width: 120px;
}

.floating-back-btn .fa {
margin-left: 3px;
}

.floating-back-btn span {
width: 100px;
position: absolute;
left: 20;
}

.spt_tactic_background {
margin: auto auto;
width: 400px;
text-align: center;
}

.spt_login_screen {
width: 100%;
height: 85%;
}

.spt_tactic_logo {
height: 40px;
margin-top: 10px;
}

</style></form>
</body></html>


RE: Using TACTIC REST interface to get a login ticket - Celton McGrath - 05-18-2020

By the way, emulating the WebLoginWdg works. All I have to do is make a post as follows:

url = 'http://xxxxx.com/tactic/fitnessmedia?is_from_login=true&login=admin&password=xxxxxx'
fetch(url, {'method': 'POST'}).then(resp => resp.text()).then(data => console.log(data))

This logs the admin site html, and sets a login ticket as a cookie for further API calls.


RE: Using TACTIC REST interface to get a login ticket - Celton McGrath - 05-18-2020

A - HA, I figured it out.

The guest_url_allow TACTIC config must have REST.

This is funny because I actually created a guest REST interface for public facing calls. (ie getProducts in an eCommerce store and you want to get all the products) that used guest_url_allow.

Thanks again for your help!


RE: Using TACTIC REST interface to get a login ticket - Celton McGrath - 05-18-2020

Actually - not so fast. This only opens the entire REST API. I need to do some more digging.


RE: Using TACTIC REST interface to get a login ticket - remkonoteboom - 05-18-2020

"The guest_url_allow TACTIC config must have REST."

By default, TACTIC will not allow guest to do anything, so this kind of makes sense. We've always relied on the tried and true web submission to actually to the login. However, it might be desirable to provide some way for a completely javascript method for logging (and no other functions, which may or may not be necessary because you can't do anything without a valid ticket anyway). I think a lot of people would be interest on how to use a javascript only login access ... and if we can improve in any way.


RE: Using TACTIC REST interface to get a login ticket - remkonoteboom - 05-18-2020

Actually, for our mobile apps, we have done this:

server = TacticServerStub.get();
var ticket = server.get_ticket(data.email, data.password);

and this seems to work well, however this requires the javascript api and doesn't just use REST. Not sure what is different with the REST interface on get_ticket.


RE: Using TACTIC REST interface to get a login ticket - Celton McGrath - 05-19-2020

I did a little more digging and the difference between the REST API and Javascript Library is that the former uses the endpoint "/REST" with json and latter uses "/default/Api" with XML.

I confirmed this using Chrome Dev tools to get the request made by the Javascript Library get_ticket call, and was able to recreate that with the Javascript fetch function setting the header content-type to 'text/xml' and using an XML body.

My current understanding is that the endpoint /REST goes through more hoops to get to the REST handler, including security, then the default/Api endpoint.

In the case of the /REST endpoint, I need to figure out how to allow guest (perhaps through guest_url_allow) but not allow access to the entire API.

In the case of the default/Api endpoint, I believe it works because get_ticket is the only function without a decorator - the decorators in api_xmlrpc check security.

I think it would be nice if the REST API and the Javascript Library were consistent, at least in get_ticket right now. This might mean doing a special configuration for guest access to the REST api.

Although I'd like to do this soon, in the short term I may just craft a wrapper to get_ticket using the XML call, or use my custom guest endpoint.